WearOS

Wear OS (anciennement Android Wear) est une version d’Android spécialement conçue pour les technologies portables ou technologies mettables (wearable technology) comme les montres connectées.

Lien : Wear OS overview

Créer une application Wear OS

Android Studio propose de créer facilement un nouveau projet pour une montre connectée Wear OS :

On choisit un template :

Les derniers réglages :

Lien : Create and run a wearable app

Déboguer une application Wear OS

Avec la montre

Il est possible de déboguer son application Wear OS via le WiFi ou Bluetooth (éventuellement avec le port USB si la montre en a un).

Lien : developer.android.com/training/wearables/apps/debugging

Il faut préalablement activer les options de développement :

  • Ouvrir les paramètres de la montre
  • Appuyer sur Système -> À propos
  • Appuyer sept fois sur le numéro de build

Ensuite, on active le débogage via Wi-Fi :

On récupère l’adresse IP de la montre (ici 192.168.52.20).

On connecte ensuite le débogueur à la montre avec adb :

$ adb connect 192.168.52.20:5555
connected to 192.168.52.20:5555

La “montre” apparaît alors dans Android Studio :

Avec l’émulateur

Il faut lancer l’AVD Manager dans le menu Tools puis créer un virtual device :

Choisir son modèle :

Puis l’image système :

On obtient :

L’émulateur d’une montre dans AVD Manager :

Il est maintenant disponible dans Android Studio :

Spécificités Wear OS

Il existe quelques différences entre les applications de téléphone et de montre :

  • des API spécifiques à la montre
  • un modèle UX différent
  • des fonctionnalités adaptées à une montre
  • et certaines API standards qui ne sont pas pris en charge (webkit, print, … cf. hasSystemFeature())

Type d’application

Il faut commencer par définir l’application en tant qu’application Wear OS.

On le spécifie (android.hardware.type.watch) dans le fichier AndroidManifest.xml du projet :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lasalle.ekawawear">
    <uses-feature android:name="android.hardware.type.watch" />
    ...
</manifest>

Une application de montre peut être considérée comme :

  • complètement indépendante d’une application téléphonique
  • semi-indépendante (une application téléphonique n’est pas requise et ne fournirait que des fonctionnalités optionnelles)
  • dépendante d’une application de téléphone

Si une application de montre est complètement indépendante ou semi-indépendante, elle appartient à la catégorie autonome.

Il faut le préciser (com.google.android.wearable.standalone) dans le fichier AndroidManifest.xml du projet :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lasalle.ekawawear">
    <uses-feature android:name="android.hardware.type.watch" />
    ...
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_ekawa"
        android:roundIcon="@mipmap/ic_ekawa_round"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/EkawaAppTheme">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="true" />
        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />
        ...
    </application>
</manifest>

Interface utilisateur

La bibliothèque de l’interface utilisateur Wear est automatiquement incluse lorsquon crée l’application avec l’assistant d’Android Studio.

Le fichier build.gradle :

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'androidx.wear:wear:1.0.0'
    compile 'com.google.android.gms:play-services-wearable:+'
}

plugins {
    id 'com.android.application'
}

