Blog para desarrollo de aplicaciones en Android, aprende paso a paso como crear aplicaciones.

Aprender a Programar Wear OS en Android

Aprender a Programar Wear OS en Android

Hola amigos 👋 Bienvenidos a un nuevo tutorial de Universo Android. Hoy aprenderemos a programar para Wear OS, el sistema operativo de Google para smartwatches y dispositivos wearables, creando aplicaciones optimizadas para pantallas pequeñas y circulares.

Al finalizar este tutorial tendrás una aplicación que incluirá:

  • Configuración completa de proyecto Wear OS
  • Diseños adaptados a pantallas circulares
  • Notificaciones en smartwatch
  • Complicaciones para watch faces
  • Sensores y monitoreo de actividad física
  • Sincronización con dispositivo móvil
  • Interfaz optimizada para wearables

🟩 1. ¿Qué es Wear OS?

Wear OS es el sistema operativo de Google diseñado específicamente para smartwatches y dispositivos wearables. Permite crear aplicaciones que aprovechan las características únicas de estos dispositivos: pantallas pequeñas, interacción rápida, sensores de salud y notificaciones instantáneas.

🟩 2. Requisitos Previos

Antes de comenzar necesitas:

  • Android Studio (versión actualizada)
  • SDK de Android API 30 o superior
  • Smartwatch con Wear OS o emulador configurado
  • Conocimientos básicos de Android

🟩 3. Crear el Proyecto Wear OS en Android Studio

Paso 1: Nuevo Proyecto

  1. Abre Android Studio
  2. Selecciona New Project
  3. En la pestaña Wear OS, selecciona Empty Wear App
  4. Configura el nombre y paquete
  5. Selecciona API 30: Android 11.0 (Wear OS 3) como mínimo
  6. Finaliza la creación

🟩 4. Configurar build.gradle

Abre el archivo build.gradle (Module: app) y verifica las dependencias:

plugins {
    id 'com.android.application'
}

