I have created a widget which show countdown at every second.
But sometimes randomly its stops and never starts, i have to remove it from screen and again add to home screen will do it start.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final AppWidgetManager mngr = appWidgetManager;
new CountDownTimer(endTime.getTimeInMillis(), 1000) {
public void onTick(long millisUntilFinished) {
updateClockValues(c, mngr);
}
public void onFinish() {
}
}.start();
}
private void updateClockValues(Context context, AppWidgetManager mngr) {
System.out.println("update widget function");
RemoteViews remoteViews;
AppWidgetManager appWidgetManager = mngr;
ComponentName thisWidget;
final long sec = // some code to find seconds...and other values
// Get views for widget
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.widgetxml);
thisWidget = new ComponentName(context, BreakingDownWidget.class);
remoteViews.setOnClickPendingIntent(R.id.rlMain, configPendingIntent);
remoteViews.setTextViewText(R.id.txtSec, sec);
// Update widget
appWidgetManager.updateAppWidget(thisWidget, remoteViews);
}
Any one can tell me, how to start widget again when getting focus, sometimes after switching between screens will stop this.
Thanks in advance.
I have created a widget which show countdown at every second.
Please do not do that. You are wasting the user's CPU time and battery life. App widgets are designed to be updated every few minutes, not once per second.
But sometimes randomly its stops and never starts, i have to remove it from screen and again add to home screen will do it start.
Of course. An AppWidgetProvider is a manifest-registered BroadcastReceiver. These components are supposed to live for a few milliseconds, long enough for onReceive() to complete, then go away. Android can and will terminate your process afterwards, if it needs the RAM. Your CountDownTimer is forking a background thread, which is not supported from a manifest-registered BroadcastReceiver.
Related
I have a widget for my app. On widget Creation when android triggers onEnabled I'm checking if a user meets a certain requirement then the user can go ahead and create a widget. But I need to stop widget creation if they don't meet a certain requirement. I can't figure out how to cancel widget creation dynamically. Here's what I was trying which didn't work.
RemoteViews views;
void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
int appWidgetId) {
// Construct the RemoteViews object
views = new RemoteViews(context.getPackageName(), R.layout.lock_widget);
views.setOnClickPendingIntent(R.id.lock_widget, getPendingSelfIntent(context, LOCK));
// Instruct the widget manager to update the widget
appWidgetManager.updateAppWidget(appWidgetId, views);
}
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
// There may be multiple widgets active, so update all of them
for (int appWidgetId : appWidgetIds) {
updateAppWidget(context, appWidgetManager, appWidgetId);
}
}
#Override
public void onEnabled(Context context) {
// Enter relevant functionality for when the first widget is created
// Query User pro status when creating widget
Log.d("Widget", "OnEnabled Fired");
views.removeAllViews(views.getLayoutId());
//views.
}
views.removeAllViews(views.getLayoutId()); doesn't seem to work.
Is there even a way to this. The workaround that I'm using is checking certain requirement check in onUpdate.
The best solution in your case, enable widget component, if user have all "certain requirement".
So i assume a following case. User uses your app, and in some cases (you enable widget component, if user lose his "certain requirement" just disable widget component, so there is no widget in widget picker). You can easily achieve this through PackageManager.
Updated: When user have a pro status, just enable widget, if not - just disable
public static void setComponentState(Context context, boolean enabled) {
int flag = (enabled ?
PackageManager.COMPONENT_ENABLED_STATE_ENABLED :
PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
ComponentName component = new ComponentName(context, YourAppWidgetProvider.class); // or make component via package name and class name (check Component constructors)
PackageManager pm = context.getApplicationContext().getPackageManager();
pm.setComponentEnabledSetting(component, flag, PackageManager.DONT_KILL_APP);
}
I created an AppWidget for my App and setup the updatePeriodMillis to 0,
because this Widget is not doing anything, if the user does not interact.
Everything works fine, untill Android cleans the ram. Then the widget won't respond anymore until the App is started again or the device is rebooted (in both cases the onUpdate() will run again).
So my question: What do I need to do, to bring it back to work, after Android kicked out the Application?
This is part of the manifest:
<receiver android:name="WidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="PATH.widgetBtnStartClicked" />
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/appwidget_provider_info" />
</receiver>
This is part of my WidgetProvider:
public class WidgetProvider extends AppWidgetProvider {
private static final String BTN_START_CLICKED = "PATH.widgetBtnStartClicked";
private static Values values;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
// get RemoteView (widget):
for (int i = 0; i < appWidgetIds.length; i++) {
int appWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.appwidget);
// Register onClick for App-start-button:
Intent intentLaunch = new Intent(BTN_APP_LAUNCH_CLICKED);
intentLaunch.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pendingIntentLaunch = PendingIntent.getBroadcast(
context, appWidgetId, intentLaunch,
PendingIntent.FLAG_UPDATE_CURRENT);
views.setOnClickPendingIntent(R.id.appwidget_btn_launch,
pendingIntentLaunch);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
#Override
public void onReceive(Context context, Intent intent) {
RemoteViews views = new RemoteViews(context.getPackageName(),
R.layout.appwidget);
super.onReceive(context, intent);
AppWidgetManager appWidgetManager = AppWidgetManager
.getInstance(context);
ComponentName componentName = new ComponentName(context,
WidgetProvider.class);
if (intent.getAction().equals(BTN_APP_LAUNCH_CLICKED)) {
//do some stuff..
}
// update views
appWidgetManager.updateAppWidget(componentName, views);
}
I hope there is everything you need to understand the problem. Just tell me, if not!
I think your app relies only on onUpdate() to refresh the widget. There are other events that cause the pending intents to 'drop off' the widget.
Such as the RAM clearing you mention.
I recommend you:
+ put your widget update code in a separate service class
+ have that class triggered when different events happen. onUpdate() and other events, example, the one that causes your RAM to be cleared.
+ ensure you update everything each time with remoteviews, because no old pending intents etc are preserved.
You can take total control over the events that trigger the widget to update.
If it suits your situation, you can also set an arbitrary alarm, using the alarmmanager and a receiver class. in this way, you can set the alarm to only be received when the phone wakes, and use that to call your update service class. in your update service class, then clear any alarm (if the update is called from another event) and set another alarm.
There are lots of questions on SO about who to use a service with widgets. It shouldnt take long to work it out. To put that info here is outside of the scope of this question.
When I'm running in debugging mode I can't seem to reach any breakpoints that are inside of the service, why is that?
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
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, WidgetProvider.class);
AppWidgetManager manager = AppWidgetManager.getInstance(this);
manager.updateAppWidget(thisWidget, updateViews);
}
public RemoteViews buildUpdate(Context context) {
return new RemoteViews(context.getPackageName(), R.id.widget_main_layout);
}
#Override
public IBinder onBind(Intent intent) {
return null;
}
}
The "onUpdate"-method is only executed if the widget is initalized (e.g. put on the homescreen) or the updatePeriodMillis are expired. If you want to execute the service e.g. by a click on the widget, you have to "attach" a pending intent like this:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final Intent intent = new Intent(context, UpdateService.class);
PendingIntent pendingIntent = PendingIntent.getService(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....);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
for(int i=0,n=appWidgetIds.length;i<n;i++){
int appWidgetId = appWidgetIds[i];
appWidgetManager.updateAppWidget(appWidgetId , views);
}
(cleaned up version of a working widget).
The point is, that the onUpdate() method is really very seldom executed. The real interaction with a widget is specified through pending intents.
Your Service might not be registered in the manifest. Or your AppWidgetProvider might not be registered in the manifest.
You might want to think of not using a service for what you're doing. If it's just running the updateViews() once a day then consider just setting android:updatePeriodMillis to 86400000 in the XML file that's linked to your appwidget. Your XML file would look something like this:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dp"
android:maxWidth="72dp"
android:updatePeriodMillis="86400000" >
</appwidget-provider>
This will have android update your appwidget once a day without having a service run in the background that might get killed by a task killer that the user is running which then stops your widget from updating. Just a note, if you need it to update faster than every 30 minutes then android:updatePeriodMillis won't work (it's minimum value is 30 minutes) at that point I'd recommend using an AlarmManager since that'll use up less battery than a Service and also won't be killed by task killers.
I have a widget that needs to perform a potentially long-running operation in onUpdate(). Just performing the operation directly resulted in ANR's. To solve this, my first attempt was to create a thread therein. I noticed that the widget would not get updated in some cases. My guess here is that once onUpdate() exits, Android may kill the process along with the unfinished thread.
My next attempt was to create an intent service. The widget's onUpdate() just starts the intent service, which does the work directly and updates the widget when done. This works, but much to my surprise it appears that onHandleIntent() is single-threaded. If I have two widgets, and then both update and start the intent service, they update sequentially ...
The two widgets case is not really important, but I'm just wondering about a best practice for this type of pattern.
To solve the two widgets case I ended up updating all the widget instances with the same data whenever any one of them is clicked. e.g., I perform the long-running process once and apply the results to all the widget instances. In my scenario this doesn't matter, but for many widgets, it might be important not to do that.
Thoughts?
but much to my surprise it appears that onHandleIntent() is single threaded
Yes.
if i have two widgets, and then both update and start the intent service, they update sequentially ...
Yes.
but i'm just wondering about a best practice for this type of pattern.
Your IntentService was a fine upstanding solution, IMHO. Remember that Android runs on slow CPUs, with devices with little RAM. Running lots of threads in parallel is generally not a good idea.
then i'm getting into starting a thread in onHandleIntent(), which requires a wake lock, and it just seems it's getting all too complicated.
Try my WakefulIntentService.
make onUpdate call your own function to cycle through the widgets and update them. Do your async task before the cycle. You will want two separate actions, one that asks for the update to start, and one that your IntentService will broadcast to let the widgets know they are finished. Hope this helps.
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
updateWidget(context, appWidgetManager, appWidgetIds);
}
private void updateWidget(Context context){
ComponentName widget = new ComponentName(context, MyWidget.class);
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(widget);
updateWidget(context, appWidgetManager, appWidgetIds);
}
private void updateWidget(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
final boolean isEnabled = true; //took out code didn't want you to see
// start intent service here
for(int i = 0; i< appWidgetIds.length; i++){
int appWidgetId = appWidgetIds[i];
Intent intent = new Intent(isEnabled ? ACTION_TOGGLE_OFF : ACTION_TOGGLE_ON);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
views.setOnClickPendingIntent(R.id.widget , pi);
views.setImageViewResource(R.id.widget_image, isEnabled? R.drawable.widget_on : R.drawable.widget_off);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
I'm trying to make work a self-updating functionality in an Android widget, something simple as changing two TextViews of it every 10 seconds. The ideal solution would be to make it similar to the genie widget (news & weather). And so far, it works okay: it updates every 10 seconds via Handler.postDelayed's Runnable.
Runnable updateWidgetText = new Runnable()
{
#Override
public void run() {
if (screenEnabled) {
AppWidgetManager gm = AppWidgetManager.getInstance(ctx);
ComponentName thisWidget = new ComponentName(ctx,AnimeUndergroundWidget.class);
int index = (int)(REFRESH_COUNT % WIDGET_NEWS_TO_SHOW);
Noticia n = AUnder.single().getNoticiaByIndex(index);
if (n!=null)
{
last = n;
RemoteViews views = new RemoteViews(ctx.getPackageName(),R.layout.auwidget);
views.setTextViewText(R.id.widget_textview, n.getTitulo());
views.setTextViewText(R.id.widget_textototal, n.getTexto().replace("\n", ""));
Intent clickintent = new Intent(INTENT_GO_TO_NEW);
PendingIntent pendingIntentClick = PendingIntent.getBroadcast(ctx, 0, clickintent, 0);
views.setOnClickPendingIntent(R.id.widget_fondo_titulo, pendingIntentClick);
Intent click2intent = new Intent(INTENT_GO_TO_NEW);
PendingIntent pendingIntentClick2 = PendingIntent.getBroadcast(ctx, 0, click2intent, 0);
views.setOnClickPendingIntent(R.id.widget_fondo_Texto, pendingIntentClick2);
gm.updateAppWidget(thisWidget, views);
}
REFRESH_COUNT++;
}
handler.removeCallbacks(this);
handler.postDelayed(this, WIDGET_REFRESH_TIME*1000);
}
};
The runnable is initially launched in the onUpdate method of my Widget class:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
ctx = context;
context.startService(new Intent(context, UpdateWidgetService.class));
handler.postDelayed(updateWidgetText,WIDGET_REFRESH_TIME*1000);
// here goes some more code, updating the views and refreshing click intents
}
I put this in case someone finds it useful.
But let me go straight to the point: when I get the phone out of sleep (turn on the screen), the widget goes crazy and starts changing the TextViews with a fastforward-like effect.
I assume it is because there are some postDelayed events in queue, or may be in the updateAppWidget queue.
I've already tried a workaround using the code shown in here: http://thinkandroid.wordpress.com/2010/01/24/handling-screen-off-and-screen-on-intents/
You can see it in the first snippet in my code, I check a boolean variable that has stored screen state to avoid using postDelayed when the screen is turned off. But that doesn't seem to fix it.
This problem is driving me crazy for one week now, so I ask out of despair: is there any way to do this properly?
Your postDelayed call isn't inside your if (screenEnabled) block, so it will continue to post the events even when the screenEnabled is false