为什么Google Geofencing无法在我的Android应用中运行?

时间:2015-07-25 04:09:45

标签: java android google-maps geofencing android-geofence

我添加了google文档中用于在android中实现Geofencing的所有必要代码。虽然我可以或多或少地了解正在发生的事情,但我试图尽可能减少这个过程。理想情况下,用户会选择一个位置并围绕它设置geofence。用户进入该区域后,应触发操作。现在我只是想对一个位置进行硬编码并使其工作然后从那里开始。据说这可能不是必需的按钮或指定事件的数组列表。

我的问题是我需要实现这个过程的最低限度代码是什么,最好的方法是什么?这是我的第一个Android应用程序,所以轻松搞砸。

public class MapsActivity extends FragmentActivity implements
    GoogleApiClient.ConnectionCallbacks,
    GoogleApiClient.OnConnectionFailedListener,
    LocationListener {


@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);


        setContentView(R.layout.activity_maps);

        // Kick off the request to build GoogleApiClient.
        buildGoogleApiClient();

        mGeofencePendingIntent = null;
        // Get the value of mGeofencesAdded from SharedPreferences. Set to false as a default.
        mGeofencesAdded = mSharedPreferences.getBoolean(Constants.GEOFENCES_ADDED_KEY, false);

        // Get the geofences used. Geofence data is hard coded in this sample.
        populateGeofenceList();
        getGeofencePendingIntent();
        getGeofencingRequest();

        addGeofencesButtonHandler(this);

        //Get the UI widgets.
        mAddGeofencesButton = (Button) findViewById(R.id.add_geofences_button);
        mRemoveGeofencesButton = (Button) findViewById(R.id.remove_geofences_button);

        mSharedPreferences = getSharedPreferences(Constants.SHARED_PREFERENCES_NAME,
        MODE_PRIVATE);
    }

}

@Override
public void onConnected(Bundle bundle) {
    mLocationRequest = LocationRequest.create();
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
    mLocationRequest.setInterval(1000); // Update location every second

    LocationServices.FusedLocationApi.requestLocationUpdates(
            mGoogleApiClient, mLocationRequest, this);
}


@Override
public void onLocationChanged(Location location) {

    double lat = location.getLatitude();
    double lng = location.getLongitude();

        myLat = lat;
        myLng = lng;
        mapCenter = new LatLng(myLat, myLng);
        mMap.moveCamera(CameraUpdateFactory.newLatLng(mapCenter));
}

/**
 * Builds a GoogleApiClient. Uses the {@code #addApi} method to request the LocationServices API.
 */
protected synchronized void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
            .addConnectionCallbacks(this)
            .addOnConnectionFailedListener(this)
            .addApi(LocationServices.API)
            .build();
}

private void setUpMap() {

    // Enable MyLocation Layer of Google Map
    mMap.setMyLocationEnabled(true);

    // Get LocationManager object from System Service LOCATION_SERVICE
    locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

    // set map type
    mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);

    // Get the name of the best provider
    String networkProvider = locationManager.NETWORK_PROVIDER;
    String gpsProvider = locationManager.GPS_PROVIDER;

    // Get Best Current Location
    myLocation = locationManager.getLastKnownLocation(networkProvider);

        // Get latitude of the current location
        double latitude = myLocation.getLatitude();

        // Get longitude of the current location
        double longitude = myLocation.getLongitude();

        // Create a LatLng object for the current location
        LatLng latLng = new LatLng(latitude, longitude);

        // Show the current location in Google Map
        mMap.moveCamera(CameraUpdateFactory.newLatLng(latLng));

        // Zoom in the Google Map
        mMap.animateCamera(CameraUpdateFactory.zoomTo(14));

        //Add Marker For event
        Intent myIntent = getIntent();
        String desc = myIntent.getStringExtra("desc");
        String addr = myIntent.getStringExtra("addr");
        venueLat = myIntent.getDoubleExtra("lat", 0.0);
        venueLng = myIntent.getDoubleExtra("lon", 0.0);

        mMap.addMarker(new MarkerOptions().position(new LatLng(venueLat, venueLng)).title(desc).snippet(addr));
    }

}

/*
*
*
*
*
* Geofence Stuff
*
*
*
*
 */


private PendingIntent getGeofencePendingIntent() {
    // Reuse the PendingIntent if we already have it.
    if (mGeofencePendingIntent != null) {
        return mGeofencePendingIntent;
    }
    Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
    // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when
    // calling addGeofences() and removeGeofences().
    return PendingIntent.getService(this, 0, intent, PendingIntent.
            FLAG_UPDATE_CURRENT);
}

private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(mGeofenceList);
    return builder.build();
}


/**
 * This sample hard codes geofence data. A real app might dynamically create geofences based on
 * the users location.
 */
public void populateGeofenceList() {
    for (Map.Entry<String, LatLng> entry : Constants.BAY_AREA_LANDMARKS.entrySet()) {

        mGeofenceList.add(new Geofence.Builder()
                // Set the request ID of the geofence. This is a string to identify this
                // geofence.
                .setRequestId(entry.getKey())

                        // Set the circular region of this geofence.
                .setCircularRegion(
                        entry.getValue().latitude,
                        entry.getValue().longitude,
                        Constants.GEOFENCE_RADIUS_IN_METERS
                )

                        // Set the expiration duration of the geofence. This geofence gets automatically
                        // removed after this period of time.
                .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)

                        // 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 |
                        Geofence.GEOFENCE_TRANSITION_EXIT)

                        // Create the geofence.
                .build());
    }
}




