Java tutorial
/* * Copyright 2016 Alejandro Alcalde * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package elbauldelprogramador.com.gpsqr; import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.location.LocationListener; import com.google.android.gms.location.LocationRequest; import com.google.android.gms.location.LocationServices; import com.google.android.gms.maps.model.LatLng; import android.Manifest; import android.app.Service; import android.content.Intent; import android.content.pm.PackageManager; import android.location.Location; import android.os.Bundle; import android.os.IBinder; import android.support.annotation.Nullable; import android.support.v4.app.ActivityCompat; import android.support.v4.content.LocalBroadcastManager; import android.util.Log; import java.text.DateFormat; import java.util.Date; /** * Created by: * * Alejandro Alcalde (elbauldelprogramador.com) * Cristina Heredia * * on 2/9/16. * * This file is part of GPSQR * * This class is a service which intent is to update at a regular interval the location of * the user. */ public class LocationUpdaterService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener { /** * The desired interval for location updates. Inexact. Updates may be more or less frequent. */ public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000; /** * The fastest rate for active location updates. Exact. Updates will never be more frequent * than this value. */ public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = UPDATE_INTERVAL_IN_MILLISECONDS / 2; public static final String COPA_RESULT = "elbauldelprogramador.com.gpsqr.REQUEST_PROCESSED"; public static final String COPA_MESSAGE = "elbauldelprogramador.com.gpsqr.COPA_MSG"; protected static final String TAG = LocationUpdaterService.class.getSimpleName(); /** * Provides the entry point to Google Play services. */ protected GoogleApiClient mGoogleApiClient; /** * Stores parameters for requests to the FusedLocationProviderApi. */ protected LocationRequest mLocationRequest; /** * Represents a geographical location. */ protected Location mCurrentLocation; /** * Time when the location was updated represented as a String. */ protected String mLastUpdateTime; /** * For notifying the UI when new locations arrive */ private LocalBroadcastManager mBroadcaster; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); // Kick off the process of building a GoogleApiClient and requesting the LocationServices // API. buildGoogleApiClient(); mGoogleApiClient.connect(); mBroadcaster = LocalBroadcastManager.getInstance(this); } /** * The system calls this method when the service is no longer used and is being destroyed. Your * service should implement this to clean up any resources such as threads, registered * listeners, receivers, etc. This is the last call the service receives. */ @Override public void onDestroy() { if (BuildConfig.DEBUG) { Log.d(TAG, "Stopping Service"); } stopLocationUpdates(); if (mGoogleApiClient != null) { mGoogleApiClient.unregisterConnectionCallbacks(this); mGoogleApiClient.unregisterConnectionFailedListener(this); mGoogleApiClient.disconnect(); } super.onDestroy(); } /** * Called by the system every time a client explicitly starts the service by calling * {@link android.content.Context#startService}, providing the arguments it supplied and a * unique integer token representing the start request. Do not call this method directly. * * <p>For backwards compatibility, the default implementation calls * {@link #onStart} and returns either {@link #START_STICKY} * or {@link #START_STICKY_COMPATIBILITY}. * * <p>If you need your application to run on platform versions prior to API * level 5, you can use the following model to handle the older {@link #onStart} * callback in that case. The <code>handleCommand</code> method is implemented by * you as appropriate: * * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/ForegroundService.java * start_compatibility} * * <p class="caution">Note that the system calls this on your * service's main thread. A service's main thread is the same * thread where UI operations take place for Activities running in the * same process. You should always avoid stalling the main * thread's event loop. When doing long-running operations, * network calls, or heavy disk I/O, you should kick off a new * thread, or use {@link android.os.AsyncTask}.</p> * * @param intent The Intent supplied to {@link android.content.Context#startService}, * as given. This may be null if the service is being restarted after * its process has gone away, and it had previously returned anything * except {@link #START_STICKY_COMPATIBILITY}. * @param flags Additional data about this start request. Currently either * 0, {@link #START_FLAG_REDELIVERY}, or {@link #START_FLAG_RETRY}. * @param startId A unique integer representing this specific request to * start. Use with {@link #stopSelfResult(int)}. * * @return The return value indicates what semantics the system should * use for the service's current started state. It may be one of the * constants associated with the {@link #START_CONTINUATION_MASK} bits. * * @see #stopSelfResult(int) */ @Override public int onStartCommand(Intent intent, int flags, int startId) { super.onStartCommand(intent, flags, startId); if (BuildConfig.DEBUG) { Log.d(TAG, "OnStartCommand"); } if (mGoogleApiClient.isConnected() /*&& mRequestingLocationUpdates*/) { // TODO startLocationUpdates(); } // TODO: Call it here, or stopService from Activity stopSelf(); return START_STICKY; } /** * Builds a GoogleApiClient. Uses the {@code #addApi} method to request the * LocationServices API. */ protected synchronized void buildGoogleApiClient() { if (BuildConfig.DEBUG) { Log.d(TAG, "Building GoogleApiClient"); } mGoogleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this) .addOnConnectionFailedListener(this).addApi(LocationServices.API).build(); createLocationRequest(); } /** * Sets up the location request. Android has two location request settings: * {@code ACCESS_COARSE_LOCATION} and {@code ACCESS_FINE_LOCATION}. These settings control * the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in * the AndroidManifest.xml. * <p/> * When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update * interval (5 seconds), the Fused Location Provider API returns location updates that are * accurate to within a few feet. * <p/> * These settings are appropriate for mapping applications that show real-time location * updates. */ protected void createLocationRequest() { mLocationRequest = new LocationRequest(); // Sets the desired interval for active location updates. This interval is // inexact. You may not receive updates at all if no location sources are available, or // you may receive them slower than requested. You may also receive updates faster than // requested if other applications are requesting location at a faster interval. mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS); // Sets the fastest rate for active location updates. This interval is exact, and your // application will never receive updates faster than this value. mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS); mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY); } /** * Requests location updates from the FusedLocationApi. */ protected void startLocationUpdates() { // The final argument to {@code requestLocationUpdates()} is a LocationListener // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this); } /** * Removes location updates from the FusedLocationApi. */ protected void stopLocationUpdates() { // It is a good practice to remove location requests when the activity is in a paused or // stopped state. Doing so helps battery performance and is especially // recommended in applications that request frequent location updates. // The final argument to {@code requestLocationUpdates()} is a LocationListener // (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html). LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this); } /** * This method send the current location to the activity who called the service, This way the * location can be used in the UI. * * @param message The location */ private void sendResult(LatLng message) { Intent intent = new Intent(COPA_RESULT); if (message != null) intent.putExtra(COPA_MESSAGE, message); mBroadcaster.sendBroadcast(intent); } @Override public void onConnected(@Nullable Bundle bundle) { if (BuildConfig.DEBUG) { Log.d(TAG, "Connected to GoogleApiClient"); } // If the initial location was never previously requested, we use // FusedLocationApi.getLastLocation() to get it. If it was previously requested, we store // its value in the Bundle and check for it in onCreate(). We // do not request it again unless the user specifically requests location updates by pressing // the Start Updates button. // // Because we cache the value of the initial location in the Bundle, it means that if the // user launches the activity, // moves to a new location, and then changes the device orientation, the original location // is displayed as the activity is re-created. if (mCurrentLocation == null) { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_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; } mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); if (mCurrentLocation != null) { sendResult(new LatLng(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude())); if (BuildConfig.DEBUG) { Log.d(TAG, mCurrentLocation.getLatitude() + ", " + mCurrentLocation.getLongitude()); } } } // If the user presses the Start Updates button before GoogleApiClient connects, we set // mRequestingLocationUpdates to true (see startUpdatesButtonHandler()). Here, we check // the value of mRequestingLocationUpdates and if it is true, we start location updates. // if (mRequestingLocationUpdates) { startLocationUpdates(); // } } @Override public void onConnectionSuspended(int i) { // The connection to Google Play services was lost for some reason. We call connect() to // attempt to re-establish the connection. Log.i(TAG, "Connection to Google Api Client suspended"); mGoogleApiClient.connect(); } @Override public void onLocationChanged(Location location) { mCurrentLocation = location; mLastUpdateTime = DateFormat.getTimeInstance().format(new Date()); sendResult(new LatLng(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude())); if (BuildConfig.DEBUG) { Log.d(TAG, mCurrentLocation.getLatitude() + ", " + mCurrentLocation.getLongitude()); } } @Override public void onConnectionFailed(ConnectionResult connectionResult) { // Refer to the javadoc for ConnectionResult to see what error codes might be returned in // onConnectionFailed. Log.w(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode()); } }