Chuyển đến nội dung chính

Get You Last known Location & Current Location using FusedLocationProviderClient

Get You Last known Location & Current Location using FusedLocationProviderClient

get your last known location using fused location provider client      get your current location using fused location provider client.png














I would like to cover very basic and simple example of retreiving last known location & Current Location  using Fused location API.  I have written many example for fused location API in my previous posts but in this post i will show how to get retrieve the location without implementing Google Api Client.
All these magics happen after the release of version 11.0.0 of Google Play services SDK.

FusedLocationProviderApi is Deprecated

We have previously used following way to retrieve the last known location.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
We no need to define
LocationServices.FusedLocationApi
 anymore. it is deprecated. It will be removed in the future release. Instead You can use
LocationServices.getFusedLocationProviderClient(this);

No need to implement Google API Client anymore

with the 11.0.+ of Google play services SDK, everything happen behind the scene.  There are so many changes brought into new API. You no need to write boiler plate code to handle Google API Client Connection.

Don’t Use Android Framework’s Location API

I have been telling you this in many of my posts. Android framework’s location API is not good if you are solely depending on user’s location. It is weird even though it works well, it is not powerful like Google’s Fused Location API. Also Android framework’s location API doesn’t support Activity Detection as well. If you are trying to build your new location based app, i strongly recommend you to use Fused location base API.

Advantages of using latest version of Google Play Service SDK

Google’s Latest Fused API will automatically resolve connection failures for you through listeners for you therefore you don’t need to write code to update Google Play services and other failures. In the previous version of Fused API, connection failures happens in the onConnectionFailed method, But in the latest version (11.0.0 Google Play Services SDK ) of the implementation connection problems will fail the Task with an ApiException:
  1. You no need to wait for Google Api client connection. It will automatically happen for you.
  2. Latest version is more easy to implement and movable to shared class. You can write one common class to handle location related logics.
  3. You don’t want to know about behind the scene connection updates such as Google API client success and failures. Everything will be automatically managed for you.
  4. Easy to handle Asynchronous way of coding.

Lets dive into the example,

Create Simple Android App

Get Last known Location using Fused Location Provider Client simple android app.png

I assume that you have created an empty activity with one textview.
Mention the Coarse location and Fine location Permissions in the manifest files first.

Enable Device Location Services when app started

android enable location services settings open

I have created a method to enable the location services and called that method in the onCreatemethod.  This is only works when your location is disabeld in the settings.


