Speaking in Android. I have an application and a widget. The widget is supposed to auto update every n seconds, and it works fine until the parent app is killed. Once the app is killed, it does not update any longer. This occurs on both my timer based updates and the onClickPendingIntent updates.
Is there any way to check if the parent application is running within the widget, and if it is not running to start it? (preferably without using getRunningTasks (or any other extra permissions), but if I have to that's fine)
Here's the important bits of my widget code, for debugging it will display two numbers, one number that is updated based on the timer and one number that is updated on keypress. The widget is drawn based on the timer.
private static final String SYNC_CLICKED = "automaticWidgetSyncButtonClick";
public static int counter = 0; // time based update
public static int counter2 = 0; // onClick update
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
Timer timer = new Timer();
// 2 seconds for debug
timer.scheduleAtFixedRate(new MyTime(context, appWidgetManager), 1, 2000);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_main);
watchWidget = new ComponentName(context, HelloWidget.class);
remoteViews.setOnClickPendingIntent(R.id.widget_textview, getPendingSelfIntent(context, SYNC_CLICKED));
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);
}
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (SYNC_CLICKED.equals(intent.getAction())) {
counter2++;
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName watchWidget;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_main);
watchWidget = new ComponentName(context, WidgetUpdater.class);
remoteViews.setTextViewText(R.id.widget_textview, "Updating...");
// unrelated async task here
appWidgetManager.updateAppWidget(watchWidget, remoteViews);
}
}
private class MyTime extends TimerTask {
RemoteViews widgetView;
AppWidgetManager appWidgetManager;
SharedPreferences settings;
ComponentName thisWidget;
public MyTime(Context context, AppWidgetManager appWidgetManager) {
this.appWidgetManager = appWidgetManager;
settings = context.getSharedPreferences("prefs", 0);
widgetView = new RemoteViews(context.getPackageName(), R.layout.widget_main);
thisWidget = new ComponentName(context, HelloWidget.class);
}
#Override
public void run() {
counter++;
String hashrate = settings.getString("hashrate", null);
String rejected = settings.getString("rejected", null);
widgetView.setTextViewText(R.id.widget_textview, counter + "-" + counter2);
appWidgetManager.updateAppWidget(thisWidget, widgetView);
}
}
Once again, when the parent app gets killed my widget stops updating. Is there any way for me to start the parent application using my onClick method in the widget if it's not presently running?
The problem is eventually your AppWidgetProvider will be destroyed by the system. It's really just a BroadcastReceiver, the lifecycle of which is limited to the duration of onReceive(), as per the documentation:
A BroadcastReceiver object is only valid for the duration of the call
to onReceive(Context, Intent). Once your code returns from this
function, the system considers the object to be finished and no longer
active.
Letting your BroadcastReceiver hold onto your TimerTask implementation will fail, as you have already experienced. The best approach it to use AlarmManager to send a PendingIntent at the next scheduled interval. This PendingIntent could either start up a BroadcastReceiver or a Service (perhaps an IntentService) which will perform the update and schedule the next alarm.
Related
I am trying to update textview in an app widget but nothing is refreshing
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
for(int appWidgetID : appWidgetIds){
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context,0,intent,0);
RemoteViews views = new RemoteViews(context.getPackageName(),R.layout.timetable_widget);
views.setOnClickPendingIntent(R.id.btnPress,pendingIntent);
views.setTextViewText(R.id.textView2, "HELLO__WORLD");
appWidgetManager.updateAppWidget(appWidgetID,views);
}
}
Do you really receive an update broadcast? The update time for widgets is forced to at least 30mins, like this:
<appwidget-provider
...
android:updatePeriodMillis="xxx" // if set a millis less than 30mins, it will not work
...
/>
Or set the updatePeriodMillisupdate to 0 and update widget by sending custom broadcast:
Register intent-filter of your action
Receive your action in your widget' s onReceive and update the text
To prevent any ANR timeouts, we perform the update in a service.
fun updateWidget(context: Context) {
val appWidgetManager = AppWidgetManager.getInstance(context)
val widgetComponentName = ComponentName(context.packageName, YourWidget::class.java.name)
val appWidgetIds: IntArray = appWidgetManager.getAppWidgetIds(widgetComponentName)
RemoteViews(context.packageName, R.layout.your_widget).apply {
setTextViewText(R.id.text, "new text")
appWidgetManager.updateAppWidget(appWidgetIds, this)
}
}
I am a beginer, so be kind. I have an app with four buttons. If I run the app and click a single button, data is sent to specific device (one of four) using Bluetooth. I also have a widget that needs to call a method for data sending for the first device, so simulating a click of the first button i.e. calling a method in the Mainactivity class with a parameter 1.
Thank you for your help.
So currently from a widget I can start the Mainactivity, opening an app, but I would like to run a specific method instead.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.io_t3widget);
views.setOnClickPendingIntent(R.id.appwidget_text, pendingIntent);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
I have a calendar widget and I'm updating it myself everyday at 12:01AM as well when the size is changed so I don't have a need for automatic update. I also set the updatePeriodMillis to 0. The problem I'm having is that the App Widget calls onAppWidgetOptionsChanged():
Every time the screen unlocks
Every time you're brought home
It wouldn't be an issue for me, but the problem is you can see for a split second the App Widget updating. Anyone know of any way I can prevent that from happening?
For reference here is part of my WidgetProvider class
#Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action != null) {
if (action.equals(ACTION_REFRESH) || action.equals(ACTION_NEXT) || action.equals(ACTION_PREVIOUS)) {
context.sendBroadcast(new Intent(action));
}
}
Bundle extra = intent.getExtras();
if (extra != null && extra.containsKey(KEY_APP_WIDGET_ID)) {
updateAppWidget(context, AppWidgetManager.getInstance(context), extra.getInt(KEY_APP_WIDGET_ID));
}
super.onReceive(context, intent);
}
#Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
Intent broadcastIntent = new Intent(ACTION_REFRESH);
broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
context.sendBroadcast(broadcastIntent);
updateAppWidget(context, appWidgetManager, appWidgetId);
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
private static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
Intent intent = new Intent(context, WidgetService.class);
views.setRemoteAdapter(R.id.widget_calendar_container, intent);
// Buttons on widget click handlers
views.setOnClickPendingIntent(R.id.widget_refresh, getPendingSelfIntent(context, ACTION_REFRESH, appWidgetId));
views.setOnClickPendingIntent(R.id.widget_next, getPendingSelfIntent(context, ACTION_NEXT, appWidgetId));
views.setOnClickPendingIntent(R.id.widget_previous, getPendingSelfIntent(context, ACTION_PREVIOUS, appWidgetId));
// Widget Container click handler
Intent homeIntent = new Intent(Intent.ACTION_MAIN);
homeIntent.addCategory(Intent.CATEGORY_LAUNCHER);
homeIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
homeIntent.setComponent(new ComponentName(context.getPackageName(), Home.class.getName()));
PendingIntent homePendingIntent = PendingIntent.getActivity(context, 0, homeIntent, 0);
views.setPendingIntentTemplate(R.id.widget_calendar_container, homePendingIntent);
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_calendar_container);
}
I prefer another answer, but this is what I got so far.
#Override
public void onAppWidgetOptionsChanged(Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
int width = Util.dp2px(newOptions.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH));
if (width != WidgetRemoteViewsFactory.width) {
Intent broadcastIntent = new Intent(ACTION_REFRESH);
broadcastIntent.putExtra(KEY_SIZE_CHANGE, width);
LocalBroadcastManager.getInstance(context).sendBroadcast(broadcastIntent);
updateAppWidget(context, appWidgetManager, appWidgetId);
super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
}
}
I made the field I am interested in width static on WidgetRemoteViewsFactory and I am only broadcasting when that field changes. I can't seem to prevent onAppWidgetOptionsChanged() from being called because AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED is being broadcasted and received every time the screen is awaken.
Android Documentation says
Called in response to the AppWidgetManager.ACTION_APPWIDGET_OPTIONS_CHANGED broadcast when this widget has been layed out at a new size. -
AppWidgetProvider#onAppWidgetOptionsChanged
But that action is being broadcasted every time the screen awakens.
If anyone has a better solution, please feel free to post it. There has to be a better solution or this is a bug on Android's side.
I have an Android app whose service is started when app starts and when app is killed, service is also destroyed.
I have a home screen widget starting alongside with app. It has 3 imageviews - "Play","Next","Previous".
On press of any of these imageview in widget, they need to talk to service and service talks to library etc.
When service has an answer, it responds to widget with status OK or NO. Based on the response, I need to update the imageviews.
When the app is killed, I need to gray out my widget fully. In this situation, when I click on the greyed out widget, I need to start the app. Could anyone help me please
how I could achieve this.
Here is my widget code -
public class MyWidgetProvider extends AppWidgetProvider {
private Intent serviceIntent;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews remoteViews;
ComponentName componentName;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
componentName = new ComponentName(context, ControlScreenWidgetProvider.class);
remoteViews.setOnClickPendingIntent(R.id.PLAY,getPendingSelfIntent(context, "PLAY",componentName));
remoteViews.setOnClickPendingIntent(R.id.NEXT,getPendingSelfIntent(context, "NEXT",componentName));
remoteViews.setOnClickPendingIntent(R.id.PREVIOUS,getPendingSelfIntent(context, "PREVIOUS",componentName));
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent)
{
super.onReceive(context, intent);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
RemoteViews remoteViews;
ComponentName componentName;
remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
componentName = new ComponentName(context, MyWidgetProvider.class);
if ("PLAY".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.PLAY,getPendingSelfIntent(context, "PLAY",componentName));
// Dont know what to do here
}
else if ("NEXT".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.lock_door,getPendingSelfIntent(context, "NEXT",componentName));
// Dont know what to do here
}
else if ("PREVIOUS".equals(intent.getAction())) {
remoteViews.setOnClickPendingIntent(R.id.lock_door,getPendingSelfIntent(context, "PREVIOUS",componentName));
// Dont know what to do here
}
appWidgetManager.updateAppWidget(componentName, remoteViews);
}
protected PendingIntent getPendingSelfIntent(Context context, String action, ComponentName componentName) {
Intent intent = new Intent(context, MyWidgetProvider.class);
intent.setAction(action);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, componentName);
return PendingIntent.getService(context, 0, intent, 0);
}
}
I'm working on an android widget and it works great in API Level 5 or greater. It's not supported at all in API Level 1 or 2. It should work absolutely fine in 3 and 4 but for some reason the widget doesn't update.
The onUpdate method gets called and executes without errors; however, in 3 and 4 it doesn't change the text of the widget. I'm pretty much at a loss. Any thoughts?
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
DataAccess helper = new DataAccess(context);
String text = helper.getCurrentText();
helper.close();
if (text != null)
views.setTextViewText(R.id.widget_text, text);
Intent intent = new Intent(context, WidgetDetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget, pending);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
Try this:
At the end of your update method you need to call the super.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget);
DataAccess helper = new DataAccess(context);
String text = helper.getCurrentText();
helper.close();
if (text != null)
views.setTextViewText(R.id.widget_text, text);
Intent intent = new Intent(context, WidgetDetailsActivity.class);
PendingIntent pending = PendingIntent.getActivity(context, 0, intent, 0);
views.setOnClickPendingIntent(R.id.widget, pending);
appWidgetManager.updateAppWidget(appWidgetIds, views);
super.onUpdate(context, appWidgetManager, appWidgetIds);
}
You need to start service on update method and put your all code in service also,here I give onReceive and service demo
context.startService(new Intent(context, UpdateService.class));
public static class UpdateService extends Service {
#Override
public void onStart(Intent intent, int startId) {
// Build the widget update for today
RemoteViews updateViews = buildUpdate(this);
// Push update for this widget to the home screen
ComponentName thisWidget = new ComponentName(this,
Widget.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
#Override
public void onReceive(Context context, Intent intent) {
final RemoteViews remoteViews = buildUpdate(context);
ComponentName cn = new ComponentName(context, Widget.class);
AppWidgetManager.getInstance(context).updateAppWidget(cn, remoteViews);
super.onReceive(context, intent);
}
where buildUpdate is method for your widget updating code