I have an android widget that fetches data from a server every 10 minutes and display's it on the screen.
I'd like to add a "Refresh" button to that widget.
When the user clicks that button I'd like to run the method that fetches the information from the server.
Adding an event handler to a button in an application is very easy, however I couldn't find an example for a widget.
I'd like to get some help with adding a function to a button click in a widget.
Here is one example more that should help:
package com.automatic.widget;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
public class Widget extends AppWidgetProvider {
private static final String SYNC_CLICKED = "automaticWidgetSyncButtonClick";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
watchWidget = new ComponentName(context, Widget.class);
remoteViews.setOnClickPendingIntent(R.id.sync_button, getPendingSelfIntent(context, SYNC_CLICKED));
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
super.onReceive(context, intent);
if (SYNC_CLICKED.equals(intent.getAction())) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
watchWidget = new ComponentName(context, Widget.class);
remoteViews.setTextViewText(R.id.sync_button, "TESTING");
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
}
protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
}
I found out how to do that.
Add an action to the AndroidManifest.xml file in the > <receiver><intent-filter> tag:
<action android:name="MY_PACKAGE_NAME.WIDGET_BUTTON" />
In the provider add a constant that matches the action name:
public static String WIDGET_BUTTON = "MY_PACKAGE_NAME.WIDGET_BUTTON";
In the onUpdate() method add a pending intent that matches the action:
Intent intent = new Intent(WIDGET_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );
Finally, in the onRecieve() method, check the action name:
if (WIDGET_BUTTON.equals(intent.getAction())) {
//your code here
}
Here is another answer with the following benefits:
It handles all App Widget instances (a user might have multiple instances of your widget in various configurations/sizes on your screen). Coding for all instances is what the official documentation prescribes. See Guide > App Widgets > Using the AppWidgetProvider Class , scroll down to the code example for "ExampleAppWidgetProvider".
The workhorse code in onReceive in effect calls onUpdate (so you reduce code duplication).
The code in onUpdate(Context context) is generalised so that it can be dropped into any AppWidgetProvider subclass.
The code:
public class MyWidget extends AppWidgetProvider {
private static final String ACTION_UPDATE_CLICK =
"com.example.myapp.action.UPDATE_CLICK";
private static int mCount = 0;
private static String getMessage() {
return String.valueOf(mCount++);
}
private PendingIntent getPendingSelfIntent(Context context, String action) {
// An explicit intent directed at the current class (the "self").
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
String message = getMessage();
// Loop for every App Widget instance that belongs to this provider.
// Noting, that is, a user might have multiple instances of the same
// widget on
// their home screen.
for (int appWidgetID : appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.my_widget);
remoteViews.setTextViewText(R.id.textView_output, message);
remoteViews.setOnClickPendingIntent(R.id.button_update,
getPendingSelfIntent(context,
ACTION_UPDATE_CLICK)
);
appWidgetManager.updateAppWidget(appWidgetID, remoteViews);
}
}
/**
* A general technique for calling the onUpdate method,
* requiring only the context parameter.
*
* #author John Bentley, based on Android-er code.
* #see <a href="http://android-er.blogspot.com
* .au/2010/10/update-widget-in-onreceive-method.html">
* Android-er > 2010-10-19 > Update Widget in onReceive() method</a>
*/
private void onUpdate(Context context) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance
(context);
// Uses getClass().getName() rather than MyWidget.class.getName() for
// portability into any App Widget Provider Class
ComponentName thisAppWidgetComponentName =
new ComponentName(context.getPackageName(),getClass().getName()
);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
thisAppWidgetComponentName);
onUpdate(context, appWidgetManager, appWidgetIds);
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (ACTION_UPDATE_CLICK.equals(intent.getAction())) {
onUpdate(context);
}
}
}
The widget looks like this
This builds on the getPendingSelfIntent work of #Kels, #SharonHaimPour and #Erti-ChrisEelmaa.
It also builds on Android-er > 2010-10-19 > Update Widget in onReceive() method (not me) where it is demonstrated how to call onUpdate from onReceive, on an App Widget instance basis. I make that code general and wrap it in callOnUpdate.
protected PendingIntent getPendingSelfIntent(Context context, String action) {
Intent intent = new Intent(context, getClass());
intent.setAction(action);
return PendingIntent.getBroadcast(context, 0, intent, 0);
}
views.setOnClickPendingIntent(R.id.Timm, getPendingSelfIntent(context,
"ham"));
Also prefer URL :
How to correctly handle click events on Widget
If you solved it in a different way, please provide this as an answer
In the pendingIntent, we can also put extra attribute appWidgetId to reuse it later in onReceive to update the widget clicked widget instance
class ExampleAppWidgetProvider : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray {
appWidgetIds.forEach { appWidgetId ->
Log.e("TAG", "onUpdate $appWidgetId")
val pendingRefreshClickIntent: PendingIntent = Intent(context, javaClass).let {
it.action = ACTION_REFRESH_CLICK
it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId)
return#let PendingIntent.getBroadcast(
context,
appWidgetId, // click in all instances widget will work well (base on Alireza Mirian comment in the top answer)
it,
PendingIntent.FLAG_UPDATE_CURRENT
)
}
val views = RemoteViews(
context.packageName,
R.layout.example_appwidget
)
views.setOnClickPendingIntent(R.id.button_refresh, pendingRefreshClickIntent)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
override fun onReceive(context: Context?, intent: Intent?) {
super.onReceive(context, intent)
Log.i("TAG", "onReceive " + intent?.action)
if (intent?.action == ACTION_REFRESH_CLICK) {
val appWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: return
Log.i("TAG", "onReceive appWidgetId $appWidgetId")
val appWidgetManager = AppWidgetManager.getInstance(context)
val views = RemoteViews(context!!.packageName, R.layout.example_appwidget)
views.setTextViewText(R.id.text_data, "a " + (Math.random() * 9).roundToInt())
appWidgetManager.updateAppWidget(appWidgetId, views)
}
}
companion object {
private const val ACTION_REFRESH_CLICK = "com.example.androidwidgetbuttonclick.action.ACTION_REFRESH_CLICK"
}
}
Widget initial layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/text_data"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="AA"
android:textSize="20sp" />
<Button
android:id="#+id/button_refresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Refresh" />
</LinearLayout>
I tried the solution suggested by Sharon Haim Pour above, but my onReceive() method in AppWidgetProvider class has never been called on button press.
Intent intent = new Intent(WIDGET_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );
After some research I could resolve the problem by updating the code as below:
Intent intent = new Intent(context, MY_APPWIDGETPROVIDER_CLASS.class);
intent.setAction(WIDGET_BUTTON);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.MY_BUTTON_ID, pendingIntent );
Do not forget to put below:
appWidgetManager.updateAppWidget(appWidgetId, views);
Unlike the other answers here which use onReceive(), I found that it's actually a lot cleaner and simpler to do everything in onUpdate().
The official Android codelab Advanced Android 02.1: App widgets offers this solution. The example code there is in Java. Here I present the solution in Kotlin.
class MyAppWidgetProvider : AppWidgetProvider() {
override fun onUpdate(
context: Context?,
appWidgetManager: AppWidgetManager?,
appWidgetIds: IntArray?
) {
appWidgetIds?.forEach { appWidgetId ->
val views = RemoteViews(
context?.packageName,
R.layout.appwidget
)
// Coroutine to perform background IO task.
GlobalScope.launch(Dispatchers.IO) {
// Suspend function.
val apiData = Api.retrofitService.getData()
updateWidgetUI(views, apiData)
context?.let {
views.setOnClickPendingIntent(
R.id.widget_button,
getUpdatePendingIntent(it, appWidgetId)
)
}
appWidgetManager?.updateAppWidget(appWidgetId, views)
}
}
}
private fun updateWidgetUI(views: RemoteViews, apiData: ApiData){
views.apply {
setTextViewText(R.id.widget_value_textview, apiData.value)
setTextViewText(
R.id.widget_last_updated_value_textview,
DateFormat.getTimeInstance(DateFormat.MEDIUM).format(Date())
)
}
}
private fun getUpdatePendingIntent(context: Context, appWidgetId: Int): PendingIntent {
val intent = Intent(context, MyAppWidgetProvider::class.java).also {
it.action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
// It's very important to use intArrayOf instead of arrayOf,
// as a primitive int array is expected.
it.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(appWidgetId))
}
// Set the immutability flag for Android 12.
val flags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
} else {
PendingIntent.FLAG_UPDATE_CURRENT
}
return PendingIntent.getBroadcast(
context,
appWidgetId,
intent,
flags
)
}
// No need for onReceive().
}
The key here is to use the built-int AppWidgetManager.ACTION_APPWIDGET_UPDATE action instead of a custom action.
Related
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.
I'm starting with widgets and got a very nice tutorial on the internet, got the example run perfectly, but when i tried to change somethings I got stuck.
The thing is: I just want to change the image from my imageButton when i press it, I've tried somethings but nothing seems to work. I didn't get how the RemoteView and Intent works exactly. So if someone can explain it shortly I would appreciate it =)
Here's the code:
public class HelloWidget extends AppWidgetProvider {
private ImageButton wifi;
public static String ACTION_WIDGET_CONFIGURE = "ConfigureWidget";
public static String ACTION_WIDGET_RECEIVER = "ActionReceiverWidget";
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
Intent configIntent = new Intent(context, ClickOneActivity.class);
configIntent.setAction(ACTION_WIDGET_CONFIGURE);
Intent active = new Intent(context, HelloWidget.class);
active.setAction(ACTION_WIDGET_RECEIVER);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context, 0, active, 0);
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.button_wifi, actionPendingIntent);
remoteViews.setOnClickPendingIntent(R.id.button_two, configPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
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 {
// check, if our Action was called
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) {
Toast.makeText(context, "Teste", Toast.LENGTH_LONG).show();
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.main);
remoteViews.setInt(R.id.button_wifi, "toogleOnOff", R.drawable.icon);
}
super.onReceive(context, intent);
}
}
}
There's a lot of the tutorial code i got as you can see =p
Thx since now
Looks like you need to understand a little more about RemoteViews. When you call functions like setOnClickPendingIntent, setInt, etc. on the RemoteViews object it basically just stores these operations and arguments internally. Then when the widget is displayed it just plays those operations back to construct the widget's views. So after giving the RemoteViews to the AppWidgetManager by calling updateAppWidget, you can't change them again unless you rebuild the whole RemoteViews and call updateAppWidget again.
As an answer to your question, you want to use a state list as the background for the button. There's a good example here.
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)
}
}
When we click the widget at that time I need to open an activity screen (or application). How to do this?
You need to set an onClickpendingIntent on your widget
Intent intent = new Intent(context, ExampleActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
Check this out
Processing more than one button click at Android Widget
Include this code in yout WidgetProvider class's onUpdate() method.
for(int j = 0; j < appWidgetIds.length; j++)
{
int appWidgetId = appWidgetIds[j];
try {
Intent intent = new Intent("android.intent.action.MAIN");
intent.addCategory("android.intent.category.LAUNCHER");
intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
intent.setComponent(new ComponentName("your Application package",
"fully qualified name of main activity of the app"));
PendingIntent pendingIntent = PendingIntent.getActivity(
context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(),
layout id);
views.setOnClickPendingIntent(view Id on which onclick to be handled, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
} catch (ActivityNotFoundException e) {
Toast.makeText(context.getApplicationContext(),
"There was a problem loading the application: ",
Toast.LENGTH_SHORT).show();
}
}
The Android developer pages for App Widgets has information and a full example doing exactly this: http://developer.android.com/guide/topics/appwidgets/index.html
Using Kotlin
You need to add PendingIntent on Click of your widget Views
remoteViews.setOnClickPendingIntent(R.id.widgetRoot,
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0))
Where widgetRoot is the id of my widget's parent ViewGroup
In On Update
Pending intent is usually added in onUpdate callback
override fun onUpdate(
context: Context,
appWidgetManager: AppWidgetManager,
appWidgetIds: IntArray) {
// There may be multiple widgets active, so update all of them
val widgetIds = appWidgetManager.getAppWidgetIds( ComponentName(context, ClockWidget::class.java))
for (appWidgetId in widgetIds) {
// Construct the RemoteViews object
val remoteViews = RemoteViews(context.packageName, R.layout.clock_widget)
//Open App on Widget Click
remoteViews.setOnClickPendingIntent(R.id.weatherRoot,
PendingIntent.getActivity(context, 0, Intent(context, MainActivity::class.java), 0))
//Update Widget
remoteViews.setTextViewText(R.id.appWidgetText, Date().toString())
appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
}
}
}
very simple(In xamarin c# android mono):
public override void OnReceive(Context context, Intent intent)
{
if (ViewClick.Equals(intent.Action))
{
var pm = context.PackageManager;
try
{
var packageName = "com.companyname.YOURPACKAGENAME";
var launchIntent = pm.GetLaunchIntentForPackage(packageName);
context.StartActivity(launchIntent);
}
catch
{
// Something went wrong :)
}
}
base.OnReceive(context, intent);
}
Would like a button in my widget to fire the APPWIDGET_UPDATE intent on the widget class to force an update, but I dont see APPWIDGET_UPDATE as a static field in Intent.
Is this possible, and how would one do this?
Intent intent = new Intent(context, BaseWidgetProvider.class);
intent.setAction({APPWIDGET_UPDATE INTENT HERE})
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.MyWidgetButton, pendingIntent);
Yes, it's possible. You'll find the action in AppWidgetManager:
intent.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE)
Edit: You will need to provide the ids of the widgets you want to update. Below is a complete sample.
AppWidgetManager widgetManager = AppWidgetManager.getInstance(context);
ComponentName widgetComponent = new ComponentName(context, YourWidget.class);
int[] widgetIds = widgetManager.getAppWidgetIds(widgetComponent);
Intent update = new Intent();
update.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, widgetIds);
update.setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
context.sendBroadcast(update);
I know this is a very old question, but I think this might be interesting, because Android updated the AppWidgets refresh policies. I think this change could prevent the exising answer to work as expected.
This is my solution, using RemoteViews and a collection.
public static final String ACTION_WIDGET_UPDATE = "com.yourpackage.widget.ACTION_UPDATE";
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_WIDGET_UPDATE)) {
int widgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, 0);
AppWidgetManager.getInstance(context)
.notifyAppWidgetViewDataChanged(widgetId, R.id.widgetColectionRoot);
}
super.onReceive(context, intent);
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int widgetId : appWidgetIds) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
RemoteViews collectionRemoteView = getRemoteViews(widgetId, context);
appWidgetManager.updateAppWidget(widgetId, collectionRemoteView);
}
}
}
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
#SuppressWarnings("deprecation")
private RemoteViews getRemoteViews(int widgetId, Context context) {
// Sets up the intent that points to the RemoteViewService
// that will
// provide the views for this collection.
Intent widgetUpdateServiceIntent = new Intent(context,
RemoteViewsService.class);
widgetUpdateServiceIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, widgetId);
// When intents are compared, the extras are ignored, so we need
// to embed the extras
// into the data so that the extras will not be ignored.
widgetUpdateServiceIntent.setData(
Uri.parse(widgetUpdateServiceIntent.toUri(Intent.URI_INTENT_SCHEME)));
RemoteViews collectionRemoteView = new RemoteViews(context.getPackageName(),
R.layout.widget_collection);
collectionRemoteView.setRemoteAdapter(widgetId,
R.id.widgetColectionRoot, widgetUpdateServiceIntent);
collectionRemoteView.setEmptyView(R.id.widgetColectionRoot, R.id.widgetEmpty);
// This section makes it possible for items to have
// individualized behavior.
// It does this by setting up a pending intent template.
// Individuals items of a collection
// cannot set up their own pending intents. Instead, the
// collection as a whole sets
// up a pending intent template, and the individual items set a
// fillInIntent
// to create unique behavior on an item-by-item basis.
Intent selectItemIntent = new Intent(context,
BrochuresWidgetProvider.class);
Intent refreshIntent = new Intent(selectItemIntent);
refreshIntent.setAction(ACTION_WIDGET_UPDATE);
PendingIntent refreshPendingIntent = PendingIntent.getBroadcast(
context, 0, refreshIntent, PendingIntent.FLAG_UPDATE_CURRENT);
collectionRemoteView.setOnClickPendingIntent(R.id.widgetReload,
refreshPendingIntent);
return collectionRemoteView;
}
Of course, you also need to register that intent-filter on your manifest, inside your widget provider declaration.