public void checkForLocationSettings() {     try {         LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);         builder.addLocationRequest(locationRequest);         SettingsClient settingsClient = LocationServices.getSettingsClient(MainActivity.this);         settingsClient.checkLocationSettings(builder.build())                 .addOnSuccessListener(MainActivity.thisnew OnSuccessListener<LocationSettingsResponse>() {                     @Override                     public void onSuccess(LocationSettingsResponse locationSettingsResponse) {                         //Setting is success...                         Toast.makeText(MainActivity.this"Enabled the Location successfully. Now you can press the buttons..", Toast.LENGTH_SHORT).show();                     }                 })                 .addOnFailureListener(MainActivity.thisnew OnFailureListener() {                     @Override                     public void onFailure(@NonNull Exception e) {                         int statusCode = ((ApiException) e).getStatusCode();                         switch (statusCode) {                             case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:                                 try {                                     // Show the dialog by calling startResolutionForResult(), and check the                                     // result in onActivityResult().                                     ResolvableApiException rae = (ResolvableApiException) e;                                     rae.startResolutionForResult(MainActivity.this, REQUEST_PERMISSIONS_LOCATION_SETTINGS_REQUEST_CODE);                                 } catch (IntentSender.SendIntentException sie) {                                     sie.printStackTrace();                                 }                                 break;                             case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:                                 Toast.makeText(MainActivity.this"Setting change is not available.Try in another device.", Toast.LENGTH_LONG).show();                         }                     }                 });     } catch (Exception ex) {         ex.printStackTrace();     } }


User Must Accept the Permission

android location permission request runtime
Permission is must on devices which are running API 23 or later.  To show the Run time permission, You should do the following technique.
  1. Check if the user has granted permission.
  2. Check if user has already denied the permission. (Check by calling ActivityCompat.shouldShowRequestPermissionRationale)
  3. Show Permission Dialog.

if (
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(thisManifest.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.

requestPermissions(REQUEST_PERMISSIONS_LAST_LOCATION_REQUEST_CODE);
return;
}

Check if user has denied
boolean shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(thisManifest.permission.ACCESS_COARSE_LOCATION);

// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
showSnackbar("Permission is must to find the location""Ok",
new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
startLocationPermissionRequest(requestCode);
}
});

} else {
start
}
Request Dialog (Above picture show the dialog)
private void startLocationPermissionRequest(int requestCode) {
ActivityCompat.requestPermissions(MainActivity.thisnew String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
}

Initialize Fused LocationApi Client and other settings in onCreate
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

resultTextView = (TextView) findViewById((R.id.resultText));

mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

checkForLocationRequest();
checkForLocationSettings();
}

Get Current Location is So Easy

public void callCurrentLocation(View view) {
try {
if (
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) {
// 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.
requestPermissions(REQUEST_PERMISSIONS_CURRENT_LOCATION_REQUEST_CODE);
return;
}

mFusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {

currentLocation = (Location) locationResult.getLastLocation();

String result = "Current Location Latitude is " +
currentLocation.getLatitude() + "\n" +
"Current location Longitude is " + currentLocation.getLongitude();

resultTextView.setText(result);
}
}, Looper.myLooper());

} catch (Exception ex) {
ex.printStackTrace();
}
}

Suppose if you want to see my full Activity class,
package locationnew.appsgit.com.locationexamplenew;

import android.Manifest;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.Location;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Looper;
import android.provider.Settings;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GoogleApiAvailability;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.ResolvableApiException;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationCallback;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationResult;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResponse;
import com.google.android.gms.location.LocationSettingsStatusCodes;
import com.google.android.gms.location.SettingsClient;
import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.OnFailureListener;
import com.google.android.gms.tasks.OnSuccessListener;
import com.google.android.gms.tasks.Task;

public class MainActivity extends AppCompatActivity {

private static final String TAG = MainActivity.class.getSimpleName();

private static final int REQUEST_PERMISSIONS_LOCATION_SETTINGS_REQUEST_CODE = 33;
private static final int REQUEST_PERMISSIONS_LAST_LOCATION_REQUEST_CODE = 34;
private static final int REQUEST_PERMISSIONS_CURRENT_LOCATION_REQUEST_CODE = 35;

private FusedLocationProviderClient mFusedLocationClient;

protected static long MIN_UPDATE_INTERVAL = 30 * 1000; // 1 minute is the minimum Android recommends, but we use 30 seconds

protected Location mLastLocation;

private TextView resultTextView;
LocationRequest locationRequest;
Location lastLocation = null;
Location currentLocation = null;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

resultTextView = (TextView) findViewById((R.id.resultText));

mFusedLocationClient = LocationServices.getFusedLocationProviderClient(this);

checkForLocationRequest();
checkForLocationSettings();
}

@Override
protected void onResume() {
super.onResume();

GoogleApiAvailability googleApiAvailability = GoogleApiAvailability.getInstance();
int result = googleApiAvailability.isGooglePlayServicesAvailable(this);

if (result != ConnectionResult.SUCCESS && result != ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
Toast.makeText(this"Are you running in Emulator ? try a real device.", Toast.LENGTH_SHORT).show();
}
}

@Override
public void onStart() {
super.onStart();
}

