没有触发地理围栏(意图服务)

时间:2016-08-20 00:17:16

标签: android intentservice geofencing

我正在开发一个应用程序,该应用程序需要使用地理围栏以及从服务器提取的数据,并在特定条件下进行更新。

我面临的问题是,现在没有触发地理围栏,更糟糕的是,当我开始监控它时,甚至没有创建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";
    }
}

0 个答案:

没有答案