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