I am currently working on a map app that has points of interest built into it.
These points are supposed to be announced to the user by means of a proximity alert trigger.
Here is the addproximityAlert() code that I'm using
loc.addProximityAlert(lat, longe, radius, -1, PendingIntent.getActivity(
c, 0, new Intent().putExtra(loc_name, loc_name), flag));
The idea is that once the alert fires an alert dialog pops up with a short blurb about the site with the option to either close the alert or get more info(uses WebView).
Thus far I have no run-time or compile-time errors but as I approach each site, nothing happens.
My theory on why nothing happens is that either;
1) I haven't used the PendingIntent correctly, or
2) I haven't set up the BroadcastReceiver correctly
Here is the XML code for the BroadcastRecevier,
<receiver android:name=".ProxyAlertReceiver" >
<intent-filter>
<action android:name="entering" />
</intent-filter>
</receiver>
My current plan to fix this issue is to modify the PendingIntent to use a new Intent like this;
...new Intent(myContext, ProxyAlertReceiver.class)...
and see if I get any results.
Opinions and advice on my issue would be greatly appreciated!
Have you tried PendingIntent.getBroadcast(...)?
Intent locationReachedIntent = new Intent("entering");
PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 1234,
locationReachedIntent, 0);
locationManager.addProximityAlert(longitude, latitude, radius, -1, pendingIntent);
I have the above code working in my application.
Use This
Intent locationIntent = new Intent();
Bundle extras= new Bundle();
extras.putString("loc_name",loc_name);
locationIntent.putExtras(extras);
PendingIntent pendingIntent= new PendingIntent.getActivity(this,0,locationIntent,0);
loc.addProximityAlert(lat, longe, radius, -1, pendingIntent, flag));
I assume your loc_name is a string. This will work.
Implementing a proximity alert depends on more than just calling the addProximity method on a Location Manager.
You must also:
Create a receiver class which will fire when alert is triggered and will receive a status (entering or exiting) and the action name*;
public class ProximityReceiver extends BroadcastReceiver {
public String TAG ="ProxReceiver";
#Override
public void onReceive(Context context, Intent intent) {
/* your code here - sample below */
final String key = LocationManager.KEY_PROXIMITY_ENTERING;
final Boolean entering = intent.getBooleanExtra(key, false);
if (entering) {
Toast.makeText(context, "LocationReminderReceiver entering", Toast.LENGTH_SHORT).show();
Log.v(TAG, "Poi entering");
} else {
Toast.makeText(context, "LocationReminderReceiver exiting", Toast.LENGTH_SHORT).show();
Log.v(TAG, "Poi exiting");
}
Log.v(TAG,"Poi receive intent["+intent.toString()+"]");
Bundle extras = intent.getExtras();
// debugging only
int counterExtras = extras.size();
if (extras != null) {
for (String key : extras.keySet()) {
Object value = extras.get(key);
Log.d(TAG, "Prox Poi extra "+String.format("key[%s] value[%s] class[%s] count[%s]", key,
value.toString(), value.getClass().getName(), String.valueOf(counterExtras)));
}
} else {
Log.v(TAG, "Prox Poi extra empty");
}
}
}
Declare this receiver in your Manifest file;
<receiver android:name=".ProximityReceiver" >
<intent-filter>
<action android:name="my" />
</intent-filter>
</receiver>
Register (associate your pending intents to) this receiver, adding proximity alert(s). Only register your receiver ONCE in your code. If one registers a receiver multiple times, it will fire once for every receiver instance (you reach a POI, which registers a pending intent called "my". **
// create proximity alert
Intent locationIntent = new Intent("my");
ProximityReceiver proximityReceiver = new ProximityReceiver();
PendingIntent pendingIntent = PendingIntent.getBroadcast(mapView.getContext(), <some identifying text>,
locationIntent, 0);
loc.addProximityAlert(lat, longe, radius, -1, PendingIntent.getActivity(
c, 0, new Intent().putExtra(loc_name, loc_name), flag));
IntentFilter filter = new IntentFilter("my");
context.registerReceiver(proximityReceiver, filter);
Where context can be this if running in same activity.
Unless you want to keep on receiving alerts even when in background (or even terminated), you must implement removal and re-creation of proximity alerts in your onPause and onResume methods, like this SO question (jump to the end of the question).
note * In this example, "my" will be the action name (see Intent declaration) for an action and will be passed along with the intent AND a bundle of extras containing, at least, the key entering (LocationManager.KEY_PROXIMITY_ENTERING) with boolean value, which gives you the state of the alert, if one is entering (1) or exiting (0) the proximity radius.
note ** If you have registered the receiver for "my" multiple times, it will fire multiple times for every proximity alert event that calls an intent named "my".
Related
I'm trying to create a pinned shortcut on the desktop for an app. The CreateShortcut method is called from a button and presents the android create-shortcut dialog. When the caller selects ok, the broadcast receiver should get called and execute finish so the activity exits.
This is the first time I've used a broadcast receiver but it looks like it's pretty straight-forward. Just create a receiver, register it with an intent filter that has the same action as an intent and when the intent is sent it should cause the receiver to be called, right?
The shortcut is created just fine but the broadcast receiver never gets called. I'm not seeing any messages on logcat.
private void CreateShortcut(final Context c) {
if (ShortcutManagerCompat
.isRequestPinShortcutSupported(c)) {
Intent shortcutIntent = new Intent(
c, CreateAppHomeShortcut.class);
shortcutIntent.setAction(
Intent.ACTION_CREATE_SHORTCUT);
ShortcutInfoCompat shortcutInfo
= new ShortcutInfoCompat
.Builder(c, "shortcut")
.setShortLabel(c.getString(R.string.app_name))
.setIcon(IconCompat.createWithResource(
c, R.drawable.qmark)
)
.setIntent(shortcutIntent)
.build();
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(
Context context, Intent intent) {
Log.d(TAG, "msg received");
unregisterReceiver(this);
finish();
}
}
, new IntentFilter(
Intent.ACTION_CREATE_SHORTCUT
)
);
PendingIntent successCallback =
PendingIntent.getBroadcast(
c, 99
, shortcutIntent, 0
);
ShortcutManagerCompat.requestPinShortcut(c,
shortcutInfo,
successCallback.getIntentSender()
);
}
}
I've been working on this several days and I'm stumped.
Thanks
I finally got the callback to my BroadcastReceiver. My main problem was that I was using the intents wrong. I thought that the brodcast receiver intent and the shortcut intent could be the same as long as the action was correct. Wrong! The shortcut intent must hava an action set but in the tests I did, it didn't seem to care what that action was. And the broadcast receiver was created as "Intent = new Intent(context, class); setAction(...); ", the shortcut would be created and function fine but the broadcast receiver was never invoked. The only way I could get the broadcast receiver to work was with an Intent just for it with nothing but the action set (or possibly extras) set. I couldn't get the program to work using the same intent to create the shortcut and invoke the broadcast receiver.
The other problem encountered was that the interface allows you to create multiple pinned shortcuts -- and would then call your broadcast receiver once for each shortcut created. I discovered you can query the interface for all pinned shortcuts and filter by id to find out if your shortcut already exists and use that info to avoid creating multiple identical pinned shortcuts on your home page.
The code below seems to work fine API26+ for creating a shortcut and the receiver gets called as long as the user accepts the shortcut. The docs state that they will only call your receiver on the user's acceptance. That of course makes detecting the end of the user's interaction rather difficult. Since the request gets buried in my actual app, the plan was to open this as part of a separate activity, but I don't have any way to detect that the user is done if he doesn't want the shortcut. If anyone has suggestions, I'd appreciate hearing them.
// Create a shortcut and exit the activity. If the shortcut
// already exists,just exit.
private void CreateShortcut(final Context c) {
if (Build.VERSION.SDK_INT >= 26) {
ShortcutManager sm =
getSystemService(ShortcutManager.class);
if (sm != null && sm.isRequestPinShortcutSupported()) {
final String shortcutId = "StartApp";
boolean shortcutExists = false;
// We create the shortcut multiple times if given the
// opportunity. If the shortcut exists, put up
// a toast message and exit.
List<ShortcutInfo> shortcuts
= sm.getPinnedShortcuts();
for (int i = 0;
i < shortcuts.size() && !shortcutExists; i++) {
shortcutExists
= shortcuts.get(i).getId().equals(shortcutId);
if (shortcutExists) {
Toast.makeText(c , String.format(
"Shortcut %s already exists."
, shortcutId
)
, Toast.LENGTH_LONG
).show();
finishActivity();
}
else {
// this is the intent that actually creates the
// shortcut.
Intent shortcutIntent
= new Intent(c, CreateAppHomeShortcut.class);
shortcutIntent.setAction(
Intent.ACTION_CREATE_SHORTCUT);
ShortcutInfo shortcutInfo = new ShortcutInfo
.Builder(c, shortcutId)
.setShortLabel(
c.getString(R.string.app_name))
.setIcon(createWithResource(c
, R.drawable.qmark))
.setIntent(shortcutIntent)
.build();
// this intent is used to wake up the broadcast
// receiver.
// I couldn't get createShortcutResultIntent to
// work but just a simple intent as used for a
// normal broadcast intent works fine.
Intent broadcastIntent
= new Intent(Intent.ACTION_CREATE_SHORTCUT);
// create an anonymous broadcaster. Unregister
// to prevent leaks when done.
registerReceiver(new BroadcastReceiver() {
#Override
public void onReceive(
Context c, Intent intent) {
unregisterReceiver(this);
Log.d(TAG, String.format(
"ShortcutReceiver activity = "
+ "\"$1%s\""
, intent.getAction()));
finishActivity();
}
}
, new IntentFilter(
Intent.ACTION_CREATE_SHORTCUT)
);
PendingIntent successCallback
= PendingIntent.getBroadcast(
c, 99
, broadcastIntent, 0);
// Shortcut gets created here.
sm.requestPinShortcut(shortcutInfo
, successCallback.getIntentSender());
}
}
}
}
I'm having some trouble getting my proximity alert to work on my Android app that's running on an Emulator. Basically the proximity alert should start an activity that will (for now) print to the log, however when a desired location is set for the alert, and the emulator's location is set at that particular location, nothing happens. Here is the code for the proximity alert:
LocationManager lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
Intent intent = new Intent(MY_PROXIMITY_ALERT);
PendingIntent proxIntent = PendingIntent.getActivity(MapActivity.this, 0, intent, 0);
lm.addProximityAlert(latlng.latitude, latlng.longitude, 100, -1, proxIntent);
Now MY_PROXIMITY_ALERT is declared in the manifest as stated below:
<receiver android:name=".myLocationReceiver">
<intent-filter>
<action android:name="PROXIMITY_ALERT"/>
</intent-filter>
</receiver>
And here is my code for myLocationReceiver
public class myLocationReceiver extends BroadcastReceiver{
private static final String TAG = "myLocationReceiver";
#Override
public void onReceive(Context context, Intent intent) {
final String key = LocationManager.KEY_PROXIMITY_ENTERING;
final Boolean entering = intent.getBooleanExtra(key, false);
if(entering) {
Log.d(TAG, "onReceive: Entering proximity of location");
}
}
}
I believe my problem has something to do with the Intent or PendingIntent object but I'm not entirely sure. Also I have heard that usually the GPS will take about a minute to actually register the proximity, but I still do not get a log message even after some time.
Thanks!
You've created an Intent with action MY_PROXIMITY_ALERT and then used PendingIntent.getActivity() to get a PendingIntent to pass to the LocationManager. When the proximity conditions are satisfied, LocationManager will try to start an Activity that is listening for action MY_PROXIMITY_ALERT.
Intent intent = new Intent(MY_PROXIMITY_ALERT);
PendingIntent proxIntent = PendingIntent.getActivity(MapActivity.this, 0, intent, 0);
In your manifest, you've declared a BroadcastReceiver that is listening for action MY_PROXIMITY_ALERT. This won't work.
Since you want the proximity alert to trigger a BroadcastReceiver, you need to get the PendingIntent like this:
Intent intent = new Intent(MY_PROXIMITY_ALERT);
PendingIntent proxIntent = PendingIntent.getBroadcast(MapActivity.this, 0, intent, 0);
Personally, I think it would be better to use an "explicit" Intent instead of an "implicit" Intent. In that case you would do it like this:
Intent intent = new Intent(MapActivity.this, myLocationReceiver.class);
PendingIntent proxIntent = PendingIntent.getBroadcast(MapActivity.this, 0, intent, 0);
You don't need to use the ACTION in the Intent.
Using an "explicit" Intent tells Android exactly what component (class) to launch. If you use an "implicit" Intent, Android has to search for components that advertise that they can handle certain ACTIONs.
I want some methods to execute when I click on Notification Action Button.
I have searched on this site, but everything seems to be in order and my IntentService is not being called.
My Action-Button Intent
Intent off = new Intent();
off.setAction("action");
off.putExtra("test", "off");
PendingIntent pOff = PendingIntent.getService(context, 22, off, 0);
Notification Builder
NotificationCompat.Builder mBuilder = new NotificationCompat.Builder(context)
.setSmallIcon(/**/)
.setContentTitle(/**/)
.setContentText(/**/)
.addAction(/**/, "Off", pOff)
.setContentIntent(pendingIntent)
.setDefaults(Notification.DEFAULT_SOUND)
.setAutoCancel(true);
Intent Service Class
public class NotificationServiceClass extends IntentService {
public NotificationServiceClass(String name) {
super(name);
}
public NotificationServiceClass () {
super("NotificationServiceClass");
}
#Override
protected void onHandleIntent(Intent intent) {
Log.i("test", "onHandle");
if (intent.getAction().equals("action")) {
Log.i("test", "action");
Bundle bundle = intent.getExtras();
if (bundle != null) {
Log.i("test", "onHandleBundleNotNull");
if (bundle.containsKey("test")) {
Log.i("test", bundle.getString("test"));
}
}
}
}
}
XML Declaration for Service class
<service
android:name=".Manager.NotificationServiceClass"
android:exported="false">
</service>
Per the Intents and Intent Filters training, the Intent you've built is an implicit Intent:
Implicit intents do not name a specific component, but instead declare a general action to perform, which allows a component from another app to handle it. For example, if you want to show the user a location on a map, you can use an implicit intent to request that another capable app show a specified location on a map.
What you actually want is an explicit intent: one that specifies the component to start by name as per the note on the same page:
Note: When starting a Service, you should always specify the component name. Otherwise, you cannot be certain what service will respond to the intent, and the user cannot see which service starts.
When constructing your Intent, you should use
// Note how you explicitly name the class to use
Intent off = new Intent(context, NotificationServiceClass.class);
off.setAction("action");
off.putExtra("test", "off");
PendingIntent pOff = PendingIntent.getService(context, 22, off, 0);
In looking at your code, I do not see you telling the PendingIntent what class to use for your service.
You should add:
off.setClass(this, NotificationServiceClass.class);
Otherwise the PendingIntent has nothing to do.
I would like to use Android's LocationManager and the addProximityAlert method to setup proximity alerts. For this, I've created a small application that shows a crosshair on top of a map, plus a text field for the proximity alert name and a button to trigger the addition of the alert.
Unfortunately, the BroadcastReceiver that should receive the proximity alerts is not triggered. I've tested the intent alone (not wrapped via a PendingIntent) and that works. Also, I see that once a proximity alert is set, the GPS / location icon appears in the notification bar.
I've found information about proximity alerts a bit confusing - some are telling the alerts cannot be used if the activity is no longer in the foreground. I think it should work, so I assume something else is wrong.
1 Adding a Proximity alert
GeoPoint geo = mapView.getMapCenter();
Toast.makeText(this, geo.toString(), Toast.LENGTH_LONG).show();
Log.d("demo", "Current center location is: " + geo);
PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, getLocationAlertIntent(), 0);
locationManager.addProximityAlert(geo.getLatitudeE6()/1E6, geo.getLongitudeE6()/1E6, 1000f, 8*60*60*1000, pIntent);
The intent itself is here:
private Intent getLocationAlertIntent()
{
Intent intent = new Intent("com.hybris.proxi.LOCATION_ALERT");
intent.putExtra("date", new Date().toString());
intent.putExtra("name", locationName.getEditableText().toString());
return intent;
}
I created a receiver which is supposed to receive the location alerts, registered in AndroidManifest.xml:
<receiver android:name=".LocationAlertReceiver">
<intent-filter>
<action android:name="com.hybris.proxi.LOCATION_ALERT" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
The implementation itself is hopefully straightforward. It is supposed to show a notification (and I checked that via directly sending an intent with a test button).
public class LocationAlertReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context ctx, Intent intent) {
Log.d("demo", "Received Intent!");
String dateString = intent.getStringExtra("date");
String locationName = intent.getStringExtra("name");
boolean isEntering = intent.getBooleanExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
NotificationManager notificationManager = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
Notification notification = new Notification.Builder(ctx)
.setContentTitle("LocAlert: " + locationName)
.setContentText(dateString + "|enter: " + isEntering)
.setSmallIcon(R.drawable.ic_stat_loc_notification)
.build();
notificationManager.notify(randomInteger(), notification);
}
private int randomInteger()
{
Random rand = new Random(System.currentTimeMillis());
return rand.nextInt(1000);
}
A few things that I am not 100% sure of, maybe this triggers something in you:
I assume it is OK to register a proximity alert using a pending intent like I do and the Activity that created the proximity alert can later be closed.
Converting from the Map via getCenter returns a GeoPoint with lat/lon as int values. I thnk I am correctly converting them the the double values expected by addProximityAlert by dividing by 1E6
The distance from the center is relatively large - 1000m - I assume that is a nice value.
Examples that I've found online used broadcast receivers registered programmatically. this is not what I want to do. but the book Android 4 Profession Dev by Reto Meier mentions that registering the broadcast receiver in xml is also fine.
Any help greatly appreciated!!
I ran into the same issue. Setting target class of the intent explicitly helped in my case. In your case it should look the following:
private Intent getLocationAlertIntent()
{
Intent intent = new Intent(context, LocationAlertReceiver.class);
intent.setAction("com.hybris.proxi.LOCATION_ALERT"); //not sure if this is needed
intent.putExtra("date", new Date().toString());
intent.putExtra("name", locationName.getEditableText().toString());
return intent;
}
I am trying to use Android's LocationManager requestLocationUpdates. Everything is working until I try to extract the actual location object that in my broadcast receiver. Do I need to specifically define the "extras" to my custom intent so that the Android LocationManager before I pass it to requestLocationUpdates so it knows how to add it into the intent, or will it create the extras-bundle regardless when it passes the fired intent to the broadcast receiver?
My code looks like this:
Intent intent = new Intent("com.myapp.swarm.LOCATION_READY");
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
0, intent, 0);
//Register for broadcast intents
int minTime = 5000;
int minDistance = 0;
lm.requestLocationUpdates(LocationManager.GPS_PROVIDER, minTime,
minDistance, pendingIntent);
I have a broadcast receiver that is defined in the manifesto as:
<receiver android:name=".LocationReceiver">
<intent-filter>
<action android:name="com.myapp.swarm.LOCATION_READY" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
And the broadcast receiver class as:
public class LocationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do this when the system sends the intent
Bundle b = intent.getExtras();
Location loc = (Location)b.get("KEY_LOCATION_CHANGED");
Toast.makeText(context, loc.toString(), Toast.LENGTH_SHORT).show();
}
}
My "loc" object is coming up null.
OK, I managed to fix it by changing the KEY_LOCATION_CHANGED in the broadcast receiver code to:
public class LocationReceiver extends BroadcastReceiver {
#Override
public void onReceive(Context context, Intent intent) {
//Do this when the system sends the intent
Bundle b = intent.getExtras();
Location loc = (Location)b.get(android.location.LocationManager.KEY_LOCATION_CHANGED);
Toast.makeText(context, loc.toString(), Toast.LENGTH_SHORT).show();
}
}
i ve tried to code and test the solution you proposed, since i am facing similar problems concerning proximity alerts and intents carrying location objects. According the information you provided, you managed to overcome the null object retrieval, on the BroadcastReceiver's side. What you might did not observe is that now you should be receiving the same location as the one your intent was first created (also seen it as: intent caching problem).
In order to overcome this problem, i used FLAG_CANCEL_CURRENT, as being proposed by many people here and it works pretty fine, fetching fresh (and juicy :P) location values. So the line defining your pending intent should look like this:
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(),
0, intent, PendingIntent.FLAG_CANCEL_CURRENT);
However, you can ignore this if:
your purpose was just to receive a location value once
you managed to overcome it in some other way not visible in your post