public void callLastKnownLocation(View view) {
try {
if (
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(thisManifest.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.
requestPermissions(REQUEST_PERMISSIONS_LAST_LOCATION_REQUEST_CODE);
return;
}

getLastLocation();

} catch (Exception ex) {
ex.printStackTrace();
}
}

public void callCurrentLocation(View view) {
try {
if (
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED &&
ActivityCompat.checkSelfPermission(thisManifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED
) {
// 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.
requestPermissions(REQUEST_PERMISSIONS_CURRENT_LOCATION_REQUEST_CODE);
return;
}

mFusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {

currentLocation = (Location) locationResult.getLastLocation();

String result = "Current Location Latitude is " +
currentLocation.getLatitude() + "\n" +
"Current location Longitude is " + currentLocation.getLongitude();

resultTextView.setText(result);
}
}, Looper.myLooper());

} catch (Exception ex) {
ex.printStackTrace();
}
}

@SuppressWarnings("MissingPermission")
private void getLastLocation() {

mFusedLocationClient.getLastLocation()
.addOnCompleteListener(thisnew OnCompleteListener<Location>() {
@Override
public void onComplete(@NonNull Task<Location> task) {
if (task.isSuccessful() && task.getResult() != null) {
mLastLocation = task.getResult();

String result = "Last known Location Latitude is " +
mLastLocation.getLatitude() + "\n" +
"Last known longitude Longitude is " + mLastLocation.getLongitude();

resultTextView.setText(result);
} else {
showSnackbar("No Last known location found. Try current location..!");
}
}
});
}

private void showSnackbar(final String text) {
View container = findViewById(R.id.container);
if (container != null) {
Snackbar.make(container, text, Snackbar.LENGTH_LONG).show();
}
}

private void showSnackbar(final String mainTextString, final String actionString,
View.OnClickListener listener) {
Snackbar.make(findViewById(android.R.id.content),
mainTextString,
Snackbar.LENGTH_INDEFINITE)
.setAction(actionString, listener).show();
}

private void startLocationPermissionRequest(int requestCode) {
ActivityCompat.requestPermissions(MainActivity.thisnew String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, requestCode);
}

private void requestPermissions(final int requestCode) {
boolean shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale(thisManifest.permission.ACCESS_COARSE_LOCATION);

// Provide an additional rationale to the user. This would happen if the user denied the
// request previously, but didn't check the "Don't ask again" checkbox.
if (shouldProvideRationale) {
showSnackbar("Permission is must to find the location""Ok",
new View.OnClickListener() {
@Override
public void onClick(View view) {
// Request permission
startLocationPermissionRequest(requestCode);
}
});

} else {
startLocationPermissionRequest(requestCode);
}
}

public void checkForLocationRequest(){
locationRequest = LocationRequest.create();
locationRequest.setInterval(MIN_UPDATE_INTERVAL);
locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
}

//Check for location settings.
public void checkForLocationSettings() {
try {
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(locationRequest);
builder.addLocationRequest(locationRequest);
SettingsClient settingsClient = LocationServices.getSettingsClient(MainActivity.this);

settingsClient.checkLocationSettings(builder.build())
.addOnSuccessListener(MainActivity.thisnew OnSuccessListener<LocationSettingsResponse>() {
@Override
public void onSuccess(LocationSettingsResponse locationSettingsResponse) {
//Setting is success...
Toast.makeText(MainActivity.this"Enabled the Location successfully. Now you can press the buttons..", Toast.LENGTH_SHORT).show();
}
})
.addOnFailureListener(MainActivity.thisnew OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {

int statusCode = ((ApiException) e).getStatusCode();
switch (statusCode) {
case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:

try {
// Show the dialog by calling startResolutionForResult(), and check the
// result in onActivityResult().
ResolvableApiException rae = (ResolvableApiException) e;
rae.startResolutionForResult(MainActivity.this, REQUEST_PERMISSIONS_LOCATION_SETTINGS_REQUEST_CODE);
} catch (IntentSender.SendIntentException sie) {
sie.printStackTrace();
}
break;
case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
Toast.makeText(MainActivity.this"Setting change is not available.Try in another device.", Toast.LENGTH_LONG).show();
}

}
});

} catch (Exception ex) {
ex.printStackTrace();
}
}

