Friday, October 15, 2010

App Widget using Alarm Manager

In the old exercise "A simple Home Screen App Widget with configure activity", it have a minimum rate of 30 minutes in android:updatePeriodMillis. In order to make the Home Screen App Widget do something faster than 30 minutes, Alarm Manager can be used.

In the preiouse exercise "Schedule a repeating alarm", we know how to triger a repeating service using Alarm Manager. It will be used here to trigger the onReceive() method of the AppWidgetProvider.

App Widget using Alarm Manager

Modify AndroidManifest.xml to add a new custom intent-filter, "MY_OWN_WIDGET_UPDATE".
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.exercise.HelloWidget"
android:versionCode="1"
android:versionName="1.0">
<application android:icon="@drawable/icon" android:label="@string/app_name">
<receiver android:name="HelloWidgetProvider" >
<intent-filter>
   <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
          <action android:name="MY_OWN_WIDGET_UPDATE" />
      </intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="@xml/hellowidgetproviderinfo" />
</receiver>
<activity android:name=".HelloWidgetConfig"
           android:label="Hello Widget Config">
     <intent-filter>
         <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE" />
     </intent-filter>
 </activity>
</application>
<uses-sdk android:minSdkVersion="7" />

</manifest>


Modify HelloWidgetProvider.java to implement onReceive() method, to handle the "MY_OWN_WIDGET_UPDATE" action.
package com.exercise.HelloWidget;

import java.text.SimpleDateFormat;
import java.util.Date;

import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
import android.widget.Toast;

public class HelloWidgetProvider extends AppWidgetProvider {

private static SimpleDateFormat formatter = new SimpleDateFormat("dd MMM yyyy  hh:mm:ss a");
static String strWidgetText = "";

public static String MY_WIDGET_UPDATE = "MY_OWN_WIDGET_UPDATE";

@Override
 public void onReceive(Context context, Intent intent) {
  // TODO Auto-generated method stub
  super.onReceive(context, intent);
  
  if(MY_WIDGET_UPDATE.equals(intent.getAction())){
   Toast.makeText(context, "onReceiver()", Toast.LENGTH_LONG).show();
  }
 }

@Override
public void onDeleted(Context context, int[] appWidgetIds) {
// TODO Auto-generated method stub
//super.onDeleted(context, appWidgetIds);
Toast.makeText(context, "onDeleted()", Toast.LENGTH_LONG).show();
}

@Override
public void onDisabled(Context context) {
// TODO Auto-generated method stub
//super.onDisabled(context);
Toast.makeText(context, "onDisabled()", Toast.LENGTH_LONG).show();
}

@Override
public void onEnabled(Context context) {
// TODO Auto-generated method stub
//super.onEnabled(context);
Toast.makeText(context, "onEnabled()", Toast.LENGTH_LONG).show();
}

@Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// TODO Auto-generated method stub
super.onUpdate(context, appWidgetManager, appWidgetIds);

final int N = appWidgetIds.length;
 for (int i=0; i<N; i++) {
     int appWidgetId = appWidgetIds[i];
     updateAppWidget(context, appWidgetManager, appWidgetId);

     Toast.makeText(context, "onUpdate(): " + String.valueOf(i) + " : " +  String.valueOf(appWidgetId), Toast.LENGTH_LONG).show();
 }

}

public static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId){
String currentTime = formatter.format(new Date());
strWidgetText = currentTime;

RemoteViews updateViews = new RemoteViews(context.getPackageName(), R.layout.hellowidget_layout);
updateViews.setTextViewText(R.id.widgettext, "[" + String.valueOf(appWidgetId) + "]" + strWidgetText);
appWidgetManager.updateAppWidget(appWidgetId, updateViews);

Toast.makeText(context, "updateAppWidget(): " + String.valueOf(appWidgetId) + "\n" + strWidgetText, Toast.LENGTH_LONG).show();

}

}


Modify HelloWidgetConfig.java to start Alarm Manager.
package com.exercise.HelloWidget;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;

public class HelloWidgetConfig extends Activity {

Button configOkButton;
int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;

@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);

setResult(RESULT_CANCELED);

 setContentView(R.layout.config);

 configOkButton = (Button)findViewById(R.id.okconfig);
 configOkButton.setOnClickListener(configOkButtonOnClickListener);

 Intent intent = getIntent();
 Bundle extras = intent.getExtras();
 if (extras != null) {
     mAppWidgetId = extras.getInt(
             AppWidgetManager.EXTRA_APPWIDGET_ID,
             AppWidgetManager.INVALID_APPWIDGET_ID);
 }

 // If they gave us an intent without the widget id, just bail.
 if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
     finish();
 }
}

private Button.OnClickListener configOkButtonOnClickListener
= new Button.OnClickListener(){

@Override
public void onClick(View arg0) {
// TODO Auto-generated method stub

final Context context = HelloWidgetConfig.this;

AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);

//RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.hellowidget_layout);
//appWidgetManager.updateAppWidget(mAppWidgetId, views);
HelloWidgetProvider.updateAppWidget(context, appWidgetManager, mAppWidgetId);

Toast.makeText(context, "HelloWidgetConfig.onClick(): " + String.valueOf(mAppWidgetId) , Toast.LENGTH_LONG).show();

//prepare Alarm Service to trigger Widget
   Intent intent = new Intent(HelloWidgetProvider.MY_WIDGET_UPDATE);
   PendingIntent pendingIntent = PendingIntent.getBroadcast(HelloWidgetConfig.this, 0, intent, 0);
   AlarmManager alarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
   Calendar calendar = Calendar.getInstance();
   calendar.setTimeInMillis(System.currentTimeMillis());
   calendar.add(Calendar.SECOND, 10);
   alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), 20*1000, pendingIntent);

Intent resultValue = new Intent();
resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
setResult(RESULT_OK, resultValue);
finish();
}};

}


Known Issues:
Please note that it's a simple exercise to implement App Widget using Alarm Manager, not a completed code. There are some issues:

- It's strongly recommanded that NOT to set Widget in such high repeating rate if not necessage, otherwise your battery will go low soon.

- The exercise haven't cancel the Alarm Service, it will not stop even all the Widget removed from Home Screen. (Refer to the article: Cancel Alarm Service in onDisabled()).

- There are no appWidgetManager and appWidgetId passed to onReceive() method, how to handle the individual Widget is another issue. (Refer to the article: Update Widget in onReceive() method).

3 comments:

  1. The download files link is broken. It says files not found.

    ReplyDelete
  2. Thank you that helped me.

    But one thing: please highlight your code and give it a bit more width!

    ReplyDelete
  3. Thanks for the tutorial, but you forgot , mention the declaration in the xml, file..or no? best regards!

    ReplyDelete