I'm implementing a widget and I'm facing the following problems:
1) onUpdate is called when I add the widget to the home screen, even if I specified a Configuration Activity. As soon as I add it to the home screen, the APPWIDGET_ENABLED broadcast is sent, followed by the APPWIDGET_UPDATE and then the configuration activity is launched.. Is this a bug? How should I understand in the onUpdate method that is being invoked before the configuration activity has returned? I can do it through a shared preference value, but I'd like it to behave as written on the developer guide, i.e. the onUpdate method should not be called.
2) onUpdate is not called every updatePeriodMillis seconds, which have been set to 10000, i.e. 10 seconds for testing purposes.. Did I miss something in the receiver declaration within the Manifest file? I keep receiving the Lint warning Exported receiver does not require permission but I think this is a Lint issue and not my fault.
EDIT: I've just found this within the reference docs: Note: Updates requested with updatePeriodMillis will not be delivered more than once every 30 minutes. So it is correct that the widget is not updated how often I'd specified and I've changed the time to 1800000 milliseconds.
3) I want to deliver my own broadcast action to the widget provider, is it correct to add another receiver block in the Manifest targeting the same provider class or should I add only another intent action within the intent-filter that contains the APPWIDGET_UPDATE action? BTW, I've commented my second receiver block and it is not the cause of the problems above. I created another receiver block because I wanted to declare it as not exported, in order to let the intent-filter action be triggered only by my app code and not anyone else.
AndroidManifest.xml
<receiver android:name="MyWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
<receiver android:name="MyWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE" />
</intent-filter>
</receiver>
my_widget_info.xml
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="294dp"
android:minHeight="110dp"
android:updatePeriodMillis="1800000"
android:initialLayout="#layout/my_widget_layout"
android:configure="org.test.mywidget.MyWidgetConfiguration"
android:resizeMode="none">
</appwidget-provider>
Providing an answer after digging into the source code:
1) This is expected behavior see here
This method is also called when the user adds the App Widget
2) Seems you have found your own answer. For others looking for the docs go here
Note: Updates requested with updatePeriodMillis will not be delivered more than once every 30 minutes
3) Since the AppWidgetProvider extends BroadcastReceiver but is not declared final you can add the action from your second receiver
<receiver android:name="MyWidgetProvider"
android:exported="true">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
then you can override onReceive in your MyWidgetProvider class and if the action is your custom action handle it, otherwise call to the super.onRecieve(Context context, Intent intent) like so:
#Override
public void onReceive(Context context, Intent intent) {
if(intent.getAction()
.equals("org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE")){
// handle your action
} else {
super.onRecieve(context, intent);
}
}
as per the Guide:
AppWidgetProvider is just a convenience class. If you would like to
receive the App Widget broadcasts directly, you can implement your own
BroadcastReceiver or override the onReceive(Context, Intent) callback.
One thing to note with this, the update to the remote view will only be called at the updatePeriodMillis, regardless of you adding your own action to the Intent for the provider to handle.
Good Luck and Happy Coding!
Related
I am trying to handle app removing.
My AndroidManifest.xml looks like this:
<uses-permission android:name="android.permission.GET_TASKS"/>
<receiver android:name=".receivers.UninstallIntentReceiver">
<intent-filter android:priority="0">
<action android:name="android.intent.action.QUERY_PACKAGE_RESTART" />
<data android:scheme="package" />
</intent-filter>
</receiver>
UninstallIntentReceiver.java
public class UninstallIntentReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// fetching package names from extras
LogUtils.i("HK_LOG " + this.getClass().getSimpleName(), "onReceive");
String[] packageNames = intent.getStringArrayExtra("android.intent.extra.PACKAGES");
if(packageNames!=null){
for(String packageName: packageNames){
if(packageName!=null && packageName.equals("com.betconstruct.sportsbookModule")){
// User has selected our application under the Manage Apps settings
// now initiating background thread to watch for activity
new UninstallActivity(context).start();
}
}
}
}
}
When apps is stopped, onReceive() method is called, but when app is running, onReceive() method is not getting called.
In this case onReceive() is called:
But in this case onReceive() is not called:
If I click on close button (from settings, pic that I show with arrow), after that onReceive() method is called.
I feel that I must guest how to handle this action, and solve this problem.
Any kind of suggestion will help me. Thanks.
I am trying to handle app removing.
I'm not sure android.intent.action.QUERY_PACKAGE_RESTART is the intent you need.
From javadocs:
Ask system services if there is any reason to restart the given package. The data contains the name of the package.
This looks like an action that's needed for system services, it doesn't even have a documentation. Also note the #hide annotation, which means that this action is not exposed to public API and you shouldn't rely on that.
The actual action you are interested in should be android.intent.action.PACKAGE_REMOVED.
<receiver android:name=".MyReceiver">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_REMOVED" />
<data android:scheme="package"/>
</intent-filter>
</receiver>
If I understand your question correct, you are trying to handle an action of removing an application within the same application, meaning you expect onReceive() method of BroadcastReceiver which is in Sportsbook application to be fired as soon as user removes Sportsbook application. That's not possible, because when user uninstalls your application, all your app data, apk, classes are removed, thus there doesn't exist that receiver anymore.
If you try to detect removal of application from another application (let's say you have 2 applications, and you want to track whether user removes one of your apps), then that would make sense, and onReceive() would be called as expected.
I have a clock widget application, and I need to recognize when the phone has been unlocked or not, I believe I can use action USER_PRESENT for that, but I can't get it to launch in the BroadcastReceiver class, I set it in the manifest like this:
<receiver
android:name="com.myApp.myApp.MyWidgetIntentReceiver"
android:exported="false"
android:label="widgetBroadcastReceiver" >
<intent-filter>
<action android:name="android.intent.action.USER_PRESENT" >
</action>
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/demo_widget_provider" />
</receiver>
And this is how I trying to get it in the BroadcastReceiver:
public class MyWidgetIntentReceiver extends BroadcastReceiver{
public void onReceive(Context context, Intent intent) {
if(intent.getAction().equals(Intent.ACTION_USER_PRESENT){
Log.i("TICK", intent.getAction());
}
}
}
It's not firing after I unlock the phone, can you help me out or provide me a better way to check when the phone has been unlocked? thanks!
Remove android:exported="false"
android:exported:
Whether or not the broadcast receiver can receive messages from sources outside its application — "true" if it can, and "false" if not. If "false", the only messages the broadcast receiver can receive are those sent by components of the same application or applications with the same user ID.
Source : developer.android.com
Remove android:exported="false". That worked for me on Stock Android 5
I got it to work by using registerReceiver in the onUpdate method of the AppWidgetProvider class and passing an instance of the BroadcastReceiver class to register the Intent.ACTION_USER_PRESENT, since adding it only in the Manifest was not doing anything. Thank you!
i am building a small widget for learning purpose, it simply has an configuration activity where i set the update interval. it works normally and i can create multiple instance of it.
but when i reboot the phone the alarm manager stops, and the widget won't update.
after some search and google'ng i learned that i have to add a BOOT COMPLETE receiver
but after several attempts i failed to implement so any one has an i idea about how to add that or any good source code example on widgets.
To do something at boot you simply do following.
First in the manifest, this is added under application tag:
<receiver android:name="AlarmReceiver">
<intent-filter>
<action android:name="packagename.ACTION"/>
<action android:name="packagename.ACTION2"/>
</intent-filter>
</receiver>
<receiver android:name="BootSetter" >
<intent-filter>
<action
android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
In order for this to work you need to add permission to receive the Broadcast in the manifest with following line:
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
Then you have a class BootSetter:
public class BootSetter extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
// Do your stuff
}
}
There is a similar post, though not completly the same here. It's about running an alarm every day at noon.
I think you are setting alarm manager in class other then AppWidgetProvider extended class(widget class) .Better you should set an alarmmanager in OnUpdate method AppWidgetProvider extended class (widget class)then there will be no need of setting the alarm again after boot.
Suppose I want to define a receiver for my own widget and I want it to handle my own broadcast org.test.mywidget.MY_ACTION along with the APPWIDGET_xxx system broadcasts, what is the correct way to define it, if I want it to handle my own broadcast only if it is sent from the same app package? I tried the following XML code but in this way the APPWIDGET_DELETE action was no more delivered to the widget provider:
<receiver
android:name="MyWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
<receiver
android:name="MyWidgetProvider"
android:exported="false">
<intent-filter>
<action android:name="org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE" />
</intent-filter>
</receiver>
The XML above has the problem I've mentioned (no DELETED events delivered) and it also does not seem good to me, since the receiver is redefined.. So I compacted everything as follows:
<receiver
android:name="MyWidgetProvider">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
<action android:name="org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE" />
</intent-filter>
<meta-data
android:name="android.appwidget.provider"
android:resource="#xml/my_widget_info" />
</receiver>
This works, APPWIDGET_UPDATE, APPWIDGET_DELETE and my own broadcast are all delivered but now I have a question: are now other apps able to deliver a broadcast intent with the action org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE to my widget, since the android:exported value is set to true by default? Maybe I'm missing some basic concept related to this exported value, so I would be glad if some of you can make me understand everything better :)
if I want it to handle my own broadcast only if it is sent from the same app package?
You do not need, or even want, an action string if it is all going to be within your own package. Just use the Intent constructor that takes a Java class object as the second parameter, and use that for sending broadcasts to be picked up by the receiver.
I tried the following XML code but in this way the APPWIDGET_DELETE action was no more delivered to the widget provider
That is because there is no <intent-filter> referencing that action string in your code.
are now other apps able to deliver a broadcast intent with the action org.test.mywidget.FORCE_SMALL_WIDGET_UPDATE to my widget, since the android:exported value is set to true by default?
Yes. Of course, third party apps can send APPWIDGET_UPDATE broadcasts, or even hack an Intent that identifies your component directly, and you will receive those as well.
I suspect that the right answer, in your case, is to simply implement a second BroadcastReceiver, one with no <intent-filter>, that handles operations that you solely want to be within your package. Or, if you do not need to use a PendingIntent for this BroadcastReceiver, consider LocalBroadcastManager from the Android Support package.
I have a broadcast receiver registered in Manifest:
<application ...>
<receiver android:name="com.some.pkg.NewAppReceiver" >
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
</intent-filter>
</receiver>
</appcication>
And the receiver:
public class NewAppReceiver extends BroadcastReceiver {
private static final String TAG = "NewAppReceiver";
#Override
public void onReceive(Context context, Intent intent) {
Log.d(TAG, "Intent: " + intent.getAction());
}
}
And nothing is received when I install APK manually or from the Android Market. Why?
Did you run the app that contains this broadcastReceiver before installing the other apps?
Starting at some API version, broadcastReceivers will not work till you execute the app. Put an activity and execute it.
Also , don't forget to add the following into the broadcastReceiver:
<data android:scheme="package" />
EDIT: On Android 8 and above, if your app targets API 27 or more, it will work partially, so you have to register to those events in code and not in manifest. Here's a list of intents that are still safe to use in manifest: https://developer.android.com/guide/components/broadcast-exceptions.html .
The rest should be used in code. More info here
Since android.intent.action.PACKAGE_ADDED is a System Intent (note that your own app will not receive it at its installation), your BroadcastReceiver will receive messages from sources outside your app. Thus, check you did NOT put: android:exported="false"
You also may need to add:
<data android:scheme="package" />
So, your BroadcastReceiver in your AndroidManifest.xml should look like this:
<application ...>
<receiver android:name=".NewAppReceiver" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.PACKAGE_ADDED" />
<data android:scheme="package" />
</intent-filter>
</receiver>
</appcication>
If it still doesn't work, you may try to put an higher priority, such as: android:priority="1000"
Take a look at: http://developer.android.com/guide/topics/manifest/receiver-element.html
Registering receiver from manifest would not work from API 26(android 8). Because it had performance impact on older versions.
But we can register receiver from java code and receive updates of removed and added applications.
val intentFilter = IntentFilter()
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED)
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED)
intentFilter.addDataScheme("package")
registerReceiver(YourBroadcastReceiver(), intentFilter)
Are you trying to receive the intent in the application you are installing? The documentation for ACTION_PACKAGE_ADDED says:
Note that the newly installed package does not receive this broadcast.
Another possibility is that this intent might not be delivered to components registered via the manifest but only manually (as described in an answer by Mark Murphy to Stack Overflow question Can't receive broadcasts for PACKAGE intents).
If you try to receive some other package it must be worked.
(As #Savvas noted) If you try to receive your own package's addition you can't receive it. Even if your broadcast receiver has action.PACKAGE_ADDED, receiver's onReceive method isn't triggered.
In this case your best bet is saving this data. By using sharedPreferences, add a key something like "appIsWorkedBefore", and on your launcher Activity's onCreate method set this variable as "true". And you can make your works with respect to this Boolean.
This intent action is no longer available for applications.
This is a protected intent that can only be sent by the system.
https://developer.android.com/reference/android/content/Intent#ACTION_PACKAGE_ADDED