Merge pull request 'Fix 1.16.6: icon chips + draw perf' (#8) from fix/1.16.6-icons-perf into main
This commit was merged in pull request #8.
This commit is contained in:
+28
-28
@@ -35,21 +35,21 @@ Do not start OCR / STT / dialogue memory until that loop works on a phone.
|
|||||||
## 2025-10-15 - Session 4: Testing & Makefile
|
## 2025-10-15 - Session 4: Testing & Makefile
|
||||||
|
|
||||||
**Completed:**
|
**Completed:**
|
||||||
- ✅ Successfully tested Crkl on Android emulator
|
- Successfully tested Crkl on Android emulator
|
||||||
- ✅ Verified touch detection working perfectly
|
- Verified touch detection working perfectly
|
||||||
- ✅ Created comprehensive Makefile with 50+ commands
|
- Created comprehensive Makefile with 50+ commands
|
||||||
- ✅ Created MAKEFILE_GUIDE.md for reference
|
- Created MAKEFILE_GUIDE.md for reference
|
||||||
- ✅ Created CHEATSHEET.md for quick commands
|
- Created CHEATSHEET.md for quick commands
|
||||||
- ✅ Created DEMO_GUIDE.md for presenting
|
- Created DEMO_GUIDE.md for presenting
|
||||||
- ✅ Created TEST_RESULTS.md with full validation
|
- Created TEST_RESULTS.md with full validation
|
||||||
- ✅ Created HOW_TO_TEST.md step-by-step guide
|
- Created HOW_TO_TEST.md step-by-step guide
|
||||||
|
|
||||||
**Test Results:**
|
**Test Results:**
|
||||||
- ✅ Touch detection: PASS (4/4 test points)
|
- Touch detection: PASS (4/4 test points)
|
||||||
- ✅ Accessibility service: PASS
|
- Accessibility service: PASS
|
||||||
- ✅ System-wide overlay: PASS
|
- System-wide overlay: PASS
|
||||||
- ✅ Logging: PASS
|
- Logging: PASS
|
||||||
- ✅ Performance: PASS (< 16ms latency)
|
- Performance: PASS (< 16ms latency)
|
||||||
- **POC Status: 100% COMPLETE**
|
- **POC Status: 100% COMPLETE**
|
||||||
|
|
||||||
**Key Achievements:**
|
**Key Achievements:**
|
||||||
@@ -70,13 +70,13 @@ Do not start OCR / STT / dialogue memory until that loop works on a phone.
|
|||||||
## 2025-10-15 - Session 3: Cursor AI Optimization
|
## 2025-10-15 - Session 3: Cursor AI Optimization
|
||||||
|
|
||||||
**Completed:**
|
**Completed:**
|
||||||
- ✅ Enhanced `.cursorrules` with comprehensive AI guidelines
|
- Enhanced `.cursorrules` with comprehensive AI guidelines
|
||||||
- ✅ Improved `.cursorignore` to exclude all build artifacts
|
- Improved `.cursorignore` to exclude all build artifacts
|
||||||
- ✅ Created `TESTING_ON_LINUX.md` - Complete Linux testing guide
|
- Created `TESTING_ON_LINUX.md` - Complete Linux testing guide
|
||||||
- ✅ Created `PROJECT_REVIEW.md` - Comprehensive project analysis
|
- Created `PROJECT_REVIEW.md` - Comprehensive project analysis
|
||||||
- ✅ Added `scripts/setup_emulator.sh` - One-command emulator setup
|
- Added `scripts/setup_emulator.sh` - One-command emulator setup
|
||||||
- ✅ Verified project follows Cursor AI best practices
|
- Verified project follows Cursor AI best practices
|
||||||
- ✅ Documented all testing options (emulator, unit tests, instrumentation)
|
- Documented all testing options (emulator, unit tests, instrumentation)
|
||||||
|
|
||||||
**Key Insights:**
|
**Key Insights:**
|
||||||
- **No phone needed!** Can test entirely on Linux with Android emulator
|
- **No phone needed!** Can test entirely on Linux with Android emulator
|
||||||
@@ -99,17 +99,17 @@ Do not start OCR / STT / dialogue memory until that loop works on a phone.
|
|||||||
## 2025-10-15 - Session 2: POC Implementation
|
## 2025-10-15 - Session 2: POC Implementation
|
||||||
|
|
||||||
**Completed:**
|
**Completed:**
|
||||||
- ✅ Installed Android SDK command-line tools on Linux
|
- Installed Android SDK command-line tools on Linux
|
||||||
- ✅ Created proper Android project structure (Gradle, Kotlin, Jetpack Compose)
|
- Created proper Android project structure (Gradle, Kotlin, Jetpack Compose)
|
||||||
- ✅ Implemented MainActivity with onboarding UI
|
- Implemented MainActivity with onboarding UI
|
||||||
- ✅ Created AccessibilityService with system-wide overlay
|
- Created AccessibilityService with system-wide overlay
|
||||||
- ✅ Built OverlayView with basic touch detection
|
- Built OverlayView with basic touch detection
|
||||||
- ✅ Successfully compiled and generated APK
|
- Successfully compiled and generated APK
|
||||||
- ✅ Created comprehensive testing documentation
|
- Created comprehensive testing documentation
|
||||||
|
|
||||||
**Current Status:**
|
**Current Status:**
|
||||||
- **Phase:** POC (Proof of Concept) - Milestone 1
|
- **Phase:** POC (Proof of Concept) - Milestone 1
|
||||||
- **Build:** ✅ SUCCESS
|
- **Build:** SUCCESS
|
||||||
- **APK:** `app/build/outputs/apk/debug/app-debug.apk` (15MB)
|
- **APK:** `app/build/outputs/apk/debug/app-debug.apk` (15MB)
|
||||||
- **Functionality:** Basic overlay with touch visualization
|
- **Functionality:** Basic overlay with touch visualization
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## 1.16.6-icons-perf — 2026-08-05
|
||||||
|
|
||||||
|
- VIP actions are icon-only (Todo = checkbox with checkmark)
|
||||||
|
- Perf: drop software layer + BlurMaskFilter; draw stroke via Path; throttle points
|
||||||
|
|
||||||
## 1.16.5-chip-scroll — 2026-08-05
|
## 1.16.5-chip-scroll — 2026-08-05
|
||||||
|
|
||||||
- Action chips: icon + label; horizontal scroll actually works (child was MATCH_PARENT and clipped)
|
- Action chips: icon + label; horizontal scroll actually works (child was MATCH_PARENT and clipped)
|
||||||
|
|||||||
+6
-6
@@ -134,12 +134,12 @@ Refer to `.notes/directory_structure.md` for module organization:
|
|||||||
|
|
||||||
**Critical:** All contributions must adhere to privacy-first principles:
|
**Critical:** All contributions must adhere to privacy-first principles:
|
||||||
|
|
||||||
- ✅ **DO:** Use local-only processing
|
- DO: Use local-only processing
|
||||||
- ✅ **DO:** Keep all data on device
|
- DO: Keep all data on device
|
||||||
- ✅ **DO:** Use offline ML models
|
- DO: Use offline ML models
|
||||||
- ❌ **DON'T:** Add network calls for AI features
|
- DON'T: Add network calls for AI features
|
||||||
- ❌ **DON'T:** Send data to external services
|
- DON'T: Send data to external services
|
||||||
- ❌ **DON'T:** Include cloud service dependencies
|
- DON'T: Include cloud service dependencies
|
||||||
|
|
||||||
## Review Process
|
## Review Process
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Circle
|
# Circle
|
||||||
|
|
||||||
Privacy-first Android assist: **circle on-screen text → Translate · Copy · Explain · Share · Vikunja**. On-device.
|
Android assist app: circle on-screen text, then Translate, Copy, Explain, Share,
|
||||||
|
or Vikunja. On-device processing.
|
||||||
|
|
||||||
Repo / Gradle module: **crkl** · Application id: `com.example.crkl` · Display name: **Circle**
|
Repo / Gradle module: **crkl** · Application id: `com.example.crkl` · Display name: **Circle**
|
||||||
|
|
||||||
@@ -17,14 +18,14 @@ Repo / Gradle module: **crkl** · Application id: `com.example.crkl` · Display
|
|||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- **Circle → text → act** (a11y + on-device OCR)
|
- Circle gesture captures on-screen text (accessibility service + on-device OCR)
|
||||||
- Chips: **Translate · Copy · Explain · Share · Vikunja**
|
- Action chips: Translate, Copy, Explain, Share, Vikunja
|
||||||
- **Vikunja** HTTPS for “add to todo”
|
- Vikunja HTTPS integration for add-to-todo
|
||||||
- Optional mailto / lab gog bridge (Advanced in Integrations)
|
- Optional mailto / lab gog bridge (Advanced in Integrations)
|
||||||
- Device calendar read (Advanced)
|
- Device calendar read (Advanced)
|
||||||
- Circle style (color, neon, stroke)
|
- Circle style settings (color, neon, stroke)
|
||||||
- First-run onboarding; clean panel (debug meta optional)
|
- First-run onboarding; optional debug metadata in panel
|
||||||
- Shortcuts: Quick Settings **Circle** tile, Accessibility button, volume-key a11y shortcut
|
- Shortcuts: Quick Settings Circle tile, Accessibility button, volume-key shortcut
|
||||||
- Local LLM when a `.task` model is present; otherwise stub Explain / summary
|
- Local LLM when a `.task` model is present; otherwise stub Explain / summary
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
@@ -91,4 +92,4 @@ Inference is MediaPipe on-device (`make push-model`). Without a model, Circle us
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
See repository license file.
|
MIT — see [LICENSE](LICENSE).
|
||||||
|
|||||||
@@ -11,8 +11,8 @@ android {
|
|||||||
applicationId = "com.example.crkl"
|
applicationId = "com.example.crkl"
|
||||||
minSdk = 27
|
minSdk = 27
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 22
|
versionCode = 23
|
||||||
versionName = "1.16.5-chip-scroll"
|
versionName = "1.16.6-icons-perf"
|
||||||
|
|
||||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
package com.example.crkl.accessibility
|
package com.example.crkl.accessibility
|
||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.BlurMaskFilter
|
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.graphics.Color
|
import android.graphics.Color
|
||||||
import android.graphics.Paint
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Path
|
||||||
import android.graphics.PointF
|
import android.graphics.PointF
|
||||||
import android.graphics.RectF
|
import android.graphics.RectF
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
@@ -41,8 +41,9 @@ class OverlayView(
|
|||||||
strokeWidth = dp(2.5f)
|
strokeWidth = dp(2.5f)
|
||||||
}
|
}
|
||||||
private val buttonShadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val buttonShadowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
color = Color.parseColor("#55000000")
|
// Soft disc — BlurMaskFilter forced LAYER_TYPE_SOFTWARE and lagged the whole UI.
|
||||||
maskFilter = BlurMaskFilter(dp(10f), BlurMaskFilter.Blur.NORMAL)
|
color = Color.parseColor("#33000000")
|
||||||
|
style = Paint.Style.FILL
|
||||||
}
|
}
|
||||||
|
|
||||||
private var isOverlayMode = false
|
private var isOverlayMode = false
|
||||||
@@ -69,8 +70,10 @@ class OverlayView(
|
|||||||
|
|
||||||
private var isDrawing = false
|
private var isDrawing = false
|
||||||
private var touchPath = mutableListOf<PointF>()
|
private var touchPath = mutableListOf<PointF>()
|
||||||
|
private val strokePath = Path()
|
||||||
private var hintMessage = "Draw a closed loop around text"
|
private var hintMessage = "Draw a closed loop around text"
|
||||||
private var failFlashUntil = 0L
|
private var failFlashUntil = 0L
|
||||||
|
private val minPointSpacingPx = dp(3f)
|
||||||
|
|
||||||
private val glowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
private val glowPaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||||
style = Paint.Style.STROKE
|
style = Paint.Style.STROKE
|
||||||
@@ -117,7 +120,8 @@ class OverlayView(
|
|||||||
private var touchStartedInExit = false
|
private var touchStartedInExit = false
|
||||||
|
|
||||||
init {
|
init {
|
||||||
setLayerType(LAYER_TYPE_SOFTWARE, null)
|
// Hardware layer — software + BlurMaskFilter made draw/scroll feel sticky.
|
||||||
|
setLayerType(LAYER_TYPE_HARDWARE, null)
|
||||||
setBackgroundColor(Color.TRANSPARENT)
|
setBackgroundColor(Color.TRANSPARENT)
|
||||||
isClickable = true
|
isClickable = true
|
||||||
isFocusable = false
|
isFocusable = false
|
||||||
@@ -134,14 +138,11 @@ class OverlayView(
|
|||||||
neonEnabled = neon
|
neonEnabled = neon
|
||||||
pathPaint.color = colorArgb
|
pathPaint.color = colorArgb
|
||||||
pathPaint.strokeWidth = dp(strokeDp)
|
pathPaint.strokeWidth = dp(strokeDp)
|
||||||
|
// Fake glow with a wider translucent stroke — no BlurMaskFilter.
|
||||||
glowPaint.color = colorArgb
|
glowPaint.color = colorArgb
|
||||||
glowPaint.strokeWidth = dp(strokeDp + 6f)
|
glowPaint.strokeWidth = dp(strokeDp + 5f)
|
||||||
glowPaint.alpha = if (neon) 120 else 0
|
glowPaint.alpha = if (neon) 70 else 0
|
||||||
glowPaint.maskFilter = if (neon) {
|
glowPaint.maskFilter = null
|
||||||
BlurMaskFilter(dp(12f), BlurMaskFilter.Blur.NORMAL)
|
|
||||||
} else {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,8 +152,7 @@ class OverlayView(
|
|||||||
fun enterOverlayMode() {
|
fun enterOverlayMode() {
|
||||||
if (isOverlayMode) return
|
if (isOverlayMode) return
|
||||||
isOverlayMode = true
|
isOverlayMode = true
|
||||||
touchPath.clear()
|
clearStroke()
|
||||||
selectionBounds = null
|
|
||||||
touchStartedInExit = false
|
touchStartedInExit = false
|
||||||
successFlash = false
|
successFlash = false
|
||||||
hintMessage = "Draw a closed loop around text"
|
hintMessage = "Draw a closed loop around text"
|
||||||
@@ -164,8 +164,7 @@ class OverlayView(
|
|||||||
fun exitOverlayMode() {
|
fun exitOverlayMode() {
|
||||||
if (!isOverlayMode) return
|
if (!isOverlayMode) return
|
||||||
isOverlayMode = false
|
isOverlayMode = false
|
||||||
touchPath.clear()
|
clearStroke()
|
||||||
selectionBounds = null
|
|
||||||
touchStartedInExit = false
|
touchStartedInExit = false
|
||||||
successFlash = false
|
successFlash = false
|
||||||
hintMessage = "Draw a closed loop around text"
|
hintMessage = "Draw a closed loop around text"
|
||||||
@@ -175,8 +174,7 @@ class OverlayView(
|
|||||||
|
|
||||||
private fun toggleOverlayMode() {
|
private fun toggleOverlayMode() {
|
||||||
isOverlayMode = !isOverlayMode
|
isOverlayMode = !isOverlayMode
|
||||||
touchPath.clear()
|
clearStroke()
|
||||||
selectionBounds = null
|
|
||||||
touchStartedInExit = false
|
touchStartedInExit = false
|
||||||
successFlash = false
|
successFlash = false
|
||||||
hintMessage = "Draw a closed loop around text"
|
hintMessage = "Draw a closed loop around text"
|
||||||
@@ -187,6 +185,25 @@ class OverlayView(
|
|||||||
invalidate()
|
invalidate()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun clearStroke() {
|
||||||
|
touchPath.clear()
|
||||||
|
strokePath.reset()
|
||||||
|
selectionBounds = null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun appendStrokePoint(x: Float, y: Float) {
|
||||||
|
val last = touchPath.lastOrNull()
|
||||||
|
if (last != null) {
|
||||||
|
val dx = x - last.x
|
||||||
|
val dy = y - last.y
|
||||||
|
if (dx * dx + dy * dy < minPointSpacingPx * minPointSpacingPx) return
|
||||||
|
strokePath.lineTo(x, y)
|
||||||
|
} else {
|
||||||
|
strokePath.moveTo(x, y)
|
||||||
|
}
|
||||||
|
touchPath.add(PointF(x, y))
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
if (!isOverlayMode) {
|
if (!isOverlayMode) {
|
||||||
return super.onTouchEvent(event)
|
return super.onTouchEvent(event)
|
||||||
@@ -201,13 +218,12 @@ class OverlayView(
|
|||||||
if (touchStartedInExit) return true
|
if (touchStartedInExit) return true
|
||||||
isDrawing = true
|
isDrawing = true
|
||||||
successFlash = false
|
successFlash = false
|
||||||
touchPath.clear()
|
clearStroke()
|
||||||
selectionBounds = null
|
appendStrokePoint(motionEvent.x, motionEvent.y)
|
||||||
touchPath.add(PointF(motionEvent.x, motionEvent.y))
|
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
if (!touchStartedInExit && isDrawing) {
|
if (!touchStartedInExit && isDrawing) {
|
||||||
touchPath.add(PointF(motionEvent.x, motionEvent.y))
|
appendStrokePoint(motionEvent.x, motionEvent.y)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
||||||
@@ -317,7 +333,7 @@ class OverlayView(
|
|||||||
val cx = width / 2f
|
val cx = width / 2f
|
||||||
val cy = height / 2f
|
val cy = height / 2f
|
||||||
val r = buttonSizePx / 2f
|
val r = buttonSizePx / 2f
|
||||||
canvas.drawCircle(cx, cy + dp(2f), r, buttonShadowPaint)
|
canvas.drawCircle(cx, cy + dp(2f), r + dp(1.5f), buttonShadowPaint)
|
||||||
canvas.drawCircle(cx, cy, r, buttonFillPaint)
|
canvas.drawCircle(cx, cy, r, buttonFillPaint)
|
||||||
canvas.drawCircle(cx, cy, r - dp(3f), buttonRingPaint)
|
canvas.drawCircle(cx, cy, r - dp(3f), buttonRingPaint)
|
||||||
canvas.drawText("C", cx, cy + textPaint.textSize / 3f, textPaint)
|
canvas.drawText("C", cx, cy + textPaint.textSize / 3f, textPaint)
|
||||||
@@ -355,15 +371,11 @@ class OverlayView(
|
|||||||
exitTextPaint
|
exitTextPaint
|
||||||
)
|
)
|
||||||
|
|
||||||
if (touchPath.size > 1) {
|
if (!strokePath.isEmpty) {
|
||||||
for (i in 1 until touchPath.size) {
|
if (neonEnabled && glowPaint.alpha > 0) {
|
||||||
val prev = touchPath[i - 1]
|
canvas.drawPath(strokePath, glowPaint)
|
||||||
val curr = touchPath[i]
|
|
||||||
if (neonEnabled) {
|
|
||||||
canvas.drawLine(prev.x, prev.y, curr.x, curr.y, glowPaint)
|
|
||||||
}
|
|
||||||
canvas.drawLine(prev.x, prev.y, curr.x, curr.y, pathPaint)
|
|
||||||
}
|
}
|
||||||
|
canvas.drawPath(strokePath, pathPaint)
|
||||||
}
|
}
|
||||||
|
|
||||||
selectionBounds?.let { bounds ->
|
selectionBounds?.let { bounds ->
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ import android.view.Gravity
|
|||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.animation.DecelerateInterpolator
|
import android.view.animation.DecelerateInterpolator
|
||||||
import android.widget.HorizontalScrollView
|
import android.widget.HorizontalScrollView
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import android.widget.ImageView
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.ScrollView
|
import android.widget.ScrollView
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
@@ -130,7 +132,6 @@ class ResultPanelView(
|
|||||||
val actionsRow = LinearLayout(context).apply {
|
val actionsRow = LinearLayout(context).apply {
|
||||||
orientation = HORIZONTAL
|
orientation = HORIZONTAL
|
||||||
gravity = Gravity.CENTER_VERTICAL
|
gravity = Gravity.CENTER_VERTICAL
|
||||||
setPadding(0, 0, dp(28), 0) // peek next chip past the fade
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun actionChip(
|
fun actionChip(
|
||||||
@@ -138,26 +139,20 @@ class ResultPanelView(
|
|||||||
iconRes: Int,
|
iconRes: Int,
|
||||||
bg: Int,
|
bg: Int,
|
||||||
onClick: () -> Unit
|
onClick: () -> Unit
|
||||||
): TextView {
|
): ImageButton {
|
||||||
val icon = context.getDrawable(iconRes)?.mutate()?.apply {
|
val size = dp(44)
|
||||||
setBounds(0, 0, dp(16), dp(16))
|
return ImageButton(context).apply {
|
||||||
}
|
setImageResource(iconRes)
|
||||||
return TextView(context).apply {
|
imageTintList = android.content.res.ColorStateList.valueOf(CrklUi.ChipFg)
|
||||||
text = label
|
scaleType = ImageView.ScaleType.CENTER_INSIDE
|
||||||
setTextColor(CrklUi.ChipFg)
|
setPadding(dp(10), dp(10), dp(10), dp(10))
|
||||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 12.5f)
|
|
||||||
typeface = Typeface.DEFAULT_BOLD
|
|
||||||
setCompoundDrawables(icon, null, null, null)
|
|
||||||
compoundDrawablePadding = dp(6)
|
|
||||||
gravity = Gravity.CENTER_VERTICAL
|
|
||||||
setPadding(dp(10), dp(9), dp(12), dp(9))
|
|
||||||
background = GradientDrawable().apply {
|
background = GradientDrawable().apply {
|
||||||
setColor(bg)
|
setColor(bg)
|
||||||
cornerRadius = dp(18).toFloat()
|
cornerRadius = dp(22).toFloat()
|
||||||
|
}
|
||||||
|
layoutParams = LayoutParams(size, size).apply {
|
||||||
|
marginEnd = dp(8)
|
||||||
}
|
}
|
||||||
val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
|
||||||
lp.marginEnd = dp(6)
|
|
||||||
layoutParams = lp
|
|
||||||
isClickable = true
|
isClickable = true
|
||||||
isFocusable = true
|
isFocusable = true
|
||||||
contentDescription = label
|
contentDescription = label
|
||||||
@@ -193,7 +188,7 @@ class ResultPanelView(
|
|||||||
}
|
}
|
||||||
if (onAddTodo != null) {
|
if (onAddTodo != null) {
|
||||||
actionsRow.addView(
|
actionsRow.addView(
|
||||||
actionChip("Vikunja", R.drawable.ic_chip_todo, CrklUi.Ok, onAddTodo)
|
actionChip("Todo", R.drawable.ic_chip_todo, CrklUi.Ok, onAddTodo)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -207,25 +202,15 @@ class ResultPanelView(
|
|||||||
return super.onInterceptTouchEvent(ev)
|
return super.onInterceptTouchEvent(ev)
|
||||||
}
|
}
|
||||||
}.apply {
|
}.apply {
|
||||||
isHorizontalScrollBarEnabled = true
|
isHorizontalScrollBarEnabled = false
|
||||||
isScrollbarFadingEnabled = false
|
|
||||||
scrollBarStyle = View.SCROLLBARS_INSIDE_INSET
|
|
||||||
overScrollMode = OVER_SCROLL_IF_CONTENT_SCROLLS
|
overScrollMode = OVER_SCROLL_IF_CONTENT_SCROLLS
|
||||||
isFillViewport = false
|
isFillViewport = false
|
||||||
clipToPadding = false
|
clipToPadding = false
|
||||||
setPadding(0, dp(8), dp(4), dp(6))
|
setPadding(0, dp(8), dp(8), dp(4))
|
||||||
// WRAP_CONTENT is required — MATCH_PARENT (default) clips chips instead of scrolling.
|
|
||||||
addView(
|
addView(
|
||||||
actionsRow,
|
actionsRow,
|
||||||
LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
|
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 {
|
listenView = TextView(context).apply {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!-- Filled checkbox + check — stroke-only vectors are unreliable on older APIs -->
|
||||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:width="24dp"
|
android:width="24dp"
|
||||||
android:height="24dp"
|
android:height="24dp"
|
||||||
@@ -6,5 +7,8 @@
|
|||||||
android:viewportHeight="24">
|
android:viewportHeight="24">
|
||||||
<path
|
<path
|
||||||
android:fillColor="#FFFFFFFF"
|
android:fillColor="#FFFFFFFF"
|
||||||
android:pathData="M6,5h12v2H6V5zM6,11h12v2H6v-2zM6,17h8v2H6v-2zM18.5,16.2l1.4,1.4 -3.2,3.2 -2,-2 1.4,-1.4 0.6,0.6 1.8,-1.8z" />
|
android:pathData="M6,3h12c1.7,0 3,1.3 3,3v12c0,1.7 -1.3,3 -3,3H6c-1.7,0 -3,-1.3 -3,-3V6c0,-1.7 1.3,-3 3,-3zM7.5,5.5c-0.8,0 -1.5,0.7 -1.5,1.5v10c0,0.8 0.7,1.5 1.5,1.5h9c0.8,0 1.5,-0.7 1.5,-1.5V7c0,-0.8 -0.7,-1.5 -1.5,-1.5h-9z" />
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M10.3,15.7L7,12.4l1.4,-1.4 1.9,1.9 4.9,-4.9 1.4,1.4z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|||||||
+3
-15
@@ -147,30 +147,18 @@ echo "$logs" | grep -qE 'extract:.*chars=[1-9]' || die "case2: no text extracted
|
|||||||
pass "email region extract"
|
pass "email region extract"
|
||||||
|
|
||||||
"$ADB" logcat -c
|
"$ADB" logcat -c
|
||||||
# Chip row ~y=2100 on 1080x2400 with 54% sheet; Copy sits between Translate and Explain.
|
# Icon-only chips: Translate, Copy, Explain, Share, Todo — ~44dp, y≈2100.
|
||||||
copied=0
|
copied=0
|
||||||
for y in 2090 2100 2110; do
|
for y in 2090 2100 2110; do
|
||||||
for x in 340 360 320 300 380; do
|
for x in 200 240 280 160 320; do
|
||||||
"$ADB" shell input tap "$x" "$y"
|
"$ADB" shell input tap "$x" "$y"
|
||||||
sleep 0.35
|
sleep 0.3
|
||||||
if "$ADB" logcat -d | grep -q 'action kind=COPY'; then
|
if "$ADB" logcat -d | grep -q 'action kind=COPY'; then
|
||||||
copied=1
|
copied=1
|
||||||
break 2
|
break 2
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
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"
|
[[ "$copied" = 1 ]] || die "Copy chip not triggered"
|
||||||
"$ADB" logcat -d | grep -q 'action result ok=true' || die "Copy failed"
|
"$ADB" logcat -d | grep -q 'action result ok=true' || die "Copy failed"
|
||||||
pass "Copy chip"
|
pass "Copy chip"
|
||||||
|
|||||||
Reference in New Issue
Block a user