I really wish to make a widget for my Religious app which can show an image everyday at 12 a.m. If user wishes to add a widget on the home screen then it should display an image which changes at 12 a.m. daily. I prefer to make something like a Picture Frame Widget but the user should not be requested to select the images and the interval should be every 24 hours starting from 12 a.m.
So far, I was able to do this:
public class Widget extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
super.onUpdate(context, appWidgetManager, appWidgetIds);
for (int i=0; i < appWidgetIds.length; i++){
int appWidgetId = appWidgetIds[i];
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.inspirations);
appWidgetManager.updateAppWidget(appWidgetId, views);
}
}
}
It shows the image but then I want to make sure that the new image should appear at 12 a.m. tonight.
I have never dealt with the widgets before, I would really appreciate if I can have anybody who can walk me through the steps?
Make your onUpdate() method use the current date to decide which image to show, then arrange to call onUpdate() when the date changes.
To observe date changes, make a BroadcastReceiver listen for Intent.ACTION_DATE_CHANGED which indicates that the date-time clock reached a new date. (While you're at it, listen for Intent.ACTION_TIME_CHANGED and Intent.ACTION_TIMEZONE_CHANGED as well, indicating that the clock was adjusted.)
Since AppWidgetProvider is already a BroadcastReceiver, you can use yours to receive these clock events by adding intent-filter actions to AndroidManifest.xml:
<receiver android:name=".MyAppWidgetProvider" >
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="android.intent.action.DATE_CHANGED" />
<action android:name="android.intent.action.TIME_SET" />
<action android:name="android.intent.action.TIMEZONE_CHANGED" />
</intent-filter>
...
</receiver>
In your AppWidgetProvider's void onReceive(Context context, Intent intent) method, when any of these date/time intents comes in, call the onUpdate() method to refresh the widget contents.
Note: Due to Android OS bug 2880, if the clock gets set backwards, the widget's date display might not update until the clock catches up to what was going to be the next day. In practice, this happens so rarely that you needn't worry about it but don't expect it to work if you set the clock backwards to test your code.
Related
I tried to make reminder application for Android, followed tutorial from this website Set Notification for Specific Date. Basically it used Alarm Manager to create a reminder.
This code used to call alarm manager and show notification on specific date.
reminderClient.setAlarmForNotification(calendar, uniqueid, interval);
I save all of the reminder in SQLite Database. So when this code (above) called, new record will be inserted to database and when notification show up, that record will be deleted.
The problem is whenever device restart, alarm manager stopped.
So I create a new BroadcastReceiver that receive event when device turned on.
<application ... >
<receiver android:name=".ReminderReceiver" >
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
</intent-filter>
</receiver>
</application>
public class ReminderReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//do stuff
}
}
Is it OK to get all record from database, and call setAlarmForNotification again inside OnReceive like this?
public class ReminderReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
List<MyReminder> reminders = database.getAllReminder();
Calendar cal = Calendar.getInstance();
for (int i=0; i<reminders.size(); i++) {
cal.setTime(parseStringDateToDate(reminders.get(i).getDateTime());
reminderClient.setAlarmForNotification(
cal,
reminders.get(i).getUniqueID(),
reminders.get(i).getInterval()
);
}
}
}
Or is there a better way?
BroadcastReceiver's onReceive() is called on UI thread so in common case it's not right to access a database or do any other file I/O in this method. For this task you need two things: background thread to give the system an ability to make its stuff in parallel and a Service to tell the system that it should not kill your process when onReceive() is finished. There is a component that gives you both things - IntentService. It's a Service and a working thread that finishes and stops a Service when return from onHandleIntent().
Also posibly you will need a WakeLock to ensure that all your calculations are finished and alarms are properly set. Look at WakefulBroadcastReceiver that is written just for this case.
NOTE: Actually in your case file I/O is really minimal and system boot probably is not a moment when every hundred milliseconds are crucial. But there is really no reason not to do things right.
The problem is that after I use the built in Task Manager's Clean Memory/Ram, My widget stops working .I guess this is related to the Task Manager's method of cleaning RAM.After a lot of research and some attempts, I found that i need
BroadcastReciever to listen to package changes and updates:
So i implemented but its not working because document says that the Restarted/Cleared package does not receive this broadcast
register receiver in the manifest file:
<receiver android:name="com.app.lab.receiver.onRestartReciever">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REPLACED" />
<action android:name="android.intent.action.PACKAGE_RESTARTED" />
<action android:name="android.intent.action.PACKAGE_DATA_CLEARED"/>
<data android:scheme="package" />
</intent-filter>
PACKAGE_REPLACED - called in particular to notify application update.
PACKAGE_RESTARTED - called when most memory cleaners are cleaning memmory.
the "data" row is used to monitor action applied for the specific package.
public class onRestartReciever extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// TODO Auto-generated method stub
Log.d("DEBUG", "onRestartReciever");//I am not getting this log on clearing memory from task manager
}
}
I tried to use dummy service to get its lifecycle ie to check when onDestroy is called but what I found it not a reliable way ,onDestroy may not be called when application is killed by Task Manager.
So finally, my question : Is there any way to tell the android system to reStart appWidgets when Task manager or OS cleans memory .
Note: My widget contains only one button that launches an Activity.It works most of the time but stops responding if OS itself cleans memory or user forcefully do it from task manager.I've downloaded some of the widget it seem to continue working fine after cleaning memory also.
Update: To under my problem no need of going through complete code it is simple Application . My application dose not contain any Activty or Service. It contains only widget with one button which gives toast message.There is only two class in my application(WidgetProvider and onRestartReciever) thats it
Widget class WidgetProvider.class
public class WidgetProvider extends AppWidgetProvider {
private RemoteViews remoteViews;
private ComponentName watchWidget;
PendingIntent pi;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
remoteViews = new RemoteViews(context.getPackageName(),
R.layout.touchwidget);
Intent toggleClickPlayer = new Intent(context.getApplicationContext() ,WidgetProvider.class);
toggleClickPlayer.setAction("PLAYER");
PendingIntent toggleIntentPlayer = PendingIntent.getBroadcast(context,0, toggleClickPlayer,endingIntent.FLAG_CANCEL_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.player, toggleIntentPlayer);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
watchWidget = new ComponentName(context,WidgetProvider.class);
remoteViews = new RemoteViews(context.getPackageName(),R.layout.touchwidget);
Toast.makeText(context, " Player started",Toast.LENGTH_SHORT).show();
(AppWidgetManager.getInstance(context)).updateAppWidget(watchWidget, remoteViews);
}
}
}
Widget does not bound to application life cycle. All the widgets are bound together. If all what your widget got is a button than there is no reason for it to stop working. You problem is some place else.
For some reason your button intent is fail to start what you set it to start.
If you show me your code for setting the button, I be able to help you more, but it is another question, and you better Google for answer before posting.
EDIT: It looks like you didn't understood the idea of widgets. Right now what your button is doing is starting the widget. It looks weird to me, I am not sure what exactly is happening there... I suggest that your button will start a completely new service. That service will show your toast. You defiantly do not need to listen for restart package broadcast.
I've seen several other questions about widgets not updating but all of them was because updatePeriodMillis was configured to a value less than 30min and I've set mine to 1 hour and been checking for several hours now.
I've started a separate project from my actual widget project just to test the update and I'm getting nowhere.
The widget simply updates a TextView with currentTimeMillis().
AndroidManifest.xml
<receiver android:name="TestWidget" android:exported="false">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"/>
</intent-filter>
<meta-data android:resource="#xml/widget" android:name="android.appwidget.provider"/>
</receiver>
widget.xml
<?xml version="1.0" encoding="utf-8"?>
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="#layout/widget"
android:minHeight="40dp"
android:minWidth="250dp"
android:updatePeriodMillis="3600000" >
</appwidget-provider>
everything loads fine on the 1st call, but onUpdate is never called again.
Any suggestions?
edit:
TestWidget.class only overrides onUpdate
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
final int N = appWidgetIds.length;
for (int i = 0; i < N; i++) {
// Inflate the layout
RemoteViews v = new RemoteViews(context.getPackageName(),
R.layout.widget);
v.setTextViewText(R.id.text1, Double.toString(Math.random()));
v.setTextViewText(R.id.text2,
Long.toString(System.currentTimeMillis()));
// Update the widget
appWidgetManager.updateAppWidget(appWidgetIds[i], v);
}
}
I would like to apologise to the community for the unnecessary question as it seems in the end that either my phone was acting up (considering that I reboot the thing) or I was simply being a bit inpatient.
As it seems, it really takes several several hours for the onUpdate to start being called by the framework. On the past 3 hours (I was sleeping before that) I can see the updated coming quite consistently.
I didn't delete the question, just in case someone have the same problem, YOU REALLY HAVE TO WAIT!
Thanks.
I'm trying just some very simple code to get an Android widget going but with no luck. I've looked around everywhere and haven't found a good answer.
All I want (for now) is to increment a counter when the widget is touched and display the current value.
This is my AppWidgetProvider:
public class WordWidget extends AppWidgetProvider
{
Integer touchCounter = 0;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
//This is run when a new widget is added or when the update period expires.
Log.v("wotd", "Updating " + appWidgetIds.length + " widgets");
for(int x = 0; x < appWidgetIds.length; x++)
{
Integer thisWidgetId = appWidgetIds[x];
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
remoteViews.setTextViewText(R.id.mainText, touchCounter.toString());
Intent widgetIntent = new Intent(context, WordWidget.class);
widgetIntent.setAction("UPDATE_NUMBER");
PendingIntent widgetPendingIntent = PendingIntent.getBroadcast(context, 0, widgetIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.widgetLinearLayout, widgetPendingIntent);
appWidgetManager.updateAppWidget(thisWidgetId, remoteViews);
}
}
#Override
public void onReceive(Context context, Intent intent)
{
Log.v("wotd", "In onReceive with intent=" + intent.toString());
if (intent.getAction().equals("UPDATE_NUMBER"))
{
Log.v("wotd", "In UPDATE_NUMBER");
touchCounter++;
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
remoteViews.setTextViewText(R.id.mainText, touchCounter.toString());
} else
{
Log.v("wotd", "In ELSE... going on to super.onReceive()");
super.onReceive(context, intent);
}
}
}
This is part of my manifest:
<application
android:icon="#drawable/ic_launcher"
android:label="#string/app_name" >
<receiver
android:icon="#drawable/ic_launcher"
android:name="com.example.mywidget.WordWidget"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="UPDATE_NUMBER" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/widgetinfo" />
</receiver>
</application>
The log shows the onReceive() is called immediately after being placed on the home screen, and after being touched, however the number never increases. I don't totally understand how widgets work, but are they killed after onUpdate()? So to do this I would have to use some kind of persistant storage?
Also, if I currently add another widget both would show the same values and increment even if I just touch one. Is there a way for each and any widget to have it's own counter?
Thanks!
Actually you have answered your question. But let's clarify some things:
A. AppWidgets are NOT killed as long as they are on a home screen. But they don't belong to you. They are running in the process of the home application. To be more specific their views are running in the process of the home application, this is why you are see your widget but it doesn't do what you are expected and this is why we are using RemoteViews instead of Views in order to update them.
B. AppWidgetProviders (in your case the WordWidget class), on the other hand, are destroyed as soon as the onReceive method finishes. All the variables in them are re-initialized every time the onReceive method gets called. This is why your number never increases. The purpose of an AppWidgetProvider is to update the widget's RemoteViews and to inform your application that a registered broadcast has arrived.
C. AppWidgetProvider's onUpdate method provides you an Array with the widgets Ids that must be updated. This is the only code point you can use to get the number of your widget instances and their Ids. Because of the RemoteViews there is NO way to get some useful value from the Views of your widget (for example you can NOT read the counter value from the TextView) so you must use the provided information and DO persist your counter values per widget id. When the next onUpdate gets called you read the value from the storage, increase it, update the widget with the new value and then store the new value back.
D. If your widget has to do many things or slow things (like networking) when its time to update itself, you should consider using a service for this. But in your case (to increase a number) your approach is just fine as long as you persist the counters in the persistent storage or somewhere else.
Finally I 've noticed that in your onReceive override you are calling the "super.onReceive" only if you don't receive the "UPDATE_NUMBER" action. This is NOT a good practice, unless there is a GOOD reason (that's another story) always call super.onReceive as the first or the last command in your override.
Hope this helps...
I have a simple android widget with a single text view.
I extend AppWidgetProvider class, widget information xml - appwidget-provider..., also updated my manifest file as per document provided on android website.
I also set android:updatePeriodMillis to 1800020 (~30 minutes).
When I place widget on screen all the code under onUpdate method fires for the first time. But after 1800020 milliseconds, the onUpdate doesn't called again. My widget does not update afterwards. Am I missing something with it?
I extend AppWidgetProvider as follow,
public class Testwidget extends AppWidgetProvider {
int COUNT = 0;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Log.d("WIDGET", "====================== UPDATED ======================");
super.onUpdate(context, appWidgetManager, appWidgetIds);
for(int _i=0; _i<appWidgetIds.length; _i++){
int _widId = appWidgetIds[_i];
COUNT+=1;
RemoteViews _views = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
_views.setTextViewText(R.id.tvCount,String.valueOf(COUNT));
appWidgetManager.updateAppWidget(_widId, _views);
}
}
}
My Manifest
<receiver android:name="Testwidget">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE"></action>
</intent-filter>
<meta-data android:name="android.appwidget.provider"
android:resource="#xml/widgetinfo"></meta-data>
</receiver>
Appwidget Provider
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="272dp" android:minHeight="72dp"
android:updatePeriodMillis="1800020" android:initialLayout="#layout/widgetlayout">
</appwidget-provider>
You are using the correct number for miliseconds.
But there is no guarantee that the update will happen at exactly this time interval - it could be delayed by the OS (from the documentation: "The actual update is not guaranteed to occur exactly on time with this value")
If you are not using any service it will be update after the miliseconds you have defined, to use service or complete refernce how widget works look here.
Minimum of update seconds is 1800000 (30 minutes). From there on the counter is set back to 0 so you have to wait another 30 minutes. Look at the link Pawan leaved behind.