android {
    namespace 'com.example.wearosapp'
    compileSdk 34

    defaultConfig {
        applicationId "com.example.wearosapp"
        minSdk 30
        targetSdk 34
        versionCode 1
        versionName "1.0"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {
    // Wear OS dependencies
    implementation 'androidx.wear:wear:1.3.0'
    implementation 'com.google.android.support:wearable:2.9.0'
    compileOnly 'com.google.android.wearable:wearable:2.9.0'
    
    // Material Design para Wear OS
    implementation 'androidx.wear:wear-input:1.1.0'
    implementation 'androidx.wear:wear-ongoing:1.0.0'
    
    // Core dependencies
    implementation 'androidx.appcompat:appcompat:1.6.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.3.2'
    
    // Play Services para sensores
    implementation 'com.google.android.gms:play-services-wearable:18.1.0'
}

Sincroniza el proyecto después de agregar las dependencias.

🟩 5. Diseño XML para Wear OS

Crea un archivo llamado:

📄 activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    android:padding="16dp"
    app:boxedEdges="all">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!-- Título de la App -->
        <TextView
            android:id="@+id/txtTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Wear OS App"
            android:textColor="#FFFFFF"
            android:textSize="20sp"
            android:textStyle="bold"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginTop="20dp" />

        <!-- Contador de Pasos -->
        <TextView
            android:id="@+id/txtStepCount"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="0"
            android:textColor="#4CAF50"
            android:textSize="48sp"
            android:textStyle="bold"
            app:layout_constraintTop_toBottomOf="@id/txtTitle"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@id/txtStepLabel"
            android:layout_marginTop="20dp" />

        <!-- Etiqueta de Pasos -->
        <TextView
            android:id="@+id/txtStepLabel"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Pasos hoy"
            android:textColor="#AAAAAA"
            android:textSize="16sp"
            app:layout_constraintTop_toBottomOf="@id/txtStepCount"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@id/btnStart" />

        <!-- Botón Iniciar -->
        <Button
            android:id="@+id/btnStart"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Iniciar"
            android:textColor="#FFFFFF"
            android:backgroundTint="#2196F3"
            app:layout_constraintTop_toBottomOf="@id/txtStepLabel"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toTopOf="@id/btnNotification"
            android:layout_marginTop="20dp" />

        <!-- Botón Notificación -->
        <Button
            android:id="@+id/btnNotification"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Notificación"
            android:textColor="#FFFFFF"
            android:backgroundTint="#FF5722"
            app:layout_constraintTop_toBottomOf="@id/btnStart"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            android:layout_marginTop="12dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.wear.widget.BoxInsetLayout>

🟩 6. Diseño XML con Lista Circular (WearableRecyclerView)

Crea un archivo llamado:

📄 activity_list.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.wear.widget.BoxInsetLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#000000"
    app:boxedEdges="all">

    <androidx.wear.widget.WearableRecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        app:layoutManager="androidx.wear.widget.WearableLinearLayoutManager" />

</androidx.wear.widget.BoxInsetLayout>

🟩 7. Lógica Java Principal

📄 MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.os.Build;
import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements SensorEventListener {

    private static final String CHANNEL_ID = "wear_channel";
    private static final int NOTIFICATION_ID = 1;

    TextView txtStepCount, txtTitle;
    Button btnStart, btnNotification;
    
    SensorManager sensorManager;
    Sensor stepCounterSensor;
    
    int stepCount = 0;
    boolean isTracking = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Inicializar vistas
        txtTitle = findViewById(R.id.txtTitle);
        txtStepCount = findViewById(R.id.txtStepCount);
        btnStart = findViewById(R.id.btnStart);
        btnNotification = findViewById(R.id.btnNotification);

        // Configurar sensor de pasos
        setupStepCounter();

        // Crear canal de notificación
        createNotificationChannel();

        // Configurar botones
        btnStart.setOnClickListener(v -> toggleTracking());
        btnNotification.setOnClickListener(v -> sendNotification());
    }

    private void setupStepCounter() {
        sensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
        
        if (sensorManager != null) {
            stepCounterSensor = sensorManager.getDefaultSensor(Sensor.TYPE_STEP_COUNTER);
            
            if (stepCounterSensor == null) {
                Toast.makeText(this, "Sensor de pasos no disponible", Toast.LENGTH_SHORT).show();
            }
        }
    }

    private void toggleTracking() {
        if (!isTracking) {
            startTracking();
        } else {
            stopTracking();
        }
    }

    private void startTracking() {
        if (stepCounterSensor != null) {
            sensorManager.registerListener(this, stepCounterSensor, SensorManager.SENSOR_DELAY_NORMAL);
            isTracking = true;
            btnStart.setText("Detener");
            btnStart.setBackgroundTintList(getColorStateList(android.R.color.holo_red_dark));
            Toast.makeText(this, "Seguimiento iniciado", Toast.LENGTH_SHORT).show();
        }
    }

    private void stopTracking() {
        sensorManager.unregisterListener(this);
        isTracking = false;
        btnStart.setText("Iniciar");
        btnStart.setBackgroundTintList(getColorStateList(android.R.color.holo_blue_dark));
        Toast.makeText(this, "Seguimiento detenido", Toast.LENGTH_SHORT).show();
    }

    @Override
    public void onSensorChanged(SensorEvent event) {
        if (event.sensor.getType() == Sensor.TYPE_STEP_COUNTER) {
            stepCount = (int) event.values[0];
            txtStepCount.setText(String.valueOf(stepCount));
        }
    }

    @Override
    public void onAccuracyChanged(Sensor sensor, int accuracy) {
        // No se requiere implementación
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "Wear Channel";
            String description = "Canal de notificaciones para Wear OS";
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);
            
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            if (notificationManager != null) {
                notificationManager.createNotificationChannel(channel);
            }
        }
    }

    private void sendNotification() {
        NotificationCompat.Builder builder = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(android.R.drawable.ic_dialog_info)
                .setContentTitle("Wear OS App")
                .setContentText("¡Has caminado " + stepCount + " pasos hoy!")
                .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                .setVibrate(new long[]{0, 500, 200, 500})
                .setAutoCancel(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(this);
        notificationManager.notify(NOTIFICATION_ID, builder.build());
        
        Toast.makeText(this, "Notificación enviada", Toast.LENGTH_SHORT).show();
    }

    @Override
    protected void onPause() {
        super.onPause();
        if (isTracking) {
            sensorManager.unregisterListener(this);
        }
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (isTracking && stepCounterSensor != null) {
            sensorManager.registerListener(this, stepCounterSensor, SensorManager.SENSOR_DELAY_NORMAL);
        }
    }
}

