How to restart and update a widget from a system reboot android - android

i have an appwidget that runs successfully. now when the phone is being rebooted,
the widget loses all its data and just sits on the home screen. since i can't update the widget from a broadcast receiver when the phone reboots, i created a notification
that leads to the configuration activity of the widget. Nothing works after the user sets the configuration again and leaves the configuration activity; the idle widget just remains there?(the user has to delete the widget and create a widget again).. Am assuming i am not receiving the widget id correctly or am i doing it wrongly in broadcast receiver and suppose to move all the code to onEnable in the widget method?.. How do i refresh the widget correctly. Please bear in mind that all widget updates are done from a service.
by the way i have this code in broadcast receiver for boot_completed action:
public void onReceive(final Context context, Intent intent) {
String info = context.getResources().getString(R.string.restart_setting);
int[] allids = AppWidgetManager.getInstance(context).getAppWidgetIds(new ComponentName(context, MyWidgetProvider.class));
for(int appWidgetId:allids){
NotificationManager mnotifyManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notify = new Notification(R.drawable.icon, "Weather Update", System.currentTimeMillis());
notify.defaults = Notification.DEFAULT_SOUND;
notify.defaults = Notification.DEFAULT_VIBRATE;
Intent Settings = new Intent(context, WidgetConfigure.class);
Settings.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent pending = PendingIntent.getActivity(context, 0, weatherSettings, PendingIntent.FLAG_ONE_SHOT);
notify.setLatestEventInfo(context, "Weather Update", info, pending);
notify.flags = Notification.FLAG_AUTO_CANCEL;
notify.defaults = Notification.DEFAULT_ALL;
mnotifyManager.notify(WEATHER_NOTIFICATION_ID , notify);
}
}

I just had the same problem this morning and tried to solve it with listening to the boot_completion intent. It doesn't work this way with widgets. Here's what I found.
To get notified about a reboot, you need to receive android.intent.action.ACTION_EXTERNAL_APPLICATION_AVAILABLE in addition to boot_completed and the permission of android.permission.RECEIVE_BOOT_COMPLETED.
But the whole truth is that an additional intent is not necessary. After reboot the onEnable and onUpadate of the BroadcastReceiver get called anyway.
So, the solution I've implemented, stores the config of each widget in a file, with the widget Id as part of the filename. And in onUpdate of the receiver I initialize the widget again (with click listener and all that)
Outcome is, that after reboot it takes a moment but then the widgets (all) look good and work like expected.

Related

Network request from appWidget

