Skip to content

Commit c17c261

Browse files
Merge pull request #845 from KazumaProject/feature/shortcut-toolbar-height-custom
ウェーブのエフェクトの追加
2 parents 5a55cce + efd5937 commit c17c261

61 files changed

Lines changed: 7550 additions & 62 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

app/src/main/java/com/kazumaproject/markdownhelperkeyboard/ime_service/IMEService.kt

Lines changed: 518 additions & 10 deletions
Large diffs are not rendered by default.

app/src/main/java/com/kazumaproject/markdownhelperkeyboard/ime_service/ImePreferencesSnapshot.kt

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,8 @@ data class ImePreferencesSnapshot(
108108
val landscapeForceQwertyRomajiPreference: Boolean,
109109
val shortcutTollbarVisibility: Boolean,
110110
val shortcutToolbarIntegratedInSuggestion: Boolean,
111+
val shortcutToolbarHeightDp: Int,
112+
val shortcutToolbarIconSizeDp: Int,
111113
val isDeleteLeftFlickPreference: Boolean,
112114
val isDeleteUpFlickPreference: Boolean,
113115
val isDeleteDownFlickPreference: Boolean,
@@ -183,6 +185,16 @@ data class ImePreferencesSnapshot(
183185
val keyboardTouchEffectColorModePreference: String,
184186
val keyboardTouchEffectColorPreference: Int,
185187
val keyboardTouchEffectPalettePreference: String,
188+
val cinematicWaveColorModePreference: String,
189+
val cinematicWavePrimaryColorPreference: Int,
190+
val cinematicWaveSecondaryColorPreference: Int,
191+
val cinematicWaveSecondaryColorAutoPreference: Boolean,
192+
val cinematicWaveTypePreference: String,
193+
val cinematicWaveOpacityPercentPreference: Int,
194+
val cinematicWaveIntensityPercentPreference: Int,
195+
val cinematicWaveMotionPreference: String,
196+
val cinematicWaveTouchResponsePreference: String,
197+
val cinematicWaveQualityPreference: String,
186198
val customKeyBorderEnablePreference: Boolean,
187199
val customKeyBorderEnableColor: Int,
188200
val customComposingTextPreference: Boolean,
@@ -387,6 +399,10 @@ data class ImePreferencesSnapshot(
387399
appPreference.shortcut_toolbar_visibility_preference,
388400
shortcutToolbarIntegratedInSuggestion =
389401
appPreference.shortcut_toolbar_integrated_in_suggestion_preference,
402+
shortcutToolbarHeightDp =
403+
appPreference.shortcut_toolbar_height_dp_preference,
404+
shortcutToolbarIconSizeDp =
405+
appPreference.shortcut_toolbar_icon_size_dp_preference,
390406
isDeleteLeftFlickPreference = appPreference.delete_key_left_flick_preference,
391407
isDeleteUpFlickPreference = appPreference.delete_key_up_flick_preference,
392408
isDeleteDownFlickPreference = appPreference.delete_key_down_flick_preference,
@@ -513,6 +529,27 @@ data class ImePreferencesSnapshot(
513529
keyboardTouchEffectColorPreference = appPreference.keyboard_touch_effect_color_preference,
514530
keyboardTouchEffectPalettePreference =
515531
appPreference.keyboard_touch_effect_palette_preference,
532+
cinematicWaveColorModePreference =
533+
appPreference.keyboard_touch_effect_cinematic_wave_color_mode_preference,
534+
cinematicWavePrimaryColorPreference =
535+
appPreference.keyboard_touch_effect_cinematic_wave_primary_color_preference,
536+
cinematicWaveSecondaryColorPreference =
537+
appPreference.keyboard_touch_effect_cinematic_wave_secondary_color_preference,
538+
cinematicWaveSecondaryColorAutoPreference =
539+
appPreference
540+
.keyboard_touch_effect_cinematic_wave_secondary_color_auto_preference,
541+
cinematicWaveTypePreference =
542+
appPreference.keyboard_touch_effect_cinematic_wave_type_preference,
543+
cinematicWaveOpacityPercentPreference =
544+
appPreference.keyboard_touch_effect_cinematic_wave_opacity_percent_preference,
545+
cinematicWaveIntensityPercentPreference =
546+
appPreference.keyboard_touch_effect_cinematic_wave_intensity_percent_preference,
547+
cinematicWaveMotionPreference =
548+
appPreference.keyboard_touch_effect_cinematic_wave_motion_preference,
549+
cinematicWaveTouchResponsePreference =
550+
appPreference.keyboard_touch_effect_cinematic_wave_touch_response_preference,
551+
cinematicWaveQualityPreference =
552+
appPreference.keyboard_touch_effect_cinematic_wave_quality_preference,
516553
customKeyBorderEnablePreference = appPreference.custom_theme_border_enable,
517554
customKeyBorderEnableColor = appPreference.custom_theme_border_color,
518555
customComposingTextPreference = appPreference.custom_theme_input_color_enable,

app/src/main/java/com/kazumaproject/markdownhelperkeyboard/ime_service/adapters/ShortcutAdapter.kt

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.widget.ImageView
88
import androidx.recyclerview.widget.DiffUtil
99
import androidx.recyclerview.widget.ListAdapter
1010
import androidx.recyclerview.widget.RecyclerView
11+
import com.kazumaproject.core.domain.extensions.dpToPx
1112
import com.kazumaproject.markdownhelperkeyboard.R
1213
import com.kazumaproject.markdownhelperkeyboard.short_cut.ShortcutType
1314

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

3334
private val iconColorState = ShortcutIconColorState()
3435
private var activeShortcutTypes: Set<ShortcutType> = emptySet()
36+
private var toolbarHeightPx: Int = 0
37+
private var iconSizePx: Int = 0
3538

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

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

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

75+
fun setShortcutToolbarSize(
76+
toolbarHeightPx: Int,
77+
iconSizePx: Int
78+
) {
79+
if (
80+
this.toolbarHeightPx == toolbarHeightPx &&
81+
this.iconSizePx == iconSizePx
82+
) {
83+
return
84+
}
85+
this.toolbarHeightPx = toolbarHeightPx
86+
this.iconSizePx = iconSizePx
87+
if (itemCount > 0) {
88+
notifyItemRangeChanged(0, itemCount)
89+
}
90+
}
91+
7192
// ★追加: 外部から色を設定するメソッド
7293
fun setIconColor(color: Int) {
7394
if (!iconColorState.setIconColor(color)) return
@@ -97,6 +118,23 @@ class ShortcutAdapter : ListAdapter<ShortcutType, ShortcutAdapter.ViewHolder>(Di
97118
)
98119
}
99120

121+
private fun applyShortcutToolbarSize(holder: ViewHolder) {
122+
if (toolbarHeightPx <= 0 || iconSizePx <= 0) return
123+
val context = holder.itemView.context
124+
val itemMinWidthPx = context.dpToPx(64)
125+
val horizontalPaddingPx = context.dpToPx(36)
126+
val itemWidthPx = maxOf(itemMinWidthPx, iconSizePx + horizontalPaddingPx)
127+
128+
holder.itemView.layoutParams = holder.itemView.layoutParams.apply {
129+
width = itemWidthPx
130+
height = toolbarHeightPx
131+
}
132+
holder.imageView.layoutParams = holder.imageView.layoutParams.apply {
133+
width = iconSizePx
134+
height = iconSizePx
135+
}
136+
}
137+
100138
private fun ShortcutType.resolveIconResId(): Int {
101139
return if (this in activeShortcutTypes) {
102140
activeIconResId ?: iconResId
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package com.kazumaproject.markdownhelperkeyboard.ime_service.image_effect
2+
3+
import android.content.Context
4+
import android.graphics.SurfaceTexture
5+
import android.util.AttributeSet
6+
import android.view.TextureView
7+
import android.view.View
8+
import androidx.annotation.ColorInt
9+
import androidx.annotation.VisibleForTesting
10+
import timber.log.Timber
11+
12+
class CinematicWaveEffectView @JvmOverloads constructor(
13+
context: Context,
14+
attrs: AttributeSet? = null,
15+
defStyleAttr: Int = 0
16+
) : TextureView(context, attrs, defStyleAttr), TextureView.SurfaceTextureListener {
17+
18+
private val inputQueue = CinematicWaveInputCommandQueue()
19+
private val inputInjector = CinematicWaveInputInjector(inputQueue)
20+
21+
@VisibleForTesting
22+
internal var rendererFactory: CinematicWaveRendererFactory =
23+
CinematicWaveRendererFactory { queue, callback ->
24+
CinematicWaveRenderer(
25+
inputQueue = queue,
26+
callback = callback
27+
)
28+
}
29+
30+
private var renderer: CinematicWaveRendererController? = null
31+
private var effectEnabled = false
32+
private var currentSettings = CinematicWaveSettings.Disabled
33+
private var attachedSurfaceTexture: SurfaceTexture? = null
34+
35+
init {
36+
surfaceTextureListener = this
37+
setOpaque(false)
38+
isClickable = false
39+
isFocusable = false
40+
importantForAccessibility = IMPORTANT_FOR_ACCESSIBILITY_NO
41+
visibility = View.GONE
42+
}
43+
44+
fun configure(
45+
enabled: Boolean,
46+
colorMode: String,
47+
@ColorInt primaryColor: Int,
48+
@ColorInt secondaryColor: Int,
49+
secondaryColorAuto: Boolean,
50+
waveType: String,
51+
opacityPercent: Int,
52+
intensityPercent: Int,
53+
motion: String,
54+
touchResponse: String,
55+
quality: String
56+
) {
57+
currentSettings = CinematicWaveSettings(
58+
enabled = enabled,
59+
colorMode = colorMode,
60+
primaryColor = CinematicWaveSettings.withoutTransparentAlpha(primaryColor),
61+
secondaryColor = CinematicWaveSettings.withoutTransparentAlpha(secondaryColor),
62+
secondaryColorAuto = secondaryColorAuto,
63+
waveType = waveType,
64+
opacityPercent = opacityPercent,
65+
intensityPercent = intensityPercent,
66+
motion = motion,
67+
touchResponse = touchResponse,
68+
quality = quality
69+
)
70+
71+
if (!enabled) {
72+
effectEnabled = false
73+
inputInjector.disable()
74+
renderer?.clear()
75+
renderer?.release()
76+
renderer = null
77+
visibility = View.GONE
78+
return
79+
}
80+
81+
effectEnabled = true
82+
inputInjector.configure(enabled = true)
83+
visibility = View.VISIBLE
84+
85+
val activeRenderer = ensureRenderer()
86+
activeRenderer.configure(currentSettings)
87+
activeRenderer.resume()
88+
val surface = attachedSurfaceTexture ?: surfaceTexture
89+
if (surface != null && width > 0 && height > 0) {
90+
activeRenderer.attachSurface(surface, width, height)
91+
}
92+
activeRenderer.requestRender()
93+
}
94+
95+
fun onPointerDown(pointerId: Int, x: Float, y: Float, pressure: Float = 1f) {
96+
if (!canForwardInput()) return
97+
if (inputInjector.onPointerDown(pointerId, x, y, pressure)) {
98+
renderer?.requestRender()
99+
}
100+
}
101+
102+
fun onPointerMove(pointerId: Int, x: Float, y: Float, pressure: Float = 1f) {
103+
if (!canForwardInput()) return
104+
if (inputInjector.onPointerMove(pointerId, x, y, pressure)) {
105+
renderer?.requestRender()
106+
}
107+
}
108+
109+
fun onPointerUp(
110+
pointerId: Int,
111+
x: Float? = null,
112+
y: Float? = null,
113+
pressure: Float = 1f
114+
) {
115+
if (!effectEnabled) return
116+
if (inputInjector.onPointerUp(pointerId, x, y, pressure)) {
117+
renderer?.requestRender()
118+
}
119+
}
120+
121+
fun onCancel() {
122+
if (!effectEnabled) return
123+
if (inputInjector.onCancel()) {
124+
renderer?.requestRender()
125+
}
126+
}
127+
128+
fun clearWave() {
129+
inputInjector.clearActivePointers()
130+
inputQueue.clear()
131+
renderer?.clear()
132+
}
133+
134+
fun pauseWave() {
135+
inputInjector.clearActivePointers()
136+
renderer?.pause()
137+
}
138+
139+
fun releaseWave() {
140+
inputInjector.disable()
141+
renderer?.release()
142+
renderer = null
143+
effectEnabled = false
144+
visibility = View.GONE
145+
}
146+
147+
override fun onSurfaceTextureAvailable(surface: SurfaceTexture, width: Int, height: Int) {
148+
attachedSurfaceTexture = surface
149+
if (!effectEnabled) return
150+
ensureRenderer().attachSurface(surface, width, height)
151+
}
152+
153+
override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture, width: Int, height: Int) {
154+
attachedSurfaceTexture = surface
155+
if (!effectEnabled) return
156+
renderer?.resizeSurface(width, height)
157+
}
158+
159+
override fun onSurfaceTextureDestroyed(surface: SurfaceTexture): Boolean {
160+
attachedSurfaceTexture = null
161+
renderer?.detachSurface()
162+
return true
163+
}
164+
165+
override fun onSurfaceTextureUpdated(surface: SurfaceTexture) = Unit
166+
167+
@VisibleForTesting
168+
internal fun pointerStateCountForTesting(): Int = inputInjector.pointerStateCountForTesting()
169+
170+
@VisibleForTesting
171+
internal fun queuedInputCountForTesting(): Int = inputQueue.sizeForTesting()
172+
173+
@VisibleForTesting
174+
internal fun hasRendererForTesting(): Boolean = renderer != null
175+
176+
private fun canForwardInput(): Boolean {
177+
return effectEnabled && visibility == View.VISIBLE
178+
}
179+
180+
private fun ensureRenderer(): CinematicWaveRendererController {
181+
renderer?.let { return it }
182+
return rendererFactory.create(
183+
inputQueue,
184+
CinematicWaveRendererCallback { reason, throwable ->
185+
Timber.w(throwable, "Keyboard cinematic wave effect disabled: %s", reason)
186+
disableAfterRendererFailure()
187+
}
188+
).also { renderer = it }
189+
}
190+
191+
private fun disableAfterRendererFailure() {
192+
if (!effectEnabled && renderer == null) return
193+
inputInjector.disable()
194+
renderer?.release()
195+
renderer = null
196+
effectEnabled = false
197+
visibility = View.GONE
198+
}
199+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
package com.kazumaproject.markdownhelperkeyboard.ime_service.image_effect
2+
3+
internal enum class CinematicWaveInputKind {
4+
Down,
5+
Move,
6+
Up
7+
}
8+
9+
internal data class CinematicWaveTouchPoint(
10+
val pointerId: Int,
11+
var x: Float,
12+
var y: Float,
13+
var startTimeMs: Long,
14+
var lastUpdateTimeMs: Long,
15+
var pressure: Float,
16+
var strength: Float
17+
)
18+
19+
internal data class CinematicWaveTouchSnapshot(
20+
val x: Float,
21+
val y: Float,
22+
val ageSeconds: Float,
23+
val strength: Float
24+
)
25+
26+
internal sealed class CinematicWaveInputCommand {
27+
abstract val eventTimeMs: Long
28+
29+
data class Pointer(
30+
val pointerId: Int,
31+
val x: Float,
32+
val y: Float,
33+
val pressure: Float,
34+
val kind: CinematicWaveInputKind,
35+
override val eventTimeMs: Long
36+
) : CinematicWaveInputCommand()
37+
38+
data class PointerUp(
39+
val pointerId: Int,
40+
override val eventTimeMs: Long
41+
) : CinematicWaveInputCommand()
42+
43+
data class PointerCancel(
44+
val pointerId: Int,
45+
override val eventTimeMs: Long
46+
) : CinematicWaveInputCommand()
47+
48+
data class CancelAll(
49+
override val eventTimeMs: Long
50+
) : CinematicWaveInputCommand()
51+
}

0 commit comments

Comments
 (0)