From e307c21f673d52ca30b001eaa688d26dde7db044 Mon Sep 17 00:00:00 2001 From: prabhat Date: Sun, 18 Aug 2019 15:39:51 +0530 Subject: [PATCH] under dev code --- .idea/caches/build_file_checksums.ser | Bin 0 -> 594 bytes .idea/caches/gradle_models.ser | Bin 0 -> 179611 bytes .idea/encodings.xml | 4 + .idea/gradle.xml | 3 + .idea/inspectionProfiles/Project_Default.xml | 17 + .idea/misc.xml | 17 +- app/build.gradle | 18 +- app/src/main/AndroidManifest.xml | 3 + .../prabhat/locationsample/MainActivity.java | 79 +-- build.gradle | 2 +- easywaylocation/build.gradle | 19 +- .../easywaylocation/AddressHelper.java | 156 +++++ .../easywaylocation/EasyWayLocation.java | 484 ++++++++-------- .../com/example/easywaylocation/Listener.java | 4 +- .../RationaleDialogProvider.java | 93 +++ .../easywaylocation/RequestCallback.java | 31 + .../easywaylocation/TestLocationRequest.java | 533 ++++++++++++++++++ .../easywaylocation/draw_path/DataParser.java | 100 ++++ .../draw_path/DirectionUtil.java | 304 ++++++++++ .../src/main/res/values/strings.xml | 30 + gradle.properties | 2 + gradle/wrapper/gradle-wrapper.properties | 4 +- 22 files changed, 1596 insertions(+), 307 deletions(-) create mode 100644 .idea/caches/build_file_checksums.ser create mode 100644 .idea/caches/gradle_models.ser create mode 100644 .idea/encodings.xml create mode 100644 .idea/inspectionProfiles/Project_Default.xml create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/AddressHelper.java create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/RationaleDialogProvider.java create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/RequestCallback.java create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/TestLocationRequest.java create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DataParser.java create mode 100644 easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DirectionUtil.java diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser new file mode 100644 index 0000000000000000000000000000000000000000..56480b37c270c099021239c3a245930f6966b64d GIT binary patch literal 594 zcmZ4UmVvdnh`~NNKUXg?FQq6yGexf?KR>5fFEb@IQ7^qHF(oHeub?PDD>b=9F91S2 zm1gFoxMk*~I%lLNXBU^|7Q2L-Ts|(GuF1r}Q#7BMhIJFWRF{)3Gp=8-@r z7N?e!Wagz8!!#B$)G@Gu)qZ<qPJVJ?4k%~~Qj1D5Q;SRCicdN3TlCSP(e5u>qln9coKGbT8qQWRp~b01 z#W4j%iAfnTP=~}I;s?W!E3!Q|`x!Q|ylmi{@#cq9T?vCA9@V*T%%^lYZRS!`-mt-Q u?&Xao3^J*S#g*lWl^|!9Waj6g1|8h3rd4w`i|#Jj@HJOOujPL9@d^M!``9J` literal 0 HcmV?d00001 diff --git a/.idea/caches/gradle_models.ser b/.idea/caches/gradle_models.ser new file mode 100644 index 0000000000000000000000000000000000000000..8790efaaca4661741224f7b3a608639ece6540b0 GIT binary patch literal 179611 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 literal 0 HcmV?d00001 diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..15a15b2 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/.idea/gradle.xml b/.idea/gradle.xml index 8efd28a..85165e0 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -3,6 +3,9 @@ - + diff --git a/app/build.gradle b/app/build.gradle index 8f75b4c..3f32380 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,14 +1,14 @@ apply plugin: 'com.android.application' android { - compileSdkVersion 26 + compileSdkVersion 28 defaultConfig { applicationId "com.example.prabhat.locationsample" minSdkVersion 17 - targetSdkVersion 26 + targetSdkVersion 28 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { release { @@ -16,14 +16,16 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' + } + buildToolsVersion = '28.0.3' } dependencies { implementation fileTree(include: ['*.jar'], dir: 'libs') - implementation 'com.android.support:appcompat-v7:26.1.0' - implementation 'com.android.support.constraint:constraint-layout:1.0.2' - 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' + implementation 'androidx.appcompat:appcompat:1.0.0' + implementation 'androidx.constraintlayout:constraintlayout:1.1.3' implementation project(':easywaylocation') } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d48da1d..6de41f0 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ @@ -11,6 +12,8 @@ android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" + tools:replace="android:appComponentFactory" + android:appComponentFactory="whateverString" android:supportsRtl="true" android:theme="@style/AppTheme"> diff --git a/app/src/main/java/com/example/prabhat/locationsample/MainActivity.java b/app/src/main/java/com/example/prabhat/locationsample/MainActivity.java index 4657506..4386868 100644 --- a/app/src/main/java/com/example/prabhat/locationsample/MainActivity.java +++ b/app/src/main/java/com/example/prabhat/locationsample/MainActivity.java @@ -1,20 +1,29 @@ package com.example.prabhat.locationsample; +import android.Manifest; import android.content.Intent; +import android.content.pm.PackageManager; +import android.location.Location; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import android.util.Log; import android.widget.TextView; -import android.widget.Toast; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; import com.example.easywaylocation.EasyWayLocation; import com.example.easywaylocation.Listener; +import com.example.easywaylocation.draw_path.DirectionUtil; import static com.example.easywaylocation.EasyWayLocation.LOCATION_SETTING_REQUEST_CODE; public class MainActivity extends AppCompatActivity implements Listener { - EasyWayLocation easyWayLocation; + //EasyWayLocation easyWayLocation; private TextView location, latLong, diff; private Double lati, longi; + //private TestLocationRequest testLocationRequest; + private EasyWayLocation easyWayLocation; @Override protected void onCreate(Bundle savedInstanceState) { @@ -23,57 +32,63 @@ protected void onCreate(Bundle savedInstanceState) { location = findViewById(R.id.location); latLong = findViewById(R.id.latlong); diff = findViewById(R.id.diff); - easyWayLocation = new EasyWayLocation(this); - easyWayLocation.setListener(this); +// DirectionUtil directionUtil = new DirectionUtil.Builder() +// .setDirectionKey("AIzaSyDUCCidq_7tBb0s1LRLhhvFyNqd0BeQBuI") +// + // testLocationRequest = new TestLocationRequest(this); + easyWayLocation = new EasyWayLocation(this, false,this); + if (permissionIsGranted()) { + doLocationWork(); + } else { + // Permission not granted, ask for it + //testLocationRequest.requestPermission(121); + } } - @Override - public void locationOn() { - Toast.makeText(this, "Location ON", Toast.LENGTH_SHORT).show(); - easyWayLocation.beginUpdates(); - diff.setText(String.valueOf(EasyWayLocation.calculateDistance(28.626686, 77.026409, 28.626799, 77.033619))); - } + public boolean permissionIsGranted() { - @Override - public void onPositionChanged() { - lati = easyWayLocation.getLatitude(); - longi = easyWayLocation.getLongitude(); - location.setText(EasyWayLocation.getAddress(this, lati, longi, false, true)); - latLong.setText(String.valueOf(lati) + " " + String.valueOf(longi)); - Toast.makeText(this, String.valueOf(easyWayLocation.getLongitude()) + "," + String.valueOf(easyWayLocation.getLatitude()), Toast.LENGTH_SHORT).show(); + int permissionState = ActivityCompat.checkSelfPermission(this, + Manifest.permission.ACCESS_FINE_LOCATION); + + return permissionState == PackageManager.PERMISSION_GRANTED; } - @Override - public void locationCancelled() { - easyWayLocation.showAlertDialog(getString(R.string.loc_title), getString(R.string.loc_mess), null); + private void doLocationWork() { + easyWayLocation.startLocation(); } @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { + protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) { super.onActivityResult(requestCode, resultCode, data); - switch (requestCode) { - case LOCATION_SETTING_REQUEST_CODE: - easyWayLocation.onActivityResult(resultCode); - break; + if (requestCode == LOCATION_SETTING_REQUEST_CODE) { + easyWayLocation.onActivityResult(resultCode); } } @Override protected void onResume() { super.onResume(); + easyWayLocation.startLocation(); + } - // make the device update its location - easyWayLocation.beginUpdates(); + @Override + protected void onPause() { + super.onPause(); + easyWayLocation.endUpdates(); + } + @Override + public void locationOn() { } @Override - protected void onPause() { - // stop location updates (saves battery) - easyWayLocation.endUpdates(); + public void currentLocation(Location location) { + Log.v("location_test","------------>"+location.getLatitude()); + } + @Override + public void locationCancelled() { - super.onPause(); } } diff --git a/build.gradle b/build.gradle index 4c8ff81..7b9c647 100644 --- a/build.gradle +++ b/build.gradle @@ -7,7 +7,7 @@ buildscript { jcenter() } dependencies { - classpath 'com.android.tools.build:gradle:3.0.1' + classpath 'com.android.tools.build:gradle:3.4.2' classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0' // NOTE: Do not place your application dependencies here; they belong diff --git a/easywaylocation/build.gradle b/easywaylocation/build.gradle index 46b384b..221eacd 100644 --- a/easywaylocation/build.gradle +++ b/easywaylocation/build.gradle @@ -2,17 +2,17 @@ apply plugin: 'com.android.library' apply plugin: 'com.github.dcendents.android-maven' group='com.github.prabhat1707' android { - compileSdkVersion 26 + compileSdkVersion 28 defaultConfig { minSdkVersion 16 - targetSdkVersion 26 + targetSdkVersion 28 versionCode 1 versionName "1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } @@ -22,15 +22,16 @@ android { proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } + compileOptions { + sourceCompatibility = '1.8' + targetCompatibility = '1.8' + } + buildToolsVersion = '28.0.3' } dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) - - implementation 'com.android.support:appcompat-v7:26.1.0' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'com.android.support.test:runner:1.0.1' - compile 'com.google.android.gms:play-services-location:11.8.0' - androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' + implementation 'com.google.android.gms:play-services-maps:16.1.0' + implementation 'com.google.android.gms:play-services-location:17.0.0' } diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/AddressHelper.java b/easywaylocation/src/main/java/com/example/easywaylocation/AddressHelper.java new file mode 100644 index 0000000..2b07c73 --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/AddressHelper.java @@ -0,0 +1,156 @@ +package com.example.easywaylocation; + +import android.content.Context; +import android.location.Address; +import android.location.Geocoder; +import android.location.Location; + +import java.io.IOException; +import java.util.List; +import java.util.Locale; + +/** + * Created by prabhat on 2/10/18. + */ + +public class AddressHelper { + private static final String TAG = AddressHelper.class.getSimpleName(); + + public static final int ADMIN_AREA = 0; + public static final int CITY_NAME = 1; + public static final int COUNTRY_CODE = 2; + public static final int COUNTRY_NAME = 3; + public static final int FEATURE_NAME = 4; + public static final int FULL_ADDRESS = 5; + public static final int PHONE_NUMBER = 6; + public static final int POST_CODE = 7; + public static final int PREMISES = 8; + public static final int STREET_NAME = 9; + public static final int SUB_ADMIN_AREA = 10; + public static final int SUB_THOROUGHFARE = 11; + + private Context mContext; + private Geocoder mGeocoder; + + public AddressHelper(Context context, Locale locale) { + + this.mContext = context; + this.mGeocoder = new Geocoder(mContext, locale); + + } + + public void getAddressList(Location location, RequestCallback.AddressRequestCallback callback) { + + List
addressList = null; + + try { + + addressList = mGeocoder.getFromLocation( + location.getLatitude(), location.getLongitude(), + 1); // We only want one address to be returned. + + } catch (IOException e) { + // Catch network or other IO problems + callback.onAddressFailedResult(mContext.getString(R.string.deviceLocationUtil_geocoder_not_available)); + return; + } catch (IllegalArgumentException e) { + // Catch invalid latitude or longitude values + callback.onAddressFailedResult(mContext.getString(R.string.deviceLocationUtil_geocoder_invalid_latLong)); + return; + } + + // Handle case where no address is found + if (addressList == null || addressList.size() == 0) { + callback.onAddressFailedResult(mContext.getString(R.string.deviceLocationUtil_geocoder_address_not_found)); + } else { + // Return the address list + callback.onAddressSuccessfulResult(addressList); + } + + } + + /** + * Returns a String containing the requested address element or null if not found + * + * @param elementCode A package-defined int constant representing the specific + * address element to return. + * @param location A Location object containing a latitude and longitude. + * @return String containing the requested address element if found, a reason for + * failure if necessary or null if address element doesn't exist. + */ + public String getAddressElement(int elementCode, Location location) { + + List
addressList; + Address address; + String elementString = null; + + try { + + addressList = mGeocoder.getFromLocation( + location.getLatitude(), location.getLongitude(), + 1); // We only want one address to be returned. + + } catch (IOException e) { + // Catch network or other IO problems + return mContext.getString(R.string.deviceLocationUtil_geocoder_not_available); + } catch (IllegalArgumentException e) { + // Catch invalid latitude or longitude values + return mContext.getString(R.string.deviceLocationUtil_geocoder_invalid_latLong); + } + + // Handle case where no address is found + if (addressList == null || addressList.size() == 0) { + return mContext.getString(R.string.deviceLocationUtil_geocoder_address_not_found); + } else { + // Create the Address object from the address list + address = addressList.get(0); + } + + // Get the specific address element requested by the caller + switch (elementCode) { + + case ADMIN_AREA: + elementString = address.getAdminArea(); + break; + case CITY_NAME: + elementString = address.getLocality(); + break; + case COUNTRY_CODE: + elementString = address.getCountryCode(); + break; + case COUNTRY_NAME: + elementString = address.getCountryName(); + break; + case FEATURE_NAME: + elementString = address.getFeatureName(); + break; + case FULL_ADDRESS: + elementString = address.toString(); + break; + case PHONE_NUMBER: + elementString = address.getPhone(); + break; + case POST_CODE: + elementString = address.getPostalCode(); + break; + case PREMISES: + elementString = address.getPremises(); + break; + case STREET_NAME: + elementString = address.getThoroughfare(); + break; + case SUB_ADMIN_AREA: + elementString = address.getSubAdminArea(); + break; + case SUB_THOROUGHFARE: + elementString = address.getSubThoroughfare(); + break; + default: + elementString = mContext.getString(R.string.deviceLocationUtil_geocoder_invalid_element); + break; + } + + return elementString; + } + +} diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/EasyWayLocation.java b/easywaylocation/src/main/java/com/example/easywaylocation/EasyWayLocation.java index b5a4736..4778024 100644 --- a/easywaylocation/src/main/java/com/example/easywaylocation/EasyWayLocation.java +++ b/easywaylocation/src/main/java/com/example/easywaylocation/EasyWayLocation.java @@ -1,48 +1,65 @@ package com.example.easywaylocation; +import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; +import android.app.AlertDialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.IntentSender; +import android.content.pm.PackageManager; import android.graphics.drawable.Drawable; import android.location.Address; import android.location.Geocoder; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; +import android.os.Build; import android.os.Bundle; +import android.os.Looper; import android.os.Parcel; import android.os.Parcelable; import android.provider.Settings; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.AlertDialog; +import android.text.TextUtils; import android.widget.Toast; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.RequiresApi; + import com.google.android.gms.common.ConnectionResult; import com.google.android.gms.common.api.GoogleApiClient; import com.google.android.gms.common.api.PendingResult; +import com.google.android.gms.common.api.ResolvableApiException; import com.google.android.gms.common.api.ResultCallback; import com.google.android.gms.common.api.Status; +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.LocationSettingsResult; import com.google.android.gms.location.LocationSettingsStates; import com.google.android.gms.location.LocationSettingsStatusCodes; +import com.google.android.gms.location.SettingsClient; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; import java.io.IOException; import java.util.List; import java.util.Locale; import java.util.Random; + /** * Utility class for easy access to the device location on Android */ -public class EasyWayLocation implements GoogleApiClient.OnConnectionFailedListener, GoogleApiClient.ConnectionCallbacks { +public class EasyWayLocation { /* @@ -83,6 +100,7 @@ public class EasyWayLocation implements GoogleApiClient.OnConnectionFailedListen * The factor for conversion from longitude to kilometers at zero degree in latitude */ private static final float LONGITUDE_TO_KILOMETER_AT_ZERO_LATITUDE = 111.320f; + private static final int REQUEST_CHECK_SETTINGS = 11; /** * The PRNG that is used for location blurring */ @@ -95,28 +113,32 @@ public class EasyWayLocation implements GoogleApiClient.OnConnectionFailedListen /** * The LocationManager instance used to query the device location */ - private final LocationManager mLocationManager; + //private final LocationManager mLocationManager; /** * Whether a fine location should be required or coarse location can be used */ - private final boolean mRequireFine; + //private final boolean mRequireFine; /** * Whether passive mode shall be used or not */ - private final boolean mPassive; + private boolean mPassive; /** * The internal after which new location updates are requested (in milliseconds) where longer intervals save battery */ - private final long mInterval; + private long mInterval; /** * Whether to require a new location (`true`) or accept old (last known) locations as well (`false`) */ - private final boolean mRequireNewLocation; + private boolean mRequireLastLocation; + private FusedLocationProviderClient fusedLocationClient; boolean gps_enabled = false; boolean network_enabled = false; GoogleApiClient googleApiClient; private Boolean locationReturn = true; + private Context activity; private Context context; + private LocationCallback locationCallback; + /** * The blur radius (in meters) that will be used to blur the location for privacy reasons */ @@ -130,79 +152,46 @@ public class EasyWayLocation implements GoogleApiClient.OnConnectionFailedListen */ private Location mPosition; private Listener mListener; - - /** - * Constructs a new instance with default granularity, mode and interval - * - * @param context the Context reference to get the system service from - */ - public EasyWayLocation(final Context context) { - this(context, false); - } - - /** - * Constructs a new instance with default mode and interval - * - * @param context the Context reference to get the system service from - * @param requireFine whether to require fine location or use coarse location - */ - public EasyWayLocation(final Context context, final boolean requireFine) { - this(context, requireFine, false); - } - - /** - * Constructs a new instance with default interval - * - * @param context the Context reference to get the system service from - * @param requireFine whether to require fine location or use coarse location - * @param passive whether to use passive mode (to save battery) or active mode - */ - public EasyWayLocation(final Context context, final boolean requireFine, final boolean passive) { - this(context, requireFine, passive, INTERVAL_DEFAULT); - } + private LocationRequest locationRequest; /** * Constructs a new instance * * @param context the Context reference to get the system service from - * @param requireFine whether to require fine location or use coarse location - * @param passive whether to use passive mode (to save battery) or active mode - * @param interval the interval to request new location updates after (in milliseconds) where longer intervals save battery */ - public EasyWayLocation(final Context context, final boolean requireFine, final boolean passive, final long interval) { - this(context, requireFine, passive, interval, false); + public EasyWayLocation(final Context context,final boolean requireLastLocation,final Listener listener) { + this(context, null, requireLastLocation,listener); } /** * Constructs a new instance - * - * @param context the Context reference to get the system service from - * @param requireFine whether to require fine location or use coarse location - * @param passive whether to use passive mode (to save battery) or active mode - * @param interval the interval to request new location updates after (in milliseconds) where longer intervals save battery - * @param requireNewLocation whether to require a new location (`true`) or accept old (last known) locations as well (`false`) - */ - public EasyWayLocation(final Context context, final boolean requireFine, final boolean passive, final long interval, final boolean requireNewLocation) { - mLocationManager = (LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE); - mRequireFine = requireFine; + * @param context Context reference to get the system service from + * @param locationRequestt + * location request + * @param requireLastLocation require last location or not + + */ + public EasyWayLocation(Context context, final LocationRequest locationRequestt, final boolean requireLastLocation,final Listener listener) { + // mLocationManager = (LocationManager) context.getApplicationContext().getSystemService(Context.LOCATION_SERVICE); + fusedLocationClient = LocationServices.getFusedLocationProviderClient(context); this.context = context; - mPassive = passive; - mInterval = interval; - mRequireNewLocation = requireNewLocation; - - googleApiClient = new GoogleApiClient.Builder(context) - .addApi(LocationServices.API) - .addConnectionCallbacks(this) - .addOnConnectionFailedListener(this) - .build(); - googleApiClient.connect(); - - if (!mRequireNewLocation) { - mPosition = getCachedPosition(); - cachePosition(); + this.mListener = listener; + if (locationRequest != null){ + this.locationRequest = locationRequestt; + }else { + locationRequest = new LocationRequest(); + locationRequest.setInterval(10000); + // locationRequest.setSmallestDisplacement(10F); + locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); + } + this.mRequireLastLocation = requireLastLocation; + if (mRequireLastLocation) { + getCachedPosition(); + //cachePosition(); } } + /** * For any radius `n`, calculate a random offset in the range `[-n, n]` * @@ -341,54 +330,57 @@ public void setListener(final Listener listener) { mListener = listener; } - public boolean locationEnabled() { - try { - gps_enabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); - } catch (Exception ex) { - } + public boolean hasLocationEnabled() { try { - network_enabled = mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); - } catch (Exception ex) { - } - - return !(!gps_enabled && !network_enabled); + int locationMode = 0; + String locationProviders; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){ + try { + locationMode = Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE); - } - - /** - * Whether the device has location access enabled in the settings - * - * @return whether location access is enabled or not - */ - public boolean hasLocationEnabled() { - return hasLocationEnabled(getProviderName()); - } - - private boolean hasLocationEnabled(final String providerName) { - try { - return mLocationManager.isProviderEnabled(providerName); - } catch (Exception e) { + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + return false; + } + return locationMode != Settings.Secure.LOCATION_MODE_OFF; + }else{ + locationProviders = Settings.Secure.getString(context.getContentResolver(), Settings.Secure.LOCATION_PROVIDERS_ALLOWED); + return !TextUtils.isEmpty(locationProviders); + } + }catch (Exception e){ + e.printStackTrace(); return false; } } + public void startLocation(){ + checkLocationSetting(); + } + /** * Starts updating the location and requesting new updates after the defined interval */ @SuppressLint("MissingPermission") - public void beginUpdates() { - if (mLocationListener != null) { - endUpdates(); - } + private void beginUpdates() { + locationCallback = new LocationCallback(){ + @Override + public void onLocationResult(LocationResult locationResult) { + if (locationResult == null) { + mListener.locationCancelled(); + }else { + for (Location location : locationResult.getLocations()) { + mListener.currentLocation(location); + } + } - if (!mRequireNewLocation) { - mPosition = getCachedPosition(); - } + } + }; - mLocationListener = createLocationListener(); - mLocationManager.requestLocationUpdates(getProviderName(), mInterval, 25, mLocationListener); + fusedLocationClient.requestLocationUpdates(locationRequest, + locationCallback, + Looper.getMainLooper()); } /** @@ -396,9 +388,8 @@ public void beginUpdates() { */ @SuppressLint("MissingPermission") public void endUpdates() { - if (mLocationListener != null) { - mLocationManager.removeUpdates(mLocationListener); - mLocationListener = null; + if (locationCallback != null){ + fusedLocationClient.removeLocationUpdates(locationCallback); } } @@ -501,111 +492,72 @@ public void setBlurRadius(final int blurRadius) { mBlurRadius = blurRadius; } - /** - * Creates a new LocationListener instance used internally to listen for location updates - * - * @return the new LocationListener instance - */ - private LocationListener createLocationListener() { - return new LocationListener() { - @Override - public void onLocationChanged(Location location) { - mPosition = location; - cachePosition(); - if (mListener != null) { - mListener.onPositionChanged(); - } - } - @Override - public void onStatusChanged(String provider, int status, Bundle extras) { - } - - @Override - public void onProviderEnabled(String provider) { - } - - @Override - public void onProviderDisabled(String provider) { - } - - }; - } - - /** - * Returns the name of the location provider that matches the specified settings - * - * @return the provider's name - */ - private String getProviderName() { - return getProviderName(mRequireFine); - } /** * Returns the name of the location provider that matches the specified settings and depends on the given granularity * - * @param requireFine whether to require fine location or use coarse location * @return the provider's name */ - private String getProviderName(final boolean requireFine) { - // if fine location (GPS) is required - if (requireFine) { - // we just have to decide between active and passive mode +// private String getProviderName(final boolean requireFine) { +// // if fine location (GPS) is required +// if (requireFine) { +// // we just have to decide between active and passive mode +// +// if (mPassive) { +// return PROVIDER_FINE_PASSIVE; +// } else { +// return PROVIDER_FINE; +// } +// } +// // if both fine location (GPS) and coarse location (network) are acceptable +// else { +// // if we can use coarse location (network) +// if (hasLocationEnabled(PROVIDER_COARSE)) { +// // if we wanted passive mode +// if (mPassive) { +// // throw an exception because this is not possible +// throw new RuntimeException("There is no passive provider for the coarse location"); +// } +// // if we wanted active mode +// else { +// // use coarse location (network) +// return PROVIDER_COARSE; +// } +// } +// // if coarse location (network) is not available +// else { +// // if we can use fine location (GPS) +// if (hasLocationEnabled(PROVIDER_FINE) || hasLocationEnabled(PROVIDER_FINE_PASSIVE)) { +// // we have to use fine location (GPS) because coarse location (network) was not available +// return getProviderName(true); +// } +// // no location is available so return the provider with the minimum permission level +// else { +// return PROVIDER_COARSE; +// } +// } +// } +// } - if (mPassive) { - return PROVIDER_FINE_PASSIVE; - } else { - return PROVIDER_FINE; - } - } - // if both fine location (GPS) and coarse location (network) are acceptable - else { - // if we can use coarse location (network) - if (hasLocationEnabled(PROVIDER_COARSE)) { - // if we wanted passive mode - if (mPassive) { - // throw an exception because this is not possible - throw new RuntimeException("There is no passive provider for the coarse location"); - } - // if we wanted active mode - else { - // use coarse location (network) - return PROVIDER_COARSE; - } - } - // if coarse location (network) is not available - else { - // if we can use fine location (GPS) - if (hasLocationEnabled(PROVIDER_FINE) || hasLocationEnabled(PROVIDER_FINE_PASSIVE)) { - // we have to use fine location (GPS) because coarse location (network) was not available - return getProviderName(true); - } - // no location is available so return the provider with the minimum permission level - else { - return PROVIDER_COARSE; - } - } - } - } - - /** - * Returns the last position from the cache - * - * @return the cached position - */ @SuppressLint("MissingPermission") - private Location getCachedPosition() { - if (mCachedPosition != null) { - return mCachedPosition; - } else { - try { - return mLocationManager.getLastKnownLocation(getProviderName()); - } catch (Exception e) { - return null; - } - } + private void getCachedPosition() { + fusedLocationClient.getLastLocation() + .addOnSuccessListener(new OnSuccessListener() { + @Override + public void onSuccess(Location location) { + // Got last known location. In some rare situations this can be null. + if (location != null) { + mListener.currentLocation(location); + }else { + checkLocationSetting(); + beginUpdates(); + endUpdates(); + } + } + }); } /** @@ -616,55 +568,51 @@ private void cachePosition() { mCachedPosition = mPosition; } } - - @Override - public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { - Toast.makeText(context, "failed", Toast.LENGTH_SHORT).show(); - } - - @Override - public void onConnected(@Nullable Bundle bundle) { - - LocationRequest mLocationRequest = new LocationRequest(); - LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest); - PendingResult result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build()); - - result.setResultCallback(new ResultCallback() { - @Override - public void onResult(@NonNull LocationSettingsResult result1) { - Status status = result1.getStatus(); - final LocationSettingsStates states = result1.getLocationSettingsStates(); - switch (status.getStatusCode()) { - case LocationSettingsStatusCodes.SUCCESS: - mListener.locationOn(); - break; - case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: - try { - - status.startResolutionForResult((Activity) context, LOCATION_SETTING_REQUEST_CODE); - - } catch (IntentSender.SendIntentException e) { - - } - break; - case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: - locationReturn = false; - break; - } - - } - }); - } - - @Override - public void onConnectionSuspended(int i) { - } - - public Boolean checkLocation() { - return locationReturn; - } - - +// +// @Override +// public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { +// Toast.makeText(context, "failed", Toast.LENGTH_SHORT).show(); +// } +// +// @Override +// public void onConnected(@Nullable Bundle bundle) { +// +// LocationRequest mLocationRequest = new LocationRequest(); +// LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder().addLocationRequest(mLocationRequest); +// PendingResult result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build()); +// +// result.setResultCallback(new ResultCallback() { +// @Override +// public void onResult(@NonNull LocationSettingsResult result1) { +// Status status = result1.getStatus(); +// final LocationSettingsStates states = result1.getLocationSettingsStates(); +// switch (status.getStatusCode()) { +// case LocationSettingsStatusCodes.SUCCESS: +// mListener.locationOn(); +// break; +// case LocationSettingsStatusCodes.RESOLUTION_REQUIRED: +// try { +// +// status.startResolutionForResult((Activity) context, LOCATION_SETTING_REQUEST_CODE); +// +// } catch (IntentSender.SendIntentException e) { +// +// } +// break; +// case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE: +// locationReturn = false; +// break; +// } +// +// } +// }); +// } +// +// @Override +// public void onConnectionSuspended(int i) { +// } +// +// /** * Wrapper for two coordinates (latitude and longitude) @@ -728,18 +676,19 @@ public void writeToParcel(Parcel out, int flags) { } public void onActivityResult(int result) { + if (result == Activity.RESULT_OK) { mListener.locationOn(); + beginUpdates(); } else if (result == Activity.RESULT_CANCELED) { mListener.locationCancelled(); } } - public void showAlertDialog(String title, String message, Drawable drawable){ + public void showAlertDialog(String title, String message, Drawable drawable) { AlertDialog alertDialog = new AlertDialog.Builder(context).create(); alertDialog.setTitle(title); - if (drawable != null) - { + if (drawable != null) { alertDialog.setIcon(drawable); } alertDialog.setMessage(message); @@ -775,4 +724,37 @@ public static String getAddress(Context context, Double latitude, Double longitu return add.replaceAll(",null", ""); } + private void checkLocationSetting(){ + LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder(); + builder.setAlwaysShow(true); + builder.addLocationRequest(locationRequest); + SettingsClient client = LocationServices.getSettingsClient(context); + Task task = client.checkLocationSettings(builder.build()); + + task.addOnSuccessListener(locationSettingsResponse -> { + if (mRequireLastLocation){ + beginUpdates(); + endUpdates(); + }else { + beginUpdates(); + } + + }); + + task.addOnFailureListener(e -> { + if (e instanceof ResolvableApiException) { + // Location settings are not satisfied, but this can be fixed + // by showing the user a dialog. + try { + // Show the dialog by calling startResolutionForResult(), + // and check the result in onActivityResult(). + ResolvableApiException resolvable = (ResolvableApiException) e; + resolvable.startResolutionForResult((Activity)context, + LOCATION_SETTING_REQUEST_CODE); + } catch (IntentSender.SendIntentException sendEx) { + sendEx.printStackTrace(); + } + } + }); + } } diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/Listener.java b/easywaylocation/src/main/java/com/example/easywaylocation/Listener.java index 95e7b24..08517dd 100644 --- a/easywaylocation/src/main/java/com/example/easywaylocation/Listener.java +++ b/easywaylocation/src/main/java/com/example/easywaylocation/Listener.java @@ -4,6 +4,8 @@ * Created by prabhat on 11/2/18. */ +import android.location.Location; + /** * Callback that can be implemented in order to listen for events */ @@ -11,7 +13,7 @@ public interface Listener { void locationOn(); - void onPositionChanged(); + void currentLocation(Location location); void locationCancelled(); } diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/RationaleDialogProvider.java b/easywaylocation/src/main/java/com/example/easywaylocation/RationaleDialogProvider.java new file mode 100644 index 0000000..cae190a --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/RationaleDialogProvider.java @@ -0,0 +1,93 @@ +package com.example.easywaylocation; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.app.AlertDialog; +import android.content.DialogInterface; +import android.os.Build; + +import androidx.fragment.app.FragmentActivity; + +import java.lang.ref.WeakReference; + +/** + * Created by prabhat on 2/10/18. + */ + +public class RationaleDialogProvider extends FragmentActivity { + private final WeakReference weakActivity; + private static final String TAG = RationaleDialogProvider.class.getSimpleName(); + + private String mMessage; + private int requestCode; + private String mTitle; + + /** + * Constructor. + *

+ * Takes a reference to the calling Activity as a parameter and assigns it to a WeakReference + * object in order to allow it to be properly garbage-collected if necessary. + *

+ * Any method that relies on the Activity attempts to re-acquire a strong reference to it, + * checks its state (for example not null, not finishing etc.) and exits gracefully if it + * is no longer available. + *

+ * The default dialog title and message body are assigned here but can be altered at + * run-time with a call to setTitle() and/or setMessage() as necessary. + */ + RationaleDialogProvider(Activity activity, int requestCode) { + // assign the activity to the weak reference + this.weakActivity = new WeakReference<>(activity); + this.requestCode = requestCode; + // Set the default title and message for the dialog + mTitle = activity.getString(R.string.deviceLocationUtil_default_rationale_request_title); + mMessage = activity.getString(R.string.deviceLocationUtil_default_rationale_request_messageBody); + } + + + /** + * Displays an alert dialog to the user with the title set by mTitle and the message set + * by mMessage. The default values are assigned in the constructor but can be changed + * at run-time using the setTitle() and setMessage() methods if necessary. + * + * @param callback An interface which must be implemented by the caller in order to listen + * for when the OK button has been pressed and the dialog dismissed. + */ + @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) + public void displayDialog(final RequestCallback.PermissionRequestCallback callback) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing() || activity.isDestroyed()) { + // Activity is no longer valid, don't do anything + return; + } + + // Build the alert + AlertDialog.Builder builder = new AlertDialog.Builder(activity); + builder.setTitle(mTitle) + .setMessage(mMessage) + .setCancelable(false) + .setNeutralButton("OK", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + dialogInterface.cancel(); + callback.onRationaleDialogOkPressed(requestCode); + } + }); + + // Display the alert + AlertDialog alert = builder.create(); + alert.show(); + + } + + public void setTitle(String title) { + this.mTitle = title; + } + + public void setMessage(String message) { + this.mMessage = message; + } + +}// End RationaleDialogProvider nested class + diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/RequestCallback.java b/easywaylocation/src/main/java/com/example/easywaylocation/RequestCallback.java new file mode 100644 index 0000000..f187c0e --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/RequestCallback.java @@ -0,0 +1,31 @@ +package com.example.easywaylocation; + +import android.location.Address; +import android.location.Location; + +import java.util.List; + +/** + * Created by prabhat on 2/10/18. + */ + +public class RequestCallback { + + public interface LocationRequestCallback { + void onLocationResult(Location location); + + void onFailedRequest(String result); + } + + public interface PermissionRequestCallback { + void onRationaleDialogOkPressed(int requestCode); + } + + public interface AddressRequestCallback { + void onAddressSuccessfulResult(List

addressList); + + void onAddressFailedResult(String result); + } + + +} diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/TestLocationRequest.java b/easywaylocation/src/main/java/com/example/easywaylocation/TestLocationRequest.java new file mode 100644 index 0000000..a18b23d --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/TestLocationRequest.java @@ -0,0 +1,533 @@ +package com.example.easywaylocation; + +import android.Manifest; +import android.annotation.SuppressLint; +import android.app.Activity; +import android.content.Context; +import android.content.IntentSender; +import android.content.pm.PackageManager; +import android.location.Address; +import android.location.Location; +import android.os.Build; + +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.core.app.ActivityCompat; + +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.SettingsClient; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.OnSuccessListener; +import com.google.android.gms.tasks.Task; + +import java.lang.ref.WeakReference; +import java.util.List; +import java.util.Locale; + +/** + * Created by prabhat on 2/10/18. + */ + +public class TestLocationRequest implements RequestCallback.LocationRequestCallback { + + public static final int CURRENT_LOCATION_ONE_TIME = 0; + public static final int CURRENT_LOCATION_UPDATES = 1; + public static final int LAST_KNOWN_LOCATION = 2; + public static final int SMART_LOCATION = 3; + private static final String TAG = TestLocationRequest.class.getSimpleName(); + private final WeakReference weakActivity; + private FusedLocationProviderClient mLocationClient; + private Context mContext; + private LocationRequest mLocationRequest; + private LocationCallback mLocationCallback; + // Flag to track if continuous location updates have been requested at any point within the + // object's life. False by default and set to true by any method that requests continuous updates. + private boolean mHasReceivedLocationUpdates; + // Flag to track if location updates are currently being received + private boolean mIsReceivingUpdates; + private AddressHelper mAddressHelper; + + + public TestLocationRequest(Activity activity) { + // assign the activity to the weak reference + this.weakActivity = new WeakReference<>(activity); + + + // Hold a reference to the Application Context single object + this.mContext = activity.getApplicationContext(); + + // Instantiate the location client + this.mLocationClient = LocationServices.getFusedLocationProviderClient(mContext); + + // Set the request state flags to false by default + mHasReceivedLocationUpdates = false; + mIsReceivingUpdates = false; + + // Set up the default LocationRequest parameters (these can be changed at run-time with a + // call to setLocationRequestParams) + this.mLocationRequest = new LocationRequest(); + mAddressHelper = new AddressHelper(mContext, Locale.getDefault()); + setLocationRequestParams(30000, 10000, LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY); + // Sets up the LocationRequest with an update interval of 30 seconds, a fastest + // update interval cap of 10 seconds and using balanced power accuracy priority. + } + + /** + * Retrieves and returns the device's cached last known location via an instance of + * RequestCallback.com.example.easywaylocation.RequestCallback.LocationRequestCallback, which must be implemented by the caller. + *

+ * Location can be null in certain circumstances, for example on a new or recently + * factory-reset device or if location services are turned off in device settings. + * + * @param callback An interface which must be implemented by the caller in order to + * receive the results of the location request. + */ + @SuppressLint("MissingPermission") + public void getLastKnownLocation(final RequestCallback.LocationRequestCallback callback) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + final Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing()) { + // Activity is no longer valid, don't do anything + return; + } + + // Request the last known location from the location client + mLocationClient.getLastLocation() + .addOnSuccessListener(activity, new OnSuccessListener() { + @Override + public void onSuccess(Location location) { + + if (location != null) { + // Call back to the main thread with the location result + callback.onLocationResult(location); + } else { + // Call back to the main thread to advise of a null result + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_request_returned_null)); + } + + } + }); + + } + + + /** + * Returns the device's current location via an instance of RequestCallback.com.example.easywaylocation.RequestCallback.LocationRequestCallback, + * which must be implemented by the caller. + *

+ * Turns on location updates, retrieves the current device location then turns + * location updates off again. This can be used if last location returns null + * but location services are turned on, or if a more recent or accurate location + * is needed than the one stored in the device's cache. + * + * @param callback An interface which must be implemented by the caller in order to + * receive the results of the location request. + */ + @SuppressLint("MissingPermission") + public void getCurrentLocationOneTime(final RequestCallback.LocationRequestCallback callback) { + if (mIsReceivingUpdates) { + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_requests_currently_active)); + return; + } + + // Set up the RequestCallback.LocationRequestCallback for the request + mLocationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + + // Update the request state flags + mHasReceivedLocationUpdates = true; + mIsReceivingUpdates = true; + + if (locationResult != null) { + callback.onLocationResult(locationResult.getLastLocation()); + } else { + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_request_returned_null)); + } + + // Stop location updates on result (even if null) + stopLocationUpdates(); + + } + }; + + // Start the request + mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null); + + } + + /** + * Starts a location update request with the parameters specified by mLocationRequest and + * returns the location result via an instance of RequestCallback.com.example.easywaylocation.RequestCallback.LocationRequestCallback, which must be + * implemented by the caller. + *

+ * This is inherently power-intensive so care should be taken to balance the frequency + * of requested updates with the need for accuracy. + *

+ * Location updates should be disabled using the stopLocationUpdates() method when no + * longer needed, such as when the user closes or otherwise navigates away from the app. + * + * @param callback An interface which must be implemented by the caller in order to + * receive the results of the location request. + * @see TestLocationRequest#stopLocationUpdates() + */ + @SuppressLint("MissingPermission") + public void getCurrentLocationUpdates(final RequestCallback.LocationRequestCallback callback) { + if (mIsReceivingUpdates) { + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_requests_currently_active)); + return; + } + + // Set up the LocationCallback for the request + mLocationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + + // Update the request state flags + mHasReceivedLocationUpdates = true; + mIsReceivingUpdates = true; + + if (locationResult != null) { + callback.onLocationResult(locationResult.getLastLocation()); + } else { + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_request_returned_null)); + } + + } + + }; + + // Start the request + mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null); + + } + + /** + * Returns the best available location via an instance of RequestCallback.com.example.easywaylocation.RequestCallback.LocationRequestCallback, which + * must be implemented by the caller. + *

+ * Checks if last known location is available. If unavailable (null) then a single + * location update is requested instead. + *

+ * If current location is also unavailable (due to a disabled service for example) + * then an onFailedRequest callback is executed to be handled by the caller. + *

+ * This method should be preferred over the getLastKnownLocation() and + * getCurrentLocationOneTime() methods in most use cases. + * + * @param callback An interface which must be implemented by the caller in order to + * receive the results of the location request. + */ + @SuppressLint("MissingPermission") + public void getSmartLocation(final RequestCallback.LocationRequestCallback callback) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing()) { + // Activity is no longer valid, don't do anything + return; + } + + // Request the last known location from the location client + mLocationClient.getLastLocation() + .addOnSuccessListener(activity, new OnSuccessListener() { + @Override + public void onSuccess(Location location) { + + if (location != null) { + + // Call back to the main thread with the location result + Log.i(TAG, "getSmartLocation(): " + + mContext.getString(R.string.deviceLocationUtil_location_provided_by_lastLocation)); + callback.onLocationResult(location); + + } else { + // Location result is null so request location updates to attempt to get + // the device's current location. + + // Set up the LocationCallback for the request + mLocationCallback = new LocationCallback() { + @Override + public void onLocationResult(LocationResult locationResult) { + + // Update the request state flags + mHasReceivedLocationUpdates = true; + mIsReceivingUpdates = true; + + if (locationResult != null) { + callback.onLocationResult(locationResult.getLastLocation()); + Log.i(TAG, "getSmartLocation(): " + + mContext.getString(R.string.deviceLocationUtil_location_provided_by_locationUpdates)); + // Stop location updates now that we have a location result + stopLocationUpdates(); + } else { + callback.onFailedRequest(mContext.getString(R.string.deviceLocationUtil_request_returned_null)); + // Stop location updates on null result + stopLocationUpdates(); + } + + } + + }; + + // Start the request + mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null); + + } + + } + }); + + } + + public boolean permissionIsGranted() { + + int permissionState = ActivityCompat.checkSelfPermission(mContext, + Manifest.permission.ACCESS_FINE_LOCATION); + + return permissionState == PackageManager.PERMISSION_GRANTED; + } + + + /** + * Explicitly requests permission to access the device's fine location. + *

+ * Determines if additional rationale should be provided to the user, displays it if + * so then initiates a permission request via a call to startPermissionRequest(). + * + * @param requestCode A package-defined int constant to identify the request. + * It is returned to the onRequestPermissionsResult callback + * which must be implemented by the caller. + */ + public void requestPermission(final int requestCode) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing()) { + // Activity is no longer valid, don't do anything + return; + } + + // Check device SDK version as run-time permissions were only introduced in SDK ver.23 + if (Build.VERSION.SDK_INT >= 23) { + + // Determine if additional rationale for the permission request should be displayed to the user + boolean shouldProvideRationale = ActivityCompat.shouldShowRequestPermissionRationale( + activity, Manifest.permission.ACCESS_FINE_LOCATION); + + if (shouldProvideRationale) { + + // Provide additional rationale to the user. This would happen if the user denied the request + // previously but didn't tick the "Don't ask again" checkbox. + RationaleDialogProvider dialog = new RationaleDialogProvider(activity, requestCode); + dialog.displayDialog(new RequestCallback.PermissionRequestCallback() { + @Override + public void onRationaleDialogOkPressed(int requestCode) { + startPermissionRequest(requestCode); + } + }); + + } else { + + // Request permission. It's possible this can be auto-answered if the device policy sets + // the permission in a given state or the user denied the request previously and ticked + // the "Don't ask again" checkbox. + startPermissionRequest(requestCode); + } + + } + // Fail gracefully if SDK version < 23 + } + + + /** + * Called by requestPermission() to initiate a permission request + * + * @param requestCode The request code passed in by requestPermission() + * @see TestLocationRequest#requestPermission(int) + */ + private void startPermissionRequest(int requestCode) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing()) { + // Activity is no longer valid, don't do anything + return; + } + + ActivityCompat.requestPermissions(activity, + new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, + requestCode); + // requestCode is an int constant. The onRequestPermissionsResult callback + // gets the result of the request. The calling Activity should implement + // ActivityCompat.OnRequestPermissionsResultCallback then override the + // onRequestPermissionsResult() method to handle the result. + } + + + /** + * Checks if all required location settings are satisfied + *

+ * If the settings are not satisfied a dialog requesting the user enable the required + * settings will be displayed. The result of the request can be checked in + * onActivityResult() in the calling Activity if necessary. + * + * @param requestCode A package-defined int constant to identify the request. + * It is returned to the onActivityResult callback which + * must be implemented by the caller. + */ + public void checkDeviceSettings(final int requestCode) { + // Re-acquire a strong reference to the calling activity and verify that it still exists and is active + final Activity activity = weakActivity.get(); + if (activity == null || activity.isFinishing()) { + // Activity is no longer valid, don't do anything + return; + } + + // Create a settings request builder and pass it the LocationRequest + LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder() + .addLocationRequest(mLocationRequest); + + // Create a settings client + SettingsClient client = LocationServices.getSettingsClient(mContext); + + // Create a Task from the client + Task task = client.checkLocationSettings(builder.build()); + + // Query the result of the Task to determine if the required location settings are satisfied + task.addOnSuccessListener(activity, new OnSuccessListener() { + @Override + public void onSuccess(LocationSettingsResponse locationSettingsResponse) { + // Location settings are satisfied, no need for further action + Log.i(TAG, mContext.getString(R.string.deviceLocationUtil_location_settings_satisfied)); + } + }); + task.addOnFailureListener(activity, e -> { + if (e instanceof ResolvableApiException) { + // Location settings are not satisfied, display a dialog to the user + // requesting the settings to be enabled + Log.e(TAG, mContext.getString(R.string.deviceLocationUtil_location_settings_not_satisfied)); + try { + ResolvableApiException resolvable = (ResolvableApiException) e; + // Show the dialog + resolvable.startResolutionForResult(activity, requestCode); + } catch (IntentSender.SendIntentException sendException) { + // Ignore the error. + } + + } + }); + + } + + + /** + * Determines if location updates are currently active or not + * + * @return True if receiving location updates, false if not. + */ + public boolean isReceivingLocationUpdates() { + return mIsReceivingUpdates; + } + + + /** + * Determines if continuous location updates have been initiated at any + * point within the object's life. + * + * @return True if so, false if not. + */ + public boolean hasEverReceivedLocationUpdates() { + return mHasReceivedLocationUpdates; + } + + + /** + * Stops location updates from being received. + *

+ * This should be called in the calling Activity's onPause() method so + * that location updates don't continue in the background when the user + * navigates away from the app (unless such functionality is explicitly + * required). + */ + public void stopLocationUpdates() { + + if (mLocationCallback != null && mIsReceivingUpdates) { + mLocationClient.removeLocationUpdates(mLocationCallback); + mIsReceivingUpdates = false; + Log.i(TAG, mContext.getString(R.string.deviceLocationUtil_location_updates_removed)); + } + + } + + + /** + * Resumes location updates if they have previously been set up + *

+ * This should be called in the calling Activity's onResume() method if + * you want your app to continue to receive location updates when it resumes + * + * @see TestLocationRequest#stopLocationUpdates() + */ + @SuppressLint("MissingPermission") + public void resumeLocationUpdates() { + + if (mLocationCallback != null && !mIsReceivingUpdates) { + mLocationClient.requestLocationUpdates(mLocationRequest, mLocationCallback, null); + mIsReceivingUpdates = true; + Log.i(TAG, mContext.getString(R.string.deviceLocationUtil_location_updates_resumed)); + } + + } + + + /** + * Provides a way for a calling Activity to set or change the default parameters of + * the LocationRequest object to suit its needs. + * + * @param interval Set the desired interval for active location updates, in milliseconds. + * @param fastestInterval Explicitly set the fastest interval for location updates, in + * milliseconds. This controls the fastest rate at which your + * application will receive location updates. + * @param priority Set the priority of the request. Use with a priority constant such as + * LocationRequest.PRIORITY_HIGH_ACCURACY. No other values are accepted. + */ + public void setLocationRequestParams(long interval, long fastestInterval, int priority) { + + if (mLocationRequest == null) { + mLocationRequest = new LocationRequest(); + } + + mLocationRequest.setInterval(interval); + mLocationRequest.setFastestInterval(fastestInterval); + mLocationRequest.setPriority(priority); + } + + + @Override + public void onLocationResult(Location location) { + + } + + @Override + public void onFailedRequest(String result) { + + } + + + + public void getAddressList(Location loca,RequestCallback.AddressRequestCallback addressRequestCallback) { + mAddressHelper.getAddressList(loca, addressRequestCallback); + } + + public void getAddressByElement(int elementCode, Location location) { + mAddressHelper.getAddressElement(elementCode, location); + } + + +} diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DataParser.java b/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DataParser.java new file mode 100644 index 0000000..b184249 --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DataParser.java @@ -0,0 +1,100 @@ +package com.example.easywaylocation.draw_path; + +import com.google.android.gms.maps.model.LatLng; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DataParser { + /** Receives a JSONObject and returns a list of lists containing latitude and longitude */ + public List>> parse(JSONObject jObject){ + + List>> routes = new ArrayList<>() ; + JSONArray jRoutes; + JSONArray jLegs; + JSONArray jSteps; + + try { + + jRoutes = jObject.getJSONArray("routes"); + + /** Traversing all routes */ + for(int i=0;i> path = new ArrayList<>(); + + /** Traversing all legs */ + for(int j=0;j list = decodePoly(polyline); + + /** Traversing all points */ + for(int l=0;l hm = new HashMap<>(); + hm.put("lat", Double.toString((list.get(l)).latitude) ); + hm.put("lng", Double.toString((list.get(l)).longitude) ); + path.add(hm); + } + } + routes.add(path); + } + } + + } catch (JSONException e) { + e.printStackTrace(); + }catch (Exception ignored){ + } + + + return routes; + } + + + /** + * Method to decode polyline points + decoding-polylines-from-google-maps-direction-api-with-java + * */ + private List decodePoly(String encoded) { + + List poly = new ArrayList<>(); + int index = 0, len = encoded.length(); + int lat = 0, lng = 0; + + while (index < len) { + int b, shift = 0, result = 0; + do { + b = encoded.charAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lat += dlat; + + shift = 0; + result = 0; + do { + b = encoded.charAt(index++) - 63; + result |= (b & 0x1f) << shift; + shift += 5; + } while (b >= 0x20); + int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1)); + lng += dlng; + + LatLng p = new LatLng((((double) lat / 1E5)), + (((double) lng / 1E5))); + poly.add(p); + } + + return poly; + } +} diff --git a/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DirectionUtil.java b/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DirectionUtil.java new file mode 100644 index 0000000..a80dbfa --- /dev/null +++ b/easywaylocation/src/main/java/com/example/easywaylocation/draw_path/DirectionUtil.java @@ -0,0 +1,304 @@ +package com.example.easywaylocation.draw_path; + +import android.graphics.Color; +import android.os.AsyncTask; +import android.util.Log; + +import com.google.android.gms.maps.GoogleMap; +import com.google.android.gms.maps.model.LatLng; +import com.google.android.gms.maps.model.PolylineOptions; + +import org.json.JSONObject; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; + +public class DirectionUtil { + private DirectionCallBack directionCallBack; + private GoogleMap mMap; + private String directionKey; + private ArrayList wayPoints = new ArrayList<>(); + private LatLng origin; + private LatLng destination; + private int polyLineWidth = 13; + private int polyLineColor = Color.BLUE; + private boolean isEnd = false; + + private DirectionUtil(Builder builder) { + this.directionCallBack = builder.directionCallBack; + this.destination = builder.destination; + this.origin = builder.origin; + this.polyLineWidth = builder.polyLineWidth; + this.polyLineColor = builder.polyLineColor; + this.wayPoints = builder.wayPoints; + this.mMap = builder.mMap; + } + + private DirectionUtil(DirectionCallBack directionCallBack, GoogleMap googleMap, String directionKey, ArrayList wayPoints, + LatLng origin, LatLng destination, int polyLineWidth, int polyLineColor){ + + } + + public void drawPath() throws Exception { + + if (directionKey.isEmpty()){ + throw new Exception("Direction directionKey is not valid"); + } + + if (origin == null && destination == null){ + throw new Exception("Make sure Origin and destination not null"); + } + wayPoints.add(0,origin); + wayPoints.add(wayPoints.size()-1,destination); + for(int i=1;i { + + @Override + protected String doInBackground(String... url) { + + // For storing data from web service + String data = ""; + + try { + // Fetching the data from web service + data = downloadUrl(url[0]); + Log.d("Background Task data", data); + } catch (Exception e) { + Log.d("Background Task", e.toString()); + } + return data; + } + + @Override + protected void onPostExecute(String result) { + super.onPostExecute(result); + + ParserTask parserTask = new ParserTask(); + + // Invokes the thread for parsing the JSON data + parserTask.execute(result); + + } + } + + /** + * A class to parse the Google Places in JSON format + */ + private class ParserTask extends AsyncTask>>> { + + // Parsing the data in non-ui thread + @Override + protected List>> doInBackground(String... jsonData) { + + JSONObject jObject; + List>> routes = null; + + try { + jObject = new JSONObject(jsonData[0]); + Log.d("ParserTask", jsonData[0]); + DataParser parser = new DataParser(); + Log.d("ParserTask", parser.toString()); + + // Starts parsing data + routes = parser.parse(jObject); + Log.d("ParserTask", "Executing routes"); + Log.d("ParserTask", routes.toString()); + + } catch (Exception e) { + Log.d("ParserTask", e.toString()); + e.printStackTrace(); + } + return routes; + } + + // Executes in UI thread, after the parsing process + @Override + protected void onPostExecute(List>> result) { + ArrayList points; + PolylineOptions lineOptions = null; + + // Traversing through all the routes + for (int i = 0; i < result.size(); i++) { + points = new ArrayList<>(); + lineOptions = new PolylineOptions(); + + // Fetching i-th route + List> path = result.get(i); + + // Fetching all the points in i-th route + for (int j = 0; j < path.size(); j++) { + HashMap point = path.get(j); + + double lat = Double.parseDouble(point.get("lat")); + double lng = Double.parseDouble(point.get("lng")); + LatLng position = new LatLng(lat, lng); + + points.add(position); + } + + // Adding all the points in the route to LineOptions + lineOptions.addAll(points); + lineOptions.width(polyLineWidth); + lineOptions.color(polyLineColor); + if (isEnd){ + directionCallBack.pathFindFinish(); + isEnd = false; + } + Log.d("onPostExecute", "onPostExecute lineoptions decoded"); + + } + + // Drawing polyline in the Google Map for the i-th route + if (lineOptions != null) { + mMap.addPolyline(lineOptions); + } else { + Log.d("onPostExecute", "without Polylines drawn"); + } + } + } + + public static class Builder{ + private DirectionCallBack directionCallBack; + private GoogleMap mMap; + private String key; + private ArrayList wayPoints = new ArrayList<>(); + private LatLng origin; + private LatLng destination; + private int polyLineWidth = 13; + private int polyLineColor = Color.BLUE; + + public Builder setCallback(final DirectionCallBack directionCallBack){ + this.directionCallBack = directionCallBack; + return this; + } + + public Builder setWayPoints(final ArrayList wayPoints){ + this.wayPoints = wayPoints; + return this; + } + + public Builder setOrigin(final LatLng origin){ + this.origin = origin; + return this; + } + + public Builder setDestination(final LatLng destination){ + this.destination = destination; + return this; + } + + public Builder setDirectionKey(final String key){ + this.key = key; + return this; + } + + public Builder setGoogleMap(final GoogleMap map){ + this.mMap = map; + return this; + } + + public Builder setPolyLineWidth(int polyLineWidth){ + this.polyLineWidth = polyLineWidth; + return this; + } + + public Builder setPolyLineColor(int color){ + this.polyLineColor = color; + return this; + } + + public DirectionUtil build(){ + return new DirectionUtil(this); + } + } + +} diff --git a/easywaylocation/src/main/res/values/strings.xml b/easywaylocation/src/main/res/values/strings.xml index 1d6f04c..c04780e 100644 --- a/easywaylocation/src/main/res/values/strings.xml +++ b/easywaylocation/src/main/res/values/strings.xml @@ -1,4 +1,34 @@ EasyWayLocation OK + + Location Permission Required + + This app requires location permissions in order to provide location-aware functionality. + If the permission is not granted any features that rely on such functionality may not work correctly. + + + Location request returned null + Device is already receiving location updates + + + Location provided by mLocationClient.getLastLocation() + + + Location provided by mLocationClient.requestLocationUpdates() + + + Location settings satisfied + + + Location settings not satisfied. Attempting to resolveā€¦ + + Location updates removed + Location updates resumed + + Geocoder not available. Check network connection. + Invalid latitude or longitude + No address found for this location + Invalid element code + diff --git a/gradle.properties b/gradle.properties index aac7c9b..9e6fce1 100644 --- a/gradle.properties +++ b/gradle.properties @@ -9,6 +9,8 @@ # Specifies the JVM arguments used for the daemon process. # The setting is particularly useful for tweaking memory settings. +android.enableJetifier=true +android.useAndroidX=true org.gradle.jvmargs=-Xmx1536m # When configured, Gradle will run in incubating parallel mode. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 356fb91..485c2ee 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ -#Sun Feb 11 19:37:10 IST 2018 +#Sun Jul 21 13:54:49 IST 2019 distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-5.1.1-all.zip