There are a number of similar questions on SO, but they are all pre-O, where IntentService works just fine. I tried to do it as a JobIntentService, but the delay is unacceptable. The network exchange for my widget is supposed to be really quick, or not at all, the socketTimeout is set to 100ms, so several seconds of delay is frustrating.
I want to try several solutions here. First, to create a foregroundService from the context. As far as I understood, I have 5 seconds before the service is killed, so if my exchange takes just a fraction of that, I should be good. Is this assumption correct? Is this a good use for a foregroundService?
Next, what if I just do it by starting manually a new thread in onReceive of my AppWidgetProvider? As I said, the network exchange should take less than the quarter of a second. What can possibly happen to the context during that time?
What is the best way to make a quick network request from an appWidget, that should happen immediately after it sends the broadcast?
There are several things to point out in answering this. We have appWidgets that use a service to handle transactions because jobService is not an option for us. Sharing a few things we have done which will hopefully help you.
On your first question:
The service is called from the appWidget provider and does all the work, and it must be called as a foreground service on Oreo+, if that foreground service is not started within 5 seconds of that call you will get an exception: "Context.startForegroundService did not then call Service.startForeground". There is an unresolved issue in the Google tracker that doesn't seem to have been resolved yet about this issue, so you should make sure you read this as well. Some have suggested putting the following code in both onCreate() and onStart() of the service. The issue is https://issuetracker.google.com/issues/76112072.
I would suggest doing all the work you need in the Service after calling it from the provider. (Also remember to use a notification channel for Oreo+) For example:
In appWidget provider onUpdate():
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(new Intent(serviceIntent));
} else {
context.startService(new Intent(serviceIntent));
}
Then in your service class- put this in onStartCommand() and onCreate() per the issue link I've posted above.
savedIntent = intent;
NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
//Google Issue tracker https://issuetracker.google.com/issues/76112072 as of 9/26/2018. StartForeground notification must be in both onCreate and onStartCommand to minimize
//crashes in event service was already started and not yet stopped, in which case onCreate is not called again
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(PRIMARY_NOTIF_CHANNEL, "YOUR NOTIFICATION CHANNEL", NotificationManager.IMPORTANCE_DEFAULT);
channel.setSound(null, null);
channel.enableVibration(false);
if (notificationManager != null) {
notificationManager.createNotificationChannel(channel);
}
Notification notification = new NotificationCompat.Builder(this, PRIMARY_NOTIF_CHANNEL)
.setSmallIcon(R.drawable.your_drawable)
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setContentTitle(getText(R.string.app_name))
.setStyle(new NotificationCompat.InboxStyle()
.addLine("Needs to sync periodically. Long press to hide.")
.addLine(""))
.build();
startForeground(PRIMARY_FOREGROUND_NOTIF_SERVICE_ID, notification);
}
On your second question:
AppWidget Provider's onReceive() method is triggered by broadcasts with intents, which can be used to do different tasks, partially update the appWidget, or restart the service. So based on the information you've provided, you should be able do your network call in the onReceive area of the provider, but you may need to specify a custom intent for it to trigger onReceive(), and make sure you update all instances of the appWidget as well to update the remote views. As an example, we have several custom intents which trigger the onReceive() and partially update our appWidget:
In appWidget provider:
// sample custom intent name
public static final String ACTION_CLICK_ADVANCE_STATUS = "widget_action_click_advance_status";
...
#Override
public void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
if (ACTION_CLICK_ADVANCE_STATUS.equals(intent.getAction())) {
// update all instances of appWidgets
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(new ComponentName(context, WidgetJobProvider.class));
// Do your work here
}

GcmListenerService when running in background different

I've followed this tutorial to create a new GCM Listener service:
http://www.appifiedtech.net/2015/08/01/android-gcm-push-notification-example/
The code for the listener service:
#Override
public void onMessageReceived(String from, Bundle data) {
super.onMessageReceived(from, data);
String msg = data.getString("message");
Log.d(TAG,"GCM Message received is "+msg);
// Notifying to user code goes here
notifyUser(getApplicationContext(),msg);
}
public void notifyUser(Context context,String data){
Intent intent = new Intent(context, NotificationActivity.class);
intent.putExtra("data", data);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.mipmap.ic_launcher);
builder.setAutoCancel(true);
builder.setContentTitle("New Notification");
builder.setContentIntent(pendingIntent);
builder.setContentText(data);
NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
Uri uri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
builder.setSound(uri);
notificationManager.notify(countNotification++, builder.build());
Log.v(TAG,"count "+countNotification);
}
When the app is running (foreground), this works fine and launches the Notification Activity as it should.
However, when it's running in background, I get the notification but the title and body are as defined in my server sender application, and tapping on it takes me to the Main Activity.
This essentially means that when it's running in the background something else handles the notification? Is there another handler I should implement to be able to manage that notification and send the user to the correct activity?
The screen does not wake when I receive this notification, nor does an LED light up on the phone as notifications from other applications do. How do you manage that?
(permissions, services and receiver are defined in the manifest as described in the tutorial)
In problem regarding when your apps running in the background something else handles the notification?
This SO question can help you in answering your question regarding the GCM Listener Service
In the problem regarding to the screen that doesn't wake when you receive notification.
Use ACQUIRE_CAUSES_WAKEUP
Normal wake locks don't actually turn on the illumination. Instead, they cause the illumination to remain on once it turns on (e.g. from user activity). This flag will force the screen and/or keyboard to turn on immediately, when the WakeLock is acquired. A typical use would be for notifications which are important for the user to see immediately.
You can visit this SO question in how to use it.
You remove
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_CLEAR_TASK);
Using intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
In the NotificationActivity class , you need implement the onNewIntent() callback when you open NotificationActivity existed the stack.