🟩 8. Crear Adaptador para Lista Circular

📄 WearListAdapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.List;

public class WearListAdapter extends RecyclerView.Adapter<WearListAdapter.ViewHolder> {

    private List<String> items;
    private OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(String item);
    }

    public WearListAdapter(List<String> items, OnItemClickListener listener) {
        this.items = items;
        this.listener = listener;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(android.R.layout.simple_list_item_1, parent, false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        String item = items.get(position);
        holder.textView.setText(item);
        holder.itemView.setOnClickListener(v -> listener.onItemClick(item));
    }

    @Override
    public int getItemCount() {
        return items.size();
    }

    static class ViewHolder extends RecyclerView.ViewHolder {
        TextView textView;

        ViewHolder(View itemView) {
            super(itemView);
            textView = itemView.findViewById(android.R.id.text1);
            textView.setTextColor(0xFFFFFFFF);
        }
    }
}

🟩 9. Actividad con Lista Circular

📄 ListActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.wear.widget.WearableLinearLayoutManager;
import androidx.wear.widget.WearableRecyclerView;
import android.os.Bundle;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.List;

public class ListActivity extends AppCompatActivity {

    WearableRecyclerView recyclerView;
    WearListAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_list);

        recyclerView = findViewById(R.id.recyclerView);

        // Configurar LayoutManager para pantalla curva
        WearableLinearLayoutManager layoutManager = new WearableLinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setEdgeItemsCenteringEnabled(true);

        // Crear lista de items
        List<String> items = new ArrayList<>();
        items.add("Correr");
        items.add("Caminar");
        items.add("Ciclismo");
        items.add("Natación");
        items.add("Yoga");
        items.add("Gimnasio");
        items.add("Fútbol");
        items.add("Baloncesto");

        // Configurar adaptador
        adapter = new WearListAdapter(items, item -> 
            Toast.makeText(this, "Seleccionado: " + item, Toast.LENGTH_SHORT).show()
        );

        recyclerView.setAdapter(adapter);
    }
}

🟩 10. Configurar AndroidManifest.xml

📄 AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <!-- Permisos para Wear OS -->
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.ACTIVITY_RECOGNITION" />
    <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
    
    <!-- Declarar que es una app Wear OS -->
    <uses-feature android:name="android.hardware.type.watch" />
    
    <!-- Sensor de pasos -->
    <uses-feature
        android:name="android.hardware.sensor.stepdetector"
        android:required="false" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@android:style/Theme.DeviceDefault"
        tools:targetApi="26">

        <!-- Metadata para identificar como app Wear -->
        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />

        <!-- Actividad Principal -->
        <activity
            android:name=".MainActivity"
            android:exported="true"
            android:label="@string/app_name"
            android:theme="@android:style/Theme.DeviceDefault">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Actividad de Lista -->
        <activity
            android:name=".ListActivity"
            android:exported="false"
            android:label="Actividades"
            android:theme="@android:style/Theme.DeviceDefault" />

    </application>

</manifest>

🟩 11. Sensores Disponibles en Wear OS

SensorDescripción
TYPE_STEP_COUNTERContador de pasos acumulados
TYPE_STEP_DETECTORDetecta cada paso individual
TYPE_HEART_RATEMonitor de frecuencia cardíaca
TYPE_ACCELEROMETERAcelerómetro para movimiento
TYPE_GYROSCOPEGiroscopio para rotación
TYPE_AMBIENT_TEMPERATURETemperatura ambiente
TYPE_PRESSUREPresión barométrica

🟩 12. Crear Complicación para Watch Face

📄 ComplicationService.java