android {
    compileSdkVersion 30
    buildToolsVersion "30.0.3"

    defaultConfig {
        applicationId "com.lasalle.myapplicationwatch"
        minSdkVersion 25
        targetSdkVersion 30
        versionCode 1
        versionName "1.0"

    }

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

dependencies {

    implementation 'com.google.android.support:wearable:2.6.0'
    implementation 'com.google.android.gms:play-services-wearable:17.0.0'
    implementation 'androidx.percentlayout:percentlayout:1.0.0'
    implementation 'androidx.legacy:legacy-support-v4:1.0.0'
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
    implementation 'androidx.wear:wear:1.0.0'
    compileOnly 'com.google.android.wearable:wearable:2.6.0'
}

Voici quelques-unes des principales classes spécifiques pour des montres rondes :

  • BoxInsetLayout : Un layout qui connaît la forme de l’écran et peut encadrer les enfants dans le carré central d’un écran rond.

  • CircularProgressLayout : Une disposition qui fournit un compte à rebours circulaire autour d’une vue enfant. Généralement utilisé comme minuterie automatique pour confirmer une opération après un court délai.

  • WearableRecyclerView : Une implémentation spécifique portable de la classe RecyclerView pour afficher des listes déroulantes d’objets dans des appareils carrés et ronds.

Lien : API Wear UI Library

Mode ambiant

Une montre passe du mode interactif au mode ambiant (ambient mode) lorsque celle-ci est inactive ou lorsqu’un utilisateur couvre l’écran avec sa paume.

Liens :

Wear OS gère automatiquement le passage en mode basse consommation pour une application active lorsqu’un utilisateur n’utilise plus sa montre. C’est ce qu’on appelle le mode ambiant du système (ambient mode). Si l’utilisateur interagit à nouveau avec la montre dans un certain laps de temps, Wear OS ramène l’utilisateur dans l’application là où il s’est arrêté.

Les applications Wear OS qui s’exécutent à la fois en mode ambiant et interactif sont appelées applications permanentes.

Pour prendre en charge le mode ambiant, il faut ajoutez la permission WAKE_LOCK dans le fichier AndroidManifest.xml :

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.lasalle.ekawawear">
    <uses-feature android:name="android.hardware.type.watch" />
    ...
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    ...
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_ekawa"
        android:roundIcon="@mipmap/ic_ekawa_round"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/EkawaAppTheme">
        <uses-library
            android:name="com.google.android.wearable"
            android:required="true" />
        <meta-data
            android:name="com.google.android.wearable.standalone"
            android:value="true" />
        ...
    </application>
</manifest>

La classe AmbientModeSupport :

public class MainActivity extends AppCompatActivity implements AmbientModeSupport.AmbientCallbackProvider
{
    // ...

    // un contrôleur permet de vérifier l'état ambiant en dehors des callbacks
    private AmbientModeSupport.AmbientController ambientController;

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

        ambientController = AmbientModeSupport.attach(this);

        // Exemple :
        // if (ambientController.isAmbient ()) { ... } else { ... }
    }

    //  pour réagir aux événements ambiants du système Android
    @Override
    public AmbientModeSupport.AmbientCallback getAmbientCallback()
    {
        return new MyAmbientCallback();
    }

    // ...

    // une classe interne afin d'agir sur les événements ambiants
    private class MyAmbientCallback extends AmbientModeSupport.AmbientCallback
    {
        @Override
        public void onEnterAmbient(Bundle ambientDetails)
        {
            /**
             * Si l'utilisateur n'interagit pas avec votre application pendant un certain temps pendant que l'application est affichée, ou si l'écran est couvert, le système bascule l'activité en mode ambiant.
             * Une fois que l'application est passée en mode ambiant, il faut mettre à jour l'interface utilisateur d'activité vers une disposition plus basique pour réduire la consommation d'énergie : fond noir avec un minimum de graphiques et de texte blancs.
             */
            //stateTextView.setTextColor(Color.WHITE);
            //stateTextView.getPaint().setAntiAlias(false);
        }

        @Override
        public void onExitAmbient()
        {
            // Lorsque l'utilisateur appuie sur l'écran ou soulève son poignet, l'activité passe du mode ambiant au mode interactif
            // affiche dans un état interactif en couleur
            //stateTextView.setTextColor(Color.GREEN);
            //stateTextView.getPaint().setAntiAlias(true);
        }

        @Override
        public void onUpdateAmbient()
        {
            // Le mode ambiant permet de mettre à jour l'écran avec de nouvelles informations pour l'utilisateur, mais il faut être vigialant à la durée de vie de la batterie.
            // méthode de mise à jour de l'écran une fois par minute ou moins en mode ambiant
            // cf. AlarmManager pour réveiller le processeur et mettre à jour l'écran plus fréquemment
        }
    }
}

Exemple : AlwaysOn

On peut activer le mode ambiant dans l’activité en utilisant WearableActivity :

public class MainActivity extends WearableActivity
{

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        setAmbientEnabled();
        ...
    }
}

Exemples

Lien : Exemples Wear OS

Le premier exemple à examiner est probablement celui sur l’accessibilité puisqu’il montre l’intégration de quelques widgets : WearAccessibilityApp

En voici une adaptation pour le projet Ekawa de l’étudiant IR Jean-Luc Lecomte :

Code source : EkawaWear.zip