android Local notification while app is stopped

I have followed some tutorials from SO to generate a local notification using AlertManager some time after my app closes by broadcasting to a custom BroadcastReceiver impl of mine. It works well, I can see the notification come up in the notification area of the device.
However, it only works if the app's main activity is paused. If the app is stopped (via Settings->apps), there is no notification, the broadcast receiver is never called. I had the impression that AlertManager registers my notification request in some OS service - not related to my app, that's the whole point, to have some sort of notification through which the user can restart my app. I am testing on Android 4.2.1 BTW. Any chance I am simply doing something wrong and there is actually a way to get AlertManager to successfully broadcast something out?
Here is my AlertManager code, called from my main activity's onPause (set to 10 seconds, just for testing). 'ctx' is the main activity
Calendar cal = Calendar.getInstance();
cal.add(Calendar.SECOND, 10);
Intent intent = new Intent(ctx, MyAlarmReceiver.class);
intent.putExtra("alarm_message", "hey, wake up this app!");
// note: 192837 is just some test ID:
PendingIntent sender = PendingIntent.getBroadcast(ctx, 192837, intent, PendingIntent.FLAG_UPDATE_CURRENT);
// Get the AlarmManager service
AlarmManager am = (AlarmManager) ctx.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
And here is MyAlarmReceiver.onReceive(context, intent):
try {
Bundle bundle = intent.getExtras();
String message = bundle.getString("alarm_message");
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(context)
.setSmallIcon(R.drawable.ic_launcher)
.setContentTitle("Title!!!")
.setContentText(message);
Intent resultIntent = new Intent(context, MainActivity.class);
PendingIntent resultPendingIntent =
PendingIntent.getActivity(context, 192838, resultIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
mBuilder.setContentIntent(resultPendingIntent);
NotificationManager mNotificationManager =
(NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(123423, mBuilder.build());
} catch (Exception e) {
Toast.makeText(context, "There was an error somewhere, but we still received an alarm", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
To summarize, Force closing your app means that the user explicitly said that he
didn't want to run your app anymore
Starting with 3.1 when applications are installed they are in a “stopped” state so they will not be able to run until the user explicitly launches them. Pressing Force Stop will return them to this state, so if the user force-stops your application, all the components of your app (BroadcastReceivers, Services, AlarmManager...) will no longer work again until the user manually run your application again. And this is documented at the 3.1 release notes here.
Although the documentation for AlarmManager states that
Note: The Alarm Manager is intended for cases where you want to have
your application code run at a specific time, even if your application
is not currently running. For normal timing operations (ticks,
timeouts, etc) it is easier and much more efficient to use Handler.
It will not work also after your application is force closed and this is not a bug it's a feature.
This behavior is confirmed by the Android framework developers https://groups.google.com/forum/?fromgroups=#!topic/android-developers/anUoem0qrxU

Android Wear: Activity in DisplayIntent of Notification Page gets always recreated

I'm developing some kind of media controls for my android wear. The scenario is the following:
The WearableListenerService in the wear app is notified when the media data changes, i.e. artist/title. An ongoing notification is shown which has a full screen second page with some controls in it.
Because I don't want it to show on my smartphone, the notifications are build in the wearable app.
The second page consists of an activity baked in a display intent:
Intent pageIntent = new Intent(this, ControlActivity.class);
pageIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP);
PendingIntent pendingIntent = PendingIntent.getActivity(
getApplicationContext(),
0,
pageIntent,
0);
Notification controlPage = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_launcher)
.extend(
new Notification.WearableExtender()
.setCustomSizePreset(Notification.WearableExtender.SIZE_FULL_SCREEN)
.setDisplayIntent(pendingIntent))
.build();
Notification notification = new NotificationCompat.Builder(this)
.setSmallIcon(R.drawable.icon)
.setContentTitle(currentArtist)
.setContentText(currentTitle)
.setOngoing(true)
.extend(
new NotificationCompat.WearableExtender()
.addPage(controlPage)
.addAction(new NotificationCompat.Action(button, null, intent))
.setContentAction(0)
.setContentIcon(button)
.setBackground(BitmapFactory.decodeResource(getResources(), bg))
.setHintHideIcon(true)
)
.build();
NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, notification);
Now the notification is refreshed from time to time to display new data on the first notification page which forces the activity in the second page to be recreated every time the notification is updated. This results in some ugly behavior where the user can see that the activity inside the second page is recreated.
Is there any way to prevent the activity in the second page from being completely recreated (onDestroy, onCreate) and/or only update the data on the first page of the notification as it should be?
Is it the PendingIntent creation that doesn't really work here?
The Manifest entry looks as the following:
<activity
android:name=".media.ControlActivity"
android:allowEmbedded="true"
android:exported="true"
android:launchMode="singleTop"
>
I've tried all combinations of flags as well as only updating the necessary parts but nothing changes.
Help is greatly appreciated. Let me know if you need further information.
Edit: The main problem seems to be that the embedded activity (for a custom layout) in the second page of the notification is always recreated as soon as I call notify. Reusing, only updating changed values on the first page or updating nothing has no effect.
Simply broadcast the new information to your second activity and update the fields when you receive the broadcast. And make it local so the broadcast concerns only your application.
In the second activity add the BroadcastReceiver as a member attribute :
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
/*do what you're supposed to do, update your data
you get the information from the intent by calling intent.getXExtra() as usual*/
}
};
And register it in your onCreate() method :
LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter("your-filter-name"));
In your notification activity, when you update the information, add this :
Intent intent = new Intent("your-filter-name");
intent.putExtra(...);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Your second activity won't be recreated each time and the modifications should go smoothly :)
there is no method to prevent recreate the embedded activity in the notification, you can try this way in your sence.
use a action in second page, and start activity when click the action, when the notification is updated by time, the activity will not be recreated