/**
* Callback received when a permissions request has been completed.
*/

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
@NonNull int[] grantResults) {
if (requestCode == REQUEST_PERMISSIONS_LAST_LOCATION_REQUEST_CODE) {
if (grantResults.length <= 0) {
// If user interaction was interrupted, the permission request is cancelled and you
// receive empty arrays.
Log.i(TAG, "User interaction was cancelled.");
} else if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// Permission granted.
getLastLocation();
}
}

if (requestCode == REQUEST_PERMISSIONS_CURRENT_LOCATION_REQUEST_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
callCurrentLocation(null);
}
}
}

}

I have only one textfield and two buttons in my xml file

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
android:id="@+id/container"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="locationnew.appsgit.com.locationexamplenew.MainActivity">

<LinearLayout
android:id="@+id/buttonHolder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:gravity="center"
android:layout_above="@id/resultText"
android:padding="10dp"
>
<Button
android:onClick="callLastKnownLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get Last known location"
android:layout_centerInParent="true"/>
<Button
android:onClick="callCurrentLocation"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get your Current Location"
android:layout_centerInParent="true"/>
</LinearLayout>

<TextView
android:id="@+id/resultText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"/>

</RelativeLayout>

Don’t forget to add permission for Android Api 22 and below devices in Manifest file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="locationnew.appsgit.com.locationexamplenew">

<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>

</manifest>

Hope you didn’t forget to add the dependencies to the Gradle file.
apply plugin: 'com.android.application'

android {
compileSdkVersion 26
defaultConfig {
applicationId "locationnew.appsgit.com.locationexamplenew"
minSdkVersion 18
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt')'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:26.1.0'
compile 'com.android.support:design:26.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.0.2'

//this is importan. should be 11.1.0 or above version
implementation 'com.google.android.gms:play-services-location:11.6.0'

testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.1'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'
}

And in the root Gradle file You should add the google() and jCenter repos.
// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {

repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.0.1'

// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}

allprojects {
repositories {
google()
jcenter()
}
}

task clean(type: Delete) {
delete rootProject.buildDir
}

get your current location using fused location provider client.png

Thats all my friends, if you have anything in your mind please describe it in the comment.
To download the source code check my git repo

cheers..!!

Nhận xét

Bài đăng phổ biến từ blog này

Kích thước icon cho app Android và công cụ tạo icon của Google

Để hiển thị chuẩn theo các size màn hình thì chúng ta sẽ theo các kích thước sau: 36 × 36 (ldpi) – Low 48 × 48 (mdpi) – Medium 72 × 72 (hdpi) – High 96 × 96 (x-hdpi) – x-high 144 × 144 (xx-hdpi) 192 × 192 (xxx-hdpi) 512 × 512 (Google Play store) -> Kích thước này để làm ảnh demo cho App khi upload lên store. Khi tạo icon launcher cho app nếu tạo bằng các  Launcher icon generator  cửa Google thì khi cài vào điện thoại nó sẽ bé hơn so với các app khác vì google tự động cho thêm padding vào icon. Tránh điều này thì nên tự thiết kế bằng Photoshop sau đó dùng  cái này  để tạo thì sẽ to và đẹp hơn, nó tạo nhanh và đủ các kích thước chuẩn như bên trên kia. Nếu bạn muốn bo góc thì cũng làm bo góc ở trong photoshop trước sau đó mới dùng công cụ bên trên. Kiến thức liên quan đến đơn vị đo trong Android: pixel có thể hiểu là số điểm ảnh có trong 1 dot có hình vuông vì là ảnh bitmap mà. Ảnh đen trắng binary image thì 1 dot = 1 px = 1 bit (chỉ có trạng thá...

Cấu trúc cơ bản layout trong Flutter