Utiliser la librairie Android Yoctopuce avec Kotlin

Utiliser la librairie Android Yoctopuce avec Kotlin

La semaine passée s'est tenu la conférence annuelle Google IO. Lors des différentes keynotes qui concernaient Android un accent particulier a été mis sur Kotlin, le nouveau langage de programmation pour Android. Ce langage devenant de plus en plus populaire nous avons décidé de lui consacrer un article: Nous allons voir comment utiliser notre librairie Android dans un application Android écrite en Kotlin.



Attention, cet article n'est pas un tutoriel Kotlin. Pour être franc, nous n'avons que très peu d'expérience avec ce langage, le but de cet article est de vous montrer qu'il est possible d'utiliser facilement notre librairie dans un projet de ce type. Nous partons donc du principe que vous avez déjà quelques notions de Kotlin. Si ce n'est pas le cas vous devriez commencer par lire la documentation du langage et expérimenter vos connaissances avec cette série d'exemples.

De la même manière, si vous n'avez jamais utilisé les modules Yoctopuce, vous pouvez commencer par la lecture de nos tutoriels, plus particulièrement celui sur la structure logique des modules Yoctopuce.

Pour cet article, nous allons réaliser la même application que notre article "Comment débuter avec des modules Yoctopuce sur Android" mais en écrivant le code en Kotlin au lieu de Java. Il s'agit d'un application très basique qui affiche la valeur courante du capteur Yoctopuce branché sur le port USB.

Pour réaliser cette application, il faut utiliser au minimum la version 3.0 d'Android Studio. Lors de la création du projet, il faut simplement sélectionner Kotlin comme langage de programmation.

Lors de la création du projet, il faut sélectionner Kotlin comme langage de programmation
Lors de la création du projet, il faut sélectionner Kotlin comme langage de programmation



Une fois le projet créé, il faut ajouter la librairie Yoctopuce, modifier l'interface, modifier le manifeste et écrire le code qui affiche la valeur du capteur.

Ce qui ne change pas


Tout comme pour une application Android écrite en Java, on commence par ajouter la librairie Yoctopuce au projet. La méthode ne change pas: On ajoute une référence à notre librairie dans le fichier build.gradle de l'application. Lors de la compilation, Android Studio va automatiquement télécharger et ajouter la dernière version de la librairie Yoctopuce.

...
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:design:28.0.0'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    implementation 'com.yoctopuce.android:YoctoLib:+'
}



Il faut ensuite modifier le fichier AndroidManifest.xml afin que l'application soit autorisée à accéder au port USB. Pour ce faire, il faut ajouter la ligne <uses-feature android:name="android.hardware.usb.host"/> dans la section manifest du fichier.

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

  <application
     android:allowBackup="true"
     android:icon="@mipmap/ic_launcher"
     android:label="@string/app_name"
     android:roundIcon="@mipmap/ic_launcher_round"
     android:supportsRtl="true"
     android:theme="@style/AppTheme"
     tools:ignore="GoogleAppIndexingWarning">
    <activity
       android:name=".MainActivity"
       android:label="@string/app_name"
       android:launchMode="singleInstance"
       android:theme="@style/AppTheme.NoActionBar">
      <intent-filter>
        <action android:name="android.intent.action.MAIN"/>
        <category android:name="android.intent.category.LAUNCHER"/>
      </intent-filter>
      <intent-filter>
        <action
           android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"/>
      </intent-filter>
      <meta-data
         android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
         android:resource="@xml/device_filter"/>
      </activity>
  </application>

</manifest>



Il faut aussi créer le fichier device_filter.xml avec le contenu ci-dessous dans le répertoire /src/main/res/xml de votre projet pour que l'application démarre automatiquement lors du branchement d'un module Yoctopuce.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <usb-device vendor-id="9440" />
</resources>



Nous ne nous sommes pas étendu sur ces étapes car nous les avons déjà expliquées dans un précédent article. C'est la même chose que l'on réalise un application Java ou Kotlin.

Ce qui change


La seule chose qui change par rapport à notre projet en Java: L'implémentation de MainActivity.

Au lieu de modifier un fichier MainActivity.java, on travail sur le fichier MainActivity.kt qui est écrit en Kotlin. Notez que les fichiers Kotlin (.kt) sont placés dans le même sous-répertoire que les fichiers sources .java (app/src/main/java/).

Dans la méthode onStart(), on appelle la méthode YAPI.EnableUSBHost() et ensuite la méthode RegisterHub("usb") pour démarrer la détection des modules sur le port USB.

On déclare ensuite un objet Runnable qui va périodiquement effectuer les opérations suivantes:

  1. Appeler YAPI.UpdateDeviceList une fois toutes les trois secondes, ce qui force l'API à énumérer les modules Yoctopuce.
  2. Si aucun capteur n'est déjà utilisé, rechercher le premier capteur de température détecté à l'aide de la méthode YTemperature.FirstTemperature().
  3. Mettre à jour le champ texte avec la valeur courante du capteur de température. Dans un premier temps, on vérifie que le module est toujours connecté à l'aide de la méthode isOnline() et, si c'est le cas, on récupère la température courante avec get_currentValue().
  4. Programmer la prochaine exécution de cette méthode à l'aide de la méthode postDelayed.


Pour finir, dans la méthode onStop() on appelle YAPI.FreeAPI() pour stopper la librairie Yoctopuce et libérer les ressources utilisée.