Add widget to home screen from activity lauched via intent

I'm trying to get my home screen widget to trigger the config class for a new widget but am having a problem.
When a user tries to add my widget to the home screen for the first time it kicks off a thread which downloads a file with a list of sensors and then exits. Once the download is complete the download task triggers a notification. When the user taps on the notification it should start the dataWidgetConfig activity. This works, to a point using this code
mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
final Notification notifyDetails = new Notification(R.drawable.icon,"Sensor list downloaded.",System.currentTimeMillis());
CharSequence contentTitle = "Notification Details...";
CharSequence contentText = "Sensor list has downloaded, tap to add widget.";
Intent notifyIntent = new Intent(self, dataWidgetConfig.class);
notifyIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
notifyIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
PendingIntent pendingIntent = PendingIntent.getActivity(self, appWidgetId, notifyIntent, 0);
notifyDetails.setLatestEventInfo(self, contentTitle, contentText, pendingIntent);
mNotificationManager.notify(SIMPLE_NOTFICATION_ID, notifyDetails);
My problem is, that I don't have an appWidgetId. When I add a widget via long press on the home screen I get allocated an appWidgetId, how do I ask the home screen for one? I'm guessing I can't just make one up since it might either be in use already or get allocated later.
At the moment my activity launches ok, but exits because I've deliberately set my appWidgetId as:
AppWidgetManager.INVALID_APPWIDGET_ID;
Can anyone give me any ideas on this?

Categories

Resources