diff --git a/app/build.gradle b/app/build.gradle
index 1076f3e46..afc711052 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -1,4 +1,4 @@
-apply from: '../gitutils.gradle'
+//apply from: '../gitutils.gradle'
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
@@ -78,10 +78,9 @@ android {
defaultConfig {
applicationId 'fr.free.nrw.commons'
-
versionCode 76
versionName '2.6.1'
- setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
+ //setProperty("archivesBaseName", "app-commons-v$versionName-" + getBranchName())
minSdkVersion project.minSdkVersion
targetSdkVersion project.targetSdkVersion
@@ -100,7 +99,7 @@ android {
}
debug {
testCoverageEnabled true
- versionNameSuffix "-debug-" + getBranchName() + "~" + getBuildVersion()
+ //versionNameSuffix "-debug-" + getBranchName() + "~" + getBuildVersion()
}
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b0cbdd34f..872d02b9e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -16,6 +16,9 @@
+
+
+
locationListeners = new CopyOnWriteArrayList<>();
+ private boolean isLocationManagerRegistered = false;
@Inject
public LocationServiceManager(Context context) {
+ this.context = context;
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
- provider = locationManager.getBestProvider(new Criteria(), true);
}
public boolean isProviderEnabled() {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
}
- public LatLng getLastLocation() {
- return lastLocation;
+ public boolean isLocationPermissionGranted() {
+ return ContextCompat.checkSelfPermission(context,
+ Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
}
- /**
- * Returns the accuracy of the location. The measurement is
- * given as a radius in meter of 68 % confidence.
- *
- * @return Float
- */
- public Float getLatestLocationAccuracy() {
- return latestLocationAccuracy;
+ public void requestPermissions(Activity activity) {
+ if (activity.isFinishing()) {
+ return;
+ }
+ ActivityCompat.requestPermissions(activity,
+ new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
+ LOCATION_REQUEST);
+ }
+
+ public boolean isPermissionExplanationRequired(Activity activity) {
+ if (activity.isFinishing()) {
+ return false;
+ }
+ return ActivityCompat.shouldShowRequestPermissionRationale(activity,
+ Manifest.permission.ACCESS_FINE_LOCATION);
+ }
+
+ public LatLng getLastLocation() {
+ if (lastLocation == null) {
+ return null;
+ }
+ return LatLng.from(lastLocation);
}
/** Registers a LocationManager to listen for current location.
*/
public void registerLocationManager() {
+ if (!isLocationManagerRegistered)
+ isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
+ && requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
+ }
+
+ private boolean requestLocationUpdatesFromProvider(String locationProvider) {
try {
- locationManager.requestLocationUpdates(provider, 400, 1, this);
- Location location = locationManager.getLastKnownLocation(provider);
- //Location works, just need to 'send' GPS coords
- // via emulator extended controls if testing on emulator
- Timber.d("Checking for location...");
- if (location != null) {
- this.onLocationChanged(location);
- }
+ locationManager.requestLocationUpdates(locationProvider,
+ MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS,
+ MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS,
+ this);
+ return true;
} catch (IllegalArgumentException e) {
Timber.e(e, "Illegal argument exception");
+ return false;
} catch (SecurityException e) {
Timber.e(e, "Security exception");
+ return false;
}
}
+ protected boolean isBetterLocation(Location location, Location currentBestLocation) {
+ if (currentBestLocation == null) {
+ // A new location is always better than no location
+ return true;
+ }
+
+ // Check whether the new location fix is newer or older
+ long timeDelta = location.getTime() - currentBestLocation.getTime();
+ boolean isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS;
+ boolean isSignificantlyOlder = timeDelta < -MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS;
+ boolean isNewer = timeDelta > 0;
+
+ // If it's been more than two minutes since the current location, use the new location
+ // because the user has likely moved
+ if (isSignificantlyNewer) {
+ return true;
+ // If the new location is more than two minutes older, it must be worse
+ } else if (isSignificantlyOlder) {
+ return false;
+ }
+
+ // Check whether the new location fix is more or less accurate
+ int accuracyDelta = (int) (location.getAccuracy() - currentBestLocation.getAccuracy());
+ boolean isLessAccurate = accuracyDelta > 0;
+ boolean isMoreAccurate = accuracyDelta < 0;
+ boolean isSignificantlyLessAccurate = accuracyDelta > 200;
+
+ // Check if the old and new location are from the same provider
+ boolean isFromSameProvider = isSameProvider(location.getProvider(),
+ currentBestLocation.getProvider());
+
+ // Determine location quality using a combination of timeliness and accuracy
+ if (isMoreAccurate) {
+ return true;
+ } else if (isNewer && !isLessAccurate) {
+ return true;
+ } else if (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) {
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Checks whether two providers are the same
+ */
+ private boolean isSameProvider(String provider1, String provider2) {
+ if (provider1 == null) {
+ return provider2 == null;
+ }
+ return provider1.equals(provider2);
+ }
+
/** Unregisters location manager.
*/
public void unregisterLocationManager() {
+ isLocationManagerRegistered = false;
try {
locationManager.removeUpdates(this);
} catch (SecurityException e) {
@@ -89,15 +170,11 @@ public class LocationServiceManager implements LocationListener {
@Override
public void onLocationChanged(Location location) {
- double currentLatitude = location.getLatitude();
- double currentLongitude = location.getLongitude();
- latestLocationAccuracy = location.getAccuracy();
- Timber.d("Latitude: %f Longitude: %f Accuracy %f",
- currentLatitude, currentLongitude, latestLocationAccuracy);
- lastLocation = new LatLng(currentLatitude, currentLongitude, latestLocationAccuracy);
-
- for (LocationUpdateListener listener : locationListeners) {
- listener.onLocationChanged(lastLocation);
+ if (isBetterLocation(location, lastLocation)) {
+ lastLocation = location;
+ for (LocationUpdateListener listener : locationListeners) {
+ listener.onLocationChanged(LatLng.from(lastLocation));
+ }
}
}
diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
index 578b5e91e..186746196 100644
--- a/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
+++ b/app/src/main/java/fr/free/nrw/commons/nearby/NearbyActivity.java
@@ -1,6 +1,5 @@
package fr.free.nrw.commons.nearby;
-import android.Manifest;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
@@ -10,10 +9,8 @@ import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
-import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentTransaction;
-import android.support.v4.content.ContextCompat;
import android.support.v7.app.AlertDialog;
import android.view.Menu;
import android.view.MenuInflater;
@@ -46,12 +43,13 @@ import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import timber.log.Timber;
+import static fr.free.nrw.commons.location.LocationServiceManager.LOCATION_REQUEST;
+
public class NearbyActivity extends NavigationBaseActivity implements LocationUpdateListener {
@BindView(R.id.progressBar)
ProgressBar progressBar;
- private static final int LOCATION_REQUEST = 1;
private static final String MAP_LAST_USED_PREFERENCE = "mapLastUsed";
@Inject
@@ -70,7 +68,6 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
setContentView(R.layout.activity_nearby);
ButterKnife.bind(this);
- checkLocationPermission();
bundle = new Bundle();
initDrawer();
initViewState();
@@ -102,7 +99,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
// Handle item selection
switch (item.getItemId()) {
case R.id.action_refresh:
- lockNearbyView = false;
+ lockNearbyView(false);
refreshView(true);
return true;
case R.id.action_toggle_view:
@@ -115,52 +112,9 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
}
}
- private void checkLocationPermission() {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
- if (ContextCompat.checkSelfPermission(this,
- Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
- refreshView(false);
- } else {
- if (ContextCompat.checkSelfPermission(this,
- Manifest.permission.ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
-
- // Should we show an explanation?
- if (ActivityCompat.shouldShowRequestPermissionRationale(this,
- Manifest.permission.ACCESS_FINE_LOCATION)) {
-
- // Show an explanation to the user *asynchronously* -- don't block
- // this thread waiting for the user's response! After the user
- // sees the explanation, try again to request the permission.
-
- new AlertDialog.Builder(this)
- .setMessage(getString(R.string.location_permission_rationale))
- .setPositiveButton("OK", (dialog, which) -> {
- ActivityCompat.requestPermissions(NearbyActivity.this,
- new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
- LOCATION_REQUEST);
- dialog.dismiss();
- })
- .setNegativeButton("Cancel", null)
- .create()
- .show();
-
- } else {
-
- // No explanation needed, we can request the permission.
-
- ActivityCompat.requestPermissions(this,
- new String[]{Manifest.permission.ACCESS_FINE_LOCATION},
- LOCATION_REQUEST);
-
- // MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
- // app-defined int constant. The callback method gets the
- // result of the request.
- }
- }
- }
- } else {
- refreshView(false);
+ private void requestLocationPermissions() {
+ if (!isFinishing()) {
+ locationManager.requestPermissions(this);
}
}
@@ -185,7 +139,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
.setCancelable(false)
.setPositiveButton(R.string.give_permission, (dialog, which) -> {
//will ask for the location permission again
- checkLocationPermission();
+ checkGps();
})
.setNegativeButton(R.string.cancel, (dialog, which) -> {
//dismiss dialog and finish activity
@@ -214,6 +168,37 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
.show();
} else {
Timber.d("GPS is enabled");
+ checkLocationPermission();
+ }
+ }
+
+ private void checkLocationPermission() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
+ if (locationManager.isLocationPermissionGranted()) {
+ refreshView(false);
+ } else {
+ // Should we show an explanation?
+ if (locationManager.isPermissionExplanationRequired(this)) {
+ // Show an explanation to the user *asynchronously* -- don't block
+ // this thread waiting for the user's response! After the user
+ // sees the explanation, try again to request the permission.
+ new AlertDialog.Builder(this)
+ .setMessage(getString(R.string.location_permission_rationale_nearby))
+ .setPositiveButton("OK", (dialog, which) -> {
+ requestLocationPermissions();
+ dialog.dismiss();
+ })
+ .setNegativeButton("Cancel", null)
+ .create()
+ .show();
+
+ } else {
+ // No explanation needed, we can request the permission.
+ requestLocationPermissions();
+ }
+ }
+ } else {
+ refreshView(false);
}
}
@@ -238,7 +223,6 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
@Override
protected void onStart() {
super.onStart();
- locationManager.registerLocationManager();
locationManager.addLocationListener(this);
}
@@ -262,13 +246,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
super.onResume();
lockNearbyView = false;
checkGps();
- refreshView(false);
}
+ /**
+ * This method should be the single point to load/refresh nearby places
+ *
+ * @param isHardRefresh
+ */
private void refreshView(boolean isHardRefresh) {
if (lockNearbyView) {
return;
}
+ locationManager.registerLocationManager();
LatLng lastLocation = locationManager.getLastLocation();
if (curLatLang != null && curLatLang.equals(lastLocation)) { //refresh view only if location has changed
if (isHardRefresh) {
@@ -314,7 +303,7 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
bundle.putString("PlaceList", gsonPlaceList);
bundle.putString("CurLatLng", gsonCurLatLng);
- lockNearbyView = true;
+ lockNearbyView(true);
// Begin the transaction
if (viewMode.isMap()) {
setMapFragment();
@@ -325,6 +314,18 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
hideProgressBar();
}
+ private void lockNearbyView(boolean lock) {
+ if (lock) {
+ lockNearbyView = true;
+ locationManager.unregisterLocationManager();
+ locationManager.removeLocationListener(this);
+ } else {
+ lockNearbyView = false;
+ locationManager.registerLocationManager();
+ locationManager.addLocationListener(this);
+ }
+ }
+
private void hideProgressBar() {
if (progressBar != null) {
progressBar.setVisibility(View.GONE);
@@ -353,11 +354,6 @@ public class NearbyActivity extends NavigationBaseActivity implements LocationUp
fragmentTransaction.commitAllowingStateLoss();
}
- public static void startYourself(Context context) {
- Intent settingsIntent = new Intent(context, NearbyActivity.class);
- context.startActivity(settingsIntent);
- }
-
@Override
public void onLocationChanged(LatLng latLng) {
refreshView(false);
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 81da9000f..04d49faee 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -213,4 +213,5 @@ Tap this message (or hit back) to skip this step.
Location has not changed.
Location not available.
+ Permission required to display a list of nearby places