我正在开发一个应用程序,该应用程序需要使用地理围栏以及从服务器提取的数据,并在特定条件下进行更新。
我面临的问题是,现在没有触发地理围栏,更糟糕的是,当我开始监控它时,甚至没有创建IntentService。
以下是我的LocationManager的代码(添加地理围栏的代码)。
public class LocationManager implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, ResultCallback<Status> , LocationListener{
static LocationManager sharedManager;
static Location lastLocation;
private QueryAccountsHandler queryAccountsInnerHandler = new QueryAccountsHandler();
private ArrayList<Merchant> merchantList;
private Merchant farthestMerchant;
private boolean isFetchingLocations;
LocationStatus locationStatus = LocationStatus.Unknown;
private ArrayList<Geofence> mGeofenceList;
private PendingIntent mGeofencePendingIntent;
private boolean mGeofencesAdded;
enum LocationStatus {
Unknown,
Available,
NeedsResolution,
Unavailable
}
enum GeofenceStatus {
Available,
Unavailable
}
public static void initialize() {
sharedManager = new LocationManager(UnipagosApplication.getMainApplication().getApplicationContext());
}
private LocationRequest mLocationRequest;
private Context context;
private GoogleApiClient mGoogleApiClient;
public LocationManager(Context applicationContext) {
this.context = applicationContext;
init();
getLastLocation();
}
public static synchronized LocationManager sharedManager() {
return sharedManager;
}
void init() {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mGoogleApiClient.connect();
}
LocationRequest getLocationRequest() {
if (mLocationRequest == null) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(15000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setSmallestDisplacement(30);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
return mLocationRequest;
}
public void disconnectGoogleClient() {
mGoogleApiClient.disconnect();
}
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
@Override
public void onConnected(@Nullable Bundle bundle) {
Log.w("Location manager", "On Connected");
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
.addLocationRequest(getLocationRequest());
PendingResult<LocationSettingsResult> result =
LocationServices.SettingsApi.checkLocationSettings(mGoogleApiClient,
builder.build());
result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
@Override
public void onResult(@NonNull LocationSettingsResult result) {
final Status myStatus = result.getStatus();
final LocationSettingsStates states = result.getLocationSettingsStates();
switch (myStatus.getStatusCode()) {
case LocationSettingsStatusCodes.SUCCESS:
// All location settings are satisfied. The client can
// initialize location requests here.
locationStatus = LocationStatus.Available;
startLocationUpdates();
break;
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
// Location settings are not satisfied, but this can be fixed
// by showing the user a dialog.
locationStatus = LocationStatus.NeedsResolution;
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
// Location settings are not satisfied. However, we have no way
// to fix the settings so we won't show the dialog.
locationStatus = LocationStatus.Unavailable;
break;
}
}
});
}
@Override
public void onConnectionSuspended(int i) {
Log.w("Location manager", "On Connection suspended");
}
@Override
public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
Log.w("Location manager", "On connection fail");
}
@Override
public void onResult(@NonNull Status status) {
if (status.isSuccess()) {
mGeofencesAdded = !mGeofencesAdded;
Toast.makeText(
context,
mGeofencesAdded ? "Geo cercas agregadas" :
"geo cercas removidas",
Toast.LENGTH_SHORT
).show();
} else {
}
}
@Override
public void onLocationChanged(Location location) {
int ageInSeconds = getAgeOfLocation(location, lastLocation);
if (lastLocation != null && (ageInSeconds < 5 || !location.hasAccuracy())) {
return;
}
if (location.getAccuracy() <= 100) {
lastLocation = location;
}
if (location.getAccuracy() <= 50) {
lastLocation = location;
if (shouldRefreshListOfMerchants()) {
getNearMerchants(); //Data is pulled from server
}
}
}
private int getAgeOfLocation(Location location, Location lastLocation) {
if (lastLocation == null) return -1;
long now = System.currentTimeMillis();
long ageInSeconds = ((location == null ? now : location.getTime()) - lastLocation.getTime()) / 1000;
return (int)ageInSeconds;
}
private boolean shouldRefreshListOfMerchants() {
boolean shouldRefresh;
if (isFetchingLocations || locationStatus != LocationStatus.Available){
return false;
}
if (merchantList == null) return true;
float[] results = new float[1];
Location.distanceBetween(lastLocation.getLatitude(), lastLocation.getLongitude(), farthestMerchant.getLatitude(), farthestMerchant.getLongitude(), results);
shouldRefresh = getAgeOfLocation(null, lastLocation) > 12000 || results[0] < 50;
return shouldRefresh;
}
public Location getLastLocation() {
if (lastLocation == null) {
lastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
}
return lastLocation;
}
public void getNearMerchants() {
isFetchingLocations = true;
LatLng coords = new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude());
WebServiceAuthenticatedConnector.queryAccounts(context, queryAccountsInnerHandler, coords, "value", 500, 10);
}
private class QueryAccountsHandler extends Handler {
public QueryAccountsHandler() {
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
WebServiceAuthenticatedConnector.WebServiceType type = (WebServiceAuthenticatedConnector.WebServiceType) msg.getData().getSerializable("webServiceType");
switch (msg.what) {
case Constants.messageReceivedWebServiceResponse:
{
try {
String json = msg.getData().getString("json");
JSONObject jsonObject = new JSONObject(json);
Integer resultID = Integer.valueOf(jsonObject.getString("_resultCode"));
switch (resultID) {
case Constants.WEBSERVICE_RESULT_SUCCESS_ID:
if (type == WebServiceAuthenticatedConnector.WebServiceType.QueryAccounts) {
isFetchingLocations = false;
processMerchants(jsonObject);
}
break;
default:
break;
}
} catch(JSONException e) {
// Nothing to do about network-result handling here. This exception is thrown if JSON parsing fails or if a JSON field does not exist.
}
break;
}
case Constants.webserviceException:
break;
default:
break;
}
}
}
//Method where the info retrieved from server is processed and the geofences are added to an array which will be added later.
private void processMerchants(JSONObject jsonObject) {
merchantList = new ArrayList<>();
float farthestDistance = 0;
try {
JSONArray utilitiesJSONArray = jsonObject.getJSONArray("accounts");
Log.w(this.getClass().toString(), "My location: "+ lastLocation.getLatitude() +", "+ lastLocation.getLongitude());
for (int i = 0; i < utilitiesJSONArray.length(); i++) {
JSONObject merchantJSONObject = utilitiesJSONArray.getJSONObject(i);
Merchant merchant = new Merchant();
merchant.setCategory(!merchantJSONObject.has("accountId"));
JSONObject userIdJSONObject = null;
if (merchantJSONObject.has("accountId")) {
userIdJSONObject = merchantJSONObject.getJSONObject("accountId");
} else {
userIdJSONObject = merchantJSONObject.getJSONObject("userId");
}
merchant.setTypeString(userIdJSONObject.getString("type"));
merchant.setKeyString(userIdJSONObject.getString("key"));
// Set the merchant logo
merchant.setLogoKeyString(merchantJSONObject.getString("logoKey"));
// Set the merchant name
String merchantNameString = merchantJSONObject.getString("name");
merchant.setNameString(merchantNameString);
// Set the merchant distance
if (merchantJSONObject.has("locations")) {
...
// Get the latitude and longitude
double lat = coordsJSONObject.getDouble("lat");
double lon = coordsJSONObject.getDouble("long");
...
merchant.setLatitude(lat);
merchant.setLongitude(lon);
merchantList.add(merchant);
}
// Add the merchant to the merchant list
}
if (!removeGeofences()) {
startMonitoringGeofences();
}
} catch (JSONException e) {
if (Constants.DEBUG) {
Log.e("MerchanListFragment", e.getMessage() + e.getCause());
}
}
}
private GeofencingRequest getGeofencingRequest() {
GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
// The INITIAL_TRIGGER_ENTER flag indicates that geofencing service should trigger a
// GEOFENCE_TRANSITION_ENTER notification when the geofence is added and if the device
// is already inside that geofence.
builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
// Add the geofences to be monitored by geofencing service.
builder.addGeofences(mGeofenceList);
// Return a GeofencingRequest.
return builder.build();
}
private void startMonitoringGeofences() {
if(mGeofenceList != null && mGeofenceList.size() != 0)
return;
populateGeofenceList();
try {
LocationServices.GeofencingApi.addGeofences(
mGoogleApiClient,
// The GeofenceRequest object.
getGeofencingRequest(),
// A pending intent that that is reused when calling removeGeofences(). This
// pending intent is used to generate an intent when a matched geofence
// transition is observed.
getGeofencePendingIntent()
).setResultCallback(this); // Result processed in onResult().
} catch (SecurityException securityException) {
// Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
Log.w(this.getClass().toString(), securityException.getLocalizedMessage());
}
}
private boolean removeGeofences() {
if (mGeofenceList != null && mGeofenceList.size() != 0) {
LocationServices.GeofencingApi.removeGeofences(
mGoogleApiClient,
// This is the same pending intent that was used in addGeofences().
getGeofencePendingIntent()
).setResultCallback(this);
mGeofenceList = null;
return true;
}
return false;
}
private PendingIntent getGeofencePendingIntent() {
// Reuse the PendingIntent if we already have it.
Intent intent = new Intent(context, GeofenceTransitionsIntentService.class);
// We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
// addGeofences() and removeGeofences().
return PendingIntent.getService(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
}
public void populateGeofenceList() {
mGeofenceList = new ArrayList<>();
for (Merchant merchant : merchantList) {
mGeofenceList.add(new Geofence.Builder()
// Set the request ID of the geofence. This is a string to identify this
// geofence.
.setRequestId(merchant.getKeyString())
// Set the circular region of this geofence.
.setCircularRegion(
merchant.getLatitude(),
merchant.getLatitude(),
Constants.GEOFENCE_RADIUS_IN_METERS
)
// Set the expiration duration of the geofence. This geofence gets automatically
// removed after this period of time.
.setExpirationDuration(Geofence.NEVER_EXPIRE)
// Set the transition types of interest. Alerts are only generated for these
// transition. We track entry and exit transitions in this sample.
.setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
// Create the geofence.
.build());
}
}
}
IntentService我正在使用。
public class GeofenceTransitionsIntentService extends IntentService {
protected static final String TAG = "GeofenceTransitionsIntentService";
/**
* This constructor is required, and calls the super IntentService(String)
* constructor with the name for a worker thread.
*/
public GeofenceTransitionsIntentService() {
// Use the TAG to name the worker thread.
super(TAG);
}
@Override
public void onCreate() {
super.onCreate();
}
/**
* Handles incoming intents.
* @param intent sent by Location Services. This Intent is provided to Location
* Services (inside a PendingIntent) when addGeofences() is called.
*/
@Override
protected void onHandleIntent(Intent intent) {
GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
if (geofencingEvent.hasError()) {
// String errorMessage = GeofenceErrorMessages.getErrorString(this,
// geofencingEvent.getErrorCode());
// Log.e(TAG, errorMessage);
return;
}
// Get the transition type.
int geofenceTransition = geofencingEvent.getGeofenceTransition();
// Test that the reported transition was of interest.
if (geofenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER) {
// Get the geofences that were triggered. A single event can trigger multiple geofences.
List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
// Get the transition details as a String.
String geofenceTransitionDetails = getGeofenceTransitionDetails(
this,
geofenceTransition,
triggeringGeofences
);
// Send notification and log the transition details.
sendNotification(geofenceTransitionDetails);
Log.i(TAG, geofenceTransitionDetails);
} else {
// Log the error.
// Log.e(TAG, getString(R.string.geofence_transition_invalid_type, geofenceTransition));
}
}
/**
* Gets transition details and returns them as a formatted string.
*
* @param context The app context.
* @param geofenceTransition The ID of the geofence transition.
* @param triggeringGeofences The geofence(s) triggered.
* @return The transition details formatted as String.
*/
private String getGeofenceTransitionDetails(
Context context,
int geofenceTransition,
List<Geofence> triggeringGeofences) {
String geofenceTransitionString = getTransitionString(geofenceTransition);
// Get the Ids of each geofence that was triggered.
ArrayList triggeringGeofencesIdsList = new ArrayList();
for (Geofence geofence : triggeringGeofences) {
triggeringGeofencesIdsList.add(geofence.getRequestId());
}
String triggeringGeofencesIdsString = TextUtils.join(", ", triggeringGeofencesIdsList);
return geofenceTransitionString + ": " + triggeringGeofencesIdsString;
}
/**
* Posts a notification in the notification bar when a transition is detected.
* If the user clicks the notification, control goes to the MainActivity.
*/
private void sendNotification(String notificationDetails) {
// Create an explicit content Intent that starts the main Activity.
Intent notificationIntent = new Intent(getApplicationContext(), SplashFragmentActivity.class);
// Construct a task stack.
TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
// Add the main Activity to the task stack as the parent.
stackBuilder.addParentStack(SplashFragmentActivity.class);
// Push the content Intent onto the stack.
stackBuilder.addNextIntent(notificationIntent);
// Get a PendingIntent containing the entire back stack.
PendingIntent notificationPendingIntent =
stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
// Get a notification builder that's compatible with platform versions >= 4
NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
// Define the notification settings.
builder.setSmallIcon(R.drawable.unipagos_push_notification_notification_area_icon)
// In a real app, you may want to use a library like Volley
// to decode the Bitmap.
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.unipagos_push_notification_notification_area_icon))
.setColor(Color.RED)
.setContentTitle(notificationDetails)
.setContentText("Geocerca activada")//getString(R.string.geofence_transition_notification_text))
.setContentIntent(notificationPendingIntent);
// Dismiss notification once the user touches it.
builder.setAutoCancel(true);
// Get an instance of the Notification manager
NotificationManager mNotificationManager =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
// Issue the notification
mNotificationManager.notify(0, builder.build());
}
/**
* Maps geofence transition types to their human-readable equivalents.
*
* @param transitionType A transition type constant defined in Geofence
* @return A String indicating the type of transition
*/
private String getTransitionString(int transitionType) {
switch (transitionType) {
case Geofence.GEOFENCE_TRANSITION_ENTER:
return getString(R.string.geofence_transition_entered);
case Geofence.GEOFENCE_TRANSITION_EXIT:
return getString(R.string.geofence_transition_exited);
default:
return getString(R.string.unknown_geofence_transition);
}
return "Negocio cerca";
}
}