Reputation: 2513
Fist of all I tried all the methods which I found on stackoverflow and non of them worked.
I'm developing a battery monitoring app and it contains some widgets to display information. I use a common receiver called BatteryUpdateReceiver
to listen to ACTION_BATTERY_CHANGED
event. I use this receiver to update a widget which shows current battery level. But for some reasons it does not update the widget. I tried many ways but could not figure out whats's wrong. My code is given below.
BatteryUpdateReceiver.java
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(Intent.ACTION_BATTERY_CHANGED)) {
Log.i(TAG, "Receiver ACTION_BATTERY_CHANGED fired");
RemoteViews remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widget);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
ComponentName thisWidget = new ComponentName(context.getApplicationContext(), BatteryWidget.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int i=0; i < allWidgetIds.length; i++) {
Log.i(TAG, "WID ID: " + allWidgetIds[i]);
appWidgetManager.updateAppWidget(allWidgetIds[i], remoteViews);
}
}
}
BatteryWidget.java
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
Log.i(TAG, "onUpdate: ");
int currentLevel = calculateBatteryLevel(context);
if (batteryChanged(currentLevel)) {
batteryLevel = currentLevel;
Log.i(TAG, "onUpdate: Battery level changed");
}
updateViews(context);
}
private boolean batteryChanged(int currentLevelLeft) {
return (batteryLevel != currentLevelLeft);
}
private int calculateBatteryLevel(Context context) {
Log.i(TAG, "calculateBatteryLevel: ");
Intent batteryIntent = context.getApplicationContext().registerReceiver(null, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
int level = batteryIntent.getIntExtra(BatteryManager.EXTRA_LEVEL, 0);
int scale = batteryIntent.getIntExtra(BatteryManager.EXTRA_SCALE, 100);
return level * 100 / scale;
}
private void updateViews(Context context) {
Log.i(TAG, "updateViews: ");
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
views.setTextViewText(R.id.batteryText, batteryLevel + "%");
ComponentName componentName = new ComponentName(context, BatteryWidget.class);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(componentName, views);
}
AndroidManifest.xml
<receiver android:name=".lib.receivers.BatteryUpdateReceiver" android:enabled="true" />
<receiver android:name=".widget.BatteryWidget" 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/batterywidgetinfo" />
</receiver>
I register BatteryUpdateReceiver using a service when device boot up.
IntentFilter filterBattery = new IntentFilter();
filterBattery.addAction(Intent.ACTION_BATTERY_CHANGED);
this.mBatteryStatusReceiver = new BatteryUpdateReceiver();
getApplicationContext().registerReceiver(this.mBatteryStatusReceiver, filterBattery);
Log.i(TAG, "Receiver ACTION_BATTERY_CHANGED registered by Service");
I can confirm that BatteryUpdateReceiver
works because it logs widget id. Also if I will put a receiver inside BatteryWidget
, then the widget gets updated. Can someone tell me what am I missing here? I know I can override onReceive()
method in BatteryWidget
class. But how can I archive it this way?
Thanks!
Upvotes: 4
Views: 2197
Reputation: 888
After spending innumerable hours on the same issue, I finally found a solution. @earthw0rmjim's answer pushed me halfway through but I realized that you cannot call sendBroadcast()
from a Kotlin broadcast receiver. You have to call the broadcast context and the application context in order to send a broadcast.
Here is the code that worked for me for anyone who might wish to achieve the same in Kotlin. In my case, MyWidgetProvider
is the AppWidgetProvider
class that I used to handle my home screen widget.
override fun onReceive(context: Context, intent: Intent) {
val widgetManager = AppWidgetManager.getInstance(context)
val appWidgetIds = widgetManager.getAppWidgetIds(ComponentName(context,MyWidgetProvider::class.java))
val updateIntent = Intent(
AppWidgetManager.ACTION_APPWIDGET_UPDATE,
null,
context,
MyWidgetProvider::class.java
)
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds)
context.applicationContext.sendBroadcast(updateIntent)
}
Upvotes: 0
Reputation: 19417
You should send an ACTION_APPWIDGET_UPDATE
broadcast from your receiver, rather than calling updateAppWidget()
directly.
Something like this:
@Override
public void onReceive(Context context, Intent intent) {
AppWidgetManager widgetManager = AppWidgetManager.getInstance(getApplicationContext());
int[] appWidgetIds = widgetManager.getAppWidgetIds(new ComponentName(context, BatteryWidget.class));
Intent updateIntent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, getApplicationContext(), BatteryWidget.class);
updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
sendBroadcast(updateIntent);
}
Upvotes: 4