diff --git a/.gitignore b/.gitignore index 0be35df..ab69f14 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,5 @@ local.properties .settings/ /svg + +/release diff --git a/app/build.gradle b/app/build.gradle index 2c9ee75..c255b96 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -8,8 +8,8 @@ android { applicationId "io.github.deton.androidtutcode" minSdkVersion 21 targetSdk 34 - versionCode 7 - versionName "1.4.0" + versionCode 8 + versionName "1.5.0" } buildTypes { @@ -32,15 +32,16 @@ android { targetCompatibility JavaVersion.VERSION_17 } kotlinOptions { - jvmTarget = "17" + jvmTarget = '17' } } dependencies { implementation files('libs/jdbm-1.0.jar') /* implementation 'androidx.legacy:legacy-support-v4:1.0.0-beta01' */ - implementation 'androidx.appcompat:appcompat:1.6.1' + implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'androidx.preference:preference-ktx:1.2.1' + implementation 'androidx.core:core-ktx:1.13.1' } repositories { mavenCentral() diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a8d312..50acfa2 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -25,7 +25,12 @@ + android:label="@string/label_pref_activity" + android:exported="true"> + + + + { when (flick) { - FLICK_STATE_NONE -> mService.commitTextSKK(",", 0) - FLICK_STATE_LEFT -> mService.commitTextSKK(".", 0) - FLICK_STATE_UP -> mService.commitTextSKK("-", 0) - FLICK_STATE_RIGHT -> mService.commitTextSKK(":", 0) + FLICK_STATE_NONE -> mService.commitTextSKK(",") + FLICK_STATE_LEFT -> mService.commitTextSKK(".") + FLICK_STATE_UP -> mService.commitTextSKK("-") + FLICK_STATE_RIGHT -> mService.commitTextSKK(":") } return } @@ -721,7 +721,7 @@ class FlickJPKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener val cm = mService.getSystemService(CLIPBOARD_SERVICE) as ClipboardManager val cs = cm.text val clip = cs?.toString() ?: "" - mService.commitTextSKK(clip, 1) + mService.commitTextSKK(clip) } } } diff --git a/app/src/main/java/jp/deadend/noname/skk/Keyboard.kt b/app/src/main/java/jp/deadend/noname/skk/Keyboard.kt index df8f0c4..82f22a3 100644 --- a/app/src/main/java/jp/deadend/noname/skk/Keyboard.kt +++ b/app/src/main/java/jp/deadend/noname/skk/Keyboard.kt @@ -157,11 +157,10 @@ open class Keyboard { a.recycle() } - fun onPressed() { pressed = !pressed } - - fun onReleased(inside: Boolean) { - pressed = !pressed - if (sticky && inside) { on = !on } + fun press() { pressed = true } + fun release() { + pressed = false + if (sticky) { on != on } } fun isInside(x: Int, y: Int): Boolean { @@ -497,6 +496,13 @@ open class Keyboard { a.recycle() } + fun resetKeys() { + for (key in keys) { + key.on = false + key.pressed = false + } + } + companion object { const val TAG = "Keyboard" diff --git a/app/src/main/java/jp/deadend/noname/skk/KeyboardView.kt b/app/src/main/java/jp/deadend/noname/skk/KeyboardView.kt index 9fb9eff..a8f2747 100644 --- a/app/src/main/java/jp/deadend/noname/skk/KeyboardView.kt +++ b/app/src/main/java/jp/deadend/noname/skk/KeyboardView.kt @@ -4,11 +4,11 @@ import android.content.Context import android.graphics.* import android.graphics.Paint.Align import android.graphics.drawable.Drawable +import android.os.Build import android.os.Handler import android.os.Looper import android.os.Message import android.util.AttributeSet -import android.util.TypedValue import android.view.* import android.view.GestureDetector.SimpleOnGestureListener import android.widget.PopupWindow @@ -35,15 +35,12 @@ open class KeyboardView @JvmOverloads constructor( } private var mKeyboard = Keyboard(context, R.xml.keys_null) - private var mCurrentKeyIndex = NOT_A_KEY + private var mCurrentPreviewKeyIndex = NOT_A_KEY private var mLabelTextSize = 0 private var mKeyTextSize = 0 private var mKeyTextColor = 0 - private var mShadowRadius = 0f - private var mShadowColor = 0 private var mPreviewText: TextView? = null private val mPreviewPopup = PopupWindow(context) - private var mPreviewTextSizeLarge = 0 private var mPreviewOffset = 0 private var mPreviewHeight = 0 private val mCoordinates = IntArray(2) // working variable @@ -76,7 +73,6 @@ open class KeyboardView @JvmOverloads constructor( private var mDownKey = NOT_A_KEY private var mLastKeyTime: Long = 0 private var mCurrentKeyTime: Long = 0 - private var mGestureDetector: GestureDetector? = null private var mRepeatKeyIndex = NOT_A_KEY private var mPopupLayout = 0 private var mAbortKey = false @@ -84,8 +80,8 @@ open class KeyboardView @JvmOverloads constructor( private val mClipRegion = Rect(0, 0, 0, 0) private var mPossiblePoly = false private val mSwipeTracker = SwipeTracker() - private val mSwipeThreshold: Int - private val mDisambiguateSwipe: Boolean + private val mSwipeThreshold = (500 * resources.displayMetrics.density).toInt() + private val mDisambiguateSwipe = resources.getBoolean(R.bool.config_swipeDisambiguation) // Variables for dealing with multiple pointers private var mOldPointerCount = 1 @@ -109,17 +105,69 @@ open class KeyboardView @JvmOverloads constructor( private val mHandler: Handler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { - MSG_SHOW_PREVIEW -> showKey(msg.arg1) - MSG_REMOVE_PREVIEW -> mPreviewText?.visibility = INVISIBLE - MSG_REPEAT -> if (repeatKey()) { - val repeat = Message.obtain(this, MSG_REPEAT) - sendMessageDelayed(repeat, REPEAT_INTERVAL.toLong()) + MSG_SHOW_PREVIEW -> showKeyInPreview(msg.arg1) + MSG_REMOVE_PREVIEW -> { + mPreviewText?.visibility = INVISIBLE + mPreviewPopup.dismiss() + } + MSG_REPEAT -> { + detectAndSendKey(mCurrentKey, mLastTapTime) + sendMessageDelayed(Message.obtain(this, MSG_REPEAT), REPEAT_INTERVAL.toLong()) } MSG_LONGPRESS -> openPopupIfRequired() } } } + private val mGestureDetector = GestureDetector(context, object : SimpleOnGestureListener() { + override fun onFling( + me1: MotionEvent?, me2: MotionEvent, velocityX: Float, velocityY: Float + ): Boolean { + if (mPossiblePoly || me1 == null) return false + val absX = abs(velocityX) + val absY = abs(velocityY) + val deltaX = me2.x - me1.x + val deltaY = me2.y - me1.y + val travelX = width / 2 // Half the keyboard width + val travelY = height / 2 // Half the keyboard height + mSwipeTracker.computeCurrentVelocity(1000) + val endingVelocityX = mSwipeTracker.xVelocity + val endingVelocityY = mSwipeTracker.yVelocity + var sendDownKey = false + if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { + if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { + sendDownKey = true + } else { + swipeRight() + return true + } + } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { + if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { + sendDownKey = true + } else { + swipeLeft() + return true + } + } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { + if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { + sendDownKey = true + } else { + swipeUp() + return true + } + } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { + if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { + sendDownKey = true + } else { + swipeDown() + return true + } + } + if (sendDownKey) { detectAndSendKey(mDownKey, me1.eventTime) } + return false + } + }) + init { // val a = context.obtainStyledAttributes( // attrs, R.styleable.KeyboardView, defStyleAttr, defStyleRes @@ -148,10 +196,6 @@ open class KeyboardView @JvmOverloads constructor( mLabelTextSize = a.getDimensionPixelSize(attr, 14) R.styleable.KeyboardView_popupLayout -> mPopupLayout = a.getResourceId(attr, 0) - R.styleable.KeyboardView_shadowColor -> - mShadowColor = a.getColor(attr, 0) - R.styleable.KeyboardView_shadowRadius -> - mShadowRadius = a.getFloat(attr, 0f) } } a.recycle() @@ -160,15 +204,21 @@ open class KeyboardView @JvmOverloads constructor( mPreviewText = (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater) .inflate(previewLayout, null) as TextView - mPreviewTextSizeLarge = mPreviewText?.textSize?.toInt() ?: 0 mPreviewPopup.contentView = mPreviewText mPreviewPopup.setBackgroundDrawable(null) + mPreviewPopup.isClippingEnabled = false + mPreviewPopup.animationStyle = 0 + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + mPreviewPopup.enterTransition = null + mPreviewPopup.exitTransition = null + } } else { isPreviewEnabled = false } mPreviewPopup.isTouchable = false mPopupKeyboard.setBackgroundDrawable(null) + mPopupKeyboard.isClippingEnabled = false mPopupParent = this mPaint.isAntiAlias = true @@ -178,76 +228,17 @@ open class KeyboardView @JvmOverloads constructor( mKeyBackground?.getPadding(mPadding) - mSwipeThreshold = (500 * resources.displayMetrics.density).toInt() - mDisambiguateSwipe = resources.getBoolean(R.bool.config_swipeDisambiguation) + mGestureDetector.setIsLongpressEnabled(false) resetMultiTap() } - override fun onAttachedToWindow() { - super.onAttachedToWindow() - initGestureDetector() - } - - private fun initGestureDetector() { - if (mGestureDetector == null) { - mGestureDetector = GestureDetector(context, object : SimpleOnGestureListener() { - override fun onFling( - me1: MotionEvent?, me2: MotionEvent, velocityX: Float, velocityY: Float - ): Boolean { - if (mPossiblePoly || me1 == null) return false - val absX = abs(velocityX) - val absY = abs(velocityY) - val deltaX = me2.x - me1.x - val deltaY = me2.y - me1.y - val travelX = width / 2 // Half the keyboard width - val travelY = height / 2 // Half the keyboard height - mSwipeTracker.computeCurrentVelocity(1000) - val endingVelocityX = mSwipeTracker.xVelocity - val endingVelocityY = mSwipeTracker.yVelocity - var sendDownKey = false - if (velocityX > mSwipeThreshold && absY < absX && deltaX > travelX) { - sendDownKey = if (mDisambiguateSwipe && endingVelocityX < velocityX / 4) { - true - } else { - swipeRight() - return true - } - } else if (velocityX < -mSwipeThreshold && absY < absX && deltaX < -travelX) { - sendDownKey = if (mDisambiguateSwipe && endingVelocityX > velocityX / 4) { - true - } else { - swipeLeft() - return true - } - } else if (velocityY < -mSwipeThreshold && absX < absY && deltaY < -travelY) { - sendDownKey = if (mDisambiguateSwipe && endingVelocityY > velocityY / 4) { - true - } else { - swipeUp() - return true - } - } else if (velocityY > mSwipeThreshold && absX < absY / 2 && deltaY > travelY) { - sendDownKey = if (mDisambiguateSwipe && endingVelocityY < velocityY / 4) { - true - } else { - swipeDown() - return true - } - } - if (sendDownKey) { detectAndSendKey(mDownKey, mStartX, mStartY, me1.eventTime) } - return false - } - }) - mGestureDetector?.setIsLongpressEnabled(false) - } - } - var keyboard: Keyboard get() = mKeyboard set(keyboard) { removeMessages() mKeyboard = keyboard + mKeyboard.resetKeys() requestLayout() // Hint to reallocate the buffer if the size changed mKeyboardChanged = true @@ -264,11 +255,11 @@ open class KeyboardView @JvmOverloads constructor( if (mKeyboard.setShifted(value)) { invalidateAllKeys() } } - fun setPopupParent(v: View) { + private fun setPopupParent(v: View) { mPopupParent = v } - fun setPopupOffset(x: Int, y: Int) { + private fun setPopupOffset(x: Int, y: Int) { mMiniKeyboardOffsetX = x mMiniKeyboardOffsetY = y if (mPreviewPopup.isShowing) { mPreviewPopup.dismiss() } @@ -289,10 +280,9 @@ open class KeyboardView @JvmOverloads constructor( } public override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { - var width = mKeyboard.width + paddingLeft + paddingRight - if (MeasureSpec.getSize(widthMeasureSpec) < width + 10) { - width = MeasureSpec.getSize(widthMeasureSpec) - } + val size = MeasureSpec.getSize(widthMeasureSpec) + val width = if (size < width + 10) size else mKeyboard.width + paddingLeft + paddingRight + setMeasuredDimension(width, mKeyboard.height + paddingTop + paddingBottom) } @@ -329,21 +319,19 @@ open class KeyboardView @JvmOverloads constructor( val kbdPaddingTop = paddingTop val invalidKey = mInvalidatedKey mPaint.color = mKeyTextColor - var drawSingleKey = false - if (invalidKey != null && canvas.getClipBounds(mClipRegion)) { - // Is clipRegion completely contained within the invalidated key? - if (invalidKey.x + kbdPaddingLeft - 1 <= mClipRegion.left + val drawSingleKey = ( + invalidKey != null + && canvas.getClipBounds(mClipRegion) + // Is clipRegion completely contained within the invalidated key? + && invalidKey.x + kbdPaddingLeft - 1 <= mClipRegion.left && invalidKey.y + kbdPaddingTop - 1 <= mClipRegion.top && invalidKey.x + invalidKey.width + kbdPaddingLeft + 1 >= mClipRegion.right && invalidKey.y + invalidKey.height + kbdPaddingTop + 1 >= mClipRegion.bottom - ) { - drawSingleKey = true - } - } + ) keyBackground?.alpha = backgroundAlpha canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR) - for (i in 0 until mKeyboard.keys.size) { - val key = mKeyboard.keys[i] + + for (key in mKeyboard.keys) { if (drawSingleKey && invalidKey !== key) { continue } keyBackground?.state = key.currentDrawableState @@ -366,8 +354,6 @@ open class KeyboardView @JvmOverloads constructor( mPaint.textSize = mKeyTextSize.toFloat() mPaint.typeface = Typeface.DEFAULT } - // Draw a drop shadow for the text - mPaint.setShadowLayer(mShadowRadius, 0f, 0f, mShadowColor) // Draw the text canvas.drawText( label, @@ -375,8 +361,6 @@ open class KeyboardView @JvmOverloads constructor( (key.height - mPadding.top - mPadding.bottom) / 2f + (mPaint.textSize - mPaint.descent()) / 2 + mPadding.top, mPaint ) - // Turn off drop shadow - mPaint.setShadowLayer(0f, 0f, 0f, 0) } else if (icon != null) { val drawableX = (key.width - mPadding.left - mPadding.right - icon.intrinsicWidth) / 2 + mPadding.left @@ -402,19 +386,13 @@ open class KeyboardView @JvmOverloads constructor( } } - private fun getKeyIndices(x: Int, y: Int): Int { - var primaryIndex = NOT_A_KEY - val nearestKeyIndices = mKeyboard.getNearestKeys(x, y) - for (i in nearestKeyIndices.indices) { - if (mKeyboard.keys[nearestKeyIndices[i]].isInside(x, y)) { - primaryIndex = nearestKeyIndices[i] - } - } - - return primaryIndex + private fun getKeyIndex(x: Int, y: Int): Int { + return mKeyboard.getNearestKeys(x, y).findLast { + mKeyboard.keys[it].isInside(x, y) + } ?: NOT_A_KEY } - private fun detectAndSendKey(index: Int, x: Int, y: Int, eventTime: Long) { + private fun detectAndSendKey(index: Int, eventTime: Long) { if (index != NOT_A_KEY && index < mKeyboard.keys.size) { val key = mKeyboard.keys[index] val text = key.text @@ -423,8 +401,6 @@ open class KeyboardView @JvmOverloads constructor( onKeyboardActionListener?.onRelease(NOT_A_KEY) } else { var code = key.codes[0] - //TextEntryState.keyPressedAt(key, x, y); - getKeyIndices(x, y) // Multi-tap if (mInMultiTap) { if (mTapCount != -1) { @@ -452,35 +428,31 @@ open class KeyboardView @JvmOverloads constructor( } } + private fun pressKey(keyIndex: Int) { + if (keyIndex < 0 || keyIndex >= mKeyboard.keys.size) { return } + mKeyboard.keys[keyIndex].press() + invalidateKey(keyIndex) + showPreview(keyIndex) + } + + private fun releaseKey(keyIndex: Int) { + if (keyIndex < 0 || keyIndex >= mKeyboard.keys.size) { return } + mKeyboard.keys[keyIndex].release() + invalidateKey(keyIndex) + hidePreview() + } + + private fun showPreview(keyIndex: Int) { - val oldKeyIndex = mCurrentKeyIndex - mCurrentKeyIndex = keyIndex - // Release the old key and press the new key - val keys = mKeyboard.keys - if (oldKeyIndex != mCurrentKeyIndex) { - if (oldKeyIndex != NOT_A_KEY && keys.size > oldKeyIndex) { - keys[oldKeyIndex].onReleased(mCurrentKeyIndex == NOT_A_KEY) - invalidateKey(oldKeyIndex) - } - if (mCurrentKeyIndex != NOT_A_KEY && keys.size > mCurrentKeyIndex) { - keys[mCurrentKeyIndex].onPressed() - invalidateKey(mCurrentKeyIndex) - } - } - if (oldKeyIndex != mCurrentKeyIndex && isPreviewEnabled) { + val oldKeyIndex = mCurrentPreviewKeyIndex + mCurrentPreviewKeyIndex = keyIndex + + if (isPreviewEnabled && oldKeyIndex != mCurrentPreviewKeyIndex) { mHandler.removeMessages(MSG_SHOW_PREVIEW) - if (mPreviewPopup.isShowing) { - if (keyIndex == NOT_A_KEY) { - mHandler.sendMessageDelayed( - mHandler.obtainMessage(MSG_REMOVE_PREVIEW), - DELAY_AFTER_PREVIEW.toLong() - ) - } - } if (keyIndex != NOT_A_KEY) { if (mPreviewPopup.isShowing && mPreviewText?.visibility == VISIBLE) { // Show right away, if it's already visible and finger is moving around - showKey(keyIndex) + showKeyInPreview(keyIndex) } else { mHandler.sendMessageDelayed( mHandler.obtainMessage(MSG_SHOW_PREVIEW, keyIndex, 0), @@ -491,27 +463,26 @@ open class KeyboardView @JvmOverloads constructor( } } - private fun showKey(keyIndex: Int) { + private fun hidePreview() { + mCurrentPreviewKeyIndex = NOT_A_KEY + mHandler.removeMessages(MSG_SHOW_PREVIEW) + if (isPreviewEnabled && mPreviewPopup.isShowing) { + mHandler.sendMessageDelayed( + mHandler.obtainMessage(MSG_REMOVE_PREVIEW), + DELAY_AFTER_PREVIEW.toLong() + ) + } + } + + private fun showKeyInPreview(keyIndex: Int) { mPreviewText?.let { previewText -> if (keyIndex < 0 || keyIndex >= mKeyboard.keys.size) { return } val key = mKeyboard.keys[keyIndex] - if (key.icon != null) { - previewText.setCompoundDrawables( - null, null, null, - if (key.iconPreview != null) key.iconPreview else key.icon - ) - previewText.text = null - } else { - previewText.setCompoundDrawables(null, null, null, null) - previewText.text = getPreviewText(key) - if (key.label.length > 1 && key.codes.size < 2) { - previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mKeyTextSize.toFloat()) - previewText.typeface = Typeface.DEFAULT_BOLD - } else { - previewText.setTextSize(TypedValue.COMPLEX_UNIT_PX, mPreviewTextSizeLarge.toFloat()) - previewText.typeface = Typeface.DEFAULT - } - } + if (key.icon != null || (key.label.length > 1 && key.codes.size < 2)) { return } + // show only single character keys + + previewText.text = getPreviewText(key) + previewText.typeface = Typeface.DEFAULT previewText.measure( MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED), MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED) @@ -564,6 +535,19 @@ open class KeyboardView @JvmOverloads constructor( } } + protected fun switchCaseInPreview() { + mPreviewText?.let { + val previewText = it.text + if (previewText.isNotEmpty()) { + it.text = if (Character.isLowerCase(previewText[0])) { + previewText.toString().uppercase(Locale.getDefault()) + } else { + previewText.toString().lowercase(Locale.getDefault()) + } + } + } + } + fun invalidateAllKeys() { mDirtyRect.union(0, 0, width, height) mDrawPending = true @@ -590,7 +574,7 @@ open class KeyboardView @JvmOverloads constructor( val result = onLongPress(mKeyboard.keys[mCurrentKey]) if (result) { mAbortKey = true - showPreview(NOT_A_KEY) + releaseKey(mCurrentKey) } return result } @@ -598,15 +582,16 @@ open class KeyboardView @JvmOverloads constructor( protected open fun onLongPress(key: Keyboard.Key): Boolean { val popupKeyboardId = key.popupResId if (popupKeyboardId != 0) { - var miniKeyboardContainer = mMiniKeyboardCache[key] - val mMiniKeyboard: KeyboardView - if (miniKeyboardContainer == null) { - miniKeyboardContainer = - (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater) - .inflate(mPopupLayout, null) - mMiniKeyboard = miniKeyboardContainer.findViewById(R.id.keyboardView) + val cached = mMiniKeyboardCache[key] + val miniKeyboardContainer: View + val miniKeyboardView: KeyboardView + + if (cached == null) { + miniKeyboardContainer = (context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater) + .inflate(mPopupLayout, null) miniKeyboardContainer.findViewById(R.id.closeButton)?.setOnClickListener(this) - mMiniKeyboard.onKeyboardActionListener = object : OnKeyboardActionListener { + miniKeyboardView = miniKeyboardContainer.findViewById(R.id.keyboardView) + miniKeyboardView.onKeyboardActionListener = object : OnKeyboardActionListener { override fun onKey(primaryCode: Int) { onKeyboardActionListener?.onKey(primaryCode) dismissPopupKeyboard() @@ -631,7 +616,7 @@ open class KeyboardView @JvmOverloads constructor( } } val popupChars = key.popupCharacters - val kb: Keyboard = if (popupChars != null) { + miniKeyboardView.keyboard = if (popupChars != null) { Keyboard( context, popupKeyboardId, popupChars, -1, paddingLeft + paddingRight @@ -639,39 +624,41 @@ open class KeyboardView @JvmOverloads constructor( } else { Keyboard(context, popupKeyboardId) } - mMiniKeyboard.keyboard = kb - mMiniKeyboard.setPopupParent(this) + miniKeyboardView.setPopupParent(this) + mKeyBackground?.let { miniKeyboardView.setKeyBackground(it) } // for inset miniKeyboardContainer.measure( MeasureSpec.makeMeasureSpec(width, MeasureSpec.AT_MOST), MeasureSpec.makeMeasureSpec(height, MeasureSpec.AT_MOST) ) mMiniKeyboardCache[key] = miniKeyboardContainer } else { - mMiniKeyboard = miniKeyboardContainer.findViewById(R.id.keyboardView) + miniKeyboardContainer = cached + miniKeyboardView = miniKeyboardContainer.findViewById(R.id.keyboardView) } + getLocationInWindow(mCoordinates) - var mPopupX = key.x + paddingLeft - var mPopupY = key.y + paddingTop - mPopupX += key.width - miniKeyboardContainer!!.measuredWidth - mPopupY -= miniKeyboardContainer.measuredHeight - val x = mPopupX + miniKeyboardContainer.paddingRight + mCoordinates[0] + val mPopupX = key.x + paddingLeft + key.width - miniKeyboardContainer.measuredWidth + val mPopupY = key.y + paddingTop - miniKeyboardContainer.measuredHeight + val x = (mPopupX + miniKeyboardContainer.paddingRight + mCoordinates[0]).coerceAtLeast(0) val y = mPopupY + miniKeyboardContainer.paddingBottom + mCoordinates[1] - mMiniKeyboard.setPopupOffset(x.coerceAtLeast(0), y) - mMiniKeyboard.isShifted = isShifted + miniKeyboardView.setPopupOffset(x.coerceAtLeast(0), y) + miniKeyboardView.isShifted = isShifted + mPopupKeyboard.contentView = miniKeyboardContainer mPopupKeyboard.width = miniKeyboardContainer.measuredWidth mPopupKeyboard.height = miniKeyboardContainer.measuredHeight mPopupKeyboard.showAtLocation(this, Gravity.NO_GRAVITY, x, y) mMiniKeyboardOnScreen = true - //mMiniKeyboard.onTouchEvent(getTranslatedEvent(me)); + //miniKeyboardView.onTouchEvent(getTranslatedEvent(me)); invalidateAllKeys() + return true } return false } override fun onTouchEvent(event: MotionEvent): Boolean { - if (!isEnabled) { return true } + if (!isEnabled) { return false } // Convert multi-pointer up/down events to single up/down events to // deal with the typical multi-pointer behavior of two-thumb typing val pointerCount = event.pointerCount @@ -719,7 +706,7 @@ open class KeyboardView @JvmOverloads constructor( if (touchY >= -mVerticalCorrection) { touchY += mVerticalCorrection } val action = me.action val eventTime = me.eventTime - val keyIndex = getKeyIndices(touchX, touchY) + val keyIndex = getKeyIndex(touchX, touchY) mPossiblePoly = possiblePoly // Track the last few movements to look for spurious swipes. @@ -731,8 +718,8 @@ open class KeyboardView @JvmOverloads constructor( return true } - if (mGestureDetector?.onTouchEvent(me) == true) { - showPreview(NOT_A_KEY) + if (mGestureDetector.onTouchEvent(me)) { + releaseKey(mCurrentKey) mHandler.removeMessages(MSG_REPEAT) mHandler.removeMessages(MSG_LONGPRESS) return true @@ -765,17 +752,18 @@ open class KeyboardView @JvmOverloads constructor( mHandler.sendMessageDelayed( mHandler.obtainMessage(MSG_REPEAT), REPEAT_START_DELAY.toLong() ) - repeatKey() + detectAndSendKey(mCurrentKey, mLastTapTime) + if (mAbortKey) { // Delivering the key could have caused an abort + mRepeatKeyIndex = NOT_A_KEY + } } - if (mAbortKey) { // Delivering the key could have caused an abort - mRepeatKeyIndex = NOT_A_KEY - } else { + if (!mAbortKey) { if (mCurrentKey != NOT_A_KEY) { mHandler.sendMessageDelayed( mHandler.obtainMessage(MSG_LONGPRESS, me), LONGPRESS_TIMEOUT.toLong() ) } - showPreview(keyIndex) + pressKey(keyIndex) } } MotionEvent.ACTION_MOVE -> { @@ -784,11 +772,14 @@ open class KeyboardView @JvmOverloads constructor( if (mCurrentKey == NOT_A_KEY) { mCurrentKey = keyIndex mCurrentKeyTime = eventTime - mDownTime + pressKey(keyIndex) } else { if (keyIndex == mCurrentKey) { mCurrentKeyTime += eventTime - mLastMoveTime continueLongPress = true } else if (mRepeatKeyIndex == NOT_A_KEY) { + releaseKey(mCurrentKey) + pressKey(keyIndex) resetMultiTap() mLastKey = mCurrentKey mLastCodeX = mLastX @@ -809,7 +800,6 @@ open class KeyboardView @JvmOverloads constructor( ) } } - showPreview(mCurrentKey) mLastMoveTime = eventTime } MotionEvent.ACTION_UP -> { @@ -828,20 +818,20 @@ open class KeyboardView @JvmOverloads constructor( touchX = mLastCodeX touchY = mLastCodeY } - showPreview(NOT_A_KEY) + releaseKey(mCurrentKey) + releaseKey(keyIndex) // If we're not on a repeating key (which sends on a DOWN event) if (mRepeatKeyIndex == NOT_A_KEY && !mMiniKeyboardOnScreen && !mAbortKey) { - detectAndSendKey(mCurrentKey, touchX, touchY, eventTime) + detectAndSendKey(mCurrentKey, eventTime) } - invalidateKey(keyIndex) mRepeatKeyIndex = NOT_A_KEY } MotionEvent.ACTION_CANCEL -> { removeMessages() dismissPopupKeyboard() mAbortKey = true - showPreview(NOT_A_KEY) - invalidateKey(mCurrentKey) + releaseKey(mCurrentKey) + releaseKey(keyIndex) } } mLastX = touchX @@ -849,12 +839,6 @@ open class KeyboardView @JvmOverloads constructor( return true } - private fun repeatKey(): Boolean { - val key = mKeyboard.keys[mRepeatKeyIndex] - detectAndSendKey(mCurrentKey, key.x, key.y, mLastTapTime) - return true - } - protected open fun swipeRight() = onKeyboardActionListener?.swipeRight() protected open fun swipeLeft() = onKeyboardActionListener?.swipeLeft() protected open fun swipeUp() = onKeyboardActionListener?.swipeUp() @@ -888,7 +872,7 @@ open class KeyboardView @JvmOverloads constructor( } } - fun handleBack(): Boolean { + open fun handleBack(): Boolean { if (mPopupKeyboard.isShowing) { dismissPopupKeyboard() return true diff --git a/app/src/main/java/jp/deadend/noname/skk/QwertyKeyboardView.kt b/app/src/main/java/jp/deadend/noname/skk/QwertyKeyboardView.kt index 4149b11..43084bc 100644 --- a/app/src/main/java/jp/deadend/noname/skk/QwertyKeyboardView.kt +++ b/app/src/main/java/jp/deadend/noname/skk/QwertyKeyboardView.kt @@ -4,9 +4,15 @@ import android.content.Context import android.view.KeyEvent import android.view.MotionEvent import android.util.AttributeSet +import java.io.BufferedReader +import java.io.IOException +import java.io.InputStream +import java.io.InputStreamReader class QwertyKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener { private lateinit var mService: SKKService + private val mInputHistory = StringBuilder() // suggestion用に入力の履歴を覚えておく + private val mList = mutableListOf() // suggestion用の単語リスト(頻度順) private val mLatinKeyboard = Keyboard(context, R.xml.qwerty) private val mSymbolsKeyboard = Keyboard(context, R.xml.symbols) @@ -30,6 +36,23 @@ class QwertyKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener { isShifted = false } + override fun handleBack(): Boolean { + clearSuggestions() + return super.handleBack() + } + + @Throws(IOException::class) + internal fun loadFrequencyList(inputStream: InputStream) { + mList.clear() + BufferedReader(InputStreamReader(inputStream)).use { bufferedReader -> + bufferedReader.forEachLine { line -> + if (line.length > 3) { mList.add(line) } + // 短い単語はsuggestionに入れない + // 読んだ順に入れるだけなので、ファイルの内容自体が頻度順に並んでいることが前提 + } + } + } + fun setService(listener: SKKService) { mService = listener } @@ -61,7 +84,9 @@ class QwertyKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener { val dy2 = dy * dy if (dx2 + dy2 > mFlickSensitivitySquared) { if (dy < 0 && dx2 < dy2) { + val oldMFlicked = mFlicked mFlicked = true + if (!oldMFlicked) { switchCaseInPreview() } return true } } @@ -75,6 +100,13 @@ class QwertyKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener { when (primaryCode) { Keyboard.KEYCODE_DELETE -> { if (!mService.handleBackspace()) mService.keyDownUp(KeyEvent.KEYCODE_DEL) + if (mInputHistory.length > 1) { + mInputHistory.deleteCharAt(mInputHistory.length - 1) + updateAsciiSuggestions(mInputHistory.toString()) + } else if (mInputHistory.length == 1) { + clearSuggestions() + } + return } Keyboard.KEYCODE_SHIFT -> { isShifted = !isShifted @@ -98,9 +130,39 @@ class QwertyKeyboardView : KeyboardView, KeyboardView.OnKeyboardActionListener { } else { primaryCode } - mService.commitTextSKK(code.toChar().toString(), 1) + if (keyboard == mLatinKeyboard && isShifted) { isShifted = false } + + mService.commitTextSKK(code.toChar().toString()) + if (code.toChar().isLetter()) { + mInputHistory.append(code.toChar()) + mService.getTextBeforeCursor(mInputHistory.length)?.let { + if (!mInputHistory.contentEquals(it)) { + mInputHistory.clear() + mInputHistory.append(code.toChar()) + } + updateAsciiSuggestions(mInputHistory.toString()) + return + } + } } } + + clearSuggestions() + } + + private fun updateAsciiSuggestions(str: String) { + mService.setCandidates( + mList.filter { it.startsWith(str) }.take(20) + ) + } + + fun getHistoryLength() = mInputHistory.length + + fun clearSuggestions() { + if (mInputHistory.isNotEmpty()) { + mInputHistory.clear() + mService.clearCandidatesView() + } } override fun onPress(primaryCode: Int) {} diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKDicManager.kt b/app/src/main/java/jp/deadend/noname/skk/SKKDicManager.kt index 48344a3..06ff4f0 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKDicManager.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKDicManager.kt @@ -90,6 +90,7 @@ class SKKDicManager : AppCompatActivity() { val intent = Intent(SKKService.ACTION_COMMAND) + intent.setPackage(packageName) intent.putExtra(SKKService.KEY_COMMAND, SKKService.COMMAND_RELOAD_DICS) sendBroadcast(intent) } diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKMushroom.kt b/app/src/main/java/jp/deadend/noname/skk/SKKMushroom.kt index da5ccf8..f520406 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKMushroom.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKMushroom.kt @@ -31,6 +31,7 @@ class SKKMushroom : AppCompatActivity() { val s = if (extras == null) "" else extras.getString(REPLACE_KEY) val retIntent = Intent(ACTION_BROADCAST) + retIntent.setPackage(packageName) retIntent.addCategory(CATEGORY_BROADCAST) retIntent.putExtra(REPLACE_KEY, s) sendBroadcast(retIntent) @@ -38,8 +39,8 @@ class SKKMushroom : AppCompatActivity() { finish() } - override fun onCreate(icicle: Bundle?) { - super.onCreate(icicle) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) binding = ActivityListBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKPrefs.kt b/app/src/main/java/jp/deadend/noname/skk/SKKPrefs.kt index 8048dbc..02c458c 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKPrefs.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKPrefs.kt @@ -86,11 +86,11 @@ class SKKPrefs(context: Context) { var keyWidthPort: Int get() = prefs.getInt(res.getString(R.string.prefkey_key_width_port), 100) - set(value) = prefs.edit().putInt(res.getString(R.string.prefkey_key_height_port), value).apply() + set(value) = prefs.edit().putInt(res.getString(R.string.prefkey_key_width_port), value).apply() var keyWidthLand: Int get() = prefs.getInt(res.getString(R.string.prefkey_key_width_land), 100) - set(value) = prefs.edit().putInt(res.getString(R.string.prefkey_key_height_land), value).apply() + set(value) = prefs.edit().putInt(res.getString(R.string.prefkey_key_width_land), value).apply() var keyPosition: String get() = prefs.getString(res.getString(R.string.prefkey_key_position), null) ?: "center" diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKService.kt b/app/src/main/java/jp/deadend/noname/skk/SKKService.kt index 29892f6..a4251f2 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKService.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKService.kt @@ -1,5 +1,6 @@ package jp.deadend.noname.skk +import android.Manifest import android.app.NotificationChannel import android.app.NotificationManager import android.content.BroadcastReceiver @@ -17,7 +18,10 @@ import android.speech.RecognitionListener import android.speech.RecognizerIntent import android.speech.SpeechRecognizer import android.content.ClipboardManager +import android.content.pm.PackageManager +import android.net.Uri import android.os.Build +import android.provider.Settings import android.text.InputType import android.util.Log import android.util.TypedValue @@ -49,8 +53,6 @@ class SKKService : InputMethodService() { // onKeyDown()でEnterキーのイベントを消費したかどうかのフラグ.onKeyUp()で判定するのに使う private var isEnterUsed = false - private var isCandidatesViewShownFlag = false - private val mShiftKey = SKKStickyShift(this) private var mStickyShift = false private var mSandS = false @@ -266,7 +268,7 @@ class SKKService : InputMethodService() { results?.let { it.getStringArrayList(SpeechRecognizer.RESULTS_RECOGNITION)?.let { matches -> if (matches.size == 1) { - commitTextSKK(matches[0], 0) + commitTextSKK(matches[0]) } else { val intent = Intent(this@SKKService, SKKSpeechRecognitionResultsList::class.java) intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK @@ -309,7 +311,11 @@ class SKKService : InputMethodService() { val abbrev = mAbbrevKeyboardView if (flick == null || qwerty == null || abbrev == null) return - val context = applicationContext + val context = when (skkPrefs.theme) { + "light" -> createNightModeContext(applicationContext, false) + "dark" -> createNightModeContext(applicationContext, true) + else -> applicationContext + } val config = resources.configuration val keyHeight: Int val keyWidth: Int @@ -340,6 +346,8 @@ class SKKService : InputMethodService() { qwerty.setFlickSensitivity(sensitivity) qwerty.backgroundAlpha = 255 * alpha / 100 abbrev.backgroundAlpha = 255 * alpha / 100 + + qwerty.loadFrequencyList(resources.assets.open(FREQUENCY_LIST_FILE)) } private fun checkUseSoftKeyboard(): Boolean { @@ -458,6 +466,16 @@ class SKKService : InputMethodService() { } mEngine.resetOnStartInput() + + if (mUseSoftKeyboard || skkPrefs.useCandidatesView) { + if (attribute.inputType != InputType.TYPE_NULL) { + setCandidatesViewShown(true) + mCandidateViewContainer?.setAlpha(96) + } else { + requestHideSelf(0) + } + } + when (attribute.inputType and InputType.TYPE_MASK_CLASS) { InputType.TYPE_CLASS_NUMBER, InputType.TYPE_CLASS_DATETIME, @@ -477,11 +495,6 @@ class SKKService : InputMethodService() { } } } - - if (mUseSoftKeyboard || skkPrefs.useCandidatesView) { - setCandidatesViewShown(true) - mCandidateViewContainer?.setAlpha(96) - } } /** @@ -489,37 +502,38 @@ class SKKService : InputMethodService() { * needs to be generated, like [.onCreateInputView]. */ override fun onCreateCandidatesView(): View { - val context = when (skkPrefs.theme) { - "light" -> createNightModeContext(applicationContext, false) - "dark" -> createNightModeContext(applicationContext, true) - else -> applicationContext - } + if (mCandidateViewContainer == null) { + val context = when (skkPrefs.theme) { + "light" -> createNightModeContext(applicationContext, false) + "dark" -> createNightModeContext(applicationContext, true) + else -> applicationContext + } - val container = LayoutInflater.from(context).inflate(R.layout.view_candidates, null) as CandidateViewContainer - container.initViews() - val view = container.findViewById(R.id.candidates) as CandidateView - view.setService(this) - view.setContainer(container) - mCandidateView = view + val container = LayoutInflater.from(context) + .inflate(R.layout.view_candidates, null) as CandidateViewContainer + container.initViews() - val sp = skkPrefs.candidatesSize - val px = TypedValue.applyDimension( + val sp = skkPrefs.candidatesSize + val px = TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_SP, sp.toFloat(), context.resources.displayMetrics - ).toInt() - container.setSize(px) + ).toInt() + container.setSize(px) + setCandidatesView(container) - mCandidateViewContainer = container + val view: CandidateView = container.findViewById(R.id.candidates) + view.setService(this) + view.setContainer(container) + mCandidateView = view - return container - } + mCandidateViewContainer = container + } - override fun onStartCandidatesView(info: EditorInfo, restarting: Boolean) { - isCandidatesViewShownFlag = true - } + if (mUseSoftKeyboard || skkPrefs.useCandidatesView) { + setCandidatesViewShown(true) + mCandidateViewContainer?.setAlpha(96) + } - override fun onFinishCandidatesView(finishingInput: Boolean) { - isCandidatesViewShownFlag = false - super.onFinishCandidatesView(finishingInput) + return mCandidateViewContainer!! } /** @@ -531,7 +545,6 @@ class SKKService : InputMethodService() { mQwertyInputView?.handleBack() mAbbrevKeyboardView?.handleBack() - setCandidatesViewShown(false) } override fun onDestroy() { @@ -714,11 +727,30 @@ class SKKService : InputMethodService() { fun changeLastChar(type: String) { mEngine.changeLastChar(type) } - fun commitTextSKK(text: CharSequence, newCursorPosition: Int) { - mEngine.commitTextSKK(text, newCursorPosition) + fun commitTextSKK(text: CharSequence) { + mEngine.commitTextSKK(text) } fun pickCandidateViewManually(index: Int) { - mEngine.pickCandidateViewManually(index) + if (mEngine.state === SKKASCIIState) { + mQwertyInputView?.let { qwerty -> + mCandidateView?.let { cand -> + val ic = currentInputConnection ?: return + ic.deleteSurroundingText(qwerty.getHistoryLength(), 0) + ic.commitText(cand.getContent(index), 1) + } + qwerty.clearSuggestions() + } + } else { + mEngine.pickCandidateViewManually(index) + } + } + + fun getTextBeforeCursor(length: Int): CharSequence? { + currentInputConnection?.let { + return it.getTextBeforeCursor(length, 0) + } + + return null } fun handleBackspace(): Boolean { @@ -804,6 +836,24 @@ class SKKService : InputMethodService() { // mSpeechRecognizer.stopListening() return } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // checkSelfPermission は api 23 必要 + if (listOf(Manifest.permission.RECORD_AUDIO).any { // 将来複数必要になったときのため List.any + checkSelfPermission(it) == PackageManager.PERMISSION_DENIED + }) { + mHandler.post { + Toast.makeText( + applicationContext, getText(R.string.error_permission_record_audio), Toast.LENGTH_LONG + ).show() + } + // requestPermissions は activity が必要なので雑に設定画面を出すだけにする + startActivity( + Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + .setData(Uri.fromParts("package", packageName, null)) + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + ) + return + } + } mStreamVolume = mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC) mAudioManager.setStreamVolume(AudioManager.STREAM_MUSIC, 0, 0) val intent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH) @@ -882,6 +932,7 @@ class SKKService : InputMethodService() { internal const val COMMAND_SPEECH_RECOGNITION = "io.github.deton.androidtutcode.COMMAND_SPEECH_RECOGNITION" internal const val COMMAND_RELOAD_ROMAJIMAP = "io.github.deton.androidtutcode.COMMAND_RELOAD_ROMAJIMAP" internal const val DICT_ZIP_FILE = "skk_dict_btree_db.zip" + internal const val FREQUENCY_LIST_FILE = "en_US_wordlist.txt" internal const val FILENAME_ROMAJIMAP = "romajimap.txt" private const val CHANNEL_ID = "tutcode_notification" private const val CHANNEL_NAME = "TUTCode" diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKSettingsActivity.kt b/app/src/main/java/jp/deadend/noname/skk/SKKSettingsActivity.kt index 5ac120d..9fbc109 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKSettingsActivity.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKSettingsActivity.kt @@ -122,6 +122,7 @@ class SKKSettingsActivity : AppCompatActivity(), super.onPause() val intent = Intent(SKKService.ACTION_COMMAND) + intent.setPackage(packageName) intent.putExtra(SKKService.KEY_COMMAND, SKKService.COMMAND_READ_PREFS) sendBroadcast(intent) } diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKSpeechRecognitionResultsList.kt b/app/src/main/java/jp/deadend/noname/skk/SKKSpeechRecognitionResultsList.kt index 5bba463..b8d2793 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKSpeechRecognitionResultsList.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKSpeechRecognitionResultsList.kt @@ -22,6 +22,7 @@ class SKKSpeechRecognitionResultsList : AppCompatActivity() { binding.listView.adapter = ArrayAdapter(this, R.layout.listitem_text_row, mResults) binding.listView.onItemClickListener = AdapterView.OnItemClickListener { _, _, position, _ -> val retIntent = Intent(SKKService.ACTION_COMMAND) + retIntent.setPackage(packageName) retIntent.putExtra(SKKService.KEY_COMMAND, SKKService.COMMAND_SPEECH_RECOGNITION) retIntent.putExtra(RESULTS_KEY, mResults[position]) sendBroadcast(retIntent) diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKUserDicTool.kt b/app/src/main/java/jp/deadend/noname/skk/SKKUserDicTool.kt index 22b8270..fbaee70 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKUserDicTool.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKUserDicTool.kt @@ -110,6 +110,7 @@ class SKKUserDicTool : AppCompatActivity() { } val intent = Intent(SKKService.ACTION_COMMAND) + intent.setPackage(packageName) intent.putExtra(SKKService.KEY_COMMAND, SKKService.COMMAND_COMMIT_USERDIC) sendBroadcast(intent) diff --git a/app/src/main/java/jp/deadend/noname/skk/SKKUserDictionary.kt b/app/src/main/java/jp/deadend/noname/skk/SKKUserDictionary.kt index 7332316..aa0f48c 100644 --- a/app/src/main/java/jp/deadend/noname/skk/SKKUserDictionary.kt +++ b/app/src/main/java/jp/deadend/noname/skk/SKKUserDictionary.kt @@ -39,12 +39,12 @@ class SKKUserDictionary private constructor ( // 送りがなブロック以外の部分を追加 valList.takeWhile { !it.startsWith("[") }.forEach { cd.add(it) } - if (value.contains("[") && value.contains("]")) { + if (value.contains("/[") && value.contains("/]")) { // 送りがなブロック - val regex = """\[.*?\]""".toRegex() + val regex = """/\[.*?/\]""".toRegex() regex.findAll(value).forEach { result: MatchResult -> okr.add( - result.value.substring(1, result.value.length - 2) // "[" と "/]" をとる + result.value.substring(2, result.value.length - 2) // "/[" と "/]" をとる .split('/') .let { Pair(it[0], it[1]) } ) diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKASCIIState.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKASCIIState.kt index 7c6d5a7..6e384b1 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKASCIIState.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKASCIIState.kt @@ -9,7 +9,7 @@ object SKKASCIIState : SKKState { override fun handleEisuKey(context: SKKEngine) {} override fun processKey(context: SKKEngine, pcode: Int) { - context.commitTextSKK(pcode.toChar().toString(), 1) + context.commitTextSKK(pcode.toChar().toString()) } override fun afterBackspace(context: SKKEngine) {} diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKAbbrevState.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKAbbrevState.kt index 1108b93..0d7d03b 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKAbbrevState.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKAbbrevState.kt @@ -30,7 +30,7 @@ object SKKAbbrevState : SKKState { -1010 -> { // 全角変換 val buf = kanjiKey.map { hankaku2zenkaku(it.code).toChar() }.joinToString("") - context.commitTextSKK(buf, 1) + context.commitTextSKK(buf) context.changeState(SKKHiraganaState) } else -> { diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKEngine.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKEngine.kt index c3c0292..0eee77b 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKEngine.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKEngine.kt @@ -119,7 +119,7 @@ class SKKEngine( when (state) { SKKChooseState, SKKNarrowingState -> pickCandidate(mCurrentCandidateIndex) SKKKanjiState, SKKAbbrevState -> { - commitTextSKK(mKanjiKey, 1) + commitTextSKK(mKanjiKey) changeState(SKKHiraganaState) } else -> { @@ -130,7 +130,7 @@ class SKKEngine( return false } } else { - commitTextSKK(mComposing, 1) + commitTextSKK(mComposing) mComposing.setLength(0) } } @@ -182,17 +182,16 @@ class SKKEngine( /** * commitTextのラッパー 登録作業中なら登録内容に追加し,表示を更新 * @param text - * @param newCursorPosition */ - fun commitTextSKK(text: CharSequence, newCursorPosition: Int) { + fun commitTextSKK(text: CharSequence) { val ic = mService.currentInputConnection ?: return val firstEntry = mRegistrationStack.peekFirst()?.entry if (firstEntry != null) { firstEntry.append(text) - setComposingTextSKK("", newCursorPosition) + setComposingTextSKK("", 1) } else { - ic.commitText(text, newCursorPosition) + ic.commitText(text, 1) } } @@ -464,7 +463,7 @@ class SKKEngine( // 小文字大文字変換,濁音,半濁音に使う fun changeLastChar(type: String) { when { - state === SKKKanjiState && mComposing.isEmpty() -> { + state === SKKKanjiState && mComposing.isEmpty() && mKanjiKey.isNotEmpty() -> { val s = mKanjiKey.toString() val idx = s.length - 1 val newLastChar = RomajiConverter.convertLastChar(s.substring(idx), type) ?: return @@ -474,7 +473,7 @@ class SKKEngine( setComposingTextSKK(mKanjiKey, 1) updateSuggestions(mKanjiKey.toString()) } - state === SKKNarrowingState && mComposing.isEmpty() -> { + state === SKKNarrowingState && mComposing.isEmpty() && SKKNarrowingState.mHint.isNotEmpty() -> { val hint = SKKNarrowingState.mHint val idx = hint.length - 1 val newLastChar = RomajiConverter.convertLastChar(hint.substring(idx), type) ?: return @@ -670,9 +669,9 @@ class SKKEngine( mUserDict.addEntry(regInfo.key, regEntryStr, regInfo.okurigana) mUserDict.commitChanges() if (regInfo.okurigana.isNullOrEmpty()) { - commitTextSKK(regInfo.entry, 1) + commitTextSKK(regInfo.entry) } else { - commitTextSKK(regInfo.entry.append(regInfo.okurigana), 1) + commitTextSKK(regInfo.entry.append(regInfo.okurigana)) } } reset() @@ -746,10 +745,10 @@ class SKKEngine( mUserDict.addEntry(mKanjiKey.toString(), candList[index], mOkurigana) // ユーザー辞書登録時はエスケープや注釈を消さない - commitTextSKK(candidate, 1) + commitTextSKK(candidate) val okuri = mOkurigana if (okuri != null) { - commitTextSKK(okuri, 1) + commitTextSKK(okuri) if (mRegistrationStack.isEmpty()) { mLastConversion = ConversionInfo( candidate + okuri, candList, index, mKanjiKey.toString(), okuri diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKHiraganaState.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKHiraganaState.kt index 7c5aa62..2644cb2 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKHiraganaState.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKHiraganaState.kt @@ -28,7 +28,7 @@ object SKKHiraganaState : SKKState { val hchr = context.romaji2kana(composing.toString()) when (hchr) { null -> { // ローマ字シーケンス外の文字が来た場合はそのまま確定 - context.commitTextSKK(composing, 1) + context.commitTextSKK(composing) composing.setLength(0) } "@cont" -> { // ローマ字内の文字ならComposingに積む @@ -176,7 +176,7 @@ object SKKHiraganaState : SKKState { override fun processKey(context: SKKEngine, pcode: Int) { if (context.changeInputMode(pcode, true)) return processKana(context, pcode) { engine, hchr -> - engine.commitTextSKK(hchr, 1) + engine.commitTextSKK(hchr) engine.mComposing.setLength(0) } } diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKKatakanaState.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKKatakanaState.kt index 40ebfed..1ed7139 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKKatakanaState.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKKatakanaState.kt @@ -25,7 +25,7 @@ object SKKKatakanaState : SKKState { if (context.changeInputMode(pcode, false)) return SKKHiraganaState.processKana(context, pcode) { engine, hchr -> val str = hirakana2katakana(hchr) - if (str != null) engine.commitTextSKK(str, 1) + if (str != null) engine.commitTextSKK(str) engine.mComposing.setLength(0) } } diff --git a/app/src/main/java/jp/deadend/noname/skk/engine/SKKZenkakuState.kt b/app/src/main/java/jp/deadend/noname/skk/engine/SKKZenkakuState.kt index 2b701a5..2d93cfd 100644 --- a/app/src/main/java/jp/deadend/noname/skk/engine/SKKZenkakuState.kt +++ b/app/src/main/java/jp/deadend/noname/skk/engine/SKKZenkakuState.kt @@ -22,7 +22,7 @@ object SKKZenkakuState : SKKState { } override fun processKey(context: SKKEngine, pcode: Int) { - context.commitTextSKK(hankaku2zenkaku(pcode).toChar().toString(), 1) + context.commitTextSKK(hankaku2zenkaku(pcode).toChar().toString()) } override fun afterBackspace(context: SKKEngine) {} diff --git a/app/src/main/res/drawable/key_bg.xml b/app/src/main/res/drawable/key_bg.xml index b319564..d8b0259 100644 --- a/app/src/main/res/drawable/key_bg.xml +++ b/app/src/main/res/drawable/key_bg.xml @@ -7,6 +7,13 @@ + + + + + + + diff --git a/app/src/main/res/drawable/popup_bg.xml b/app/src/main/res/drawable/popup_bg.xml new file mode 100644 index 0000000..40fb12a --- /dev/null +++ b/app/src/main/res/drawable/popup_bg.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/popup_frame.xml b/app/src/main/res/drawable/popup_frame.xml index da08b22..309b997 100644 --- a/app/src/main/res/drawable/popup_frame.xml +++ b/app/src/main/res/drawable/popup_frame.xml @@ -1,6 +1,6 @@ - + diff --git a/app/src/main/res/drawable/popup_label.xml b/app/src/main/res/drawable/popup_label.xml index 3f80745..f4bcd9e 100644 --- a/app/src/main/res/drawable/popup_label.xml +++ b/app/src/main/res/drawable/popup_label.xml @@ -1,5 +1,5 @@ - + diff --git a/app/src/main/res/layout/keyboard_key_preview.xml b/app/src/main/res/layout/keyboard_key_preview.xml index 6a09772..709a1bd 100644 --- a/app/src/main/res/layout/keyboard_key_preview.xml +++ b/app/src/main/res/layout/keyboard_key_preview.xml @@ -3,8 +3,8 @@ android:layout_width="wrap_content" android:layout_height="80sp" android:textSize="40sp" - android:textColor="?android:attr/textColorPrimaryInverse" + android:textColor="@color/key_char_color" android:minWidth="32dip" android:gravity="center" - android:background="@drawable/key_bg" + android:background="@drawable/popup_bg" /> \ No newline at end of file diff --git a/app/src/main/res/values-night/colors.xml b/app/src/main/res/values-night/colors.xml index fb06f59..9de425a 100644 --- a/app/src/main/res/values-night/colors.xml +++ b/app/src/main/res/values-night/colors.xml @@ -5,7 +5,9 @@ #BBEEEEEE #FF6E6E6E #FF555555 + #FFAFB42B #FF222222 #FF283593 #FFFFFFFF + #787878 \ No newline at end of file diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 9a25ced..377cdcd 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -23,7 +23,9 @@ #ff808080 #FFE6E6E6 #FFEBEBEB + #FFFFFF00 #FFC8C8C8 #FF00E2FF #FF000000 + #DCDCDC diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 1231541..fcf6e24 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -38,6 +38,7 @@ UTF-8のファイルではないようです この辞書はすでに存在します: %1$s 変換辞書の解凍に失敗しました。一旦アンインストールしてみてください + 「マイク」の使用を許可してください 漢字表ファイル読みこみ失敗。タブ区切りになっていない行があります PrefKeyImportRomajiMap diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 378b8bb..35d30b4 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -11,10 +11,9 @@ @drawable/key_bg -12dp 80dp + @layout/keyboard_key_preview 14sp @layout/keyboard_popup_keyboard -10dp - @android:color/transparent - 2.75 \ No newline at end of file diff --git a/app/src/main/res/xml/abbrev.xml b/app/src/main/res/xml/abbrev.xml index 250be33..20a1fc0 100644 --- a/app/src/main/res/xml/abbrev.xml +++ b/app/src/main/res/xml/abbrev.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/keys_flick_jp.xml b/app/src/main/res/xml/keys_flick_jp.xml index e974dd3..172b514 100644 --- a/app/src/main/res/xml/keys_flick_jp.xml +++ b/app/src/main/res/xml/keys_flick_jp.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/keys_flick_number.xml b/app/src/main/res/xml/keys_flick_number.xml index 1205a01..c1cf603 100644 --- a/app/src/main/res/xml/keys_flick_number.xml +++ b/app/src/main/res/xml/keys_flick_number.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/keys_flick_voice.xml b/app/src/main/res/xml/keys_flick_voice.xml index 9772b41..1f6d21f 100644 --- a/app/src/main/res/xml/keys_flick_voice.xml +++ b/app/src/main/res/xml/keys_flick_voice.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/keys_null.xml b/app/src/main/res/xml/keys_null.xml index 9610c00..2ecc167 100644 --- a/app/src/main/res/xml/keys_null.xml +++ b/app/src/main/res/xml/keys_null.xml @@ -1,5 +1,3 @@ - + diff --git a/app/src/main/res/xml/keys_popup.xml b/app/src/main/res/xml/keys_popup.xml index 6221b4f..0dfa2aa 100644 --- a/app/src/main/res/xml/keys_popup.xml +++ b/app/src/main/res/xml/keys_popup.xml @@ -1,7 +1,5 @@ - + app:keyHeight="10%p"> \ No newline at end of file diff --git a/app/src/main/res/xml/prefs_softkey.xml b/app/src/main/res/xml/prefs_softkey.xml index f0c3f6e..6feed8a 100644 --- a/app/src/main/res/xml/prefs_softkey.xml +++ b/app/src/main/res/xml/prefs_softkey.xml @@ -111,7 +111,7 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/symbols.xml b/app/src/main/res/xml/symbols.xml index 718b543..a700fe7 100644 --- a/app/src/main/res/xml/symbols.xml +++ b/app/src/main/res/xml/symbols.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/app/src/main/res/xml/symbols_shift.xml b/app/src/main/res/xml/symbols_shift.xml index bf6cb05..59d275e 100644 --- a/app/src/main/res/xml/symbols_shift.xml +++ b/app/src/main/res/xml/symbols_shift.xml @@ -1,11 +1,9 @@ - + app:keyHeight="8%p"> diff --git a/build.gradle b/build.gradle index 800fa0e..f66e089 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,12 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { - ext.kotlin_version = '1.9.0' + ext.kotlin_version = '2.0.0' repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:8.2.0' + classpath 'com.android.tools.build:gradle:8.5.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 0001fdd..7e4c624 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Sat Jan 29 13:15:47 JST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME