Confusion in using AlarmManager, intent while updating widget - android

I am new to Android development and Java and am trying to update my widget using AlarmManager but I am not able to fully understand why most of the tutorials do not update widgets in the following way. I am using textview to display a number in my widget and increment it once every second and decrement it by 10 when a widget is removed and reset to 0 when all widgets are removed.
public class widget_1_1 extends AppWidgetProvider {
private static int var1 = 0;
public void onReceive(Context context, Intent intent)
{
AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
ComponentName widgetComponent = new ComponentName(context.getPackageName(), this.getClass().getName());
int[] widgetId = widgetManager.getAppWidgetIds(widgetComponent);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_UPDATE))
{
this.onUpdate(context, AppWidgetManager.getInstance(context), widgetId);
alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()+1000, 1000, pendingIntent);
}
else if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_DELETED))
{
// one widget deleted
var1-=10;
}
else if (intent.getAction().equals(AppWidgetManager.ACTION_APPWIDGET_DISABLED))
{
// last widget deleted
alarmManager.cancel(pendingIntent);
var1=0;
}
}
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
var1++;
// Code to update widget by calling appWidgetManager.updateAppWidget here
}
}
Is there something wrong with this method above? All the tutorials I see use a private static final String alarmAction = "com.elison.widget1.ALARM_ACTION" or similar string in the class and use it to get PendingIntent. I do not understand what is its benefit and why not the above simple method?
public void onReceive(Context context, Intent intent)
{
// Some code
Intent enable = new Intent(alarmAction);
intent.setClass(context, WYDAppWidgetProvider_4_1.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, enable, 0);
// Some tutorials use PendingIntent.FLAG_UPDATE_CURRENT instead of 0 in 4th parameter
// more code
}

The only problem with your code is that PendingIntent creation should be inside if statement which checks whether its WIDGET_UPDATE action or not. And other thing is that you don't need to create AlarmManager every time as you are using repeating alarm manager. Also you extracting widgetId array manually every time, it should also be in if statement.

Related

AppWidget Imageview updatePeriodMillis

I am trying to have an ImageView in an AppWidget update every 30 minutes, but with no luck.
It uses the Glide library to load the widget, and I suspect caching.
I'm doing everything I can to prevent interferring with wake and the battery. Just needs to change the image every once and a while. On wake will be fine.
I'm thinking that 30 minute polling is when the device is active. Like, when it sleeps, it doesn't count and misses it.
Thank you for your help.
res/xml/widget_name.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialKeyguardLayout="#layout/widget_name"
android:initialLayout="#layout/widget_name"
android:minWidth="300dp"
android:minHeight="100dp"
android:previewImage="#drawable/example_appwidget_preview"
android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="1800000"
android:widgetCategory="home_screen"></appwidget-provider>
com/WidgetName/WidgetName.java
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget_name);
AppWidgetTarget awt = new AppWidgetTarget(context, R.id.imageView, remoteViews, appWidgetIds) {
#Override
public void onResourceReady(Bitmap resource, Transition<? super Bitmap> transition) {
super.onResourceReady(resource, transition);
}
};
RequestOptions options = new RequestOptions().
override(300, 300).placeholder(R.drawable.navigation_header_bg).error(R.drawable.ic_navigation_help);
// Create an Intent to launch Browser
Intent intent = new Intent(context, WidgetConfig.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, intent, 0);
Log.i("WidgetName", "src " + src);
remoteViews.setOnClickPendingIntent(R.id.imageView, pendingIntent);
Glide.with(context.getApplicationContext())
.asBitmap()
.load(src)
.apply(options)
.into(awt);
}
As taking look into your project you need to make following changes.
in res/xml/widget_name.xml set android:updatePeriodMillis="0"
Then in com/WidgetName/WidgetName.java
override onReceive method and make following changes
#Override
public void onReceive(Context context, Intent intent) {
switch (intent.getAction()) {
case AppWidgetManager.ACTION_APPWIDGET_UPDATE:
onUpdate(context, AppWidgetManager.getInstance(context), AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, LineaMeteoPremium.class)));
break;
default:
super.onReceive(context, intent);
}
}
And since you're updating all your widgets, updating them sequentially in a loop as follows with AlarmManager used as your refresh trigger (better flexibility as you can allow users to set custom refresh time too) and another issue was you were not using skipMemoryCache(true) and diskCacheStrategy(DiskCacheStrategy.NONE) methods to force glide to fetch new images instead of looking in cache.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for (int widgetId : appWidgetIds) {
setUpAlarm(context, widgetId);
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.linea_meteo_premium);
Log.e("LineaMeteo Widget", "src " + src);
AppWidgetTarget awt = new AppWidgetTarget(context, R.id.imageView, remoteViews, appWidgetIds);
RequestOptions options = new RequestOptions().
override(300, 300).placeholder(R.drawable.navigation_header_bg).error(R.drawable.ic_navigation_help).skipMemoryCache(true).diskCacheStrategy(DiskCacheStrategy.NONE);
// Create an Intent to launch Browser
Intent intent = new Intent(context, WidgetConfig.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(context, 0, intent, 0);
remoteViews.setOnClickPendingIntent(R.id.imageView, pendingIntent);
Glide.with(context.getApplicationContext())
.asBitmap()
.load(src)
.apply(options)
.into(awt);
}
}
Use the following code to set Alarm for refreshing widget based on the time you want
/**
* Sets up widget refresh alarm
*
* #param context The context of widget
* #param appWidgetId Widget ID of current widget
*/
private void setUpAlarm(Context context, int appWidgetId) {
final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long interval = (3 * 60000); // 60000 = 1minute | Change the formula based on your refresh timing needs
PendingIntent alarmPendingIntent = getRefreshWidgetPendingIntent(context, appWidgetId);
alarm.cancel(alarmPendingIntent);
// SET NEW ALARM
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarm.setExactAndAllowWhileIdle(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + interval, alarmPendingIntent);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
alarm.setExact(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + interval, alarmPendingIntent);
} else {
alarm.set(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime() + interval, alarmPendingIntent);
}
}
/**
* Get the pending intent object for refreshing the widget
*
* #param context current Context
* #param widgetId - Current Widget ID
* #return - Pending intent for refreshing widget
*/
private PendingIntent getRefreshWidgetPendingIntent(Context context, int widgetId) {
Intent intent = new Intent(context, LineaMeteoPremium.class);
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
// Make the pending intent unique...
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
return PendingIntent.getBroadcast(context, 123, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
And most important, since your app is using ClearText HTTP Traffic and cleartext support is disabled by default beginning Android 9.0, you need to add the following tag in your manifest's appilcation Tag to prevent widget crashes on android 9.+ devices.
<application
.
.
android:usesCleartextTraffic="true">

Receiving Alarm Intent Multiple Times

I need my alarm to fire once every day at 11am, and update all of my widgets. When I'm testing my application with one widget, my onReceive() method fires multiple times once the time arrives, updating my widget every time. Why does this happen?
This is my code:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
theAppWidgetManager = appWidgetManager;
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
...
}
scheduleNextUpdate(context);
}
#Override
public void onReceive(Context context, Intent intent){
if (intent.getAction().equals(ACTION_SCHEDULED_UPDATE)) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int[] ids = manager.getAppWidgetIds(new ComponentName(context, AppWidget.class));
onUpdate(context, manager, ids);
}
super.onReceive(context, intent);
}
private void scheduleNextUpdate(Context context) {
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
Intent intent = new Intent(context, AppWidget.class);
intent.setAction(ACTION_SCHEDULED_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
// Get a calendar instance for midnight tomorrow.
Calendar at11 = Calendar.getInstance();
at11.set(Calendar.HOUR_OF_DAY, 11);
at11.set(Calendar.MINUTE, 30);
at11.set(Calendar.SECOND, 1);
at11.set(Calendar.MILLISECOND, 0);
at11.add(Calendar.DAY_OF_YEAR, 1);
// For API 19 and later, set may fire the intent a little later to save battery,
// setExact ensures the intent goes off exactly at midnight.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) {
alarmManager.set(AlarmManager.RTC_WAKEUP, at11.getTimeInMillis(), pendingIntent);
} else {
alarmManager.setExact(AlarmManager.RTC_WAKEUP, at11.getTimeInMillis(), pendingIntent);
}
}
I think that the problem here is that you're scheduling next updates in onUpdate method. Description of this method says, that:
Called in response to the AppWidgetManager.ACTION_APPWIDGET_UPDATE and
AppWidgetManager.ACTION_APPWIDGET_RESTORED broadcasts when this
AppWidget provider is being asked to provide RemoteViews for a set of
AppWidgets.
so, in theory it could trigger several times. I think you should, either schedule next updates in onReceive, or check, somehow, if the broadcast for next update is already scheduled.

Android Widget click doesn't do anything after few hours

I have a widget that has a refresh button and a textview. Refresh updates the content and when user clicks on textview it starts a new activity.
Problem is it works fine for a few hours and then onclick and refresh button doesn't do anything. Nothing is captured in logcat. Also If user deletes widget and put a new one it starts working for a few hours and then the same story :(...what am I doing wrong!
Broadcast receiver.
onUpdate
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
long interval = getrefresInterval();
final Intent intent = new Intent(context, UpdateService.class);
final PendingIntent pending = PendingIntent.getService(context, 0, intent, 0);
final AlarmManager alarm = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarm.cancel(pending);
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime(),interval, pending);
// Build the intent to call the service
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),R.layout.widget);
// To react to a click we have to use a pending intent as the
// onClickListener is excecuted by the homescreen application
Intent ClickIntent = new Intent(context.getApplicationContext(),widgetHadith.class);
Intent UpdateIntent = new Intent(context.getApplicationContext(),UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context.getApplicationContext(), 0, ClickIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntentUpdate = PendingIntent.getService(context.getApplicationContext(), 0, UpdateIntent,
PendingIntent.FLAG_UPDATE_CURRENT); //use this to update text on widget. if use this put UpdateService.class to intent
remoteViews.setOnClickPendingIntent(R.id.widget_textview, pendingIntent);
remoteViews.setOnClickPendingIntent(R.id.widget_refresh, pendingIntentUpdate);
// Finally update all widgets with the information about the click listener
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
// Update the widgets via the service
context.startService(intent);
}
onReceive
public void onReceive(Context context, Intent intent) {
// v1.5 fix that doesn't call onDelete Action
final String action = intent.getAction();
if (AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)) {
final int appWidgetId = intent.getExtras().getInt(
AppWidgetManager.EXTRA_APPWIDGET_ID,
AppWidgetManager.INVALID_APPWIDGET_ID);
if (appWidgetId != AppWidgetManager.INVALID_APPWIDGET_ID) {
this.onDeleted(context, new int[] { appWidgetId });
}
} else {
super.onReceive(context, intent);
}
}
onDelete
public void onDeleted(Context context, int[] appWidgetIds) {
// Toast.makeText(context, "onDelete", Toast.LENGTH_SHORT).show();
super.onDeleted(context, appWidgetIds);
}
Service onstart where I am updating
#Override
public void onStart(Intent intent, int startId) {
RemoteViews updateViews = new RemoteViews(this.getPackageName(),R.layout.widget);
processDatabase();
Spanned text = LoadHadith();
String hadith = text.toString();
Log.d("BR", "service---> ");
// set the text of component TextView with id 'message'
updateViews.setTextViewText(R.id.widget_textview, text);
//Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this, HelloWidget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
The problem is that you can't do a partiall update for a widget, you must set all the widget features, such as the set of PendingIntent's every time you push a new remoteView.
(Partiall updates are only available for API14 and up...).
The reason your widgets are loosing their pendingIntents is that the android system saves the remoteView, and rebuilds your widget with it, in case it resets the widget (shortage of memmory, TaskManager/taskKiller in use, etc...), so you must set all the update code for the widget in the remoteView in your updateService.
Otherwise, it's just won't set the pendingIntents again.
So just add the code setting the pendingIntents to the service and your problem will be solved =]

Update AppWidget Periodically from a service

This is what I want from my AppWidget:
configuration activity comes up, when widget is added to the screen // good so far
after configuration is saved, a service is started that updates the widget // good so far
schedule an alarm periodically to run the service that updates the widget. // having troubles here
This is seriously giving me grey hair already, and I don't know what to do anymore. How do you set the update rate for an AppWidget from a service? I can update the widget from the service, but when I try to set the alarm, it does not get to the onReceive() method on the AppWidget.
Here is the code for the service update:
Intent updateWidget = new Intent();
updateWidget.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
updateWidget.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[]{appWidgetId});
Uri data = Uri.withAppendedPath(Uri.parse(WeatWidgetProvider.URI_SCHEME +
"://widget/id/"), String.valueOf(appWidgetId));
updateWidget.setData(data);
PendingIntent updatePend = PendingIntent.getBroadcast(this, 0, updateWidget, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager alarm = (AlarmManager)getSystemService(ALARM_SERVICE);
alarm.setRepeating(AlarmManager.ELAPSED_REALTIME, SystemClock.elapsedRealtime()+ updateRate, updateRate, updatePend);
And in the onreceive() of WidgetProviderClass:
public void onReceive(Context context, Intent intent){
super.onReceive(context, intent);
Log.d("OnReceive", "OnReceive called");
String action = intent.getAction();
Log.d("Action", "OnReceive:Action: " + action);
if(AppWidgetManager.ACTION_APPWIDGET_DELETED.equals(action)){
int appwidgetId = intent.getExtras().getInt(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
if(appwidgetId != AppWidgetManager.INVALID_APPWIDGET_ID){
this.onDeleted(context, new int[] {appwidgetId});
}
} else if(AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)){
if(!URI_SCHEME.equals(intent.getScheme())){
final int[] appWidgetIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
for(int appWidgetId:appWidgetIds){
SharedPreferences prefs = context.getSharedPreferences(WeatForecastConfigure.WEATHER_PREF_NAME, Context.MODE_PRIVATE);
update = prefs.getInt(WeatForecastConfigure.REFRESH_UPDATE, -1);
System.out.println(update);
if(update != -1){
Intent widgetUpdate = new Intent();
widgetUpdate.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
widgetUpdate.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, new int[] { appWidgetId });
// make this pending intent unique by adding a scheme to it
widgetUpdate.setData(Uri.withAppendedPath(Uri.parse(WeatWidgetProvider.URI_SCHEME +
"://widget/id/"), String.valueOf(appWidgetId)));
PendingIntent newPending = PendingIntent.getBroadcast(context.getApplicationContext(), 0, widgetUpdate, PendingIntent.FLAG_UPDATE_CURRENT);
// schedule the updating
AlarmManager alarms = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
alarms.setRepeating(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime(), WeatForecastConfigure.convertToMillis(update), newPending);
}
}
}
super.onReceive(context, intent);
}else{
super.onReceive(context, intent);
}
}
For onUpdate() commented lines are deliberate.
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
Log.d("OnUpdate", "OnUpdate called");
for (int appWidgetId : appWidgetIds) {
// context.startService(new Intent(context,WeatService.class));
}
//super.onUpdate(context, appWidgetManager, appWidgetIds);
}
Please help. I have no idea how to set the alarm to update. Whether I do it from the configuration class or from a service, it does not work. Thanks.
I think you need to use a standard URI scheme in your intent like this:
intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME)));
I wrote all this, and realized it will probably just confused you more, oh well.
Here is the skeleton of how I update my widgets, it is loosely based on this code http://code.google.com/p/android-sky/source/browse/#svn%2Ftrunk%2FSky (particularly the UpdateService class.
The main difference is my code
Starts the service to perform the update
Sets the Alarm (a broadcast pending intent) to alert the widget about the next update
Updates the widget and stops the service
Receives update broadcast (in the providers onReceive) and starts the service again
You seem to be trying to tell the AppWidgetProvider to kickoff through the onUpdate, I'm not sure if this is even possible?
Here is how I do it in semi pseudo code:
UpdateService class:
//based on (an old) example http://code.google.com/p/android-sky/source/browse/#svn%2Ftrunk%2FSky however other methods I have tried have not been as reliable as this
public void run() {
//set the alarm for the next update
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
long millis = System.currentTimeMillis();
//one alarm to update them all, if you wanted to you could
//add an extra appWidgetId then you might need to encode the intent
//however so it is a unique PendingIntent
Intent updateIntent = new Intent(ACTION_UPDATE_ALL); //ACTION_UPDATE_ALL should be a constant somewhere that can be used to resolve this action com.packagename.ACTION_UPDATE_ALL
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, updateIntent, 0);
// Schedule alarm, and force the device awake for this update
alarmManager.set(AlarmManager.RTC, millis + updateFrequency * DateUtils.MINUTE_IN_MILLIS, pendingIntent);
Log.d(TAG, "Next update should be at: " + DateFormat.format("h:mm:ss", millis + updateFrequency * DateUtils.MINUTE_IN_MILLIS));
while (hasMoreUpdates()) {
int appWidgetId = getNextUpdate();
if (!(appWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID)) {
Uri appWidgetUri = ContentUris.withAppendedId(Agenda.getContentUri(context), appWidgetId);
AppWidgetProviderInfo info = manager.getAppWidgetInfo(appWidgetId);
String providerName = info.provider.getClassName();
//if the provider matches one of my widget classes, call the update method
if (providerName.equals(Widget_4_1.class.getName())) {
remoteViews = Widget_4_1.buildUpdate(context, appWidgetUri, dateRows);
}
//perform more actions such as sending remoteViews to the AppWidgetManager
}
}
}
Widget_4_1 class:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
// If no specific widgets requested, collect list of all
if (appWidgetIds == null) {
appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(
context, AbstractWidget.class));
}
UpdateService.requestUpdate(appWidgetIds);
Intent i = new Intent();
i.setComponent(new ComponentName(context, UpdateService.class));
context.startService(i);
}
//called from service
public static RemoteViews buildUpdate(Context context, Uri appWidgetUri) {
int appWidgetId = (int) ContentUris.parseId(appWidgetUri);
RemoveViews remoteViews = someMethodToBuildView(appWidgetId );
//return view to service
return remoteViews;
}
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
//when the alarm triggers, just update all your widgets, why handle them separately?
if (action.equals(ACTION_UPDATE_ALL)) {
AppWidgetManager manager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = manager.getAppWidgetIds(new ComponentName(context, getClass()));
if(appWidgetIds.length>0){
UpdateService.requestUpdate(appWidgetIds);
Intent updateIntent = new Intent(context, UpdateService.class);
updateIntent.setAction(action);
context.startService(updateIntent);
}
}
//else you could catch a specific action for updating a single widget and
//tell your update service to do it manually
}
AndroidManifest.xml:
<application>
...
<receiver android:name="com.mypackage.Widget_4_1"
android:label="#string/agenda_widget_name_4_1">
<intent-filter>
<!-- IMPORTANT, needs to match the ACTION_UPDATE_ALL string (e.g. a constant somewhere) -->
<action android:name="com.packagename.UPDATE_ALL" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widget_4_1" />
</receiver>
...
</application>
Hope I haven't confused you.
As this code is copy/pasted from various parts of my project excuse typo's etc, I will fix them as they are pointed out.

Updating app widget using AlarmManager

I am trying to update a Widget more frequently than the 30 minute restriction imposed by the 1.6docs. After reading nearly every post in SO, and the developer docs, and various other sources, I thought I had got to a point where i could implement it. And so, I tried, and failed. Since then, I have trawled yet more forums and solutions, and I cannot seem to get it to update.
I have an Update class that sets the AlarmManager:
public class Update extends Service{
#Override
public void onStart(Intent intent, int startId) {
String currentTemp = Battery.outputTemp;
String currentLevel = Battery.outputLevel;
String currentCard = Battery.outputCard;
String currentInternal = Battery.memory;
String currentRam = String.valueOf(Battery.outputRam).substring(0, 3) + "MB";
// Change the text in the widget
RemoteViews updateViews = new RemoteViews(
this.getPackageName(), R.layout.main);
//update temp
updateViews.setTextViewText(R.id.batteryTemp, currentTemp);
//update %
updateViews.setTextViewText(R.id.batteryLevel, currentLevel);
//update level
updateViews.setTextViewText(R.id.sdCard, currentCard);
//update internal memory
updateViews.setTextViewText(R.id.internal, currentInternal);
//update ram
updateViews.setTextViewText(R.id.ram, currentRam);
ComponentName thisWidget = new ComponentName(this, Widget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
#Override
public IBinder onBind(Intent intent) {
// no need to bind
return null;
}
}
This has caused my onReceive in my widget class to fire frequently (i have a toast to see when it fires), yet it carries no intent (the toast is meant to display this as they are received but it is blank).
I cannot figure it out (i'm a relative newb-2 months of slow android dev), and appreciate any insight you guys have.
heres my widget class for reference:
public class Widget extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
AlarmManager alarmManager;
Intent intent = new Intent(context, Update.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
alarmManager = (AlarmManager) context
.getSystemService(Context.ALARM_SERVICE);
Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(System.currentTimeMillis());
cal.add(Calendar.SECOND, 10);
alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, cal
.getTimeInMillis(), 5 * 1000, pendingIntent);
String currentTemp = Battery.outputTemp;
String currentLevel = Battery.outputLevel;
String currentCard = Battery.outputCard;
String currentInternal = Battery.memory;
String currentRam = String.valueOf(Battery.outputRam).substring(0, 3)
+ "MB";
// Change the text in the widget
RemoteViews updateViews = new RemoteViews(context.getPackageName(),
R.layout.main);
// update temp
updateViews.setTextViewText(R.id.batteryTemp, currentTemp);
appWidgetManager.updateAppWidget(appWidgetIds, updateViews);
// update %
updateViews.setTextViewText(R.id.batteryLevel, currentLevel);
appWidgetManager.updateAppWidget(appWidgetIds, updateViews);
// update level
updateViews.setTextViewText(R.id.sdCard, currentCard);
appWidgetManager.updateAppWidget(appWidgetIds, updateViews);
// update internal memory
updateViews.setTextViewText(R.id.internal, currentInternal);
appWidgetManager.updateAppWidget(appWidgetIds, updateViews);
// update ram
updateViews.setTextViewText(R.id.ram, currentRam);
appWidgetManager.updateAppWidget(appWidgetIds, updateViews);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
Toast
.makeText(context, intent.getAction() + context,
Toast.LENGTH_LONG).show();
Bundle extras = intent.getExtras();
if (extras != null) {
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
ComponentName thisAppWidget = new ComponentName(context
.getPackageName(), Widget.class.getName());
int[] appWidgetIds = appWidgetManager
.getAppWidgetIds(thisAppWidget);
onUpdate(context, appWidgetManager, appWidgetIds);
}
}
}
This is my solution, how to automatically update widget more frequently than the 30 minutes. I use AlarmManager. Before you use AlarmManager for refreshing appwidget, make sure you know what you do, because this technique could drain the device's battery.
Read more about widget update in Android doc - especially about updatePeriodMillis parameter.
This is part of my Manifest.xml. I define custom action AUTO_UPDATE.
<receiver android:name=".appwidget.AppWidget" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="AUTO_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="#xml/appwidget_info" />
</receiver>
This is part of my AppWidget.java. In onReceive method, I handle my custom action AUTO_UPDATE. In onEnabled and onDisabled methods, I start/stop alarm.
public class AppWidget extends AppWidgetProvider
{
public static final String ACTION_AUTO_UPDATE = "AUTO_UPDATE";
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
if(intent.getAction().equals(ACTION_AUTO_UPDATE))
{
// DO SOMETHING
}
...
}
#Override
public void onEnabled(Context context)
{
// start alarm
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.startAlarm();
}
#Override
public void onDisabled(Context context)
{
// stop alarm only if all widgets have been disabled
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidgetComponentName = new ComponentName(context.getPackageName(),getClass().getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidgetComponentName);
if (appWidgetIds.length == 0) {
// stop alarm
AppWidgetAlarm appWidgetAlarm = new AppWidgetAlarm(context.getApplicationContext());
appWidgetAlarm.stopAlarm();
}
}
...
}
This is my AppWidgetAlarm.java, which starts/stops alarm. Alarm manager sends broadcast to AppWidget.
public class AppWidgetAlarm
{
private final int ALARM_ID = 0;
private final int INTERVAL_MILLIS = 10000;
private Context mContext;
public AppWidgetAlarm(Context context)
{
mContext = context;
}
public void startAlarm()
{
Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS);
Intent alarmIntent=new Intent(mContext, AppWidget.class);
alarmIntent.setAction(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
// RTC does not wake the device up
alarmManager.setRepeating(AlarmManager.RTC, calendar.getTimeInMillis(), INTERVAL_MILLIS, pendingIntent);
}
public void stopAlarm()
{
Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
alarmManager.cancel(pendingIntent);
}
}
I have an Update class that sets the AlarmManager:
No, you don't. AlarmManager appears nowhere in the code snippet.
You do have a reference to AlarmManager in the second code snippet. Problems there include:
You are setting a new repeating alarm every time the app widget updates
You are setting a 5 second frequency on the alarm, which is utter insanity
You are setting a 5 second frequency on a _WAKEUP alarm, which I think is grounds for your arrest in some jurisdictions
You have a pointless onReceive() method, even ignoring the temporary Toast
You are assuming that there will be an action string on the Intent in your Toast, but you do not specify an action string when you create the Intent that you put in the PendingIntent for the alarm
Your code refers to what I presume are static data members on a Battery class, but it is rather likely those are all empty/null... or at least they would be, if you had a sane frequency on the alarm
Thanks for this example - I also had problems using a later Android version.
This post made it work for me:
widget case that doesn't work (see the answer from Larry Schiefer).
So substituting for this from the code above:
Intent alarmIntent = new Intent(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
with this from the ref:
Intent alarmIntent=new Intent(mContext, MyWidget.class);
alarmIntent.setAction(AppWidget.ACTION_AUTO_UPDATE);
PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT);
did the job.
A little bit modified version of petrnohejl's solution. This one is working in my project. (written in kotlin):
This is part of the Manifest.xml. I added the following actions: AUTO_UPDATE, APPWIDGET_UPDATE, APPWIDGET_ENABLED, APWIDGET_DISABLED.
<receiver android:name=".AppWidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
<action android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<action android:name="android.appwidget.action.APPWIDGET_DISABLED"/>
</intent-filter>
<intent-filter>
<action android:name="ACTION_AUTO_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/appwidget_info"/>
</receiver>
This is part of the AppWidget.kt. Here I implemented the onUpdate(), onEnabled(), onDisabled(), onReceive() functions.
class AppWidget: AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
// There may be multiple widgets active, so update all of them
for (appWidgetId in appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId)
}
}
override fun onEnabled(context: Context) { // Enter relevant functionality for when the first widget is created
// start alarm
val appWidgetAlarm = AppWidgetAlarm(context.applicationContext)
appWidgetAlarm.startAlarm()
}
override fun onDisabled(context: Context) { // Enter relevant functionality for when the last widget is disabled
// stop alarm only if all widgets have been disabled
val appWidgetManager = AppWidgetManager.getInstance(context)
if (appWidgetIds.isEmpty()) {
// stop alarm
val appWidgetAlarm = AppWidgetAlarm(context.getApplicationContext())
appWidgetAlarm.stopAlarm()
}
}
companion object {
val ACTION_AUTO_UPDATE = "AUTO_UPDATE"
fun updateAppWidget(context: Context, appWidgetManager: AppWidgetManager, appWidgetId: Int) {
val widgetText = Random.nextInt(0, 100).toString()
// Construct the RemoteViews object
val views = RemoteViews(context.packageName, R.layout.appwidget)
views.setTextViewText(R.id.widget_text, widgetText)
// Instruct the widget manager to update the widget
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_text)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
// Do something
/*if (intent!!.action == ACTION_AUTO_UPDATE) {
// DO SOMETHING
}*/
}
}
And this is the AppWidgetAlarm.kt. Here it is my main modification. The answers didn't help me, but it is working. I set here a repeating alarm. (https://developer.android.com/training/scheduling/alarms)
class AppWidgetAlarm(private val context: Context?) {
private val ALARM_ID = 0
private val INTERVAL_MILLIS : Long = 10000
fun startAlarm() {
val calendar: Calendar = Calendar.getInstance()
calendar.add(Calendar.MILLISECOND, INTERVAL_MILLIS.toInt())
val alarmIntent = Intent(context, AppWidget::class.java).let { intent ->
//intent.action = AppWidget.ACTION_AUTO_UPDATE
PendingIntent.getBroadcast(context, 0, intent, 0)
}
with(context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager) {
setRepeating(AlarmManager.RTC,calendar.timeInMillis, INTERVAL_MILLIS ,alarmIntent)
}
}
fun stopAlarm() {
val alarmIntent = Intent(AppWidget.ACTION_AUTO_UPDATE)
val pendingIntent = PendingIntent.getBroadcast(context, ALARM_ID, alarmIntent, PendingIntent.FLAG_CANCEL_CURRENT)
val alarmManager = context!!.getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.cancel(pendingIntent)
}
}

Categories

Resources