Skip to content

Modals, Button Presses, Keyboard update, Progress Bar#114

Open
dupontgu wants to merge 8 commits into
mainfrom
feat/key-forwarding
Open

Modals, Button Presses, Keyboard update, Progress Bar#114
dupontgu wants to merge 8 commits into
mainfrom
feat/key-forwarding

Conversation

@dupontgu

Copy link
Copy Markdown
Contributor
  • The primary purpose of this PR is to get hardware button presses working in tools.
    • Tools now have the option to intercept most hardware button presses: wheel counter/clockwise, wheel click, camera half/click, volume up/down.
    • Any unconsumed button events will be passed along to the server/LightOS to be processed (so brightness/volume adjustments can still work while external tools are foregrounded.
    • with this ^, we've given the server the ability to take over the screen when a button is clicked, and then return to the tool where the user was previously when finished. (For example - LightOS is responsible for displaying the Volume overlay)
    • Added basic ringer volume settings to the emulator to test/demo ^.
  • Modals - added a simple API in the ui module for both tools/the server to launch transient modal overlays on top of the normal app UI
  • Tightened up the tool authentication flow with the server, we noticed in certain situations they can get out of sync and the server would start rejecting remote service calls from valid clients.
  • Added "progress bar" ui element. There is a settable version as well (put finger down to set the progress)
  • Pulled bumped Lp3Keyboard version from 0.0.11 -> 0.0.16
    • Fixed small bugs around caps button
    • Laid groundwork for swapping root layouts (Colemak added!)
    • Laid groundwork for swipe typing (!!!!!)
    • Added support for external keyboard.

Reminder that if you are testing new features, they might not be available in LightOS yet. As of posting this PR, LightOS 568 is the latest for LPIII, and that will not yet be able to handle button presses forwarded from your tools

@dupontgu
dupontgu requested review from 0x09f and brunoro July 20, 2026 18:32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very much a Kotlin noob so this is probably a dumb question, but why not use a multiline string for this?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great Q honestly, that's one of the features I always forget about. I used appendLine for the dynamic stuff and then just duped the line a bunch. Will change.

}

private val Int.isSystemKeyCode: Boolean
get() = (this == KeyEvent.KEYCODE_BACK || this == KeyEvent.KEYCODE_HOME)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the circumstance when the Android BACK button will actually be emitted, given that the LP3 doesn't have a hardware one?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

back handling is mostly for emulator, external keyboard users, or if folks want to ship their tools on non-light devices!

Also, probably flip!!

displayVoice = result.displayVoice,
enableKeyAnimation = result.enableKeyAnimation
enableKeyAnimation = result.enableKeyAnimation,
swipeEnabled = false

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is necessary because of the keyboard version bump? Will this ever be updated to true?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great callout - I think I was avoiding adding a new field to the response before necessary but now's probably the perfect time to do it.

runCatching { ctx.unbindService(this) }
.onFailure { Log.w(TAG, "Failed to unbind service", it) }
bound = false
bind(ctx, pkg)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unfamiliar with this lifecycle, could this error loop?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

to my knowledge, no - bind calls bindService which just returns false if it fails.

}

fun onDeviceKeyEventRequest(request: LightServiceMethod.DeviceKeyEvent.Request): AudioModal? {
if (request.action != ACTION_DOWN) return null

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is again probably due to my lack of context, but would this handle the case where the user is just holding the button down to move the volume up/down repeatedly, ie key repeat?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep, ACTION_DOWN is sent repeatedly with a repeatCount field.

}

// Modeled after LightOS volume rocker modal
// In ideal world, this will be used directly in LightOS

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Meaning this would be factored out into a tiny standalone package that LightOS can import directly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep! or just pulled up from the emulator module into the server module which is shared.

// DO NOT REORDER/REMOVE
Unknown, Removed, InvalidParameters, NoPermission;
// DO NOT REORDER/REMOVE — ordinals are sent over the binder wire. Append new codes only.
Unknown, Removed, InvalidParameters, NoPermission, InvalidToken;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this just be sent in some other format over the wire to prevent that possible bug?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm probably unnecessarily allergic to using strings for this, but there's really no reason this should ever change. I think the easy way to safeguard is to just have a unit test that verifies hardcoded ordinals.

insertAtCursor(buildString { appendCodePoint(code) })
}

override fun onSubmitWord(word: CharSequence) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this something that was enabled by swiping in the keyboard upgrade? If so, why is this necessary if swiping is currently not supported in tools keyboards?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It technically is supported! We just don't have a default model yet. And in this case, there are likely going to be other layouts that need to submit multiple chars at once.


private var dismissJob: Job? = null

/** Show [modal] over the UI, replacing any current modal and restarting the dismiss timer. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So in other words there's no modal stack state? ie if Modal A has a dismiss duration of 5 seconds, and Modal B is 1 second -- after Modal B appears over Modal A and then is removed 1 second later, Modal A will be cleared early? Not that it really matters I guess.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's a very good point and worth considering actually, but so far these are explicitly meant to replace each other. conceptually they take up the same "space"

@0x09f 0x09f left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This all seems fine to me! I'm definitely a Kotlin noob though so worth getting another set of 👀


override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
return if (keyCode.isSystemKeyCode) {
super.onKeyDown(keyCode, event)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
super.onKeyDown(keyCode, event)
super.onKeyUp(keyCode, event)

*/
var permissionActivity: Class<out Activity>? = null

var rootActivity: Class<out Activity>? = null

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't find where this field is used

Comment thread sdk/ui/src/main/kotlin/com/thelightphone/sdk/ui/LightProgressBar.kt Outdated
@brunoro

brunoro commented Jul 22, 2026

Copy link
Copy Markdown

Looks great overall! Some review and LP3 testing notes:

  • modals worked as expected
  • new native LP3 keyboard is amazing! so responsive
  • found one small bug worth fixing in the progress bar event handling
  • found and one potential bug on the keyDown callback
  • progress bar example is missing a back button to return to the menu
  • it would be nice to have a demo to check for button presses

@brunoro brunoro left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alright, fixed the 2 blocking issues and added the back button.
now adding the key events UI demo screen

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants