mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 14:53:59 +01:00 
			
		
		
		
	Migrated location and language module from Java to Kotlin
This commit is contained in:
		
							parent
							
								
									574b9e3c15
								
							
						
					
					
						commit
						4148049156
					
				
					 10 changed files with 467 additions and 521 deletions
				
			
		|  | @ -423,7 +423,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { | |||
|      * Moves map to GPS location | ||||
|      */ | ||||
|     private fun moveMapToGPSLocation() { | ||||
|         locationManager.lastLocation?.let { | ||||
|         locationManager.lastLocationVar?.let { | ||||
|             moveMapTo(GeoPoint(it.latitude, it.longitude)) | ||||
|         } | ||||
|     } | ||||
|  | @ -591,7 +591,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { | |||
| 
 | ||||
|     override fun onLocationPermissionGranted() { | ||||
|         if (moveToCurrentLocation || activity != "MediaActivity") { | ||||
|             if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn) { | ||||
|             if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { | ||||
|                 locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) | ||||
|                 locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) | ||||
|                 addMarkerAtGPSLocation() | ||||
|  | @ -606,7 +606,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { | |||
|      * Adds a marker at the user's GPS location | ||||
|      */ | ||||
|     private fun addMarkerAtGPSLocation() { | ||||
|         locationManager.lastLocation?.let { | ||||
|         locationManager.lastLocationVar?.let { | ||||
|             addLocationMarker(GeoPoint(it.latitude, it.longitude)) | ||||
|             markerImage.translationY = 0f | ||||
|         } | ||||
|  |  | |||
|  | @ -1,116 +1,111 @@ | |||
| package fr.free.nrw.commons.language; | ||||
| package fr.free.nrw.commons.language | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.Resources; | ||||
| import android.text.TextUtils; | ||||
| import android.content.Context | ||||
| import android.content.res.Resources | ||||
| import android.text.TextUtils | ||||
| 
 | ||||
| import androidx.annotation.ArrayRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.annotation.ArrayRes | ||||
| import fr.free.nrw.commons.R | ||||
| import java.lang.ref.SoftReference | ||||
| import java.util.Arrays | ||||
| import java.util.Locale | ||||
| 
 | ||||
| import fr.free.nrw.commons.R; | ||||
| import java.lang.ref.SoftReference; | ||||
| import java.util.Arrays; | ||||
| import java.util.List; | ||||
| import java.util.Locale; | ||||
| 
 | ||||
| /** Immutable look up table for all app supported languages. All article languages may not be | ||||
|   * present in this table as it is statically bundled with the app. */ | ||||
| public class AppLanguageLookUpTable { | ||||
|     public static final String SIMPLIFIED_CHINESE_LANGUAGE_CODE = "zh-hans"; | ||||
|     public static final String TRADITIONAL_CHINESE_LANGUAGE_CODE = "zh-hant"; | ||||
|     public static final String CHINESE_CN_LANGUAGE_CODE = "zh-cn"; | ||||
|     public static final String CHINESE_HK_LANGUAGE_CODE = "zh-hk"; | ||||
|     public static final String CHINESE_MO_LANGUAGE_CODE = "zh-mo"; | ||||
|     public static final String CHINESE_SG_LANGUAGE_CODE = "zh-sg"; | ||||
|     public static final String CHINESE_TW_LANGUAGE_CODE = "zh-tw"; | ||||
|     public static final String CHINESE_YUE_LANGUAGE_CODE = "zh-yue"; | ||||
|     public static final String CHINESE_LANGUAGE_CODE = "zh"; | ||||
|     public static final String NORWEGIAN_LEGACY_LANGUAGE_CODE = "no"; | ||||
|     public static final String NORWEGIAN_BOKMAL_LANGUAGE_CODE = "nb"; | ||||
|     public static final String TEST_LANGUAGE_CODE = "test"; | ||||
|     public static final String FALLBACK_LANGUAGE_CODE = "en"; // Must exist in preference_language_keys. | ||||
|  * present in this table as it is statically bundled with the app. */ | ||||
| class AppLanguageLookUpTable(context: Context) { | ||||
| 
 | ||||
|     @NonNull private final Resources resources; | ||||
|     companion object { | ||||
|         const val SIMPLIFIED_CHINESE_LANGUAGE_CODE = "zh-hans" | ||||
|         const val TRADITIONAL_CHINESE_LANGUAGE_CODE = "zh-hant" | ||||
|         const val CHINESE_CN_LANGUAGE_CODE = "zh-cn" | ||||
|         const val CHINESE_HK_LANGUAGE_CODE = "zh-hk" | ||||
|         const val CHINESE_MO_LANGUAGE_CODE = "zh-mo" | ||||
|         const val CHINESE_SG_LANGUAGE_CODE = "zh-sg" | ||||
|         const val CHINESE_TW_LANGUAGE_CODE = "zh-tw" | ||||
|         const val CHINESE_YUE_LANGUAGE_CODE = "zh-yue" | ||||
|         const val CHINESE_LANGUAGE_CODE = "zh" | ||||
|         const val NORWEGIAN_LEGACY_LANGUAGE_CODE = "no" | ||||
|         const val NORWEGIAN_BOKMAL_LANGUAGE_CODE = "nb" | ||||
|         const val TEST_LANGUAGE_CODE = "test" | ||||
|         const val FALLBACK_LANGUAGE_CODE = "en" // Must exist in preference_language_keys. | ||||
|     } | ||||
| 
 | ||||
|     private val resources: Resources = context.resources | ||||
| 
 | ||||
|     // Language codes for all app supported languages in fixed order. The special code representing | ||||
|     // the dynamic system language is null. | ||||
|     @NonNull private SoftReference<List<String>> codesRef = new SoftReference<>(null); | ||||
|     private var codesRef = SoftReference<List<String>>(null) | ||||
| 
 | ||||
|     // English names for all app supported languages in fixed order. | ||||
|     @NonNull private SoftReference<List<String>> canonicalNamesRef = new SoftReference<>(null); | ||||
|     private var canonicalNamesRef = SoftReference<List<String>>(null) | ||||
| 
 | ||||
|     // Native names for all app supported languages in fixed order. | ||||
|     @NonNull private SoftReference<List<String>> localizedNamesRef = new SoftReference<>(null); | ||||
| 
 | ||||
|     public AppLanguageLookUpTable(@NonNull Context context) { | ||||
|         resources = context.getResources(); | ||||
|     } | ||||
|     private var localizedNamesRef = SoftReference<List<String>>(null) | ||||
| 
 | ||||
|     /** | ||||
|      * @return Nonnull immutable list. The special code representing the dynamic system language is | ||||
|      *         null. | ||||
|      */ | ||||
|     @NonNull | ||||
|     public List<String> getCodes() { | ||||
|         List<String> codes = codesRef.get(); | ||||
|     fun getCodes(): List<String> { | ||||
|         var codes = codesRef.get() | ||||
|         if (codes == null) { | ||||
|             codes = getStringList(R.array.preference_language_keys); | ||||
|             codesRef = new SoftReference<>(codes); | ||||
|             codes = getStringList(R.array.preference_language_keys) | ||||
|             codesRef = SoftReference(codes) | ||||
|         } | ||||
|         return codes; | ||||
|         return codes | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public String getCanonicalName(@Nullable String code) { | ||||
|         String name = defaultIndex(getCanonicalNames(), indexOfCode(code), null); | ||||
|         if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) { | ||||
|             if (code.equals(Locale.CHINESE.getLanguage())) { | ||||
|                 name = Locale.CHINESE.getDisplayName(Locale.ENGLISH); | ||||
|             } else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) { | ||||
|                 name = defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null); | ||||
|     fun getCanonicalName(code: String?): String? { | ||||
|         var name = defaultIndex(getCanonicalNames(), indexOfCode(code), null) | ||||
|         if (name.isNullOrEmpty() && !code.isNullOrEmpty()) { | ||||
|             name = when (code) { | ||||
|                 Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.ENGLISH) | ||||
|                 NORWEGIAN_LEGACY_LANGUAGE_CODE -> | ||||
|                     defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null) | ||||
|                 else -> null | ||||
|             } | ||||
|         } | ||||
|         return name; | ||||
|         return name | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public String getLocalizedName(@Nullable String code) { | ||||
|         String name = defaultIndex(getLocalizedNames(), indexOfCode(code), null); | ||||
|         if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) { | ||||
|             if (code.equals(Locale.CHINESE.getLanguage())) { | ||||
|                 name = Locale.CHINESE.getDisplayName(Locale.CHINESE); | ||||
|             } else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) { | ||||
|                 name = defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null); | ||||
|     fun getLocalizedName(code: String?): String? { | ||||
|         var name = defaultIndex(getLocalizedNames(), indexOfCode(code), null) | ||||
|         if (name.isNullOrEmpty() && !code.isNullOrEmpty()) { | ||||
|             name = when (code) { | ||||
|                 Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.CHINESE) | ||||
|                 NORWEGIAN_LEGACY_LANGUAGE_CODE -> | ||||
|                     defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null) | ||||
|                 else -> null | ||||
|             } | ||||
|         } | ||||
|         return name; | ||||
|         return name | ||||
|     } | ||||
| 
 | ||||
|     public List<String> getCanonicalNames() { | ||||
|         List<String> names = canonicalNamesRef.get(); | ||||
|     fun getCanonicalNames(): List<String> { | ||||
|         var names = canonicalNamesRef.get() | ||||
|         if (names == null) { | ||||
|             names = getStringList(R.array.preference_language_canonical_names); | ||||
|             canonicalNamesRef = new SoftReference<>(names); | ||||
|             names = getStringList(R.array.preference_language_canonical_names) | ||||
|             canonicalNamesRef = SoftReference(names) | ||||
|         } | ||||
|         return names; | ||||
|         return names | ||||
|     } | ||||
| 
 | ||||
|     public List<String> getLocalizedNames() { | ||||
|         List<String> names = localizedNamesRef.get(); | ||||
|     fun getLocalizedNames(): List<String> { | ||||
|         var names = localizedNamesRef.get() | ||||
|         if (names == null) { | ||||
|             names = getStringList(R.array.preference_language_local_names); | ||||
|             localizedNamesRef = new SoftReference<>(names); | ||||
|             names = getStringList(R.array.preference_language_local_names) | ||||
|             localizedNamesRef = SoftReference(names) | ||||
|         } | ||||
|         return names; | ||||
|         return names | ||||
|     } | ||||
| 
 | ||||
|     public boolean isSupportedCode(@Nullable String code) { | ||||
|         return getCodes().contains(code); | ||||
|     fun isSupportedCode(code: String?): Boolean { | ||||
|         return getCodes().contains(code) | ||||
|     } | ||||
| 
 | ||||
|     private <T> T defaultIndex(List<T> list, int index, T defaultValue) { | ||||
|         return inBounds(list, index) ? list.get(index) : defaultValue; | ||||
|     private fun <T> defaultIndex(list: List<T>, index: Int, defaultValue: T?): T? { | ||||
|         return if (inBounds(list, index)) list[index] else defaultValue | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -121,21 +116,20 @@ public class AppLanguageLookUpTable { | |||
|      *             language is null. | ||||
|      * @return The index of the language code or -1 if the code is not supported. | ||||
|      */ | ||||
|     private int indexOfCode(@Nullable String code) { | ||||
|         return getCodes().indexOf(code); | ||||
|     private fun indexOfCode(code: String?): Int { | ||||
|         return getCodes().indexOf(code) | ||||
|     } | ||||
| 
 | ||||
|     /** @return Nonnull immutable list. */ | ||||
|     @NonNull | ||||
|     private List<String> getStringList(int id) { | ||||
|         return Arrays.asList(getStringArray(id)); | ||||
|     private fun getStringList(id: Int): List<String> { | ||||
|         return getStringArray(id).toList() | ||||
|     } | ||||
| 
 | ||||
|     private boolean inBounds(List<?> list, int index) { | ||||
|         return index >= 0 && index < list.size(); | ||||
|     private fun inBounds(list: List<*>, index: Int): Boolean { | ||||
|         return index in list.indices | ||||
|     } | ||||
| 
 | ||||
|     public String[] getStringArray(@ArrayRes int id) { | ||||
|         return resources.getStringArray(id); | ||||
|     fun getStringArray(@ArrayRes id: Int): Array<String> { | ||||
|         return resources.getStringArray(id) | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,132 +1,106 @@ | |||
| package fr.free.nrw.commons.location; | ||||
| package fr.free.nrw.commons.location | ||||
| 
 | ||||
| import android.location.Location; | ||||
| import android.net.Uri; | ||||
| import android.os.Parcel; | ||||
| import android.os.Parcelable; | ||||
| import android.location.Location | ||||
| import android.net.Uri | ||||
| import android.os.Parcel | ||||
| import android.os.Parcelable | ||||
| import kotlin.math.abs | ||||
| import kotlin.math.max | ||||
| import kotlin.math.min | ||||
| import kotlin.math.round | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| /** | ||||
|  * a latitude and longitude point with accuracy information, often of a picture | ||||
|  * A latitude and longitude point with accuracy information, often of a picture. | ||||
|  */ | ||||
| public class LatLng implements Parcelable { | ||||
| 
 | ||||
|     private final double latitude; | ||||
|     private final double longitude; | ||||
|     private final float accuracy; | ||||
| data class LatLng( | ||||
|     var latitude: Double, | ||||
|     var longitude: Double, | ||||
|     val accuracy: Float | ||||
| ) : Parcelable { | ||||
| 
 | ||||
|     /** | ||||
|      * Accepts latitude and longitude. | ||||
|      * North and South values are cut off at 90° | ||||
|      * | ||||
|      * @param latitude the latitude | ||||
|      * @param longitude the longitude | ||||
|      * @param accuracy the accuracy | ||||
|      * | ||||
|      * Examples: | ||||
|      * the Statue of Liberty is located at 40.69° N, 74.04° W | ||||
|      * The Statue of Liberty could be constructed as LatLng(40.69, -74.04, 1.0) | ||||
|      * where positive signifies north, east and negative signifies south, west. | ||||
|      */ | ||||
|     public LatLng(double latitude, double longitude, float accuracy) { | ||||
|         if (-180.0D <= longitude && longitude < 180.0D) { | ||||
|             this.longitude = longitude; | ||||
|         } else { | ||||
|             this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D; | ||||
|     init { | ||||
|         val adjustedLongitude = when { | ||||
|             longitude in -180.0..180.0 -> longitude | ||||
|             else -> ((longitude - 180.0) % 360.0 + 360.0) % 360.0 - 180.0 | ||||
|         } | ||||
|         this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude)); | ||||
|         this.accuracy = accuracy; | ||||
|         latitude = max(-90.0, min(90.0, latitude)) | ||||
|         longitude = adjustedLongitude | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Accepts a non-null [Location] and converts it to a [LatLng]. | ||||
|      */ | ||||
|     companion object { | ||||
|         /** | ||||
|          * gets the latitude and longitude of a given non-null location | ||||
|          * @param location the non-null location of the user | ||||
|          * @return LatLng the Latitude and Longitude of a given location | ||||
|          */ | ||||
|         @JvmStatic | ||||
|         fun from(location: Location): LatLng { | ||||
|             return LatLng(location.latitude, location.longitude, location.accuracy) | ||||
|         } | ||||
| 
 | ||||
|         @JvmField | ||||
|         val CREATOR: Parcelable.Creator<LatLng> = object : Parcelable.Creator<LatLng> { | ||||
|             override fun createFromParcel(parcel: Parcel): LatLng { | ||||
|                 return LatLng(parcel) | ||||
|             } | ||||
| 
 | ||||
|             override fun newArray(size: Int): Array<LatLng?> { | ||||
|                 return arrayOfNulls(size) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * An alternate constructor for this class. | ||||
|      * @param in A parcelable which contains the latitude, longitude, and accuracy | ||||
|      * @param parcel A parcelable which contains the latitude, longitude, and accuracy | ||||
|      */ | ||||
|     public LatLng(Parcel in) { | ||||
|         latitude = in.readDouble(); | ||||
|         longitude = in.readDouble(); | ||||
|         accuracy = in.readFloat(); | ||||
|     private constructor(parcel: Parcel) : this( | ||||
|         latitude = parcel.readDouble(), | ||||
|         longitude = parcel.readDouble(), | ||||
|         accuracy = parcel.readFloat() | ||||
|     ) | ||||
| 
 | ||||
|     /** | ||||
|      * Creates a hash code for the latitude and longitude. | ||||
|      */ | ||||
|     override fun hashCode(): Int { | ||||
|         var result = 1 | ||||
|         val latitudeBits = latitude.toBits() | ||||
|         result = 31 * result + (latitudeBits xor (latitudeBits ushr 32)).toInt() | ||||
|         val longitudeBits = longitude.toBits() | ||||
|         result = 31 * result + (longitudeBits xor (longitudeBits ushr 32)).toInt() | ||||
|         return result | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * gets the latitude and longitude of a given non-null location | ||||
|      * @param location the non-null location of the user | ||||
|      * @return LatLng the Latitude and Longitude of a given location | ||||
|      * Checks for equality of two LatLng objects. | ||||
|      * @param other the second LatLng object | ||||
|      */ | ||||
|     public static LatLng from(@NonNull Location location) { | ||||
|         return new LatLng(location.getLatitude(), location.getLongitude(), location.getAccuracy()); | ||||
|     override fun equals(other: Any?): Boolean { | ||||
|         if (this === other) return true | ||||
|         if (other !is LatLng) return false | ||||
|         return latitude.toBits() == other.latitude.toBits() && | ||||
|                 longitude.toBits() == other.longitude.toBits() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * creates a hash code for the longitude and longitude | ||||
|      * Returns a string representation of the latitude and longitude. | ||||
|      */ | ||||
|     public int hashCode() { | ||||
|         byte var1 = 1; | ||||
|         long var2 = Double.doubleToLongBits(this.latitude); | ||||
|         int var3 = 31 * var1 + (int)(var2 ^ var2 >>> 32); | ||||
|         var2 = Double.doubleToLongBits(this.longitude); | ||||
|         var3 = 31 * var3 + (int)(var2 ^ var2 >>> 32); | ||||
|         return var3; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * checks for equality of two LatLng objects | ||||
|      * @param o the second LatLng object | ||||
|      */ | ||||
|     public boolean equals(Object o) { | ||||
|         if (this == o) { | ||||
|             return true; | ||||
|         } else if (!(o instanceof LatLng)) { | ||||
|             return false; | ||||
|         } else { | ||||
|             LatLng var2 = (LatLng)o; | ||||
|             return Double.doubleToLongBits(this.latitude) == Double.doubleToLongBits(var2.latitude) && Double.doubleToLongBits(this.longitude) == Double.doubleToLongBits(var2.longitude); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * returns a string representation of the latitude and longitude | ||||
|      */ | ||||
|     public String toString() { | ||||
|         return "lat/lng: (" + this.latitude + "," + this.longitude + ")"; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Rounds the float to 4 digits and returns absolute value. | ||||
|      * | ||||
|      * @param coordinate A coordinate value as string. | ||||
|      * @return String of the rounded number. | ||||
|      */ | ||||
|     private String formatCoordinate(double coordinate) { | ||||
|         double roundedNumber = Math.round(coordinate * 10000d) / 10000d; | ||||
|         double absoluteNumber = Math.abs(roundedNumber); | ||||
|         return String.valueOf(absoluteNumber); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns "N" or "S" depending on the latitude. | ||||
|      * | ||||
|      * @return "N" or "S". | ||||
|      */ | ||||
|     private String getNorthSouth() { | ||||
|         if (this.latitude < 0) { | ||||
|             return "S"; | ||||
|         } | ||||
| 
 | ||||
|         return "N"; | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns "E" or "W" depending on the longitude. | ||||
|      * | ||||
|      * @return "E" or "W". | ||||
|      */ | ||||
|     private String getEastWest() { | ||||
|         if (this.longitude >= 0 && this.longitude < 180) { | ||||
|             return "E"; | ||||
|         } | ||||
| 
 | ||||
|         return "W"; | ||||
|     override fun toString(): String { | ||||
|         return "lat/lng: ($latitude,$longitude)" | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -135,64 +109,42 @@ public class LatLng implements Parcelable { | |||
|      * | ||||
|      * @return The formatted string. | ||||
|      */ | ||||
|     public String getPrettyCoordinateString() { | ||||
|         return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", " | ||||
|                + formatCoordinate(this.longitude) + " " + this.getEastWest(); | ||||
|     fun getPrettyCoordinateString(): String { | ||||
|         return "${formatCoordinate(latitude)} ${getNorthSouth()}, " + | ||||
|                 "${formatCoordinate(longitude)} ${getEastWest()}" | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return the location accuracy in meter. | ||||
|      * | ||||
|      * @return float | ||||
|      * Gets a URI for a Google Maps intent at the location. | ||||
|      */ | ||||
|     public float getAccuracy() { | ||||
|         return accuracy; | ||||
|     fun getGmmIntentUri(): Uri { | ||||
|         return Uri.parse("geo:$latitude,$longitude?z=16") | ||||
|     } | ||||
| 
 | ||||
|     override fun writeToParcel(parcel: Parcel, flags: Int) { | ||||
|         parcel.writeDouble(latitude) | ||||
|         parcel.writeDouble(longitude) | ||||
|         parcel.writeFloat(accuracy) | ||||
|     } | ||||
| 
 | ||||
|     override fun describeContents(): Int = 0 | ||||
| 
 | ||||
|     private fun formatCoordinate(coordinate: Double): String { | ||||
|         val roundedNumber = round(coordinate * 10000) / 10000 | ||||
|         return abs(roundedNumber).toString() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Return the longitude in degrees. | ||||
|      * Returns "N" or "S" depending on the latitude. | ||||
|      * | ||||
|      * @return double | ||||
|      * @return "N" or "S". | ||||
|      */ | ||||
|     public double getLongitude() { | ||||
|         return longitude; | ||||
|     } | ||||
|     private fun getNorthSouth(): String = if (latitude < 0) "S" else "N" | ||||
| 
 | ||||
|     /** | ||||
|      * Return the latitude in degrees. | ||||
|      * Returns "E" or "W" depending on the longitude. | ||||
|      * | ||||
|      * @return double | ||||
|      * @return "E" or "W". | ||||
|      */ | ||||
|     public double getLatitude() { | ||||
|         return latitude; | ||||
|     } | ||||
| 
 | ||||
|     public Uri getGmmIntentUri() { | ||||
|         return Uri.parse("geo:" + latitude + "," + longitude + "?z=16"); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int describeContents() { | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void writeToParcel(Parcel dest, int flags) { | ||||
|         dest.writeDouble(latitude); | ||||
|         dest.writeDouble(longitude); | ||||
|         dest.writeFloat(accuracy); | ||||
|     } | ||||
| 
 | ||||
|     public static final Creator<LatLng> CREATOR = new Creator<LatLng>() { | ||||
|         @Override | ||||
|         public LatLng createFromParcel(Parcel in) { | ||||
|             return new LatLng(in); | ||||
|         } | ||||
| 
 | ||||
|         @Override | ||||
|         public LatLng[] newArray(int size) { | ||||
|             return new LatLng[size]; | ||||
|         } | ||||
|     }; | ||||
|     private fun getEastWest(): String = if (longitude in 0.0..179.999) "E" else "W" | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,18 +1,16 @@ | |||
| package fr.free.nrw.commons.location; | ||||
| package fr.free.nrw.commons.location | ||||
| 
 | ||||
| import android.Manifest; | ||||
| import android.Manifest.permission; | ||||
| import android.app.Activity; | ||||
| import android.content.Intent; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.net.Uri; | ||||
| import android.provider.Settings; | ||||
| import android.widget.Toast; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import fr.free.nrw.commons.R; | ||||
| import fr.free.nrw.commons.filepicker.Constants.RequestCodes; | ||||
| import fr.free.nrw.commons.utils.DialogUtil; | ||||
| import fr.free.nrw.commons.utils.PermissionUtils; | ||||
| import android.Manifest.permission | ||||
| import android.app.Activity | ||||
| import android.content.Intent | ||||
| import android.net.Uri | ||||
| import android.provider.Settings | ||||
| import android.widget.Toast | ||||
| import androidx.core.app.ActivityCompat | ||||
| import fr.free.nrw.commons.R | ||||
| import fr.free.nrw.commons.filepicker.Constants.RequestCodes | ||||
| import fr.free.nrw.commons.utils.DialogUtil | ||||
| import fr.free.nrw.commons.utils.PermissionUtils | ||||
| 
 | ||||
| /** | ||||
|  * Helper class to handle location permissions. | ||||
|  | @ -37,18 +35,11 @@ import fr.free.nrw.commons.utils.PermissionUtils; | |||
|  * Do whatever is required by that particular activity / fragment using current location. | ||||
|  * | ||||
|  */ | ||||
| public class LocationPermissionsHelper { | ||||
| 
 | ||||
|     Activity activity; | ||||
|     LocationServiceManager locationManager; | ||||
|     LocationPermissionCallback callback; | ||||
| 
 | ||||
|     public LocationPermissionsHelper(Activity activity, LocationServiceManager locationManager, | ||||
|         LocationPermissionCallback callback) { | ||||
|         this.activity = activity; | ||||
|         this.locationManager = locationManager; | ||||
|         this.callback = callback; | ||||
|     } | ||||
| class LocationPermissionsHelper( | ||||
|     private val activity: Activity, | ||||
|     private val locationManager: LocationServiceManager, | ||||
|     private val callback: LocationPermissionCallback? | ||||
| ) { | ||||
| 
 | ||||
|     /** | ||||
|      * Ask for location permission if the user agrees on attaching location with pictures and the | ||||
|  | @ -57,31 +48,45 @@ public class LocationPermissionsHelper { | |||
|      * @param dialogTitleResource Resource id of the title of the dialog | ||||
|      * @param dialogTextResource Resource id of the text of the dialog | ||||
|      */ | ||||
|     public void requestForLocationAccess( | ||||
|         int dialogTitleResource, | ||||
|         int dialogTextResource | ||||
|     fun requestForLocationAccess( | ||||
|         dialogTitleResource: Int, | ||||
|         dialogTextResource: Int | ||||
|     ) { | ||||
|         if (checkLocationPermission(activity)) { | ||||
|             callback.onLocationPermissionGranted(); | ||||
|             callback?.onLocationPermissionGranted() | ||||
|         } else { | ||||
|             if (ActivityCompat.shouldShowRequestPermissionRationale(activity, | ||||
|                 permission.ACCESS_FINE_LOCATION)) { | ||||
|                 DialogUtil.showAlertDialog(activity, activity.getString(dialogTitleResource), | ||||
|             if (ActivityCompat.shouldShowRequestPermissionRationale( | ||||
|                     activity, | ||||
|                     permission.ACCESS_FINE_LOCATION | ||||
|                 ) | ||||
|             ) { | ||||
|                 DialogUtil.showAlertDialog( | ||||
|                     activity, | ||||
|                     activity.getString(dialogTitleResource), | ||||
|                     activity.getString(dialogTextResource), | ||||
|                     activity.getString(android.R.string.ok), | ||||
|                     activity.getString(android.R.string.cancel), | ||||
|                     () -> { | ||||
|                         ActivityCompat.requestPermissions(activity, | ||||
|                             new String[]{permission.ACCESS_FINE_LOCATION}, 1); | ||||
|                     { | ||||
|                         ActivityCompat.requestPermissions( | ||||
|                             activity, | ||||
|                             arrayOf(permission.ACCESS_FINE_LOCATION), | ||||
|                             1 | ||||
|                         ) | ||||
|                     }, | ||||
|                     { | ||||
|                         callback?.onLocationPermissionDenied( | ||||
|                             activity.getString(R.string.upload_map_location_access) | ||||
|                         ) | ||||
|                     }, | ||||
|                     () -> callback.onLocationPermissionDenied( | ||||
|                         activity.getString(R.string.upload_map_location_access)), | ||||
|                     null, | ||||
|                     false); | ||||
|                     false | ||||
|                 ) | ||||
|             } else { | ||||
|                 ActivityCompat.requestPermissions(activity, | ||||
|                     new String[]{permission.ACCESS_FINE_LOCATION}, | ||||
|                     RequestCodes.LOCATION); | ||||
|                 ActivityCompat.requestPermissions( | ||||
|                     activity, | ||||
|                     arrayOf(permission.ACCESS_FINE_LOCATION), | ||||
|                     RequestCodes.LOCATION | ||||
|                 ) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -92,33 +97,38 @@ public class LocationPermissionsHelper { | |||
|      * @param activity Activity object | ||||
|      * @param dialogTextResource int id of the required string resource | ||||
|      */ | ||||
|     public void showLocationOffDialog(Activity activity, int dialogTextResource) { | ||||
|         DialogUtil | ||||
|             .showAlertDialog(activity, | ||||
|                 activity.getString(R.string.ask_to_turn_location_on), | ||||
|                 activity.getString(dialogTextResource), | ||||
|                 activity.getString(R.string.title_app_shortcut_setting), | ||||
|                 activity.getString(R.string.cancel), | ||||
|                 () -> openLocationSettings(activity), | ||||
|                 () -> Toast.makeText(activity, activity.getString(dialogTextResource), | ||||
|                     Toast.LENGTH_LONG).show() | ||||
|             ); | ||||
|     fun showLocationOffDialog(activity: Activity, dialogTextResource: Int) { | ||||
|         DialogUtil.showAlertDialog( | ||||
|             activity, | ||||
|             activity.getString(R.string.ask_to_turn_location_on), | ||||
|             activity.getString(dialogTextResource), | ||||
|             activity.getString(R.string.title_app_shortcut_setting), | ||||
|             activity.getString(R.string.cancel), | ||||
|             { openLocationSettings(activity) }, | ||||
|             { | ||||
|                 Toast.makeText( | ||||
|                     activity, | ||||
|                     activity.getString(dialogTextResource), | ||||
|                     Toast.LENGTH_LONG | ||||
|                 ).show() | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Opens the location access page in settings, for user to turn on location services | ||||
|      * | ||||
|      * @param activity Activtiy object | ||||
|      * @param activity Activity object | ||||
|      */ | ||||
|     public void openLocationSettings(Activity activity) { | ||||
|         final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); | ||||
|         final PackageManager packageManager = activity.getPackageManager(); | ||||
|     fun openLocationSettings(activity: Activity) { | ||||
|         val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS) | ||||
|         val packageManager = activity.packageManager | ||||
| 
 | ||||
|         if (intent.resolveActivity(packageManager) != null) { | ||||
|             activity.startActivity(intent); | ||||
|             activity.startActivity(intent) | ||||
|         } else { | ||||
|             Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG) | ||||
|                 .show(); | ||||
|                 .show() | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -128,16 +138,22 @@ public class LocationPermissionsHelper { | |||
|      * @param activity Activity object | ||||
|      * @param dialogTextResource int id of the required string resource | ||||
|      */ | ||||
|     public void showAppSettingsDialog(Activity activity, int dialogTextResource) { | ||||
|         DialogUtil | ||||
|             .showAlertDialog(activity, activity.getString(R.string.location_permission_title), | ||||
|                 activity.getString(dialogTextResource), | ||||
|                 activity.getString(R.string.title_app_shortcut_setting), | ||||
|                 activity.getString(R.string.cancel), | ||||
|                 () -> openAppSettings(activity), | ||||
|                 () -> Toast.makeText(activity, activity.getString(dialogTextResource), | ||||
|                     Toast.LENGTH_LONG).show() | ||||
|             ); | ||||
|     fun showAppSettingsDialog(activity: Activity, dialogTextResource: Int) { | ||||
|         DialogUtil.showAlertDialog( | ||||
|             activity, | ||||
|             activity.getString(R.string.location_permission_title), | ||||
|             activity.getString(dialogTextResource), | ||||
|             activity.getString(R.string.title_app_shortcut_setting), | ||||
|             activity.getString(R.string.cancel), | ||||
|             { openAppSettings(activity) }, | ||||
|             { | ||||
|                 Toast.makeText( | ||||
|                     activity, | ||||
|                     activity.getString(dialogTextResource), | ||||
|                     Toast.LENGTH_LONG | ||||
|                 ).show() | ||||
|             } | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -145,22 +161,20 @@ public class LocationPermissionsHelper { | |||
|      * | ||||
|      * @param activity Activity object | ||||
|      */ | ||||
|     public void openAppSettings(Activity activity) { | ||||
|         Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); | ||||
|         Uri uri = Uri.fromParts("package", activity.getPackageName(), null); | ||||
|         intent.setData(uri); | ||||
|         activity.startActivity(intent); | ||||
|     private fun openAppSettings(activity: Activity) { | ||||
|         val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) | ||||
|         val uri = Uri.fromParts("package", activity.packageName, null) | ||||
|         intent.data = uri | ||||
|         activity.startActivity(intent) | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /** | ||||
|      * Check if apps have access to location even after having individual access | ||||
|      * | ||||
|      * @return Returns true if location services are on and false otherwise | ||||
|      */ | ||||
|     public boolean isLocationAccessToAppsTurnedOn() { | ||||
|         return (locationManager.isNetworkProviderEnabled() | ||||
|             || locationManager.isGPSProviderEnabled()); | ||||
|     fun isLocationAccessToAppsTurnedOn(): Boolean { | ||||
|         return locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled() | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|  | @ -169,18 +183,18 @@ public class LocationPermissionsHelper { | |||
|      * @param activity Activity object | ||||
|      * @return Returns true if location permission is granted and false otherwise | ||||
|      */ | ||||
|     public boolean checkLocationPermission(Activity activity) { | ||||
|         return PermissionUtils.hasPermission(activity, | ||||
|             new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); | ||||
|     fun checkLocationPermission(activity: Activity): Boolean { | ||||
|         return PermissionUtils.hasPermission( | ||||
|             activity, | ||||
|             arrayOf(permission.ACCESS_FINE_LOCATION) | ||||
|         ) | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Handle onPermissionDenied within individual classes based on the requirements | ||||
|      */ | ||||
|     public interface LocationPermissionCallback { | ||||
| 
 | ||||
|         void onLocationPermissionDenied(String toastMessage); | ||||
| 
 | ||||
|         void onLocationPermissionGranted(); | ||||
|     interface LocationPermissionCallback { | ||||
|         fun onLocationPermissionDenied(toastMessage: String) | ||||
|         fun onLocationPermissionGranted() | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -1,90 +1,85 @@ | |||
| package fr.free.nrw.commons.location; | ||||
| package fr.free.nrw.commons.location | ||||
| 
 | ||||
| import android.Manifest.permission; | ||||
| import android.app.Activity; | ||||
| import android.content.Context; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.location.Location; | ||||
| import android.location.LocationListener; | ||||
| import android.location.LocationManager; | ||||
| import android.os.Bundle; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import java.util.HashSet; | ||||
| import java.util.List; | ||||
| import java.util.Set; | ||||
| import java.util.concurrent.CopyOnWriteArrayList; | ||||
| import android.Manifest | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.content.pm.PackageManager | ||||
| import android.location.Location | ||||
| import android.location.LocationListener | ||||
| import android.location.LocationManager | ||||
| import android.os.Bundle | ||||
| import androidx.core.app.ActivityCompat | ||||
| import timber.log.Timber | ||||
| import java.util.concurrent.CopyOnWriteArrayList | ||||
| 
 | ||||
| import timber.log.Timber; | ||||
| 
 | ||||
| public class LocationServiceManager implements LocationListener { | ||||
| class LocationServiceManager(private val context: Context) : LocationListener { | ||||
| 
 | ||||
|     // Maybe these values can be improved for efficiency | ||||
|     private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 10 * 100; | ||||
|     private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 1; | ||||
|     companion object { | ||||
|         // Maybe these values can be improved for efficiency | ||||
|         private const val MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 10 * 100L | ||||
|         private const val MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 1f | ||||
|     } | ||||
| 
 | ||||
|     private LocationManager locationManager; | ||||
|     private Location lastLocation; | ||||
|     //private Location lastLocationDuplicate; // Will be used for nearby card view on contributions activity | ||||
|     private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>(); | ||||
|     private boolean isLocationManagerRegistered = false; | ||||
|     private Set<Activity> locationExplanationDisplayed = new HashSet<>(); | ||||
|     private Context context; | ||||
|     private val locationManager: LocationManager = | ||||
|         context.getSystemService(Context.LOCATION_SERVICE) as LocationManager | ||||
|     var lastLocationVar: Location? = null | ||||
|     private val locationListeners = CopyOnWriteArrayList<LocationUpdateListener>() | ||||
|     private var isLocationManagerRegistered = false | ||||
|     private val locationExplanationDisplayed = mutableSetOf<Activity>() | ||||
| 
 | ||||
|     /** | ||||
|      * Constructs a new instance of LocationServiceManager. | ||||
|      * | ||||
|      * @param context the context | ||||
|      */ | ||||
|     public LocationServiceManager(Context context) { | ||||
|         this.context = context; | ||||
|         this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); | ||||
|     fun getLastLocation(): LatLng? { | ||||
|         if (lastLocationVar == null) { | ||||
|             lastLocationVar = getLastKnownLocation() | ||||
|             return lastLocationVar?.let { LatLng.from(it) } | ||||
|         } | ||||
|         return LatLng.from(lastLocationVar!!) | ||||
|     } | ||||
| 
 | ||||
|     public LatLng getLastLocation() { | ||||
|         if (lastLocation == null) { | ||||
|                 lastLocation = getLastKnownLocation(); | ||||
|                 if(lastLocation != null) { | ||||
|                     return LatLng.from(lastLocation); | ||||
|                 } | ||||
|                 else { | ||||
|                     return null; | ||||
|                 } | ||||
|         } | ||||
|         return LatLng.from(lastLocation); | ||||
|     } | ||||
|     private fun getLastKnownLocation(): Location? { | ||||
|         val providers = locationManager.getProviders(true) | ||||
|         var bestLocation: Location? = null | ||||
|         for (provider in providers) { | ||||
|             val location: Location? = if ( | ||||
|                 ActivityCompat.checkSelfPermission( | ||||
|                     context, | ||||
|                     Manifest.permission.ACCESS_FINE_LOCATION) | ||||
|                 == | ||||
|                 PackageManager.PERMISSION_GRANTED && | ||||
|                 ActivityCompat.checkSelfPermission( | ||||
|                     context, | ||||
|                     Manifest.permission.ACCESS_COARSE_LOCATION) | ||||
|                 == | ||||
|                 PackageManager.PERMISSION_GRANTED | ||||
|             ) { | ||||
|                 locationManager.getLastKnownLocation(provider) | ||||
|             } else { | ||||
|                 null | ||||
|             } | ||||
| 
 | ||||
|     private Location getLastKnownLocation() { | ||||
|         List<String> providers = locationManager.getProviders(true); | ||||
|         Location bestLocation = null; | ||||
|         for (String provider : providers) { | ||||
|             Location l=null; | ||||
|             if (ActivityCompat.checkSelfPermission(context, permission.ACCESS_FINE_LOCATION) | ||||
|                 == PackageManager.PERMISSION_GRANTED | ||||
|                 && ActivityCompat.checkSelfPermission(context, permission.ACCESS_COARSE_LOCATION) | ||||
|                 == PackageManager.PERMISSION_GRANTED) { | ||||
|                 l = locationManager.getLastKnownLocation(provider); | ||||
|             } | ||||
|             if (l == null) { | ||||
|                 continue; | ||||
|             } | ||||
|             if (bestLocation == null | ||||
|                 || l.getAccuracy() < bestLocation.getAccuracy()) { | ||||
|                 bestLocation = l; | ||||
|             if ( | ||||
|                 location != null | ||||
|                 && | ||||
|                 (bestLocation == null || location.accuracy < bestLocation.accuracy) | ||||
|             ) { | ||||
|                 bestLocation = location | ||||
|             } | ||||
|         } | ||||
|         if (bestLocation == null) { | ||||
|             return null; | ||||
|         } | ||||
|         return bestLocation; | ||||
|         return bestLocation | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Registers a LocationManager to listen for current location. | ||||
|      */ | ||||
|     public void registerLocationManager() { | ||||
|     fun registerLocationManager() { | ||||
|         if (!isLocationManagerRegistered) { | ||||
|             isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) | ||||
|                     && requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); | ||||
|             isLocationManagerRegistered = | ||||
|                 requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) && | ||||
|                     requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -94,100 +89,86 @@ public class LocationServiceManager implements LocationListener { | |||
|      * @param locationProvider the location provider | ||||
|      * @return true if successful | ||||
|      */ | ||||
|     public boolean requestLocationUpdatesFromProvider(String locationProvider) { | ||||
|         try { | ||||
|             // If both providers are not available | ||||
|             if (locationManager == null || !(locationManager.getAllProviders().contains(locationProvider))) { | ||||
|                 return false; | ||||
|             } | ||||
|             locationManager.requestLocationUpdates(locationProvider, | ||||
|     fun requestLocationUpdatesFromProvider(locationProvider: String): Boolean { | ||||
|         return try { | ||||
|             if (locationManager.allProviders.contains(locationProvider)) { | ||||
|                 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; | ||||
|                     this | ||||
|                 ) | ||||
|                 true | ||||
|             } else { | ||||
|                 false | ||||
|             } | ||||
|         } catch (e: IllegalArgumentException) { | ||||
|             Timber.e(e, "Illegal argument exception") | ||||
|             false | ||||
|         } catch (e: SecurityException) { | ||||
|             Timber.e(e, "Security exception") | ||||
|             false | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Returns whether a given location is better than the current best location. | ||||
|      * | ||||
|      * @param location            the location to be tested | ||||
|      * @param location the location to be tested | ||||
|      * @param currentBestLocation the current best location | ||||
|      * @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly | ||||
|      * LOCATION_SLIGHTLY_CHANGED if location changed slightly | ||||
|      */ | ||||
|     private LocationChangeType isBetterLocation(Location location, Location currentBestLocation) { | ||||
| 
 | ||||
|     private fun isBetterLocation(location: Location, currentBestLocation: Location?): LocationChangeType { | ||||
|         if (currentBestLocation == null) { | ||||
|             // A new location is always better than no location | ||||
|             return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; | ||||
|             return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED | ||||
|         } | ||||
| 
 | ||||
|         // 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 isNewer = timeDelta > 0; | ||||
|         val timeDelta = location.time - currentBestLocation.time | ||||
|         val isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS | ||||
|         val isNewer = timeDelta > 0 | ||||
|         val accuracyDelta = (location.accuracy - currentBestLocation.accuracy).toInt() | ||||
|         val isMoreAccurate = accuracyDelta < 0 | ||||
|         val isSignificantlyLessAccurate = accuracyDelta > 200 | ||||
|         val isFromSameProvider = isSameProvider(location.provider, currentBestLocation.provider) | ||||
| 
 | ||||
|         // 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()); | ||||
| 
 | ||||
|         float[] results = new float[5]; | ||||
|         val results = FloatArray(5) | ||||
|         Location.distanceBetween( | ||||
|                         currentBestLocation.getLatitude(), | ||||
|                         currentBestLocation.getLongitude(), | ||||
|                         location.getLatitude(), | ||||
|                         location.getLongitude(), | ||||
|                         results); | ||||
|             currentBestLocation.latitude, currentBestLocation.longitude, | ||||
|             location.latitude, location.longitude, | ||||
|             results | ||||
|         ) | ||||
| 
 | ||||
|         // If it's been more than two minutes since the current location, use the new location | ||||
|         // because the user has likely moved | ||||
|         if (isSignificantlyNewer | ||||
|                 || isMoreAccurate | ||||
|                 || (isNewer && !isLessAccurate) | ||||
|                 || (isNewer && !isSignificantlyLessAccurate && isFromSameProvider)) { | ||||
|             if (results[0] < 1000) { // Means change is smaller than 1000 meter | ||||
|                 return LocationChangeType.LOCATION_SLIGHTLY_CHANGED; | ||||
|             } else { | ||||
|                 return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; | ||||
|         return when { | ||||
|             isSignificantlyNewer | ||||
|                     || | ||||
|                     isMoreAccurate | ||||
|                     || | ||||
|                     (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) -> { | ||||
|                 if (results[0] < 1000) LocationChangeType.LOCATION_SLIGHTLY_CHANGED | ||||
|                 else LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED | ||||
|             } | ||||
|         } else{ | ||||
|             return LocationChangeType.LOCATION_NOT_CHANGED; | ||||
|             else -> LocationChangeType.LOCATION_NOT_CHANGED | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Checks whether two providers are the same | ||||
|      */ | ||||
|     private boolean isSameProvider(String provider1, String provider2) { | ||||
|         if (provider1 == null) { | ||||
|             return provider2 == null; | ||||
|         } | ||||
|         return provider1.equals(provider2); | ||||
|     private fun isSameProvider(provider1: String?, provider2: String?): Boolean { | ||||
|         return provider1 == provider2 | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * Unregisters location manager. | ||||
|      */ | ||||
|     public void unregisterLocationManager() { | ||||
|         isLocationManagerRegistered = false; | ||||
|         locationExplanationDisplayed.clear(); | ||||
|     fun unregisterLocationManager() { | ||||
|         isLocationManagerRegistered = false | ||||
|         locationExplanationDisplayed.clear() | ||||
|         try { | ||||
|             locationManager.removeUpdates(this); | ||||
|         } catch (SecurityException e) { | ||||
|             Timber.e(e, "Security exception"); | ||||
|             locationManager.removeUpdates(this) | ||||
|         } catch (e: SecurityException) { | ||||
|             Timber.e(e, "Security exception") | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -196,9 +177,9 @@ public class LocationServiceManager implements LocationListener { | |||
|      * | ||||
|      * @param listener the new listener | ||||
|      */ | ||||
|     public void addLocationListener(LocationUpdateListener listener) { | ||||
|     fun addLocationListener(listener: LocationUpdateListener) { | ||||
|         if (!locationListeners.contains(listener)) { | ||||
|             locationListeners.add(listener); | ||||
|             locationListeners.add(listener) | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -207,64 +188,55 @@ public class LocationServiceManager implements LocationListener { | |||
|      * | ||||
|      * @param listener the listener to be removed | ||||
|      */ | ||||
|     public void removeLocationListener(LocationUpdateListener listener) { | ||||
|         locationListeners.remove(listener); | ||||
|     fun removeLocationListener(listener: LocationUpdateListener) { | ||||
|         locationListeners.remove(listener) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onLocationChanged(Location location) { | ||||
|         Timber.d("on location changed"); | ||||
|             if (isBetterLocation(location, lastLocation) | ||||
|                     .equals(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)) { | ||||
|                 lastLocation = location; | ||||
|                 //lastLocationDuplicate = location; | ||||
|                 for (LocationUpdateListener listener : locationListeners) { | ||||
|                     listener.onLocationChangedSignificantly(LatLng.from(lastLocation)); | ||||
|                 } | ||||
|             } else if (location.distanceTo(lastLocation) >= 500) { | ||||
|                 // Update nearby notification card at every 500 meters. | ||||
|                 for (LocationUpdateListener listener : locationListeners) { | ||||
|                     listener.onLocationChangedMedium(LatLng.from(lastLocation)); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             else if (isBetterLocation(location, lastLocation) | ||||
|                     .equals(LocationChangeType.LOCATION_SLIGHTLY_CHANGED)) { | ||||
|                 lastLocation = location; | ||||
|                 //lastLocationDuplicate = location; | ||||
|                 for (LocationUpdateListener listener : locationListeners) { | ||||
|                     listener.onLocationChangedSlightly(LatLng.from(lastLocation)); | ||||
|                 } | ||||
|             } | ||||
|     override fun onLocationChanged(location: Location) { | ||||
|         Timber.d("on location changed") | ||||
|         val changeType = isBetterLocation(location, lastLocationVar) | ||||
|         if (changeType == LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) { | ||||
|             lastLocationVar = location | ||||
|             locationListeners.forEach { it.onLocationChangedSignificantly(LatLng.from(location)) } | ||||
|         } else if (lastLocationVar?.let { location.distanceTo(it) }!! >= 500) { | ||||
|             locationListeners.forEach { it.onLocationChangedMedium(LatLng.from(location)) } | ||||
|         } else if (changeType == LocationChangeType.LOCATION_SLIGHTLY_CHANGED) { | ||||
|             lastLocationVar = location | ||||
|             locationListeners.forEach { it.onLocationChangedSlightly(LatLng.from(location)) } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onStatusChanged(String provider, int status, Bundle extras) { | ||||
|         Timber.d("%s's status changed to %d", provider, status); | ||||
|     @Deprecated("Deprecated in Java", ReplaceWith( | ||||
|         "Timber.d(\"%s's status changed to %d\", provider, status)", | ||||
|         "timber.log.Timber" | ||||
|     ) | ||||
|     ) | ||||
|     override fun onStatusChanged(provider: String?, status: Int, extras: Bundle?) { | ||||
|         Timber.d("%s's status changed to %d", provider, status) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onProviderEnabled(String provider) { | ||||
|         Timber.d("Provider %s enabled", provider); | ||||
| 
 | ||||
| 
 | ||||
|     override fun onProviderEnabled(provider: String) { | ||||
|         Timber.d("Provider %s enabled", provider) | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void onProviderDisabled(String provider) { | ||||
|         Timber.d("Provider %s disabled", provider); | ||||
|     override fun onProviderDisabled(provider: String) { | ||||
|         Timber.d("Provider %s disabled", provider) | ||||
|     } | ||||
| 
 | ||||
|     public boolean isNetworkProviderEnabled() { | ||||
|         return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); | ||||
|     fun isNetworkProviderEnabled(): Boolean { | ||||
|         return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER) | ||||
|     } | ||||
| 
 | ||||
|     public boolean isGPSProviderEnabled() { | ||||
|         return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); | ||||
|     fun isGPSProviderEnabled(): Boolean { | ||||
|         return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) | ||||
|     } | ||||
| 
 | ||||
|     public enum LocationChangeType{ | ||||
|         LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers | ||||
|         LOCATION_SLIGHTLY_CHANGED,      //User might be walking or driving | ||||
|         LOCATION_MEDIUM_CHANGED,      //Between slight and significant changes, will be used for nearby card view updates. | ||||
|     enum class LocationChangeType { | ||||
|         LOCATION_SIGNIFICANTLY_CHANGED, | ||||
|         LOCATION_SLIGHTLY_CHANGED, | ||||
|         LOCATION_MEDIUM_CHANGED, | ||||
|         LOCATION_NOT_CHANGED, | ||||
|         PERMISSION_JUST_GRANTED, | ||||
|         MAP_UPDATED, | ||||
|  | @ -272,3 +244,12 @@ public class LocationServiceManager implements LocationListener { | |||
|         CUSTOM_QUERY | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -1,7 +1,12 @@ | |||
| package fr.free.nrw.commons.location; | ||||
| package fr.free.nrw.commons.location | ||||
| 
 | ||||
| public interface LocationUpdateListener { | ||||
|     void onLocationChangedSignificantly(LatLng latLng); // Will be used to update all nearby markers on the map | ||||
|     void onLocationChangedSlightly(LatLng latLng); // Will be used to track users motion | ||||
|     void onLocationChangedMedium(LatLng latLng); // Will be used updating nearby card view notification | ||||
| interface LocationUpdateListener { | ||||
|     // Will be used to update all nearby markers on the map | ||||
|     fun onLocationChangedSignificantly(latLng: LatLng) | ||||
| 
 | ||||
|     // Will be used to track users motion | ||||
|     fun onLocationChangedSlightly(latLng: LatLng) | ||||
| 
 | ||||
|     // Will be used updating nearby card view notification | ||||
|     fun onLocationChangedMedium(latLng: LatLng) | ||||
| } | ||||
|  | @ -42,8 +42,8 @@ class LanguagesAdapter constructor( | |||
|         AppLanguageLookUpTable(context) | ||||
| 
 | ||||
|     init { | ||||
|         languageNamesList = language.localizedNames | ||||
|         languageCodesList = language.codes | ||||
|         languageNamesList = language.getLocalizedNames() | ||||
|         languageCodesList = language.getCodes() | ||||
|     } | ||||
| 
 | ||||
|     private val filter = LanguageFilter() | ||||
|  | @ -117,7 +117,7 @@ class LanguagesAdapter constructor( | |||
|      */ | ||||
|     fun getIndexOfUserDefaultLocale(context: Context): Int { | ||||
|         val userLanguageCode = context.locale?.language ?: return DEFAULT_INDEX | ||||
|         return language.codes.indexOf(userLanguageCode).takeIf { it >= 0 } ?: DEFAULT_INDEX | ||||
|         return language.getCodes().indexOf(userLanguageCode).takeIf { it >= 0 } ?: DEFAULT_INDEX | ||||
|     } | ||||
| 
 | ||||
|     fun getIndexOfLanguageCode(languageCode: String): Int = languageCodesList.indexOf(languageCode) | ||||
|  | @ -128,17 +128,17 @@ class LanguagesAdapter constructor( | |||
|         override fun performFiltering(constraint: CharSequence?): FilterResults { | ||||
|             val filterResults = FilterResults() | ||||
|             val temp: LinkedHashMap<String, String> = LinkedHashMap() | ||||
|             if (constraint != null && language.localizedNames != null) { | ||||
|                 val length: Int = language.localizedNames.size | ||||
|             if (constraint != null) { | ||||
|                 val length: Int = language.getLocalizedNames().size | ||||
|                 var i = 0 | ||||
|                 while (i < length) { | ||||
|                     val key: String = language.codes[i] | ||||
|                     val value: String = language.localizedNames[i] | ||||
|                     val key: String = language.getCodes()[i] | ||||
|                     val value: String = language.getLocalizedNames()[i] | ||||
|                     val defaultlanguagecode = getIndexOfUserDefaultLocale(context) | ||||
|                     if (value.contains(constraint, true) || | ||||
|                         Locale(key) | ||||
|                             .getDisplayName( | ||||
|                                 Locale(language.codes[defaultlanguagecode]), | ||||
|                                 Locale(language.getCodes()[defaultlanguagecode]), | ||||
|                             ).contains(constraint, true) | ||||
|                     ) { | ||||
|                         temp[key] = value | ||||
|  |  | |||
|  | @ -62,5 +62,5 @@ class LatLngTests { | |||
|     private fun assertPrettyCoordinateString( | ||||
|         expected: String, | ||||
|         place: LatLng, | ||||
|     ) = assertEquals(expected, place.prettyCoordinateString) | ||||
|     ) = assertEquals(expected, place.getPrettyCoordinateString()) | ||||
| } | ||||
|  |  | |||
|  | @ -248,11 +248,11 @@ class MediaDetailFragmentUnitTests { | |||
|     @Throws(Exception::class) | ||||
|     fun testOnUpdateCoordinatesClickedCurrentLocationNull() { | ||||
|         `when`(media.coordinates).thenReturn(null) | ||||
|         `when`(locationManager.lastLocation).thenReturn(null) | ||||
|         `when`(locationManager.getLastLocation()).thenReturn(null) | ||||
|         `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") | ||||
|         fragment.onUpdateCoordinatesClicked() | ||||
|         Mockito.verify(media, Mockito.times(1)).coordinates | ||||
|         Mockito.verify(locationManager, Mockito.times(1)).lastLocation | ||||
|         Mockito.verify(locationManager, Mockito.times(1)).getLastLocation() | ||||
|         val shadowActivity: ShadowActivity = shadowOf(activity) | ||||
|         val startedIntent = shadowActivity.nextStartedActivity | ||||
|         val shadowIntent: ShadowIntent = shadowOf(startedIntent) | ||||
|  | @ -276,11 +276,11 @@ class MediaDetailFragmentUnitTests { | |||
|     @Throws(Exception::class) | ||||
|     fun testOnUpdateCoordinatesClickedCurrentLocationNotNull() { | ||||
|         `when`(media.coordinates).thenReturn(null) | ||||
|         `when`(locationManager.lastLocation).thenReturn(LatLng(-0.000001, -0.999999, 0f)) | ||||
|         `when`(locationManager.getLastLocation()).thenReturn(LatLng(-0.000001, -0.999999, 0f)) | ||||
|         `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") | ||||
| 
 | ||||
|         fragment.onUpdateCoordinatesClicked() | ||||
|         Mockito.verify(locationManager, Mockito.times(3)).lastLocation | ||||
|         Mockito.verify(locationManager, Mockito.times(3)).getLastLocation() | ||||
|         val shadowActivity: ShadowActivity = shadowOf(activity) | ||||
|         val startedIntent = shadowActivity.nextStartedActivity | ||||
|         val shadowIntent: ShadowIntent = shadowOf(startedIntent) | ||||
|  |  | |||
|  | @ -54,8 +54,8 @@ class LanguagesAdapterTest { | |||
|                 .from(context) | ||||
|                 .inflate(R.layout.row_item_languages_spinner, null) as View | ||||
| 
 | ||||
|         languageNamesList = language.localizedNames | ||||
|         languageCodesList = language.codes | ||||
|         languageNamesList = language.getLocalizedNames() | ||||
|         languageCodesList = language.getCodes() | ||||
| 
 | ||||
|         languagesAdapter = LanguagesAdapter(context, selectedLanguages) | ||||
|     } | ||||
|  | @ -124,12 +124,12 @@ class LanguagesAdapterTest { | |||
|         var i = 0 | ||||
|         var s = 0 | ||||
|         while (i < length) { | ||||
|             val key: String = language.codes[i] | ||||
|             val value: String = language.localizedNames[i] | ||||
|             val key: String = language.getCodes()[i] | ||||
|             val value: String = language.getLocalizedNames()[i] | ||||
|             if (value.contains(constraint, true) || | ||||
|                 Locale(key) | ||||
|                     .getDisplayName( | ||||
|                         Locale(language.codes[defaultlanguagecode!!]), | ||||
|                         Locale(language.getCodes()[defaultlanguagecode!!]), | ||||
|                     ).contains(constraint, true) | ||||
|             ) { | ||||
|                 s++ | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Saifuddin
						Saifuddin