import android.app.PendingIntent;
import android.content.Intent;
import androidx.wear.watchface.complications.data.ComplicationData;
import androidx.wear.watchface.complications.data.ComplicationType;
import androidx.wear.watchface.complications.data.PlainComplicationText;
import androidx.wear.watchface.complications.data.ShortTextComplicationData;
import androidx.wear.watchface.complications.datasource.ComplicationDataSourceService;
import androidx.wear.watchface.complications.datasource.ComplicationRequest;

public class StepComplicationService extends ComplicationDataSourceService {

    @Override
    public void onComplicationRequest(ComplicationRequest request, ComplicationRequestListener listener) {
        
        // Obtener conteo de pasos (simulado)
        int steps = 5432;
        
        // Crear Intent para abrir la app
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(
            this, 0, intent, PendingIntent.FLAG_IMMUTABLE
        );

        // Crear complicación
        ShortTextComplicationData complicationData = new ShortTextComplicationData.Builder(
            PlainComplicationText.Builder(steps + " pasos").build(),
            PlainComplicationText.Builder("Pasos").build()
        )
        .setTapAction(pendingIntent)
        .build();

        listener.onComplicationData(complicationData);
    }

    @Override
    public void onComplicationActivated(int complicationInstanceId, ComplicationType type) {
        // Complicación activada
    }

    @Override
    public void onComplicationDeactivated(int complicationInstanceId) {
        // Complicación desactivada
    }

    @Override
    public ComplicationType[] getSupportedComplicationTypes(int complicationInstanceId) {
        return new ComplicationType[]{
            ComplicationType.SHORT_TEXT,
            ComplicationType.LONG_TEXT
        };
    }
}

🟩 13. Configurar Emulador Wear OS

  1. Abre AVD Manager en Android Studio
  2. Clic en Create Virtual Device
  3. Selecciona categoría Wear OS
  4. Elige un dispositivo (Wear OS Square o Round)
  5. Selecciona API 30 o superior
  6. Finaliza y ejecuta el emulador

▶️ Cómo Ejecutar tu Aplicación

  1. Abre Android Studio
  2. Conecta tu smartwatch o inicia el emulador Wear OS
  3. Presiona Run para ejecutar el proyecto
  4. La aplicación se instalará en tu dispositivo Wear OS
  5. Verás la interfaz optimizada para smartwatch funcionando

🧪 Resultado Final

Tu aplicación Wear OS mostrará:

  • Interfaz adaptada a pantallas circulares y cuadradas
  • Contador de pasos en tiempo real
  • Botones optimizados para smartwatch
  • Notificaciones con vibración
  • Lista circular con desplazamiento curvo
  • Sensores de actividad física funcionando

📥 Descargar Proyecto de Ejemplo

Puedes descargar el proyecto completo desde el siguiente enlace:

👉 Estamos trabajando en ello! 

🙌 Gracias por Visitar mi Blog

Si este tutorial te fue útil:

✔️ Compártelo
✔️ Déjame un comentario
✔️ Sígueme para más contenido sobre Android y programación

¡Estoy aquí para ayudarte!

❓ Preguntas Frecuentes (FAQ)

1. ¿Puedo desarrollar para Wear OS sin tener un smartwatch físico?
Sí, Android Studio incluye emuladores de Wear OS que simulan tanto pantallas circulares como cuadradas para desarrollo y testing.

2. ¿Qué diferencias hay entre una app normal de Android y una de Wear OS?
Las apps de Wear OS tienen interfaces más compactas, diseños adaptados a pantallas pequeñas y circulares, enfoque en notificaciones rápidas y acceso a sensores específicos de wearables.

3. ¿Puedo sincronizar datos entre mi app móvil y mi app Wear OS?
Sí, puedes usar la Wearable Data Layer API de Google Play Services para sincronizar datos entre dispositivos móviles y smartwatches.

4. ¿Qué sensores están disponibles en Wear OS?
Los smartwatches Wear OS incluyen sensores como contador de pasos, monitor de frecuencia cardíaca, acelerómetro, giroscopio, GPS y algunos modelos incluyen sensores adicionales de salud.


No hay comentarios:

Publicar un comentario