POINTCOUNTER
POINTCOUNTER

Un marcador de pádel y tenis que vive en la muñeca.

Tocas la mitad izquierda o la derecha de la pantalla. Él lleva los puntos, los juegos, los sets, el tie-break, el punto de oro y recuerda quién saca. Y lo replica en el móvil mientras juegas.

▶ Lo que se ve en el reloj es una grabación real, sin montaje: 23 segundos de partido entero.

40 · AD
SETS 1–0
BOLA DE PARTIDO
Las pantallas

Tres pantallas y ni una de más

Pantalla de configuración con selector de deporte
ANTES
Configuración
Deporte, formato, punto de oro y quién abre el saque. Cada ajuste cicla al tocarlo: cero submenús.
Aviso de bola de partido en naranja sobre el marcador
DURANTE
Bola de partido
El aviso sale solo cuando el punto vale algo: rotura, set o partido. En naranja, imposible de perderse.
Pantalla de victoria con el resumen del partido
DESPUÉS
Resumen
Marcador set a set, duración y puntos jugados. Revancha en un toque, con el mismo formato.
Reloj y móvil

El marcador, también en el bolsillo

El marcador tal y como se ve en el reloj
EN VIVO
PÁDEL
SETS 0-0
0juegos0
PUNTO DE ORO
J1
40
J2
40

El reloj publica cada punto por la Wearable Data Layer API. Como el dato queda guardado en el canal, abrir el móvil a mitad de partido enseña el marcador al instante, sin esperar al punto siguiente.

La app de móvil

El reloj cuenta, el móvil recuerda

El reloj tiene una pantalla de 45 milímetros y una batería que cuidar. El teléfono tiene teclado, espacio y memoria larga. Cada uno hace lo que se le da bien.

NOMBRESEscritos con un teclado de verdad
Pones los nombres en el móvil y viajan al reloj, que deja de llamarlos J1 y J2. Es el único dato que va del teléfono al reloj, y va por su propio canal: el marcador sigue circulando en una sola dirección.
HISTORIALLo que el reloj no puede guardar
Cada partido terminado se archiva solo, con su marcador set a set, duración, puntos y fecha. El partido del reloj vive en memoria; el móvil es el que tiene sitio para acordarse.
ESTADÍSTICASPuntos, roturas y rachas
Puntos ganados, juegos arrancados al saque contrario, mejor racha seguida y porcentaje. Se calculan dentro del motor y llegan ya resueltas: el móvil sigue sin implementar ni una regla del reglamento.
COMPARTIRUna imagen, no una captura
El resultado se dibuja sobre un lienzo de 1080×1080 y sale por el selector del sistema. Se genera igual en cualquier teléfono, sin depender del tamaño de pantalla ni de la densidad.
Llévatela

Descárgala y juega

Gratis y de código abierto. Sin anuncios, sin cuenta y sin que salga un solo dato del reloj ni del teléfono.

Para el reloj

WEAR OS 3+ · ANDROID 11 (API 30)

El marcador. Es la app principal: la que tocas mientras juegas, y la que lleva la cuenta.

Descargar APK →

Para el móvil

ANDROID 8+ (API 26)

La companion. Replica el marcador en vivo, guarda el historial y comparte el resultado como imagen.

Descargar APK →

En el móvil basta con abrir el archivo y aceptar instalar de orígenes desconocidos. En el reloj no hay atajo: Wear OS no permite instalar un APK descargándolo desde el propio reloj, hay que activar la depuración ADB y mandarlo desde un ordenador con adb install. Los pasos, uno a uno, están en el README.

La arquitectura

Un módulo abajo, dos apps encima

Toda la puntuación vive en un módulo de Kotlin puro, sin una sola dependencia de Android. El reloj y el móvil se apoyan en él sin copiar una línea.

mobile-appPINTA Y RECUERDA
wear-appPINTA Y REENVÍA
core-scoringKOTLIN PURO · 0 DEPS
CORE-SCORING

Reglas, estado inmutable, historial y la foto del marcador que viaja. Kotlin JVM, sin Android: por eso los 33 tests corren en CI sin necesitar el SDK.

Sport.kt · MatchState.kt · Match.kt · LiveScore.kt · MatchStats.kt
WEAR-APP

Pinta el estado y reenvía los toques. No calcula puntuación: cuando cambia una regla, no hay nada que tocar aquí.

MatchViewModel.kt · ScoreScreen.kt · ScoreBroadcaster.kt
MOBILE-APP

No calcula ni un punto del reglamento: recibe el marcador y las estadísticas ya resueltas. Lo que sí hace es recordar, que es justo lo que el reloj no puede.

LiveScoreViewModel.kt · LiveScoreScreen.kt · MatchHistory.kt · ShareCard.kt · NamesPublisher.kt
Decisiones de diseño

Por qué se lee de un vistazo

COLORUn color por jugador
J1 en volt, J2 en cian. Sabes de quién es cada número sin leer ninguna etiqueta, que es lo único que se le puede pedir a una pantalla que miras medio segundo entre punto y punto.
FONDONegro de verdad
No gris oscuro: #000000. En AMOLED los píxeles negros están apagados, así que gasta menos batería y da el contraste máximo a pleno sol.
ZONA TÁCTILLa pantalla entera
No hay botones que acertar. La mitad izquierda es un jugador y la derecha el otro: un objetivo de 227 píxeles de ancho se acierta sin mirar y con la mano sudada.
FORMABotón que abraza la curva
El EdgeButton de Material 3 Expressive, el lenguaje visual nuevo de Wear OS. Se curva con el borde del reloj en vez de ser un rectángulo al que le sobran las esquinas.
TACTOVibración con significado
Un pulso corto para el punto, dos para el juego, tres para el set y una secuencia larga para el partido. Se puede jugar sin mirar el reloj.
SALIDANada se pierde sin querer
Se sale manteniendo pulsado o con el gesto de volver, y siempre con confirmación que enseña cómo va el marcador. Sin eso, el gesto del sistema cerraba la app y se llevaba el partido por delante.
Bajo el capó

