Android Widget V2 : Ajout Wifi

Suite de mon precedant artcile sur la creation d’un widget android :
http://djynet.net/?p=585

J’ai décidé d’ajouter un bouton supplémentaire pour changer l’état du wifi sur le téléphone. La premiere etape est d’ajouter la possibilite de controler le widi depuis notre application en ajoutant les permissions dans le manifest :

<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

Puis ajouter un bouton dans le layout du widget :

<Button
        android:id="@ id/sync_button2"
        android:layout_width="40dp"
        android:layout_height="match_parent"
        android:background="@color/green"
        android:text="@string/button1"
        android:layout_toLeftOf="@ id/sync_button"/>

Il est ensuite possible d’utiliser la classe android “wifimanager” pour intergair avec le module wifi.

import android.net.wifi.WifiManager;
...
 WifiManager wifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
        if (wifiManager.isWifiEnabled()) {
            wifiManager.setWifiEnabled(false);
        } else {
            wifiManager.setWifiEnabled(true);
        }

Dans notre cas on utilisera un event/broadcast pour déclenché cette action. Il s’agit du même type de code utilise dans mon article précédant.

Screenshots_2014-02-12-21-15-57

L’ensemble du code est disponible sur Bitbucket :

https://charly37@bitbucket.org/charly37/wifi3gstarterv2

 

Creation widget android

Dans cet article je vais expliquer comment creer un widget pour android. Le but etant d’avoir un widget sur le bureau avec un bouton et un compteur de clique.

Widget layout

La première étape est la création du layout pour notre widget. Ce dernier sera compose d’un « textview » pour afficher notre compteur et d’un bouton pour augmenter le compteur. On Placera le tout dans un « relative layout » avec la zone de text a gauche et le bouton a droite.

Voila le xml correspondant :

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/yellow">

    <Button
        android:id="@ id/sync_button"
        android:layout_width="40dp"
        android:layout_height="match_parent"
        android:background="@color/blue"
        android:layout_alignParentRight="true"
        android:text="" />

    <TextView
        android:id="@ id/desc"
        android:layout_width="80dp"
        android:layout_height="match_parent"
        android:maxLines="1"
        android:text=""
        android:background="@color/pink"
        android:textColor="#fcfcfc"
        android:textSize="13sp"
        android:textStyle="normal" />

</RelativeLayout>

Ce fichier xml est a place dans /res/layout/widget_layout.xml

PS : Ce widget utilise des couleurs définies dans le fichier colors.xml. Cette méthode est décrite dans un de mes précédents posts.

Widget info

La seconde étape est de créer un fichier xml qui décrit notre widget. Ce fichier xml contient les informations générale de notre widget comme sa taille et la fréquence a laquelle on souhaite que l ‘OS l’update. Pour éviter une trop grand utilisation de la batterie Android ne prendra pas en compte les valeurs inférieur a 30 minutes.

Voila le fichier xml :

<?xml version="1.0" encoding="utf-8"?>

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/widget_layout"
    android:minHeight="40dp"
    android:minWidth="120dp"
    android:previewImage="@drawable/ic_launcher2"
    android:resizeMode="horizontal|vertical"
    android:updatePeriodMillis="1800000" >
</appwidget-provider>

TODO

AppWidgetProvider

Il s’agit d’une classe qui va se charger de la mise a jour du layout lors de la réception de l’événement “android.appwidget.action.APPWIDGET_UPDATE”. Pour cela la classe hérite de la classe de base android “AppWidgetProvider”.

public class MyAppWidgetProvider extends AppWidgetProvider

Cette classe facilite la création de widget en étendant la classe android générique de réception d’événement “BroadcastReceiver”.

Notre classe “MyAppWidgetProvider” doit donc implémenter la méthode “onUpdate” qui est appele a chaque update de notre widget. Dans cette méthode on va updater la view “remote” du widget. En effet le widget est embarque dans le homescreen et utilise donc une “remote view”.

