【问题标题】:How to Create Geofence in an Android?如何在 Android 中创建地理围栏?
【发布时间】:2016-09-01 00:27:30
【问题描述】:

如何在当前纬度、经度上创建地理围栏(创建和监控地理围栏)。 我正在尝试多个示例但不创建。 使用此代码:

public Geofence geofence(float radius, double latitude, double longitude) {
    String id = UUID.randomUUID().toString();
    return new Geofence.Builder()
            .setRequestId(id)
            .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER)
            .setCircularRegion(latitude, longitude, radius)
            .setExpirationDuration(Geofence.NEVER_EXPIRE)
            .build();
}

【问题讨论】:

    标签: android-geofence


    【解决方案1】:

    Google 提供了这个教程,非常容易理解:

    http://io2015codelabs.appspot.com/codelabs/geofences

    Udacity 还有一门教授位置服务的课程,包括地理围栏:

    https://www.udacity.com/course/google-location-services-on-android--ud876-1

    将 Google Play 服务添加到 Gradle 文件:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:appcompat-v7:22.0.0'
        compile 'com.google.android.gms:play-services:7.3.0'
    }
    

    添加到清单文件:

    <meta-data
        android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
    

    添加到 Activity 的 XML 布局文件中:

    <Button
            android:id="@+id/add_geofences_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentLeft="true"
            android:onClick="addGeofencesButtonHandler"
            android:text="Add GeoFences" />
    

    添加到 Activity 的 Java 文件中:

    public class MainActivity extends Activity
    implements
    GoogleApiClient.ConnectionCallbacks, 
    GoogleApiClient.OnConnectionFailedListener, 
    ResultCallback<Status>{ 
    
     @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mAddGeofencesButton = (Button) findViewById(R.id.add_geofences_button);
            // Empty list for storing geofences.
            mGeofenceList = new ArrayList<Geofence>();
    
            // Get the geofences used. Geofence data is hard coded in this sample.
            populateGeofenceList();
    
            // Kick off the request to build GoogleApiClient.
            buildGoogleApiClient();
        }
    
    protected synchronized void buildGoogleApiClient() {
            mGoogleApiClient = new GoogleApiClient.Builder(this)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
        }
    
    public void populateGeofenceList() {
    for (Map.Entry<String, LatLng> entry : Constants.LANDMARKS.entrySet()) {
    mGeofenceList.add(new Geofence.Builder()
       .setRequestId(entry.getKey())
       .setCircularRegion(
       entry.getValue().latitude,
       entry.getValue().longitude,
       Constants.GEOFENCE_RADIUS_IN_METERS
       )
       .setExpirationDuration(Constants.GEOFENCE_EXPIRATION_IN_MILLISECONDS)
       .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER |
       Geofence.GEOFENCE_TRANSITION_EXIT)
       .build());
       }
    }
    
    @Override
    protected void onStart() {
        super.onStart();
        if (!mGoogleApiClient.isConnecting() || !mGoogleApiClient.isConnected()) {
            mGoogleApiClient.connect();
        }
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        if (mGoogleApiClient.isConnecting() || mGoogleApiClient.isConnected()) {
            mGoogleApiClient.disconnect();
        }
    }
    
    @Override
    public void onConnected(Bundle connectionHint) {
    
    }
    
    @Override
    public void onConnectionFailed(ConnectionResult result) {
        // Do something with result.getErrorCode());
    }
    
    @Override
    public void onConnectionSuspended(int cause) {
        mGoogleApiClient.connect();
    }
    
        public void addGeofencesButtonHandler(View view) {
                if (!mGoogleApiClient.isConnected()) {
                    Toast.makeText(this, "Google API Client not connected!", Toast.LENGTH_SHORT).show();
                    return;
                }
    
                try {
                    LocationServices.GeofencingApi.addGeofences(
                            mGoogleApiClient,
                            getGeofencingRequest(),
                            getGeofencePendingIntent()
                    ).setResultCallback(this); // Result processed in onResult().
                } catch (SecurityException securityException) {
                    // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
                }
        }
    
       private GeofencingRequest getGeofencingRequest() {
            GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
            builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
            builder.addGeofences(mGeofenceList);
            return builder.build();
        }
    
        private PendingIntent getGeofencePendingIntent() {
                Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
                // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling addgeoFences()
                return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            }
    
    public void onResult(Status status) {
            if (status.isSuccess()) {
                Toast.makeText(
                        this,
                        "Geofences Added",
                        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());
            }
        }
    

    创建一个名为 Constans 的 java 文件:

    public class Constants {
    
        public static final long GEOFENCE_EXPIRATION_IN_MILLISECONDS = 12 * 60 * 60 * 1000;
        public static final float GEOFENCE_RADIUS_IN_METERS = 20;
    
        public static final HashMap<String, LatLng> LANDMARKS = new     HashMap<String, LatLng>();
        static {
            // San Francisco International Airport.
            LANDMARKS.put("Moscone South", new LatLng(37.783888,-122.4009012));
    
            // Googleplex.
            LANDMARKS.put("Japantown", new LatLng(37.785281,-122.4296384));
    
            // Test
            LANDMARKS.put("SFO", new LatLng(37.621313,-122.378955));
        }
    }
    

    创建一个名为 GeofenceTransitionsIntentService 的 java 文件:

    public class GeofenceTransitionsIntentService extends IntentService {
      protected static final String TAG = "GeofenceTransitionsIS";
    
      public GeofenceTransitionsIntentService() {
        super(TAG);  // use TAG to name the IntentService worker thread
      }
    
      @Override
      protected void onHandleIntent(Intent intent) {
        GeofencingEvent event = GeofencingEvent.fromIntent(intent);
        if (event.hasError()) {
          Log.e(TAG, "GeofencingEvent Error: " + event.getErrorCode());
          return;
        }
      }
    
     String description = getGeofenceTransitionDetails(event);
       sendNotification(description);
     }
    
       private static String getGeofenceTransitionDetails(GeofencingEvent event) {
    String transitionString =
        GeofenceStatusCodes.getStatusCodeString(event.getGeofenceTransition());
    List triggeringIDs = new ArrayList();
    for (Geofence geofence : event.getTriggeringGeofences()) {
      triggeringIDs.add(geofence.getRequestId());
    }
    return String.format("%s: %s", transitionString, TextUtils.join(", ", triggeringIDs));
    }
    
      private void sendNotification(String notificationDetails) {
        // Create an explicit content Intent that starts MainActivity.
        Intent notificationIntent = new Intent(getApplicationContext(), MainActivity.class);
    
        // Get a PendingIntent containing the entire back stack.
        TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
        stackBuilder.addParentStack(MainActivity.class).addNextIntent(notificationIntent);
        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.setColor(Color.RED)
            .setContentTitle(notificationDetails)
            .setContentText("Click notification to return to App")
            .setContentIntent(notificationPendingIntent)
            .setAutoCancel(true);
    
        // Fire and notify the built Notification.
        NotificationManager notificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
        notificationManager.notify(0, builder.build());
      }
    

    您可能应该坚持他们的教程,而不是将代码复制并粘贴到您的项目中:D

    Source

    【讨论】:

    • 在onResult中,是否应该在顶部有@Override?我在 GeofenceErrorMessages.getErrorString(this, status.getStatusCode()); 中的 GeofenceErrorMessages 上显示红色文本,我找不到 GeoFenceErrorMessages 类
    • 您好,@Override 是必需的,因为它将覆盖 onResult 的默认方法实现。 GeoFenceErrorMessages 类是一个自定义类,用于获取地理围栏的错误 ID 并返回错误消息,这部分不是地理围栏工作所必需的,但这是一个很好的做法,更多信息可以在 Udacity 的本课程中找到:udacity.com/course/google-location-services-on-android--ud876-1
    • @MarcolaCarr 我们需要在 manifest 文件中的 application 标签下声明 GeofenceTransitionsIntentService 吗?
    【解决方案2】:

    我在服务中这样做

    地理位置服务

        public class GeolocationService extends Service implements LocationListener,
            GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks {
    
        private Context mContext;
        private GoogleApiClient mGoogleApiClient;
        private LocationRequest mLocationRequest;
    //    private Location mLastLocation;
        private PendingIntent mGeofencePendingIntent;
        private String mLastUpdateTime;
        public static boolean isGeoFenceAdded = false;
        private boolean mUpdateGeoFence = false;
        private boolean mRemoveAllGeoFence = false;
    
        private static final long TIME_OUT = 100;
    
        @Override
        public void onCreate() {
            super.onCreate();
    
            mContext = GeolocationService.this;
            buildGoogleApiClient();
            createLocationRequest();
    
        }
    
        protected synchronized void buildGoogleApiClient() {
            mGoogleApiClient = new GoogleApiClient.Builder(mContext)
                    .addConnectionCallbacks(this)
                    .addOnConnectionFailedListener(this)
                    .addApi(LocationServices.API)
                    .build();
            /// FIXME: 2/15/2017 connect should be handled through onStart and onStop of Activity
            mGoogleApiClient.connect();
        }
    
        protected void createLocationRequest() {
            mLocationRequest = new LocationRequest();
            mLocationRequest.setInterval(5000);//set the interval in which you want to get locations
            mLocationRequest.setFastestInterval(2500);//if a location is available sooner you can get it (i.e. another app is using the location services)
            mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        }
    
        @Override
        public int onStartCommand(Intent intent, int flags, int startId) {
            if (intent != null) {
                if (intent.getAction() != null) {
                    if (intent.getAction().equals(Constants.ACTION_UPDATE_GEOFENCE)) {
                        //// FIXME: 3/21/2017 you can also receive triggered location here..
                        mUpdateGeoFence = true;
                        isGeoFenceAdded = false;
                        mRemoveAllGeoFence = false;
                    } else if (intent.getAction().equals(Constants.ACTION_ADD_GEOFENCE)) {
                        mUpdateGeoFence = false;
                        isGeoFenceAdded = false;
                        mRemoveAllGeoFence = false;
                    } else if (intent.getAction().equals(Constants.ACTION_REMOVE_ALL_GEOFENCE)) {
                        mRemoveAllGeoFence = true;
                        isGeoFenceAdded = true;
                        mUpdateGeoFence = false;
                    }
                }
            }
            //try this for null as http://stackoverflow.com/a/25096022/3496570
            ///return START_REDELIVER_INTENT;
            return super.onStartCommand(intent, flags, startId);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    
        @Override
        public void onConnected(@Nullable Bundle bundle) {
            startLocationUpdates(mContext);
        }
    
        @Override
        public void onConnectionSuspended(int i) {
            switch (i) {
                case CAUSE_SERVICE_DISCONNECTED:
                    /*if (onLocationUpdateListener != null)
                        onLocationUpdateListener.onError(
                                Constants.ErrorType.SERVICE_DISCONNECTED);*/
                    break;
                case CAUSE_NETWORK_LOST:
                    /*if (onLocationUpdateListener != null)
                        onLocationUpdateListener.onError(
                                Constants.ErrorType.NETWORK_LOST);*/
                    break;
            }
    
            //// FIXME: 3/2/2017 check is it right to check for re Connecting..
            //---  http://stackoverflow.com/a/27350444/3496570
            ///mGoogleApiClient.connect();
        }
    
        @Override
        public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
            /*if (onLocationUpdateListener != null)
                onLocationUpdateListener.onError(
                        Constants.ErrorType.CONNECTION_FAIL);*/
    
            //// FIXME: 3/3/2017 call a transparent activity and call startResolutionForresult from their and return result to service using action
            if (connectionResult.hasResolution()) {
                /*try {
                    // !!!
                    connectionResult.startResolutionForResult(this, REQUEST_CODE_RESOLVE_ERR);
                } catch (IntentSender.SendIntentException e) {
                    e.printStackTrace();
                }*/
            } else {
                /*GoogleApiAvailability.getInstance().getErrorDialog(mContext, connectionResult.getErrorCode(), 0).show();
                return;*/
            }
        }
    
        @Override
        public void onLocationChanged(final Location currentLocation) {
            setupGeoFencePoints(currentLocation);
            /*if (onLocationUpdateListener != null && mLocation != null) {
                onLocationUpdateListener.onLocationChange(mLocation);
            }*/
        }
    
        private void setupGeoFencePoints(final Location currentLocation) {
    
            mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
    //        mLastLocation = currentLocation;
    
            if (currentLocation != null && isGeoFenceAdded == false)
            {
                if (mUpdateGeoFence) {
                    if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
                        LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient
                                , getGeofencePendingIntent()).setResultCallback(new ResultCallback<Status>() {
                            @Override
                            public void onResult(@NonNull Status status) {
                                if (status.isSuccess()) {
                                    //if old geoFence's remove successfully then add new ones.
                                    addGeoFences(currentLocation);
                                }
                            }
                        });
                    }
                } else {
                    addGeoFences(currentLocation);
                }
            }
            else if(isGeoFenceAdded && mRemoveAllGeoFence ){
                if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
                {
                    LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient
                            , mGeofencePendingIntent).setResultCallback(new ResultCallback<Status>() {
                        @Override
                        public void onResult(@NonNull Status status) {
                            if (status.isSuccess()) {
    
                                mRemoveAllGeoFence = false;
                                isGeoFenceAdded = false;
                                //if old geoFence's remove successfully then do nothing.
                                stopLocationUpdate();
    
                                if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
                                    mGoogleApiClient.disconnect();
                            }
                        }
                    });
                }
            }
        }
    
        @Override
        public void onDestroy() {
            super.onDestroy();
            stopLocationUpdate();
    
            if (mGoogleApiClient != null && mGoogleApiClient.isConnected())
                mGoogleApiClient.disconnect();
        }
    
        private void startLocationUpdates(final Context mContext) {
    
            if (ActivityCompat.checkSelfPermission(mContext,
                    Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
                    && ActivityCompat.checkSelfPermission(mContext,
                    Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                return;
            }
    
    //        mLastLocation = FusedLocationApi.getLastLocation(mGoogleApiClient);
    
            PendingResult<Status> pendingResult = FusedLocationApi.requestLocationUpdates(
                    mGoogleApiClient, mLocationRequest, this);
            pendingResult.setResultCallback(new ResultCallback<Status>() {
                @Override
                public void onResult(@NonNull Status status) {
                    //update it's code too.
                    if (status.isSuccess()) {
                        Toast.makeText(mContext, "location Update Started",
                                Toast.LENGTH_SHORT).show();
                    } else if (status.hasResolution()) {
                        Toast.makeText(mContext, "Open intent to resolve",
                                Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    
        private void stopLocationUpdate() {
            //three types of constructor ..
            if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
                FusedLocationApi.removeLocationUpdates(
                        mGoogleApiClient, this);
            }
        }
    
        public void addGeoFences(Location currentLocation) {
            if (mGoogleApiClient != null && mGoogleApiClient.isConnected()) {
                try {
    
                    if (getGeofencingRequest(currentLocation) != null) {
                        LocationServices.GeofencingApi.addGeofences(
                                mGoogleApiClient,
                                // The GeofenceRequest object.
                                getGeofencingRequest(currentLocation),
                                // 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()
                                //).await(TimeOut,TimeUnit.Miilisecponds);
                        ).setResultCallback(new ResultCallback<Status>() {
                            @Override
                            public void onResult(@NonNull Status status) {
                                if (status.isSuccess()) {
                                    Toast.makeText(mContext, "Geo Fence Added", Toast.LENGTH_SHORT).show();
    
                                    isGeoFenceAdded = true;
                                    mRemoveAllGeoFence = false;
                                    mUpdateGeoFence = false;
    
                                    /// FIXME: 3/2/2017 I didn't have to draw it.
                                    ///broadcastDrawGeoFenceOnMap();
    
                                } else {
                                    String errorMessage = getErrorString(mContext, status.getStatusCode());
                                    Toast.makeText(mContext, "Status Failed", Toast.LENGTH_SHORT).show();
                                }
                            }
                        }); // Result processed in onResult().
                    }
    
                } catch (SecurityException securityException) {
                    securityException.printStackTrace();
                    // Catch exception generated if the app does not use ACCESS_FINE_LOCATION permission.
                    //logSecurityException(securityException);
                }
            }
        }
    
        private PendingIntent getGeofencePendingIntent() {
            if (mGeofencePendingIntent != null) {
                return mGeofencePendingIntent;
            }
            // Reuse the PendingIntent if we already have it.
            /// FIXME: 2/9/2017 Update the below class with a receiever..
            Intent intent = new Intent(mContext, GeofenceReceiver.class);///GeofenceTransitionsIntentService.class);
            // We use FLAG_UPDATE_CURRENT so that we get the same pending intent back when calling
            // addGeofences() and removeGeofences().
            /// FIXME: 3/1/2017 It must be reciever not IntentService
            mGeofencePendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
            return mGeofencePendingIntent;
        }
    
        private GeofencingRequest getGeofencingRequest(Location mCurrentLocation) {
    
            /// FIXME: 2/13/2017 mLastLocation can be null because it will take time for the first time.
            /// this request should be called after first mLastLocation has been fetched..
            GeofencingRequest geofencingRequest = null;
            if (mCurrentLocation != null) {
                List<SimpleGeofence> simpleFenceList = SimpleGeofenceStore
                        .getInstance().getLatestGeoFences(mCurrentLocation);
    
                simpleFenceList.add(new SimpleGeofence("currentLocation",
                        mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude(),
                        100f, GEOFENCE_EXPIRATION_IN_MILLISECONDS,
                        Geofence.GEOFENCE_TRANSITION_EXIT));
    
                ListSharedPref.saveAnyTypeOfList(ListSharedPref.GEO_FENCE_LIST_KEY, simpleFenceList);
    
                GeofencingRequest.Builder geofencingRequestBuilder = new GeofencingRequest.Builder();
                geofencingRequestBuilder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    
                for (SimpleGeofence simpleGeofence : simpleFenceList)
                    geofencingRequestBuilder.addGeofence(simpleGeofence.toGeofence());
    
                geofencingRequest = geofencingRequestBuilder.build();
            }
            // Return a GeofencingRequest.
            return geofencingRequest;
        }
    }
    

    地理围栏接收器

    public class GeofenceReceiver extends IntentService {
        public static final int NOTIFICATION_ID = 1;
    
        public GeofenceReceiver() {
            super("GeofenceReceiver");
        }
    
        @Override
        protected void onHandleIntent(Intent intent) {
            GeofencingEvent geoEvent = GeofencingEvent.fromIntent(intent);
    
            Location triggredLocation = geoEvent.getTriggeringLocation();
    
            if (geoEvent.hasError()) {
                Log.d(HomeActivity.TAG, "Error GeofenceReceiver.onHandleIntent");
            } else {
                Log.d(HomeActivity.TAG, "GeofenceReceiver : Transition -> "
                        + geoEvent.getGeofenceTransition());
    
                int transitionType = geoEvent.getGeofenceTransition();
    
                if (transitionType == Geofence.GEOFENCE_TRANSITION_ENTER
                        || transitionType == Geofence.GEOFENCE_TRANSITION_DWELL
                        || transitionType == Geofence.GEOFENCE_TRANSITION_EXIT) {
                    List<Geofence> triggerList = geoEvent.getTriggeringGeofences();
    
                    //if(triggerList.g)
    
    
                    Type listType = new TypeToken<ArrayList<SimpleGeofence>>(){}.getType();
                    List<SimpleGeofence> geoFenceList = GenericPref.readAnyTypeOfList(GenericPref.GEO_FENCE_LIST_KEY,listType);
    
                    for (Geofence geofence : triggerList)
                    {
                        /*SimpleGeofence sg = SimpleGeofenceStore.getInstance()
                                .getSimpleGeofences().get(geofence.getRequestId());*/
    
                        SimpleGeofence sg = null;
                        for(SimpleGeofence simpleGeofence : geoFenceList){
                            if(simpleGeofence.getId().equalsIgnoreCase(geofence.getRequestId())){
                                sg = simpleGeofence;
                                break;
                            }
                        }
    
                        String transitionName = "";
                        switch (transitionType) {
                            case Geofence.GEOFENCE_TRANSITION_DWELL:
                                transitionName = "dwell";
                                break;
    
                            case Geofence.GEOFENCE_TRANSITION_ENTER:
                                transitionName = "enter";
    
                                String date = DateFormat.format("yyyy-MM-dd hh:mm:ss",
                                        new Date()).toString();
                                EventDataSource eds = new EventDataSource(
                                        getApplicationContext());
                                eds.create(transitionName, date, geofence.getRequestId());
                                eds.close();
    
                                GeofenceNotification geofenceNotification = new GeofenceNotification(
                                        this);
                                if(sg != null){
                                    geofenceNotification
                                            .displayNotification(sg, transitionType);
                                }
                                break;
    
                            case Geofence.GEOFENCE_TRANSITION_EXIT:
                                transitionName = "exit";
                                broadcastUpdateGeoFences();
                                //update your List
                                // Unregister all geoFences and reRegister it again
                                break;
                        }
                    }
                }
            }
        }
    
        public void broadcastUpdateGeoFences() {
            //// FIXME: 3/2/2017 what if app is closed
            HomeActivity.geofencesAlreadyRegistered = false;
            MainActivity.isGeoFenceAdded = false;
            Intent intent = new Intent(Constants.RECEIVER_GEOFENCE);
            intent.putExtra("done", 1);
            sendBroadcast(intent);
        }
    }
    

    【讨论】:

      【解决方案3】:

      我的要求是当我点击地图时,然后在那个位置,我正在创建地理围栏圈。并在我们当前的位置进入该栅栏以及退出该栅栏时显示通知。

      MapsActivity.java

      import android.Manifest;
      import android.annotation.SuppressLint;
      import android.app.AlertDialog;
      import android.app.PendingIntent;
      import android.content.Context;
      import android.content.DialogInterface;
      import android.content.Intent;
      import android.content.pm.PackageManager;
      import android.graphics.Color;
      import android.location.Location;
      
      import com.google.android.gms.common.api.ResultCallback;
      import com.google.android.gms.common.api.Status;
      import com.google.android.gms.location.Geofence;
      import com.google.android.gms.location.GeofencingRequest;
      import com.google.android.gms.location.LocationListener;
      
      import android.os.Build;
      import android.support.annotation.NonNull;
      import android.support.v4.app.ActivityCompat;
      import android.support.v4.app.FragmentActivity;
      import android.os.Bundle;
      import android.support.v4.content.ContextCompat;
      import android.util.Log;
      import android.view.MenuItem;
      import android.widget.Toast;
      
      import com.google.android.gms.common.ConnectionResult;
      import com.google.android.gms.common.api.GoogleApiClient;
      import com.google.android.gms.location.LocationRequest;
      import com.google.android.gms.location.LocationServices;
      import com.google.android.gms.maps.CameraUpdateFactory;
      import com.google.android.gms.maps.GoogleMap;
      import com.google.android.gms.maps.OnMapReadyCallback;
      import com.google.android.gms.maps.SupportMapFragment;
      import com.google.android.gms.maps.model.BitmapDescriptorFactory;
      import com.google.android.gms.maps.model.Circle;
      import com.google.android.gms.maps.model.CircleOptions;
      import com.google.android.gms.maps.model.LatLng;
      import com.google.android.gms.maps.model.Marker;
      import com.google.android.gms.maps.model.MarkerOptions;
      
      public class MapsActivity extends FragmentActivity implements OnMapReadyCallback,
              GoogleApiClient.ConnectionCallbacks,
              GoogleApiClient.OnConnectionFailedListener,
              LocationListener,
              GoogleMap.OnMapClickListener, ResultCallback<Status> {
      
          private GoogleMap mMap;
          SupportMapFragment mapFragment;
          LocationRequest mLocationRequest;
          GoogleApiClient mGoogleApiClient;
          Location mLastLocation;
          Marker mCurrLocationMarker;
      
          @Override
          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              setContentView(R.layout.activity_maps);
              // Obtain the SupportMapFragment and get notified when the map is ready to be used.
              mapFragment = (SupportMapFragment) getSupportFragmentManager()
                      .findFragmentById(R.id.map);
              mapFragment.getMapAsync(this);
          }
      
      
          /**
           * Manipulates the map once available.
           * This callback is triggered when the map is ready to be used.
           * This is where we can add markers or lines, add listeners or move the camera. In this case,
           * we just add a marker near Sydney, Australia.
           * If Google Play services is not installed on the device, the user will be prompted to install
           * it inside the SupportMapFragment. This method will only be triggered once the user has
           * installed Google Play services and returned to the app.
           */
          @Override
          public void onMapReady(GoogleMap googleMap) {
              mMap = googleMap;
      
              mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
      
              mMap.setOnMapClickListener(this);
      
              //Initialize Google Play Services
              if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
                  if (ContextCompat.checkSelfPermission(this,
                          Manifest.permission.ACCESS_FINE_LOCATION)
                          == PackageManager.PERMISSION_GRANTED) {
                      //Location Permission already granted
                      buildGoogleApiClient();
                      mMap.setMyLocationEnabled(true);
                  } else {
                      //Request Location Permission
                      checkLocationPermission();
                  }
              } else {
                  buildGoogleApiClient();
                  mMap.setMyLocationEnabled(true);
              }
          }
      
          protected synchronized void buildGoogleApiClient() {
              mGoogleApiClient = new GoogleApiClient.Builder(this)
                      .addConnectionCallbacks(this)
                      .addOnConnectionFailedListener(this)
                      .addApi(LocationServices.API)
                      .build();
              mGoogleApiClient.connect();
          }
      
          @Override
          public void onConnected(Bundle bundle) {
              mLocationRequest = new LocationRequest();
              mLocationRequest.setInterval(1000);
              mLocationRequest.setFastestInterval(1000);
              mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
              if (ContextCompat.checkSelfPermission(this,
                      Manifest.permission.ACCESS_FINE_LOCATION)
                      == PackageManager.PERMISSION_GRANTED) {
                  LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
              }
          }
      
          @Override
          public void onConnectionSuspended(int i) {
          }
      
      
          @Override
          public void onLocationChanged(Location location) {
              mLastLocation = location;
              if (mCurrLocationMarker != null) {
                  mCurrLocationMarker.remove();
              }
      
              //Place current location marker
              LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
              MarkerOptions markerOptions = new MarkerOptions();
              markerOptions.position(latLng);
              markerOptions.title("Current Position");
              markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA));
              mCurrLocationMarker = mMap.addMarker(markerOptions);
      
              //move map camera
              mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 15));
      
              Location targetLocation = new Location("");//provider name is unnecessary
              targetLocation.setLatitude(18.569557d);//your coords of course
              targetLocation.setLongitude(73.879369d);
      
             // This is also working
      
             /* if (location.distanceTo(targetLocation) < 100) {
                  // bingo!
                  Toast.makeText(this, "You are at MplusSoft Technology", Toast.LENGTH_SHORT).show();
              } else {
                  Toast.makeText(this, "You are not at your place.", Toast.LENGTH_SHORT).show();
      
              }*/
      
          }
      
      
          public static final int MY_PERMISSIONS_REQUEST_LOCATION = 99;
      
          private void checkLocationPermission() {
              if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
                      != PackageManager.PERMISSION_GRANTED) {
      
                  // Should we show an explanation?
                  if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                          Manifest.permission.ACCESS_FINE_LOCATION)) {
      
                      // Show an explanation to the user *asynchronously* -- don't block
                      // this thread waiting for the user's response! After the user
                      // sees the explanation, try again to request the permission.
                      new AlertDialog.Builder(this)
                              .setTitle("Location Permission Needed")
                              .setMessage("This app needs the Location permission, please accept to use location functionality")
                              .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                                  @Override
                                  public void onClick(DialogInterface dialogInterface, int i) {
                                      //Prompt the user once explanation has been shown
                                      ActivityCompat.requestPermissions(MapsActivity.this,
                                              new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                                              MY_PERMISSIONS_REQUEST_LOCATION);
                                  }
                              })
                              .create()
                              .show();
      
      
                  } else {
                      // No explanation needed, we can request the permission.
                      ActivityCompat.requestPermissions(this,
                              new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
                              MY_PERMISSIONS_REQUEST_LOCATION);
                  }
              }
          }
      
          @Override
          public void onRequestPermissionsResult(int requestCode,
                                                 String permissions[], int[] grantResults) {
              switch (requestCode) {
                  case MY_PERMISSIONS_REQUEST_LOCATION: {
                      // If request is cancelled, the result arrays are empty.
                      if (grantResults.length > 0
                              && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
      
                          // permission was granted, yay! Do the
                          // location-related task you need to do.
                          if (ContextCompat.checkSelfPermission(this,
                                  Manifest.permission.ACCESS_FINE_LOCATION)
                                  == PackageManager.PERMISSION_GRANTED) {
      
                              if (mGoogleApiClient == null) {
                                  buildGoogleApiClient();
                              }
                              mMap.setMyLocationEnabled(true);
                          }
      
                      } else {
      
                          // permission denied, boo! Disable the
                          // functionality that depends on this permission.
                          Toast.makeText(this, "permission denied", Toast.LENGTH_LONG).show();
                      }
                      return;
                  }
      
                  // other 'case' lines to check for other
                  // permissions this app might request
              }
          }
      
          @Override
          public void onPause() {
              super.onPause();
      
              //stop location updates when Activity is no longer active
              if (mGoogleApiClient != null) {
                  LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, (com.google.android.gms.location.LocationListener) this);
              }
          }
      
      
          @Override
          public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
      
          }
      
      
          //------------------------------------- Geo fencing -------------------------------------
      
      
          @Override
          protected void onResume() {
              super.onResume();
      
          }
      
          @Override
          public void onResult(@NonNull Status status) {
              Log.e("onResult: ", "onResult: " + status);
              if (status.isSuccess()) {
                  drawGeofence();
              } else {
                  // inform about fail
              }
          }
      
      
          @Override
          public void onMapClick(LatLng latLng) {
              markerForGeofence(latLng);
          }
      
          private Marker geoFenceMarker;
      
          // Create a marker for the geofence creation
          private void markerForGeofence(LatLng latLng) {
              Log.e("markerForGeofence", "markerForGeofence(" + latLng + ")");
              String title = latLng.latitude + ", " + latLng.longitude;
              // Define marker options
              MarkerOptions markerOptions = new MarkerOptions()
                      .position(latLng)
                      .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
                      .title(title);
              if (mMap != null) {
                  // Remove last geoFenceMarker
                  if (geoFenceMarker != null)
                      geoFenceMarker.remove();
      
                  geoFenceMarker = mMap.addMarker(markerOptions);
      
                  startGeofence();
              }
          }
      
      
          private static final long GEO_DURATION = 60 * 60 * 1000;
          private static final String GEOFENCE_REQ_ID = "My Geofence";
          private static final float GEOFENCE_RADIUS = 50.0f; // in meters
      
          // Create a Geofence
          private Geofence createGeofence(LatLng latLng, float radius) {
              Log.e("createGeofence", "createGeofence");
              return new Geofence.Builder()
                      .setRequestId(GEOFENCE_REQ_ID)
                      .setCircularRegion(latLng.latitude, latLng.longitude, radius)
                      .setExpirationDuration(GEO_DURATION)
                      .setTransitionTypes(Geofence.GEOFENCE_TRANSITION_ENTER
                              | Geofence.GEOFENCE_TRANSITION_EXIT)
                      .build();
          }
      
          // Create a Geofence Request
          private GeofencingRequest createGeofenceRequest(Geofence geofence) {
              Log.e("createGeofenceRequest", "createGeofenceRequest");
              return new GeofencingRequest.Builder()
                      .setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER)
                      .addGeofence(geofence)
                      .build();
          }
      
          private PendingIntent geoFencePendingIntent;
          private final int GEOFENCE_REQ_CODE = 0;
      
          @SuppressLint("LongLogTag")
          private PendingIntent createGeofencePendingIntent() {
              Log.e("createGeofencePendingIntent", "createGeofencePendingIntent");
              if (geoFencePendingIntent != null)
                  return geoFencePendingIntent;
      
              Intent intent = new Intent(this, GeofenceTrasitionService.class);
              return PendingIntent.getService(
                      this, GEOFENCE_REQ_CODE, intent, PendingIntent.FLAG_UPDATE_CURRENT);
          }
      
          // Add the created GeofenceRequest to the device's monitoring list
          private void addGeofence(GeofencingRequest request) {
              Log.e("addGeofence", "addGeofence");
              //if (checkPermission())
              if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                  // TODO: Consider calling
                  //    ActivityCompat#requestPermissions
                  // here to request the missing permissions, and then overriding
                  //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                  //                                          int[] grantResults)
                  // to handle the case where the user grants the permission. See the documentation
                  // for ActivityCompat#requestPermissions for more details.
                  return;
              }
              LocationServices.GeofencingApi.addGeofences(
                      mGoogleApiClient,
                      request,
                      createGeofencePendingIntent()
              ).setResultCallback(this);
          }
      
      
      
          // Draw Geofence circle on GoogleMap
          private Circle geoFenceLimits;
          private void drawGeofence() {
              Log.e("drawGeofence()", "drawGeofence()");
      
              if ( geoFenceLimits != null )
                  geoFenceLimits.remove();
      
              CircleOptions circleOptions = new CircleOptions()
                      .center( geoFenceMarker.getPosition())
                      .strokeColor(Color.argb(50, 70,70,70))
                      .fillColor( Color.argb(100, 150,150,150) )
                      .radius( GEOFENCE_RADIUS );
              geoFenceLimits = mMap.addCircle( circleOptions );
          }
      
      
      
          // Start Geofence creation process
          private void startGeofence() {
              Log.e("startGeofence", "startGeofence()");
              if( geoFenceMarker != null ) {
                  Geofence geofence = createGeofence( geoFenceMarker.getPosition(), GEOFENCE_RADIUS );
                  GeofencingRequest geofenceRequest = createGeofenceRequest( geofence );
                  addGeofence( geofenceRequest );
              } else {
                  Log.e("Geofence marker is null", "Geofence marker is null");
              }
          }
      
          static Intent makeNotificationIntent(Context geofenceService, String msg)
          {
              Log.e("makeNotificationIntent ",msg);
              return new Intent(geofenceService,MainActivity.class);
          }
      
      }
      

      GeofenceTrasitionService.java

      import android.app.IntentService;
      import android.app.Notification;
      import android.app.NotificationManager;
      import android.app.PendingIntent;
      import android.app.TaskStackBuilder;
      import android.content.Context;
      import android.content.Intent;
      import android.graphics.Color;
      import android.support.v4.app.NotificationCompat;
      import android.text.TextUtils;
      import android.util.Log;
      
      import com.google.android.gms.location.Geofence;
      import com.google.android.gms.location.GeofenceStatusCodes;
      import com.google.android.gms.location.GeofencingEvent;
      
      import java.util.ArrayList;
      import java.util.List;
      
      public class GeofenceTrasitionService extends IntentService {
      
          private static final String TAG = GeofenceTrasitionService.class.getSimpleName();
          public static final int GEOFENCE_NOTIFICATION_ID = 0;
      
          public GeofenceTrasitionService() {
              super(TAG);
          }
      
          @Override
          protected void onHandleIntent(Intent intent) {
              // Retrieve the Geofencing intent
              GeofencingEvent geofencingEvent = GeofencingEvent.fromIntent(intent);
      
              // Handling errors
              if ( geofencingEvent.hasError() ) {
                  String errorMsg = getErrorString(geofencingEvent.getErrorCode() );
                  Log.e( TAG, errorMsg );
                  return;
              }
      
              // Retrieve GeofenceTrasition
              int geoFenceTransition = geofencingEvent.getGeofenceTransition();
              // Check if the transition type
              if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER ||
                      geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT ) {
                  // Get the geofence that were triggered
                  List<Geofence> triggeringGeofences = geofencingEvent.getTriggeringGeofences();
                  // Create a detail message with Geofences received
                  String geofenceTransitionDetails = getGeofenceTrasitionDetails(geoFenceTransition, triggeringGeofences );
                  // Send notification details as a String
                  sendNotification( geofenceTransitionDetails );
              }
          }
      
          // Create a detail message with Geofences received
          private String getGeofenceTrasitionDetails(int geoFenceTransition, List<Geofence> triggeringGeofences) {
              // get the ID of each geofence triggered
              ArrayList<String> triggeringGeofencesList = new ArrayList<>();
              for ( Geofence geofence : triggeringGeofences ) {
                  triggeringGeofencesList.add( geofence.getRequestId() );
              }
      
              String status = null;
              if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_ENTER )
                  status = "Entering ";
              else if ( geoFenceTransition == Geofence.GEOFENCE_TRANSITION_EXIT )
                  status = "Exiting ";
              return status + TextUtils.join( ", ", triggeringGeofencesList);
          }
      
          // Send a notification
          private void sendNotification( String msg ) {
              Log.i(TAG, "sendNotification: " + msg );
      
              // Intent to start the main Activity
              Intent notificationIntent = MapsActivity.makeNotificationIntent(
                      getApplicationContext(), msg
              );
      
              TaskStackBuilder stackBuilder = TaskStackBuilder.create(this);
              stackBuilder.addParentStack(MapsActivity.class);
              stackBuilder.addNextIntent(notificationIntent);
              PendingIntent notificationPendingIntent = stackBuilder.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
      
              // Creating and sending Notification
              NotificationManager notificatioMng =
                      (NotificationManager) getSystemService( Context.NOTIFICATION_SERVICE );
              notificatioMng.notify(
                      GEOFENCE_NOTIFICATION_ID,
                      createNotification(msg, notificationPendingIntent));
          }
      
          // Create a notification
          private Notification createNotification(String msg, PendingIntent notificationPendingIntent) {
              NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this);
              notificationBuilder
                      .setSmallIcon(R.drawable.ic_launcher_background)
                      .setColor(Color.RED)
                      .setContentTitle(msg)
                      .setContentText("Geofence Notification!")
                      .setContentIntent(notificationPendingIntent)
                      .setDefaults(Notification.DEFAULT_LIGHTS | Notification.DEFAULT_VIBRATE | Notification.DEFAULT_SOUND)
                      .setAutoCancel(true);
              return notificationBuilder.build();
          }
      
          // Handle errors
          private static String getErrorString(int errorCode) {
              switch (errorCode) {
                  case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
                      return "GeoFence not available";
                  case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
                      return "Too many GeoFences";
                  case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
                      return "Too many pending intents";
                  default:
                      return "Unknown error.";
              }
          }
      }
      

      activity_maps.xml

          <?xml version="1.0" encoding="utf-8"?>
      <fragment xmlns:android="http://schemas.android.com/apk/res/android"
          xmlns:map="http://schemas.android.com/apk/res-auto"
          xmlns:tools="http://schemas.android.com/tools"
          android:id="@+id/map"
          android:name="com.google.android.gms.maps.SupportMapFragment"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          tools:context=".MapsActivity" />
      

      清单

        <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      
      
        <meta-data
                  android:name="com.google.android.geo.API_KEY"
                  android:value="@string/google_maps_key" />
      
              <activity
                  android:name=".MapsActivity"
                  android:label="@string/title_activity_maps"> <intent-filter>
                  <action android:name="android.intent.action.MAIN" />
      
                  <category android:name="android.intent.category.LAUNCHER" />
              </intent-filter></activity>
      
              <service android:name=".GeofenceTrasitionService"/>
      

      build.gradle

      implementation 'com.android.support:appcompat-v7:28.0.0-rc01'
      implementation 'com.google.android.gms:play-services-maps:15.0.1'
      implementation 'com.google.android.gms:play-services-location:15.0.1'
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-11-07
        • 1970-01-01
        • 2016-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-29
        相关资源
        最近更新 更多