El reglamento es la parte difícil

0
tests del reglamento
0
deps de Android en el motor
0
toques para empezar
puntos que deshacer
MOTOREstado inmutable
Sumar un punto no modifica el partido: devuelve uno nuevo. El historial es la lista de esos estados, así que deshacer es quitar el último, con la profundidad que quieras y sin casos especiales.
AVISOSSin reglas duplicadas
Para saber si hay bola de partido, el motor simula el punto siguiente sobre sí mismo y mira si alguien gana. No hay una segunda copia del reglamento que pueda desincronizarse.
SAQUELa rotación del tie-break
Uno saca el primer punto y a partir de ahí se alterna cada dos. Es el detalle que nadie nota cuando está bien y todo el mundo nota cuando está mal.
SINCRONÍAEl móvil no calcula nada
El reloj manda una foto ya resuelta del marcador, no el partido en bruto. Las dos pantallas no pueden discrepar porque solo hay un sitio donde se aplican las reglas.
REUTILIZABLEUn módulo, tres consumidores
La puntuación vive en Kotlin puro, sin tocar Android. La usan el reloj, el móvil y los tests, sin copiar una línea.
Notas de desarrollo

Las trampas que me comí

Ninguna de estas sale en el «hola mundo» de Wear OS. Todas costaron un rato de más, y están aquí por si le ahorran la tarde a alguien.

EDGEBUTTONSolo dentro de su hueco
El EdgeButton únicamente se coloca bien en el slot edgeButton de ScreenScaffold. Suelto dentro de un Box flota en mitad de la pantalla y tapa el marcador. Y ese slot exige el estado de una lista lazy (ScalingLazyListState y compañía): con un ScrollState normal no compila.
DEPENDENCIASEl artefacto que no existe
Es androidx.wear.compose:compose-material3. El nombre que uno escribe por intuición, wear-compose-material3, no existe y rompe la resolución con un error que no apunta a nada.
LAYOUTLa pantalla es redonda
Todo lo pegado al borde superior o inferior se corta. Y en el marcador las alturas van naturales, no por peso: en cuanto usas weight(), la fila de juegos se recorta al crecer el contenido un poco. Que weight() venga del scope de Row/Column y no se importe también cuesta descubrirlo: el import que autocompleta apunta a una API interna.
NAVEGACIÓNEl gesto que borra el partido
Sin un BackHandler, el gesto de volver cierra la app entera y se lleva el partido en curso por delante. Ahora salir pide confirmación y enseña cómo iba el marcador antes de tirarlo.
KOTLIN / JVMremoveLast() ya estaba cogido
MutableList.removeLast() choca con la API SequencedCollection que Java 21 añadió, y en Android peta en tiempo de ejecución en vez de al compilar. La versión que sí funciona es removeAt(lastIndex).
DATA LAYERDataClient, no MessageClient
Un mensaje se entrega y se pierde; un DataItem se queda en el canal y también le llega a quien se conecte después. Por eso abrir el móvil a mitad de partido enseña el marcador al instante en vez de esperar al punto siguiente. Con mensajes, la pantalla se quedaba en blanco hasta que alguien puntuaba.
El montaje

Cada versión está donde está por algo

No es la combinación más nueva, es la que encaja con la única plataforma que tengo instalada. Subir cualquiera de estas piezas obliga a mover las demás.

AGP9.3.0

El compileSdk máximo que soporta es 37, que es justo la plataforma instalada.

Gradle9.5.0

El mínimo que exige AGP 9.3.0. Por debajo ni arranca.

Kotlin2.3.21

Va incorporado en AGP 9, así que no se aplica el plugin de Kotlin Android por separado.

ComposeWear M3 1.6.2

Material 3 Expressive: es de donde salen EdgeButton y ScreenScaffold.

BytecodeJava 17

Emitido desde el JDK 25 de Android Studio, porque no hay un JDK 17 en la máquina.

CISolo el motor

El workflow corre únicamente los tests de core-scoring: es Kotlin puro y no necesita el SDK de Android, que no siempre está en el runner.

Honestidad

Lo que todavía no está

No sobrevive a que el sistema mate el proceso. El partido vive en memoria: si Android decide reclamar la app a mitad de set, se pierde. Persistirlo con SavedStateHandle es la siguiente mejora natural y la que más falta hace.

La sincronización funciona, pero solo está probada con dos emuladores. Comprobado de punta a punta: el marcador viaja punto a punto, los avisos llegan ya traducidos, abrir el móvil a mitad de partido enseña el marcador al instante, y al terminar el teléfono pasa a «sin partido». En un reloj y un teléfono de verdad todavía no lo he probado, así que no lo doy por hecho.

Si el reloj muere a mitad de partido, el móvil se queda con el último marcador. No llega a mandar el aviso de «sin partido». La marca de tiempo ya viaja en cada envío, así que bastaría con darlo por caducado pasado un rato.