1 — Pulsing session dot (live)
Current spec — alpha only
Live
Recent
Dead
<objectAnimator android:propertyName="alpha" android:valueFrom="1.0" android:valueTo="0.35" android:duration="900" android:interpolator="@anim/ease_in_out" android:repeatMode="reverse" android:repeatCount="infinite"/>

Current: alpha 1.0 → 0.35, ease-in-out, 900ms, reversed infinite. Confirmed — keep this timing, it's unobtrusive and semantically clear.

Proposed enhancement — rings variant (optional)
With rings
Alpha only
// CSS equivalent — Android uses box-shadow property @keyframes pulse-rings { 0% { box-shadow: 0 0 0 0 rgba(63,185,80,0.5); } 70% { box-shadow: 0 0 0 10px rgba(63,185,80,0); } 100% { box-shadow: 0 0 0 0 rgba(63,185,80,0); } } // duration: 1800ms (2× the alpha cycle) // In Android: ValueAnimator on custom View

The ring variant is more "alive" but more visually heavy. Recommend keeping alpha-only for the drawer (dense list) and using rings only on the status pill in the pane header where it has breathing room.

2 — Drawer slide-in / out
Live demo — hover to open
// DrawerLayout onDrawerSlide interpolation <translate android:fromXDelta="-100%" android:toXDelta="0" android:duration="280" android:interpolator= "@interpolator/fast_out_slow_in"/>
cubic-bezier(0.4, 0.0, 0.2, 1) — Material Design "Standard" easing. Fast out of the start position, decelerates gently to rest. 280ms — snappy but not jarring. This is already the Android DrawerLayout default; explicitly specify to prevent platform version drift.
Easing curve
Fast out, Slow in — cubic-bezier(0.4, 0.0, 0.2, 1)
Duration 280ms
Easing fast_out_slow_in
Dismiss (swipe close) linear_out_slow_in 200ms
3 — Keybar slide-in (terminal pane focus)
Live demo — hover to show keybar
jonathan@argus:~/kpmg$ _
ESC
TAB
CTRL
ALT
// Fires when: terminal pane becomes active pane <translate android:fromYDelta="100%" android:toYDelta="0" android:duration="220" android:interpolator= "@interpolator/linear_out_slow_in"/> // Dismiss (code-server pane focused): <translate android:fromYDelta="0" android:toYDelta="100%" android:duration="180" android:interpolator= "@interpolator/fast_out_linear_in"/>

220ms appear, 180ms dismiss. Decelerate on enter (linear_out_slow_in) — gives the bar a sense of weight landing. Accelerate on exit (fast_out_linear_in) — snaps out of the way quickly.

Trigger conditions
Show keybar when:
Active pane changes to a PaneType.TERMINAL pane. Also show on first terminal pane add.
Hide keybar when:
Active pane changes to non-terminal. Also hide when IME (soft keyboard) is up on Android 12+ via WindowInsetsController.
No animation when:
Switching between two terminal panes — keybar stays put, no animation triggered.
4 — Pane add / remove transition
Pane enters from the right edge
Before (2 panes)
pane 1
pane 2
+ Add pane 3 →
After (3 panes, pane 3 animated in)
pane 1
pane 2
pane 3
// New pane view enters from right, existing panes compress ValueAnimator.ofFloat(0f, finalWeight).apply { duration = 300 interpolator = FastOutSlowInInterpolator() addUpdateListener { layoutParams.weight = it.animatedValue as Float requestLayout() } }.start() // New pane alpha fade-in newPaneView.alpha = 0f newPaneView.animate() .alpha(1f) .setDuration(250) .setStartDelay(50) .setInterpolator(FastOutSlowInInterpolator()) .start()

Existing panes shrink their weight proportionally as the new pane expands from zero weight. The simultaneous weight animation + alpha fade-in takes 300ms total. No slide from offscreen — it grows in place from the right.

Status pill state transitions
All 4 states — 300ms cross-fade between them:
live
idle
stale
disc
// Status pill: cross-fade between state drawables TransitionDrawable([fromDrawable, toDrawable]).also { pillView.background = it it.startTransition(300) } // Dot color: ValueAnimator on textColor + drawable tint // Duration: 300ms, FastOutSlowIn // Pulsing dot (live state only): // Start ObjectAnimator on alpha when state == LIVE // Cancel animator on state change from LIVE
State change rule: live→idle is immediate (instant, no animation delay). idle→disconnected shows a 500ms shake micro-animation on the dot before going red, so the user catches the change. disconnected→idle/live fades normally.
Animation spec summary
Animation Duration Easing Property Notes
Session dot pulse 900ms ease-in-out alpha 1.0 → 0.35, reversed ∞ Live sessions only. Yellow/dead = static.
Drawer open 280ms fast_out_slow_in translateX -100% → 0 Material Standard easing.
Drawer close 200ms linear_out_slow_in translateX 0 → -100% Faster dismiss than open.
Keybar appear 220ms linear_out_slow_in translateY 100% → 0 Terminal pane activated.
Keybar dismiss 180ms fast_out_linear_in translateY 0 → 100% Non-terminal pane activated.
Pane add 300ms fast_out_slow_in weight 0→final + alpha 0→1 (+50ms delay) Weight via ValueAnimator + requestLayout.
Pane remove 200ms fast_out_linear_in alpha 1→0 then weight collapse 250ms Fade first, then weight collapse.
Status pill state change 300ms fast_out_slow_in background cross-fade + dot color idle→disc adds 500ms shake before red.