On définie aussi le comportement du widget lors de l’appuie sur le bouton. Plus exactement on va générer un événement “net.djynet.intent.action.UPDATE_WIDGET” :

Intent intent = new Intent();
intent.setAction("net.djynet.intent.action.UPDATE_WIDGET");
return PendingIntent.getBroadcast(context, 0, intent,PendingIntent.FLAG_UPDATE_CURRENT);

Cet événement sera capture par une autre classe décrite ci-dessous.

BroadcastReceiver

Cette seconde classe sera en charge de gérer l’événement “net.djynet.intent.action.UPDATE_WIDGET” crée lors d’un clique sur le bouton. Pour cela notre classe va également hérité de”BroadcastReceiver” (et non AppWidgetProvider car il ne s’agit pas directement d’un événement standard android pour les widgets)

public class MyBroadcastReceiver extends BroadcastReceiver

La méthode “onreceive” doit être surcharge pour gérer l’événement de clique avec :

public void onReceive(Context context, Intent intent) {
        Log.i(context.getString(R.string.app_name_log), "Entering MyBroadcastReceiver:onReceive");
        if (intent.getAction().equals("net.djynet.intent.action.UPDATE_WIDGET")) {
            updateWidgetPictureAndButtonListener(context);
        }
    }

Tout comme notre “AppWidgetProvider” cette méthode va mettre a jour la vue de notre widget en indiquant le nombre de clique effectue sur le bouton.

private void updateWidgetPictureAndButtonListener(Context context) {
        Log.i(context.getString(R.string.app_name_log), "Entering MyBroadcastReceiver:updateWidgetPictureAndButtonListener");
        RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_layout);

        // updating view
        remoteViews.setTextViewText(R.id.desc, getDesc(context));

        // re-registering for click listener
        remoteViews.setOnClickPendingIntent(R.id.sync_button, MyAppWidgetProvider.buildButtonPendingIntent(context));

        MyAppWidgetProvider.pushWidgetUpdate(context.getApplicationContext(),remoteViews);
    }

Manifest

Pour rappel le manifest d’une application android permet au système de savoir ce que fait notre application et ses interactions avec le système android.

Dans notre manifest on va créer 2 « receiver » pour notre widget. On utilise le tag « receiver » pour les widget car ils font partis des « boradcast receiver ». Cela signifie qu’ils fonctionne sur la réception d’événements. Pour chaque receiver on indique donc l’événement et la classe qui sera en charge de sa gestion.

  • android.appwidget.action.APPWIDGET_UPDATE : Il s’agit de l’événement trige par le systeme lorsque ce dernier pense qu il est nécessaire d’updater le widget. La classe en charge de la gestion de cet événement est “net.djynet.wifistarter3g_main.MyAppWidgetProvider”
  • net.djynet.intent.action.UPDATE_WIDGET : Il s’agit de l’événement qu on va triger lors d’un appuie sur le bouton du widget. La classe en charge de la gestion de cet événement est “net.djynet.wifistarter3g_main.MyBroadcastReceiver”.

On va également indiquer le fichier de configuration associe a notre widget dans le tag « meta-data ».

Voila notre manifest :

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

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <receiver android:name="net.djynet.wifistarter3g_main.MyAppWidgetProvider"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/demo_widget_provider" />
        </receiver>

        <receiver android:name="net.djynet.wifistarter3g_main.MyBroadcastReceiver"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="net.djynet.intent.action.UPDATE_WIDGET" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/demo_widget_provider" />
        </receiver>

    </application>

</manifest>

 Resultat

Voila le résultat une fois le widget installe sur le bureau :

WidgetResult1

Les appuies sur le bouton (en bleue a droite du widget) vont augmenter le compteur affiche comme on peut le voir ci dessous.

Widget2

Le résultat n’est pas très jolie mais permet de bien visualise les différents éléments du layout.

L’ensemble du code est disponible sur bitbucket ICI.