public void addGeofencesButtonHandler(View view) {
    if (!mGoogleApiClient.isConnected()) {
        Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
        return;
    }

    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((ResultCallback<Status>) this); // Result processed in onResult().
    } catch (SecurityException securityException) {
        // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
        // logSecurityException(securityException);
    }
}



/**
 * Runs when the result of calling addGeofences() and removeGeofences() becomes available.
 * Either method can complete successfully or with an error.
 *
 * Since this activity implements the {@link ResultCallback} interface, we are required to
 * define this method.
 *
 * @param status The Status returned through a PendingIntent when addGeofences() or
 *               removeGeofences() get called.
 */
public void onResult(Status status) {
    if (status.isSuccess()) {
        // Update state and save in shared preferences.
        mGeofencesAdded = !mGeofencesAdded;
        SharedPreferences.Editor editor = mSharedPreferences.edit();
        editor.putBoolean(Constants.GEOFENCES_ADDED_KEY, mGeofencesAdded);
        editor.commit();

        // Update the UI. Adding geofences enables the Remove Geofences button, and removing
        // geofences enables the Add Geofences button.
        setButtonsEnabledState();

        Toast.makeText(
                this,
                getString(mGeofencesAdded ? R.string.geofences_added :
                        R.string.geofences_removed),
                Toast.LENGTH_SHORT
        ).show();
    } else {
        // Get the status code for the error and log it using a user-friendly message.
        String errorMessage = GeofenceErrorMessages.getErrorString(this,
                status.getStatusCode());
        Log.e(TAG, errorMessage);
    }
}


/**
 * Ensures that only one button is enabled at any time. The Add Geofences button is enabled
 * if the user hasnt yet added geofences. The Remove Geofences button is enabled if the
 * user has added geofences.
 */
private void setButtonsEnabledState() {
    if (mGeofencesAdded) {
        mAddGeofencesButton.setEnabled(false);
        mRemoveGeofencesButton.setEnabled(true);
    } else {
        mAddGeofencesButton.setEnabled(true);
        mRemoveGeofencesButton.setEnabled(false);
    }
}


}




/*
* ---------------------------------------------------------------------------------------
*/



public class GeofenceTransitionsIntentService extends IntentService {
protected static final String TAG = "geofence-transitions-service";


/**
 * 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 ||
            geofenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT) {

        // 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);
       // Toast.makeText(getApplicationContext(), geofenceTransitionDetails, Toast.LENGTH_SHORT).show();
        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(), MapsActivity.class);

    // Construct a task stack.
    TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);

    // Add the main Activity to the task stack as the parent.
    stackBuilder.addParentStack(MapsActivity.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 thats compatible with platform versions >= 4
    NotificationCompat.Builder builder = new NotificationCompat.Builder(this);

    // Define the notification settings.
    builder.setSmallIcon(R.drawable.ic_launcher)
            // In a real app, you may want to use a library like Volley
            // to decode the Bitmap.
            .setLargeIcon(BitmapFactory.decodeResource(getResources(),
                    R.drawable.ic_launcher))
            .setColor(Color.RED)
            .setContentTitle(notificationDetails)
            .setContentText(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);
    }
}
}

/*
* --------------------------------------------------------------------------
*/


public class Constants {
private Constants() {
}

public static final String PACKAGE_NAME = "com.google.android.gms.location.Geofence";

public static final String SHARED_PREFERENCES_NAME = PACKAGE_NAME + ".SHARED_PREFERENCES_NAME";

public static final String GEOFENCES_ADDED_KEY = PACKAGE_NAME + ".GEOFENCES_ADDED_KEY";

/**
 * Used to set an expiration time for a geofence. After this amount of time Location Services
 * stops tracking the geofence.
 */
public static final long GEOFENCE_EXPIRATION_IN_HOURS = 12;

/**
 * For this sample, geofences expire after twelve hours.
 */
public static final long GEOFENCE_EXPIRATION_IN_MILLISECONDS =
        GEOFENCE_EXPIRATION_IN_HOURS * 60 * 60 * 1000;
public static final float GEOFENCE_RADIUS_IN_METERS = 1609; // 1 mile, 1.6 km

/**
 * Map for storing information about airports in the San Francisco bay area.
 */
public static final HashMap<String, LatLng> BAY_AREA_LANDMARKS = new HashMap<String, LatLng>();
static {

    BAY_AREA_LANDMARKS.put("Home", new LatLng(29.382798, -98.529470));


    BAY_AREA_LANDMARKS.put("Other Home", new LatLng(29.472491,-98.571244));
}
}

1 个答案:

答案 0 :(得分:1)

以下是有关您的代码的一些建议:

  • 尝试使其更加模块化。根据您期望支持的功能将不同文件中的代码分开(例如,地图代码可以与位置分开等)。
  • 确保您的应用拥有最新版本的Google Play服务,并使用FusedLocationApi。
  • 确保地理围栏半径为100米。否则,不会触发输入退出代码。
  • 在清单文件中添加必要的权限,例如ACCESS_FINE_LOCATION,BroadCastreceiver(如果您使用的话)等。

有关代码实现,请参阅以下tutorial