Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,8 @@ data class ImePreferencesSnapshot(
val landscapeForceQwertyRomajiPreference: Boolean,
val shortcutTollbarVisibility: Boolean,
val shortcutToolbarIntegratedInSuggestion: Boolean,
val shortcutToolbarHeightDp: Int,
val shortcutToolbarIconSizeDp: Int,
val isDeleteLeftFlickPreference: Boolean,
val isDeleteUpFlickPreference: Boolean,
val isDeleteDownFlickPreference: Boolean,
Expand Down Expand Up @@ -183,6 +185,16 @@ data class ImePreferencesSnapshot(
val keyboardTouchEffectColorModePreference: String,
val keyboardTouchEffectColorPreference: Int,
val keyboardTouchEffectPalettePreference: String,
val cinematicWaveColorModePreference: String,
val cinematicWavePrimaryColorPreference: Int,
val cinematicWaveSecondaryColorPreference: Int,
val cinematicWaveSecondaryColorAutoPreference: Boolean,
val cinematicWaveTypePreference: String,
val cinematicWaveOpacityPercentPreference: Int,
val cinematicWaveIntensityPercentPreference: Int,
val cinematicWaveMotionPreference: String,
val cinematicWaveTouchResponsePreference: String,
val cinematicWaveQualityPreference: String,
val customKeyBorderEnablePreference: Boolean,
val customKeyBorderEnableColor: Int,
val customComposingTextPreference: Boolean,
Expand Down Expand Up @@ -387,6 +399,10 @@ data class ImePreferencesSnapshot(
appPreference.shortcut_toolbar_visibility_preference,
shortcutToolbarIntegratedInSuggestion =
appPreference.shortcut_toolbar_integrated_in_suggestion_preference,
shortcutToolbarHeightDp =
appPreference.shortcut_toolbar_height_dp_preference,
shortcutToolbarIconSizeDp =
appPreference.shortcut_toolbar_icon_size_dp_preference,
isDeleteLeftFlickPreference = appPreference.delete_key_left_flick_preference,
isDeleteUpFlickPreference = appPreference.delete_key_up_flick_preference,
isDeleteDownFlickPreference = appPreference.delete_key_down_flick_preference,
Expand Down Expand Up @@ -513,6 +529,27 @@ data class ImePreferencesSnapshot(
keyboardTouchEffectColorPreference = appPreference.keyboard_touch_effect_color_preference,
keyboardTouchEffectPalettePreference =
appPreference.keyboard_touch_effect_palette_preference,
cinematicWaveColorModePreference =
appPreference.keyboard_touch_effect_cinematic_wave_color_mode_preference,
cinematicWavePrimaryColorPreference =
appPreference.keyboard_touch_effect_cinematic_wave_primary_color_preference,
cinematicWaveSecondaryColorPreference =
appPreference.keyboard_touch_effect_cinematic_wave_secondary_color_preference,
cinematicWaveSecondaryColorAutoPreference =
appPreference
.keyboard_touch_effect_cinematic_wave_secondary_color_auto_preference,
cinematicWaveTypePreference =
appPreference.keyboard_touch_effect_cinematic_wave_type_preference,
cinematicWaveOpacityPercentPreference =
appPreference.keyboard_touch_effect_cinematic_wave_opacity_percent_preference,
cinematicWaveIntensityPercentPreference =
appPreference.keyboard_touch_effect_cinematic_wave_intensity_percent_preference,
cinematicWaveMotionPreference =
appPreference.keyboard_touch_effect_cinematic_wave_motion_preference,
cinematicWaveTouchResponsePreference =
appPreference.keyboard_touch_effect_cinematic_wave_touch_response_preference,
cinematicWaveQualityPreference =
appPreference.keyboard_touch_effect_cinematic_wave_quality_preference,
customKeyBorderEnablePreference = appPreference.custom_theme_border_enable,
customKeyBorderEnableColor = appPreference.custom_theme_border_color,
customComposingTextPreference = appPreference.custom_theme_input_color_enable,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.widget.ImageView
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView
import com.kazumaproject.core.domain.extensions.dpToPx
import com.kazumaproject.markdownhelperkeyboard.R
import com.kazumaproject.markdownhelperkeyboard.short_cut.ShortcutType

Expand All @@ -32,6 +33,8 @@ class ShortcutAdapter : ListAdapter<ShortcutType, ShortcutAdapter.ViewHolder>(Di

private val iconColorState = ShortcutIconColorState()
private var activeShortcutTypes: Set<ShortcutType> = emptySet()
private var toolbarHeightPx: Int = 0
private var iconSizePx: Int = 0

/**
* ViewHolder now captures clicks and calls the adapter's listener.
Expand All @@ -58,6 +61,7 @@ class ShortcutAdapter : ListAdapter<ShortcutType, ShortcutAdapter.ViewHolder>(Di

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = getItem(position)
applyShortcutToolbarSize(holder)
holder.imageView.setImageResource(item.resolveIconResId()) // Enumからアイコン取得

// ★追加: 色が設定されていれば適用し、なければ解除する
Expand All @@ -68,6 +72,23 @@ class ShortcutAdapter : ListAdapter<ShortcutType, ShortcutAdapter.ViewHolder>(Di
}
}

fun setShortcutToolbarSize(
toolbarHeightPx: Int,
iconSizePx: Int
) {
if (
this.toolbarHeightPx == toolbarHeightPx &&
this.iconSizePx == iconSizePx
) {
return
}
this.toolbarHeightPx = toolbarHeightPx
this.iconSizePx = iconSizePx
if (itemCount > 0) {
notifyItemRangeChanged(0, itemCount)
}
}

// ★追加: 外部から色を設定するメソッド
fun setIconColor(color: Int) {
if (!iconColorState.setIconColor(color)) return
Expand Down Expand Up @@ -97,6 +118,23 @@ class ShortcutAdapter : ListAdapter<ShortcutType, ShortcutAdapter.ViewHolder>(Di
)
}

private fun applyShortcutToolbarSize(holder: ViewHolder) {
if (toolbarHeightPx <= 0 || iconSizePx <= 0) return
val context = holder.itemView.context
val itemMinWidthPx = context.dpToPx(64)
val horizontalPaddingPx = context.dpToPx(36)
val itemWidthPx = maxOf(itemMinWidthPx, iconSizePx + horizontalPaddingPx)

holder.itemView.layoutParams = holder.itemView.layoutParams.apply {
width = itemWidthPx
height = toolbarHeightPx
}
holder.imageView.layoutParams = holder.imageView.layoutParams.apply {
width = iconSizePx
height = iconSizePx
}
}

private fun ShortcutType.resolveIconResId(): Int {
return if (this in activeShortcutTypes) {
activeIconResId ?: iconResId
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
package com.kazumaproject.markdownhelperkeyboard.ime_service.image_effect

import android.content.Context
import android.graphics.SurfaceTexture
import android.util.AttributeSet
import android.view.TextureView
import android.view.View
import androidx.annotation.ColorInt
import androidx.annotation.VisibleForTesting
import timber.log.Timber

class CinematicWaveEffectView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : TextureView(context, attrs, defStyleAttr), TextureView.SurfaceTextureListener {

private val inputQueue = CinematicWaveInputCommandQueue()
private val inputInjector = CinematicWaveInputInjector(inputQueue)

@VisibleForTesting
internal var rendererFactory: CinematicWaveRendererFactory =
CinematicWaveRendererFactory { queue, callback ->
CinematicWaveRenderer(
inputQueue = queue,
callback = callback
)
}

private var renderer: CinematicWaveRendererController? = null
private var effectEnabled = false
private var currentSettings = CinematicWaveSettings.Disabled
private var attachedSurfaceTexture: SurfaceTexture? = null

init {
surfaceTextureListener = this
setOpaque(false)
isClickable = false
isFocusable = false
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
visibility = View.GONE
}

fun configure(
enabled: Boolean,
colorMode: String,
@ColorInt primaryColor: Int,
@ColorInt secondaryColor: Int,
secondaryColorAuto: Boolean,
waveType: String,
opacityPercent: Int,
intensityPercent: Int,
motion: String,
touchResponse: String,
quality: String
) {
currentSettings = CinematicWaveSettings(
enabled = enabled,
colorMode = colorMode,
primaryColor = CinematicWaveSettings.withoutTransparentAlpha(primaryColor),
secondaryColor = CinematicWaveSettings.withoutTransparentAlpha(secondaryColor),
secondaryColorAuto = secondaryColorAuto,
waveType = waveType,
opacityPercent = opacityPercent,
intensityPercent = intensityPercent,
motion = motion,
touchResponse = touchResponse,
quality = quality
)

if (!enabled) {
effectEnabled = false
inputInjector.disable()
renderer?.clear()
renderer?.release()
renderer = null
visibility = View.GONE
return
}

effectEnabled = true
inputInjector.configure(enabled = true)
visibility = View.VISIBLE

val activeRenderer = ensureRenderer()
activeRenderer.configure(currentSettings)
activeRenderer.resume()
val surface = attachedSurfaceTexture ?: surfaceTexture
if (surface != null && width > 0 && height > 0) {
activeRenderer.attachSurface(surface, width, height)
}
activeRenderer.requestRender()
}

fun onPointerDown(pointerId: Int, x: Float, y: Float, pressure: Float = 1f) {
if (!canForwardInput()) return
if (inputInjector.onPointerDown(pointerId, x, y, pressure)) {
renderer?.requestRender()
}
}

fun onPointerMove(pointerId: Int, x: Float, y: Float, pressure: Float = 1f) {
if (!canForwardInput()) return
if (inputInjector.onPointerMove(pointerId, x, y, pressure)) {
renderer?.requestRender()
}
}

fun onPointerUp(
pointerId: Int,
x: Float? = null,
y: Float? = null,
pressure: Float = 1f
) {
if (!effectEnabled) return
if (inputInjector.onPointerUp(pointerId, x, y, pressure)) {
renderer?.requestRender()
}
}

fun onCancel() {
if (!effectEnabled) return
if (inputInjector.onCancel()) {
renderer?.requestRender()
}
}

fun clearWave() {
inputInjector.clearActivePointers()
inputQueue.clear()
renderer?.clear()
}

fun pauseWave() {
inputInjector.clearActivePointers()
renderer?.pause()
}

fun releaseWave() {
inputInjector.disable()
renderer?.release()
renderer = null
effectEnabled = false
visibility = View.GONE
}

override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
attachedSurfaceTexture = surface
if (!effectEnabled) return
ensureRenderer().attachSurface(surface, width, height)
}

override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
attachedSurfaceTexture = surface
if (!effectEnabled) return
renderer?.resizeSurface(width, height)
}

override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
attachedSurfaceTexture = null
renderer?.detachSurface()
return true
}

override fun onSurfaceTextureUpdated(surface: SurfaceTexture) = Unit

@VisibleForTesting
internal fun pointerStateCountForTesting(): Int = inputInjector.pointerStateCountForTesting()

@VisibleForTesting
internal fun queuedInputCountForTesting(): Int = inputQueue.sizeForTesting()

@VisibleForTesting
internal fun hasRendererForTesting(): Boolean = renderer != null

private fun canForwardInput(): Boolean {
return effectEnabled && visibility == View.VISIBLE
}

private fun ensureRenderer(): CinematicWaveRendererController {
renderer?.let { return it }
return rendererFactory.create(
inputQueue,
CinematicWaveRendererCallback { reason, throwable ->
Timber.w(throwable, "Keyboard cinematic wave effect disabled: %s", reason)
disableAfterRendererFailure()
}
).also { renderer = it }
}

private fun disableAfterRendererFailure() {
if (!effectEnabled && renderer == null) return
inputInjector.disable()
renderer?.release()
renderer = null
effectEnabled = false
visibility = View.GONE
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.kazumaproject.markdownhelperkeyboard.ime_service.image_effect

internal enum class CinematicWaveInputKind {
Down,
Move,
Up
}

internal data class CinematicWaveTouchPoint(
val pointerId: Int,
var x: Float,
var y: Float,
var startTimeMs: Long,
var lastUpdateTimeMs: Long,
var pressure: Float,
var strength: Float
)

internal data class CinematicWaveTouchSnapshot(
val x: Float,
val y: Float,
val ageSeconds: Float,
val strength: Float
)

internal sealed class CinematicWaveInputCommand {
abstract val eventTimeMs: Long

data class Pointer(
val pointerId: Int,
val x: Float,
val y: Float,
val pressure: Float,
val kind: CinematicWaveInputKind,
override val eventTimeMs: Long
) : CinematicWaveInputCommand()

data class PointerUp(
val pointerId: Int,
override val eventTimeMs: Long
) : CinematicWaveInputCommand()

data class PointerCancel(
val pointerId: Int,
override val eventTimeMs: Long
) : CinematicWaveInputCommand()

data class CancelAll(
override val eventTimeMs: Long
) : CinematicWaveInputCommand()
}
Loading
Loading