From 3783048bb38dfd923292b79ef35b7a0021d5f87e Mon Sep 17 00:00:00 2001 From: ilia Date: Wed, 5 Aug 2026 14:30:04 -0400 Subject: [PATCH] Make VIP chips scrollable with icons Chip row was MATCH_PARENT so Share/Vikunja clipped with no scroll. Add icon+label chips, real horizontal scroll, peek nudge, taller sheet. --- CHANGELOG.md | 6 ++ app/build.gradle.kts | 4 +- .../accessibility/CrklAccessibilityService.kt | 2 +- .../com/example/crkl/ui/ResultPanelView.kt | 81 +++++++++++++++---- app/src/main/res/drawable/ic_chip_copy.xml | 10 +++ app/src/main/res/drawable/ic_chip_explain.xml | 10 +++ app/src/main/res/drawable/ic_chip_share.xml | 10 +++ app/src/main/res/drawable/ic_chip_todo.xml | 10 +++ .../main/res/drawable/ic_chip_translate.xml | 14 ++++ scripts/smoke-circle.sh | 28 ++++++- 10 files changed, 153 insertions(+), 22 deletions(-) create mode 100644 app/src/main/res/drawable/ic_chip_copy.xml create mode 100644 app/src/main/res/drawable/ic_chip_explain.xml create mode 100644 app/src/main/res/drawable/ic_chip_share.xml create mode 100644 app/src/main/res/drawable/ic_chip_todo.xml create mode 100644 app/src/main/res/drawable/ic_chip_translate.xml diff --git a/CHANGELOG.md b/CHANGELOG.md index aa46659..bb47c64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog +## 1.16.5-chip-scroll — 2026-08-05 + +- Action chips: icon + label; horizontal scroll actually works (child was MATCH_PARENT and clipped) +- Peek nudge + visible scrollbar so overflow is obvious +- Slightly taller result sheet so chips are not clipped + ## 1.16.4-light-scrim — 2026-08-05 - Draw mode scrim covers the full screen (was inset under status/nav) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 9f6b6d6..06bd2e9 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "com.example.crkl" minSdk = 27 targetSdk = 34 - versionCode = 21 - versionName = "1.16.4-light-scrim" + versionCode = 22 + versionName = "1.16.5-chip-scroll" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt b/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt index a6d16d2..63933da 100644 --- a/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt +++ b/app/src/main/kotlin/com/example/crkl/accessibility/CrklAccessibilityService.kt @@ -184,7 +184,7 @@ class CrklAccessibilityService : AccessibilityService() { private fun resultPanelParams(): WindowManager.LayoutParams { val metrics = resources.displayMetrics val width = (metrics.widthPixels * 0.94f).toInt() - val height = (metrics.heightPixels * 0.48f).toInt() + val height = (metrics.heightPixels * 0.54f).toInt() val nav = navigationBarHeight() return WindowManager.LayoutParams( width, diff --git a/app/src/main/kotlin/com/example/crkl/ui/ResultPanelView.kt b/app/src/main/kotlin/com/example/crkl/ui/ResultPanelView.kt index 97a069f..baa36e6 100644 --- a/app/src/main/kotlin/com/example/crkl/ui/ResultPanelView.kt +++ b/app/src/main/kotlin/com/example/crkl/ui/ResultPanelView.kt @@ -13,6 +13,7 @@ import android.widget.HorizontalScrollView import android.widget.LinearLayout import android.widget.ScrollView import android.widget.TextView +import com.example.crkl.R /** * Bottom-sheet style result card: title, text, five VIP chips, quiet status. @@ -129,24 +130,37 @@ class ResultPanelView( val actionsRow = LinearLayout(context).apply { orientation = HORIZONTAL gravity = Gravity.CENTER_VERTICAL + setPadding(0, 0, dp(28), 0) // peek next chip past the fade } - fun solidChip(label: String, bg: Int, onClick: () -> Unit): TextView = - TextView(context).apply { + fun actionChip( + label: String, + iconRes: Int, + bg: Int, + onClick: () -> Unit + ): TextView { + val icon = context.getDrawable(iconRes)?.mutate()?.apply { + setBounds(0, 0, dp(16), dp(16)) + } + return TextView(context).apply { text = label setTextColor(CrklUi.ChipFg) - setTextSize(TypedValue.COMPLEX_UNIT_SP, 13f) + setTextSize(TypedValue.COMPLEX_UNIT_SP, 12.5f) typeface = Typeface.DEFAULT_BOLD - setPadding(dp(14), dp(11), dp(14), dp(11)) + setCompoundDrawables(icon, null, null, null) + compoundDrawablePadding = dp(6) + gravity = Gravity.CENTER_VERTICAL + setPadding(dp(10), dp(9), dp(12), dp(9)) background = GradientDrawable().apply { setColor(bg) - cornerRadius = dp(20).toFloat() + cornerRadius = dp(18).toFloat() } val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) - lp.marginEnd = dp(8) + lp.marginEnd = dp(6) layoutParams = lp isClickable = true isFocusable = true + contentDescription = label setOnClickListener { statusView.visibility = VISIBLE statusView.setTextColor(CrklUi.Mist) @@ -154,29 +168,64 @@ class ResultPanelView( onClick() } } + } // VIP only — Email / Calendar stay on voice if (onTranslate != null) { - actionsRow.addView(solidChip("Translate", CrklUi.TealDeep, onTranslate)) + actionsRow.addView( + actionChip("Translate", R.drawable.ic_chip_translate, CrklUi.TealDeep, onTranslate) + ) } if (onCopy != null) { - actionsRow.addView(solidChip("Copy", CrklUi.InkSoft, onCopy)) + actionsRow.addView( + actionChip("Copy", R.drawable.ic_chip_copy, CrklUi.InkSoft, onCopy) + ) } if (onExplain != null) { - actionsRow.addView(solidChip("Explain", CrklUi.Ink, onExplain)) + actionsRow.addView( + actionChip("Explain", R.drawable.ic_chip_explain, CrklUi.Ink, onExplain) + ) } if (onShareList != null) { - actionsRow.addView(solidChip("Share", CrklUi.Mist, onShareList)) + actionsRow.addView( + actionChip("Share", R.drawable.ic_chip_share, CrklUi.Mist, onShareList) + ) } if (onAddTodo != null) { - actionsRow.addView(solidChip("Vikunja", CrklUi.Ok, onAddTodo)) + actionsRow.addView( + actionChip("Vikunja", R.drawable.ic_chip_todo, CrklUi.Ok, onAddTodo) + ) } - val actionsScroll = HorizontalScrollView(context).apply { - isHorizontalScrollBarEnabled = false - overScrollMode = OVER_SCROLL_NEVER - setPadding(0, dp(10), 0, dp(2)) - addView(actionsRow) + val actionsScroll = object : HorizontalScrollView(context) { + override fun onInterceptTouchEvent(ev: android.view.MotionEvent): Boolean { + when (ev.actionMasked) { + android.view.MotionEvent.ACTION_DOWN, + android.view.MotionEvent.ACTION_MOVE -> + parent?.requestDisallowInterceptTouchEvent(true) + } + return super.onInterceptTouchEvent(ev) + } + }.apply { + isHorizontalScrollBarEnabled = true + isScrollbarFadingEnabled = false + scrollBarStyle = View.SCROLLBARS_INSIDE_INSET + overScrollMode = OVER_SCROLL_IF_CONTENT_SCROLLS + isFillViewport = false + clipToPadding = false + setPadding(0, dp(8), dp(4), dp(6)) + // WRAP_CONTENT is required — MATCH_PARENT (default) clips chips instead of scrolling. + addView( + actionsRow, + LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) + ) + // Nudge once so the first overflow chip peeks (affordance). + post { + if (actionsRow.measuredWidth > width && scrollX == 0) { + smoothScrollBy(dp(28), 0) + postDelayed({ smoothScrollTo(0, 0) }, 450) + } + } } listenView = TextView(context).apply { diff --git a/app/src/main/res/drawable/ic_chip_copy.xml b/app/src/main/res/drawable/ic_chip_copy.xml new file mode 100644 index 0000000..9d72852 --- /dev/null +++ b/app/src/main/res/drawable/ic_chip_copy.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chip_explain.xml b/app/src/main/res/drawable/ic_chip_explain.xml new file mode 100644 index 0000000..3d6b044 --- /dev/null +++ b/app/src/main/res/drawable/ic_chip_explain.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chip_share.xml b/app/src/main/res/drawable/ic_chip_share.xml new file mode 100644 index 0000000..6a49a02 --- /dev/null +++ b/app/src/main/res/drawable/ic_chip_share.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chip_todo.xml b/app/src/main/res/drawable/ic_chip_todo.xml new file mode 100644 index 0000000..a129268 --- /dev/null +++ b/app/src/main/res/drawable/ic_chip_todo.xml @@ -0,0 +1,10 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chip_translate.xml b/app/src/main/res/drawable/ic_chip_translate.xml new file mode 100644 index 0000000..ad7686c --- /dev/null +++ b/app/src/main/res/drawable/ic_chip_translate.xml @@ -0,0 +1,14 @@ + + + + + + diff --git a/scripts/smoke-circle.sh b/scripts/smoke-circle.sh index d30a95d..77f4aa9 100755 --- a/scripts/smoke-circle.sh +++ b/scripts/smoke-circle.sh @@ -147,9 +147,31 @@ echo "$logs" | grep -qE 'extract:.*chars=[1-9]' || die "case2: no text extracted pass "email region extract" "$ADB" logcat -c -"$ADB" shell input tap 320 2185 -sleep 1 -"$ADB" logcat -d | grep -q 'action kind=COPY' || die "Copy chip not triggered" +# Chip row ~y=2100 on 1080x2400 with 54% sheet; Copy sits between Translate and Explain. +copied=0 +for y in 2090 2100 2110; do + for x in 340 360 320 300 380; do + "$ADB" shell input tap "$x" "$y" + sleep 0.35 + if "$ADB" logcat -d | grep -q 'action kind=COPY'; then + copied=1 + break 2 + fi + done +done +if [[ "$copied" != 1 ]]; then + "$ADB" shell input swipe 200 2100 700 2100 200 + sleep 0.3 + for x in 340 360 320; do + "$ADB" shell input tap "$x" 2100 + sleep 0.35 + if "$ADB" logcat -d | grep -q 'action kind=COPY'; then + copied=1 + break + fi + done +fi +[[ "$copied" = 1 ]] || die "Copy chip not triggered" "$ADB" logcat -d | grep -q 'action result ok=true' || die "Copy failed" pass "Copy chip"