A la fin, le fichier MainActivity.kt est le suivant:

import com.yoctopuce.YoctoAPI.YAPI
import com.yoctopuce.YoctoAPI.YAPI_Exception
import com.yoctopuce.YoctoAPI.YSensor
import com.yoctopuce.YoctoAPI.YTemperature


class MainActivity : AppCompatActivity() {
  lateinit var temperatureTextView: TextView
  lateinit var handler: Handler
  var hardwaredetect = 0
  var sensor: YSensor? = null

  override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    setSupportActionBar(toolbar)
    temperatureTextView = findViewById(R.id.temperature)

    fab.setOnClickListener { view ->
        Snackbar.make(view,
            "Replace with your own action",
            Snackbar.LENGTH_LONG)
            .setAction("Action", null).show()
    }
    handler = Handler()
  }

  override fun onStart() {
    super.onStart()
    try {
        YAPI.EnableUSBHost(this)
        YAPI.RegisterHub("usb")
    } catch (e: YAPI_Exception) {
        Snackbar.make(temperatureTextView,
            "Error:" + e.localizedMessage,
              Snackbar.LENGTH_INDEFINITE).show()
    }
    handler.postDelayed(_periodicUpdate, 500)
  }

  override fun onStop() {
    handler.removeCallbacks(_periodicUpdate)
    YAPI.FreeAPI()
    super.onStop()
  }

  private val _periodicUpdate = object : Runnable {
    override fun run() {
      try {
        if (hardwaredetect == 0) {
            YAPI.UpdateDeviceList()
        }
        hardwaredetect = (hardwaredetect + 1) % 20
        if (sensor == null) {
            sensor = YTemperature.FirstTemperature()
        }
        sensor?.let {
            if (it.isOnline) {
                val text = String.format(Locale.US, "%.2f %s",
                    it.get_currentValue(), it.get_unit())
                temperatureTextView.text = text
            } else {
                temperatureTextView.text = "OFFLINE"
                sensor = null
            }

        }
      } catch (e: YAPI_Exception) {
        Snackbar.make(temperatureTextView,
            "Error:" + e.localizedMessage,
            Snackbar.LENGTH_INDEFINITE).show()
      }
      handler.postDelayed(this, 500)
    }
  }
}



Hormis la syntaxe Kotlin, le code est similaire à ce que nous avions écrit en Java.

Une fois toutes ces modifications faites, l'application peut être compilée et utilisée sur n'importe quel téléphone ou tablette qui a le support USB on the Go.

L'application demo mais écrite en Kotlin
L'application demo mais écrite en Kotlin



Le code source de cette application est disponible à cette adresse: https://github.com/yoctopuce-examples/android_template_kotlin.


La gestion des checked exceptions en Kotlin


Kotlin ne gère pas les Checked Exceptions (aussi appelées "exceptions explicites" en français). En soit, cette décision est tout à fait valable, mais pour un langage qui interagit avec des API et des librairies écrites en Java, cela peut induire le développeur en erreur.

Prenons la méthode get_currentValue() de notre librairie qui lève une exception de type YAPI_Exception si le module a été débranché. Cette exception est déclarée comme "checked" dans la signature de la méthode à l'aide du mot-clé "throws". Le but de ces checked exceptions est de forcer l'appelant à gérer cette erreur. C'est pour cette raison qu'en Java, le compilateur retourne une erreur si l'on utilise cette méthode sans traiter cette exception.

Avec Kotlin, c'est différent. Le compilateur ne vérifie pas que cette exception est traitée par l'appelant. Par conséquence, si le développeur oublie de traiter cette exception le compilateur ne va rien dire, mais l'application va planter si le module est débranché.

La plupart des méthodes de la librairie Yoctopuce utilisent des "checked exceptions". En cas de doute, vous pouvez consulter la documentation pour savoir si une méthode peut lever une exception ou pas. Si c'est le cas, n'oubliez pas de traiter cette exception car le compilateur Kotlin ne vous préviendra pas.

Conclusion


Comme nous venons de le voir, il est possible d'utiliser notre librairie dans une application Android qu'elle soit écrite en Java ou en Kotlin. Pour être franc, ce n'est pas vraiment étonnant car Google a tout fait pour que les deux langages puissent coexister. Il n'y a pas besoin d'écrire de wrapper pour utiliser du code Java ou de le transpiler. Les fonctions sont directement utilisables, elles sont simplement écrites dans un autre langage.

A ce sujet, il faut souligner une fonctionnalité très pratique d'Android Studio: Il est possible de traduire du code Java en Kotlin et vice versa. Par exemple, vous pouvez copier du code écrit en Java, et le coller dans votre fichier source Kotlin. Android Studio détecte automatiquement que c'est du code Java et le traduit à la volée. C'est très pratique pour débuter. Du reste, c'est comme ça que nous avons commencé à utiliser Kotlin :->

Cependant, pour être tout à fait honnête, nous ne sommes pas vraiment convaincu par le langage Kotlin. De notre point de vue, Kotlin est un langage difficile et donne un faux sentiment de simplicité. Nous allons continuer à utiliser Java pour nos développements Android tout en nous assurant que notre librairie reste compatible avec ce nouveau langage.


Commenter aucun commentaire Retour au blog












Yoctopuce, get your stuff connected.