Migrated location and language module from Java to Kotlin

This commit is contained in:
Saifuddin 2024-12-02 21:50:24 +05:30
parent 574b9e3c15
commit 4148049156
10 changed files with 467 additions and 521 deletions

View file

@ -423,7 +423,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
* Moves map to GPS location * Moves map to GPS location
*/ */
private fun moveMapToGPSLocation() { private fun moveMapToGPSLocation() {
locationManager.lastLocation?.let { locationManager.lastLocationVar?.let {
moveMapTo(GeoPoint(it.latitude, it.longitude)) moveMapTo(GeoPoint(it.latitude, it.longitude))
} }
} }
@ -591,7 +591,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
override fun onLocationPermissionGranted() { override fun onLocationPermissionGranted() {
if (moveToCurrentLocation || activity != "MediaActivity") { if (moveToCurrentLocation || activity != "MediaActivity") {
if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn) { if (locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) {
locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
addMarkerAtGPSLocation() addMarkerAtGPSLocation()
@ -606,7 +606,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
* Adds a marker at the user's GPS location * Adds a marker at the user's GPS location
*/ */
private fun addMarkerAtGPSLocation() { private fun addMarkerAtGPSLocation() {
locationManager.lastLocation?.let { locationManager.lastLocationVar?.let {
addLocationMarker(GeoPoint(it.latitude, it.longitude)) addLocationMarker(GeoPoint(it.latitude, it.longitude))
markerImage.translationY = 0f markerImage.translationY = 0f
} }

View file

@ -1,116 +1,111 @@
package fr.free.nrw.commons.language; package fr.free.nrw.commons.language
import android.content.Context; import android.content.Context
import android.content.res.Resources; import android.content.res.Resources
import android.text.TextUtils; import android.text.TextUtils
import androidx.annotation.ArrayRes; import androidx.annotation.ArrayRes
import androidx.annotation.NonNull; import fr.free.nrw.commons.R
import androidx.annotation.Nullable; 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 /** 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. */ * present in this table as it is statically bundled with the app. */
public class AppLanguageLookUpTable { class AppLanguageLookUpTable(context: Context) {
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.
@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 // Language codes for all app supported languages in fixed order. The special code representing
// the dynamic system language is null. // 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. // 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. // Native names for all app supported languages in fixed order.
@NonNull private SoftReference<List<String>> localizedNamesRef = new SoftReference<>(null); private var localizedNamesRef = SoftReference<List<String>>(null)
public AppLanguageLookUpTable(@NonNull Context context) {
resources = context.getResources();
}
/** /**
* @return Nonnull immutable list. The special code representing the dynamic system language is * @return Nonnull immutable list. The special code representing the dynamic system language is
* null. * null.
*/ */
@NonNull fun getCodes(): List<String> {
public List<String> getCodes() { var codes = codesRef.get()
List<String> codes = codesRef.get();
if (codes == null) { if (codes == null) {
codes = getStringList(R.array.preference_language_keys); codes = getStringList(R.array.preference_language_keys)
codesRef = new SoftReference<>(codes); codesRef = SoftReference(codes)
} }
return codes; return codes
} }
@Nullable fun getCanonicalName(code: String?): String? {
public String getCanonicalName(@Nullable String code) { var name = defaultIndex(getCanonicalNames(), indexOfCode(code), null)
String name = defaultIndex(getCanonicalNames(), indexOfCode(code), null); if (name.isNullOrEmpty() && !code.isNullOrEmpty()) {
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) { name = when (code) {
if (code.equals(Locale.CHINESE.getLanguage())) { Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.ENGLISH)
name = Locale.CHINESE.getDisplayName(Locale.ENGLISH); NORWEGIAN_LEGACY_LANGUAGE_CODE ->
} else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) { defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null)
name = defaultIndex(getCanonicalNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null); else -> null
} }
} }
return name; return name
} }
@Nullable fun getLocalizedName(code: String?): String? {
public String getLocalizedName(@Nullable String code) { var name = defaultIndex(getLocalizedNames(), indexOfCode(code), null)
String name = defaultIndex(getLocalizedNames(), indexOfCode(code), null); if (name.isNullOrEmpty() && !code.isNullOrEmpty()) {
if (TextUtils.isEmpty(name) && !TextUtils.isEmpty(code)) { name = when (code) {
if (code.equals(Locale.CHINESE.getLanguage())) { Locale.CHINESE.language -> Locale.CHINESE.getDisplayName(Locale.CHINESE)
name = Locale.CHINESE.getDisplayName(Locale.CHINESE); NORWEGIAN_LEGACY_LANGUAGE_CODE ->
} else if (code.equals(NORWEGIAN_LEGACY_LANGUAGE_CODE)) { defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null)
name = defaultIndex(getLocalizedNames(), indexOfCode(NORWEGIAN_BOKMAL_LANGUAGE_CODE), null); else -> null
} }
} }
return name; return name
} }
public List<String> getCanonicalNames() { fun getCanonicalNames(): List<String> {
List<String> names = canonicalNamesRef.get(); var names = canonicalNamesRef.get()
if (names == null) { if (names == null) {
names = getStringList(R.array.preference_language_canonical_names); names = getStringList(R.array.preference_language_canonical_names)
canonicalNamesRef = new SoftReference<>(names); canonicalNamesRef = SoftReference(names)
} }
return names; return names
} }
public List<String> getLocalizedNames() { fun getLocalizedNames(): List<String> {
List<String> names = localizedNamesRef.get(); var names = localizedNamesRef.get()
if (names == null) { if (names == null) {
names = getStringList(R.array.preference_language_local_names); names = getStringList(R.array.preference_language_local_names)
localizedNamesRef = new SoftReference<>(names); localizedNamesRef = SoftReference(names)
} }
return names; return names
} }
public boolean isSupportedCode(@Nullable String code) { fun isSupportedCode(code: String?): Boolean {
return getCodes().contains(code); return getCodes().contains(code)
} }
private <T> T defaultIndex(List<T> list, int index, T defaultValue) { private fun <T> defaultIndex(list: List<T>, index: Int, defaultValue: T?): T? {
return inBounds(list, index) ? list.get(index) : defaultValue; return if (inBounds(list, index)) list[index] else defaultValue
} }
/** /**
@ -121,21 +116,20 @@ public class AppLanguageLookUpTable {
* language is null. * language is null.
* @return The index of the language code or -1 if the code is not supported. * @return The index of the language code or -1 if the code is not supported.
*/ */
private int indexOfCode(@Nullable String code) { private fun indexOfCode(code: String?): Int {
return getCodes().indexOf(code); return getCodes().indexOf(code)
} }
/** @return Nonnull immutable list. */ /** @return Nonnull immutable list. */
@NonNull private fun getStringList(id: Int): List<String> {
private List<String> getStringList(int id) { return getStringArray(id).toList()
return Arrays.asList(getStringArray(id));
} }
private boolean inBounds(List<?> list, int index) { private fun inBounds(list: List<*>, index: Int): Boolean {
return index >= 0 && index < list.size(); return index in list.indices
} }
public String[] getStringArray(@ArrayRes int id) { fun getStringArray(@ArrayRes id: Int): Array<String> {
return resources.getStringArray(id); return resources.getStringArray(id)
} }
} }

View file

@ -1,132 +1,106 @@
package fr.free.nrw.commons.location; package fr.free.nrw.commons.location
import android.location.Location; import android.location.Location
import android.net.Uri; import android.net.Uri
import android.os.Parcel; import android.os.Parcel
import android.os.Parcelable; 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 { data class LatLng(
var latitude: Double,
private final double latitude; var longitude: Double,
private final double longitude; val accuracy: Float
private final float accuracy; ) : Parcelable {
/** /**
* Accepts latitude and longitude. * Accepts latitude and longitude.
* North and South values are cut off at 90° * North and South values are cut off at 90°
* *
* @param latitude the latitude
* @param longitude the longitude
* @param accuracy the accuracy
*
* Examples: * Examples:
* the Statue of Liberty is located at 40.69° N, 74.04° W * 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) * 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. * where positive signifies north, east and negative signifies south, west.
*/ */
public LatLng(double latitude, double longitude, float accuracy) { init {
if (-180.0D <= longitude && longitude < 180.0D) { val adjustedLongitude = when {
this.longitude = longitude; longitude in -180.0..180.0 -> longitude
} else { else -> ((longitude - 180.0) % 360.0 + 360.0) % 360.0 - 180.0
this.longitude = ((longitude - 180.0D) % 360.0D + 360.0D) % 360.0D - 180.0D;
} }
this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude)); latitude = max(-90.0, min(90.0, latitude))
this.accuracy = accuracy; 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. * 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) { private constructor(parcel: Parcel) : this(
latitude = in.readDouble(); latitude = parcel.readDouble(),
longitude = in.readDouble(); longitude = parcel.readDouble(),
accuracy = in.readFloat(); 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 * Checks for equality of two LatLng objects.
* @param location the non-null location of the user * @param other the second LatLng object
* @return LatLng the Latitude and Longitude of a given location
*/ */
public static LatLng from(@NonNull Location location) { override fun equals(other: Any?): Boolean {
return new LatLng(location.getLatitude(), location.getLongitude(), location.getAccuracy()); 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() { override fun toString(): String {
byte var1 = 1; return "lat/lng: ($latitude,$longitude)"
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";
} }
/** /**
@ -135,64 +109,42 @@ public class LatLng implements Parcelable {
* *
* @return The formatted string. * @return The formatted string.
*/ */
public String getPrettyCoordinateString() { fun getPrettyCoordinateString(): String {
return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", " return "${formatCoordinate(latitude)} ${getNorthSouth()}, " +
+ formatCoordinate(this.longitude) + " " + this.getEastWest(); "${formatCoordinate(longitude)} ${getEastWest()}"
} }
/** /**
* Return the location accuracy in meter. * Gets a URI for a Google Maps intent at the location.
*
* @return float
*/ */
public float getAccuracy() { fun getGmmIntentUri(): Uri {
return accuracy; 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() { private fun getNorthSouth(): String = if (latitude < 0) "S" else "N"
return longitude;
}
/** /**
* Return the latitude in degrees. * Returns "E" or "W" depending on the longitude.
* *
* @return double * @return "E" or "W".
*/ */
public double getLatitude() { private fun getEastWest(): String = if (longitude in 0.0..179.999) "E" else "W"
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];
}
};
}

View file

@ -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.Manifest.permission; import android.app.Activity
import android.app.Activity; import android.content.Intent
import android.content.Intent; import android.net.Uri
import android.content.pm.PackageManager; import android.provider.Settings
import android.net.Uri; import android.widget.Toast
import android.provider.Settings; import androidx.core.app.ActivityCompat
import android.widget.Toast; import fr.free.nrw.commons.R
import androidx.core.app.ActivityCompat; import fr.free.nrw.commons.filepicker.Constants.RequestCodes
import fr.free.nrw.commons.R; import fr.free.nrw.commons.utils.DialogUtil
import fr.free.nrw.commons.filepicker.Constants.RequestCodes; import fr.free.nrw.commons.utils.PermissionUtils
import fr.free.nrw.commons.utils.DialogUtil;
import fr.free.nrw.commons.utils.PermissionUtils;
/** /**
* Helper class to handle location permissions. * Helper class to handle location permissions.
@ -37,51 +35,58 @@ import fr.free.nrw.commons.utils.PermissionUtils;
* Do whatever is required by that particular activity / fragment using current location. * Do whatever is required by that particular activity / fragment using current location.
* *
*/ */
public class LocationPermissionsHelper { class LocationPermissionsHelper(
private val activity: Activity,
Activity activity; private val locationManager: LocationServiceManager,
LocationServiceManager locationManager; private val callback: LocationPermissionCallback?
LocationPermissionCallback callback; ) {
public LocationPermissionsHelper(Activity activity, LocationServiceManager locationManager,
LocationPermissionCallback callback) {
this.activity = activity;
this.locationManager = locationManager;
this.callback = callback;
}
/** /**
* Ask for location permission if the user agrees on attaching location with pictures and the * Ask for location permission if the user agrees on attaching location with pictures and the
* app does not have the access to location * app does not have the access to location
* *
* @param dialogTitleResource Resource id of the title of the dialog * @param dialogTitleResource Resource id of the title of the dialog
* @param dialogTextResource Resource id of the text of the dialog * @param dialogTextResource Resource id of the text of the dialog
*/ */
public void requestForLocationAccess( fun requestForLocationAccess(
int dialogTitleResource, dialogTitleResource: Int,
int dialogTextResource dialogTextResource: Int
) { ) {
if (checkLocationPermission(activity)) { if (checkLocationPermission(activity)) {
callback.onLocationPermissionGranted(); callback?.onLocationPermissionGranted()
} else { } else {
if (ActivityCompat.shouldShowRequestPermissionRationale(activity, if (ActivityCompat.shouldShowRequestPermissionRationale(
permission.ACCESS_FINE_LOCATION)) { activity,
DialogUtil.showAlertDialog(activity, activity.getString(dialogTitleResource), permission.ACCESS_FINE_LOCATION
)
) {
DialogUtil.showAlertDialog(
activity,
activity.getString(dialogTitleResource),
activity.getString(dialogTextResource), activity.getString(dialogTextResource),
activity.getString(android.R.string.ok), activity.getString(android.R.string.ok),
activity.getString(android.R.string.cancel), activity.getString(android.R.string.cancel),
() -> { {
ActivityCompat.requestPermissions(activity, ActivityCompat.requestPermissions(
new String[]{permission.ACCESS_FINE_LOCATION}, 1); 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, null,
false); false
)
} else { } else {
ActivityCompat.requestPermissions(activity, ActivityCompat.requestPermissions(
new String[]{permission.ACCESS_FINE_LOCATION}, activity,
RequestCodes.LOCATION); arrayOf(permission.ACCESS_FINE_LOCATION),
RequestCodes.LOCATION
)
} }
} }
} }
@ -92,33 +97,38 @@ public class LocationPermissionsHelper {
* @param activity Activity object * @param activity Activity object
* @param dialogTextResource int id of the required string resource * @param dialogTextResource int id of the required string resource
*/ */
public void showLocationOffDialog(Activity activity, int dialogTextResource) { fun showLocationOffDialog(activity: Activity, dialogTextResource: Int) {
DialogUtil DialogUtil.showAlertDialog(
.showAlertDialog(activity, activity,
activity.getString(R.string.ask_to_turn_location_on), activity.getString(R.string.ask_to_turn_location_on),
activity.getString(dialogTextResource), activity.getString(dialogTextResource),
activity.getString(R.string.title_app_shortcut_setting), activity.getString(R.string.title_app_shortcut_setting),
activity.getString(R.string.cancel), activity.getString(R.string.cancel),
() -> openLocationSettings(activity), { openLocationSettings(activity) },
() -> Toast.makeText(activity, activity.getString(dialogTextResource), {
Toast.LENGTH_LONG).show() Toast.makeText(
); activity,
activity.getString(dialogTextResource),
Toast.LENGTH_LONG
).show()
}
)
} }
/** /**
* Opens the location access page in settings, for user to turn on location services * 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) { fun openLocationSettings(activity: Activity) {
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS); val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
final PackageManager packageManager = activity.getPackageManager(); val packageManager = activity.packageManager
if (intent.resolveActivity(packageManager) != null) { if (intent.resolveActivity(packageManager) != null) {
activity.startActivity(intent); activity.startActivity(intent)
} else { } else {
Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG) 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 activity Activity object
* @param dialogTextResource int id of the required string resource * @param dialogTextResource int id of the required string resource
*/ */
public void showAppSettingsDialog(Activity activity, int dialogTextResource) { fun showAppSettingsDialog(activity: Activity, dialogTextResource: Int) {
DialogUtil DialogUtil.showAlertDialog(
.showAlertDialog(activity, activity.getString(R.string.location_permission_title), activity,
activity.getString(dialogTextResource), activity.getString(R.string.location_permission_title),
activity.getString(R.string.title_app_shortcut_setting), activity.getString(dialogTextResource),
activity.getString(R.string.cancel), activity.getString(R.string.title_app_shortcut_setting),
() -> openAppSettings(activity), activity.getString(R.string.cancel),
() -> Toast.makeText(activity, activity.getString(dialogTextResource), { openAppSettings(activity) },
Toast.LENGTH_LONG).show() {
); Toast.makeText(
activity,
activity.getString(dialogTextResource),
Toast.LENGTH_LONG
).show()
}
)
} }
/** /**
@ -145,22 +161,20 @@ public class LocationPermissionsHelper {
* *
* @param activity Activity object * @param activity Activity object
*/ */
public void openAppSettings(Activity activity) { private fun openAppSettings(activity: Activity) {
Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS); val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
Uri uri = Uri.fromParts("package", activity.getPackageName(), null); val uri = Uri.fromParts("package", activity.packageName, null)
intent.setData(uri); intent.data = uri
activity.startActivity(intent); activity.startActivity(intent)
} }
/** /**
* Check if apps have access to location even after having individual access * Check if apps have access to location even after having individual access
* *
* @return Returns true if location services are on and false otherwise * @return Returns true if location services are on and false otherwise
*/ */
public boolean isLocationAccessToAppsTurnedOn() { fun isLocationAccessToAppsTurnedOn(): Boolean {
return (locationManager.isNetworkProviderEnabled() return locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()
|| locationManager.isGPSProviderEnabled());
} }
/** /**
@ -169,18 +183,18 @@ public class LocationPermissionsHelper {
* @param activity Activity object * @param activity Activity object
* @return Returns true if location permission is granted and false otherwise * @return Returns true if location permission is granted and false otherwise
*/ */
public boolean checkLocationPermission(Activity activity) { fun checkLocationPermission(activity: Activity): Boolean {
return PermissionUtils.hasPermission(activity, return PermissionUtils.hasPermission(
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}); activity,
arrayOf(permission.ACCESS_FINE_LOCATION)
)
} }
/** /**
* Handle onPermissionDenied within individual classes based on the requirements * Handle onPermissionDenied within individual classes based on the requirements
*/ */
public interface LocationPermissionCallback { interface LocationPermissionCallback {
fun onLocationPermissionDenied(toastMessage: String)
void onLocationPermissionDenied(String toastMessage); fun onLocationPermissionGranted()
void onLocationPermissionGranted();
} }
} }

View file

@ -1,90 +1,85 @@
package fr.free.nrw.commons.location; package fr.free.nrw.commons.location
import android.Manifest.permission; import android.Manifest
import android.app.Activity; import android.app.Activity
import android.content.Context; import android.content.Context
import android.content.pm.PackageManager; import android.content.pm.PackageManager
import android.location.Location; import android.location.Location
import android.location.LocationListener; import android.location.LocationListener
import android.location.LocationManager; import android.location.LocationManager
import android.os.Bundle; import android.os.Bundle
import androidx.core.app.ActivityCompat; import androidx.core.app.ActivityCompat
import java.util.HashSet; import timber.log.Timber
import java.util.List; import java.util.concurrent.CopyOnWriteArrayList
import java.util.Set;
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 companion object {
private static final long MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS = 10 * 100; // Maybe these values can be improved for efficiency
private static final long MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS = 1; 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 val locationManager: LocationManager =
private Location lastLocation; context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
//private Location lastLocationDuplicate; // Will be used for nearby card view on contributions activity var lastLocationVar: Location? = null
private final List<LocationUpdateListener> locationListeners = new CopyOnWriteArrayList<>(); private val locationListeners = CopyOnWriteArrayList<LocationUpdateListener>()
private boolean isLocationManagerRegistered = false; private var isLocationManagerRegistered = false
private Set<Activity> locationExplanationDisplayed = new HashSet<>(); private val locationExplanationDisplayed = mutableSetOf<Activity>()
private Context context;
/** /**
* Constructs a new instance of LocationServiceManager. * Constructs a new instance of LocationServiceManager.
* *
* @param context the context
*/ */
public LocationServiceManager(Context context) { fun getLastLocation(): LatLng? {
this.context = context; if (lastLocationVar == null) {
this.locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE); lastLocationVar = getLastKnownLocation()
return lastLocationVar?.let { LatLng.from(it) }
}
return LatLng.from(lastLocationVar!!)
} }
public LatLng getLastLocation() { private fun getLastKnownLocation(): Location? {
if (lastLocation == null) { val providers = locationManager.getProviders(true)
lastLocation = getLastKnownLocation(); var bestLocation: Location? = null
if(lastLocation != null) { for (provider in providers) {
return LatLng.from(lastLocation); val location: Location? = if (
} ActivityCompat.checkSelfPermission(
else { context,
return null; Manifest.permission.ACCESS_FINE_LOCATION)
} ==
} PackageManager.PERMISSION_GRANTED &&
return LatLng.from(lastLocation); ActivityCompat.checkSelfPermission(
} context,
Manifest.permission.ACCESS_COARSE_LOCATION)
==
PackageManager.PERMISSION_GRANTED
) {
locationManager.getLastKnownLocation(provider)
} else {
null
}
private Location getLastKnownLocation() { if (
List<String> providers = locationManager.getProviders(true); location != null
Location bestLocation = null; &&
for (String provider : providers) { (bestLocation == null || location.accuracy < bestLocation.accuracy)
Location l=null; ) {
if (ActivityCompat.checkSelfPermission(context, permission.ACCESS_FINE_LOCATION) bestLocation = 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 (bestLocation == null) { return bestLocation
return null;
}
return bestLocation;
} }
/** /**
* Registers a LocationManager to listen for current location. * Registers a LocationManager to listen for current location.
*/ */
public void registerLocationManager() { fun registerLocationManager() {
if (!isLocationManagerRegistered) { if (!isLocationManagerRegistered) {
isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) isLocationManagerRegistered =
&& requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) &&
requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
} }
} }
@ -94,100 +89,86 @@ public class LocationServiceManager implements LocationListener {
* @param locationProvider the location provider * @param locationProvider the location provider
* @return true if successful * @return true if successful
*/ */
public boolean requestLocationUpdatesFromProvider(String locationProvider) { fun requestLocationUpdatesFromProvider(locationProvider: String): Boolean {
try { return try {
// If both providers are not available if (locationManager.allProviders.contains(locationProvider)) {
if (locationManager == null || !(locationManager.getAllProviders().contains(locationProvider))) { locationManager.requestLocationUpdates(
return false; locationProvider,
}
locationManager.requestLocationUpdates(locationProvider,
MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS, MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS,
MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS, MIN_LOCATION_UPDATE_REQUEST_DISTANCE_IN_METERS,
this); this
return true; )
} catch (IllegalArgumentException e) { true
Timber.e(e, "Illegal argument exception"); } else {
return false; false
} catch (SecurityException e) { }
Timber.e(e, "Security exception"); } catch (e: IllegalArgumentException) {
return false; 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. * 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 * @param currentBestLocation the current best location
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly * @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
* LOCATION_SLIGHTLY_CHANGED if location changed slightly * 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) { 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 val timeDelta = location.time - currentBestLocation.time
long timeDelta = location.getTime() - currentBestLocation.getTime(); val isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS
boolean isSignificantlyNewer = timeDelta > MIN_LOCATION_UPDATE_REQUEST_TIME_IN_MILLIS; val isNewer = timeDelta > 0
boolean 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 val results = FloatArray(5)
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];
Location.distanceBetween( Location.distanceBetween(
currentBestLocation.getLatitude(), currentBestLocation.latitude, currentBestLocation.longitude,
currentBestLocation.getLongitude(), location.latitude, location.longitude,
location.getLatitude(), results
location.getLongitude(), )
results);
// If it's been more than two minutes since the current location, use the new location return when {
// because the user has likely moved isSignificantlyNewer
if (isSignificantlyNewer ||
|| isMoreAccurate isMoreAccurate
|| (isNewer && !isLessAccurate) ||
|| (isNewer && !isSignificantlyLessAccurate && isFromSameProvider)) { (isNewer && !isSignificantlyLessAccurate && isFromSameProvider) -> {
if (results[0] < 1000) { // Means change is smaller than 1000 meter if (results[0] < 1000) LocationChangeType.LOCATION_SLIGHTLY_CHANGED
return LocationChangeType.LOCATION_SLIGHTLY_CHANGED; else LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED
} else {
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED;
} }
} else{ else -> LocationChangeType.LOCATION_NOT_CHANGED
return LocationChangeType.LOCATION_NOT_CHANGED;
} }
} }
/** /**
* Checks whether two providers are the same * Checks whether two providers are the same
*/ */
private boolean isSameProvider(String provider1, String provider2) { private fun isSameProvider(provider1: String?, provider2: String?): Boolean {
if (provider1 == null) { return provider1 == provider2
return provider2 == null;
}
return provider1.equals(provider2);
} }
/** /**
* Unregisters location manager. * Unregisters location manager.
*/ */
public void unregisterLocationManager() { fun unregisterLocationManager() {
isLocationManagerRegistered = false; isLocationManagerRegistered = false
locationExplanationDisplayed.clear(); locationExplanationDisplayed.clear()
try { try {
locationManager.removeUpdates(this); locationManager.removeUpdates(this)
} catch (SecurityException e) { } catch (e: SecurityException) {
Timber.e(e, "Security exception"); Timber.e(e, "Security exception")
} }
} }
@ -196,9 +177,9 @@ public class LocationServiceManager implements LocationListener {
* *
* @param listener the new listener * @param listener the new listener
*/ */
public void addLocationListener(LocationUpdateListener listener) { fun addLocationListener(listener: LocationUpdateListener) {
if (!locationListeners.contains(listener)) { 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 * @param listener the listener to be removed
*/ */
public void removeLocationListener(LocationUpdateListener listener) { fun removeLocationListener(listener: LocationUpdateListener) {
locationListeners.remove(listener); locationListeners.remove(listener)
} }
@Override override fun onLocationChanged(location: Location) {
public void onLocationChanged(Location location) { Timber.d("on location changed")
Timber.d("on location changed"); val changeType = isBetterLocation(location, lastLocationVar)
if (isBetterLocation(location, lastLocation) if (changeType == LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) {
.equals(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED)) { lastLocationVar = location
lastLocation = location; locationListeners.forEach { it.onLocationChangedSignificantly(LatLng.from(location)) }
//lastLocationDuplicate = location; } else if (lastLocationVar?.let { location.distanceTo(it) }!! >= 500) {
for (LocationUpdateListener listener : locationListeners) { locationListeners.forEach { it.onLocationChangedMedium(LatLng.from(location)) }
listener.onLocationChangedSignificantly(LatLng.from(lastLocation)); } else if (changeType == LocationChangeType.LOCATION_SLIGHTLY_CHANGED) {
} lastLocationVar = location
} else if (location.distanceTo(lastLocation) >= 500) { locationListeners.forEach { it.onLocationChangedSlightly(LatLng.from(location)) }
// 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 @Deprecated("Deprecated in Java", ReplaceWith(
public void onStatusChanged(String provider, int status, Bundle extras) { "Timber.d(\"%s's status changed to %d\", provider, status)",
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 override fun onProviderDisabled(provider: String) {
public void onProviderDisabled(String provider) { Timber.d("Provider %s disabled", provider)
Timber.d("Provider %s disabled", provider);
} }
public boolean isNetworkProviderEnabled() { fun isNetworkProviderEnabled(): Boolean {
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER); return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
} }
public boolean isGPSProviderEnabled() { fun isGPSProviderEnabled(): Boolean {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
} }
public enum LocationChangeType{ enum class LocationChangeType {
LOCATION_SIGNIFICANTLY_CHANGED, //Went out of borders of nearby markers LOCATION_SIGNIFICANTLY_CHANGED,
LOCATION_SLIGHTLY_CHANGED, //User might be walking or driving LOCATION_SLIGHTLY_CHANGED,
LOCATION_MEDIUM_CHANGED, //Between slight and significant changes, will be used for nearby card view updates. LOCATION_MEDIUM_CHANGED,
LOCATION_NOT_CHANGED, LOCATION_NOT_CHANGED,
PERMISSION_JUST_GRANTED, PERMISSION_JUST_GRANTED,
MAP_UPDATED, MAP_UPDATED,
@ -272,3 +244,12 @@ public class LocationServiceManager implements LocationListener {
CUSTOM_QUERY CUSTOM_QUERY
} }
} }

View file

@ -1,7 +1,12 @@
package fr.free.nrw.commons.location; package fr.free.nrw.commons.location
public interface LocationUpdateListener { interface LocationUpdateListener {
void onLocationChangedSignificantly(LatLng latLng); // Will be used to update all nearby markers on the map // Will be used to update all nearby markers on the map
void onLocationChangedSlightly(LatLng latLng); // Will be used to track users motion fun onLocationChangedSignificantly(latLng: LatLng)
void onLocationChangedMedium(LatLng latLng); // Will be used updating nearby card view notification
} // Will be used to track users motion
fun onLocationChangedSlightly(latLng: LatLng)
// Will be used updating nearby card view notification
fun onLocationChangedMedium(latLng: LatLng)
}

View file

@ -42,8 +42,8 @@ class LanguagesAdapter constructor(
AppLanguageLookUpTable(context) AppLanguageLookUpTable(context)
init { init {
languageNamesList = language.localizedNames languageNamesList = language.getLocalizedNames()
languageCodesList = language.codes languageCodesList = language.getCodes()
} }
private val filter = LanguageFilter() private val filter = LanguageFilter()
@ -117,7 +117,7 @@ class LanguagesAdapter constructor(
*/ */
fun getIndexOfUserDefaultLocale(context: Context): Int { fun getIndexOfUserDefaultLocale(context: Context): Int {
val userLanguageCode = context.locale?.language ?: return DEFAULT_INDEX 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) fun getIndexOfLanguageCode(languageCode: String): Int = languageCodesList.indexOf(languageCode)
@ -128,17 +128,17 @@ class LanguagesAdapter constructor(
override fun performFiltering(constraint: CharSequence?): FilterResults { override fun performFiltering(constraint: CharSequence?): FilterResults {
val filterResults = FilterResults() val filterResults = FilterResults()
val temp: LinkedHashMap<String, String> = LinkedHashMap() val temp: LinkedHashMap<String, String> = LinkedHashMap()
if (constraint != null && language.localizedNames != null) { if (constraint != null) {
val length: Int = language.localizedNames.size val length: Int = language.getLocalizedNames().size
var i = 0 var i = 0
while (i < length) { while (i < length) {
val key: String = language.codes[i] val key: String = language.getCodes()[i]
val value: String = language.localizedNames[i] val value: String = language.getLocalizedNames()[i]
val defaultlanguagecode = getIndexOfUserDefaultLocale(context) val defaultlanguagecode = getIndexOfUserDefaultLocale(context)
if (value.contains(constraint, true) || if (value.contains(constraint, true) ||
Locale(key) Locale(key)
.getDisplayName( .getDisplayName(
Locale(language.codes[defaultlanguagecode]), Locale(language.getCodes()[defaultlanguagecode]),
).contains(constraint, true) ).contains(constraint, true)
) { ) {
temp[key] = value temp[key] = value

View file

@ -62,5 +62,5 @@ class LatLngTests {
private fun assertPrettyCoordinateString( private fun assertPrettyCoordinateString(
expected: String, expected: String,
place: LatLng, place: LatLng,
) = assertEquals(expected, place.prettyCoordinateString) ) = assertEquals(expected, place.getPrettyCoordinateString())
} }

View file

@ -248,11 +248,11 @@ class MediaDetailFragmentUnitTests {
@Throws(Exception::class) @Throws(Exception::class)
fun testOnUpdateCoordinatesClickedCurrentLocationNull() { fun testOnUpdateCoordinatesClickedCurrentLocationNull() {
`when`(media.coordinates).thenReturn(null) `when`(media.coordinates).thenReturn(null)
`when`(locationManager.lastLocation).thenReturn(null) `when`(locationManager.getLastLocation()).thenReturn(null)
`when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297") `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297")
fragment.onUpdateCoordinatesClicked() fragment.onUpdateCoordinatesClicked()
Mockito.verify(media, Mockito.times(1)).coordinates 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 shadowActivity: ShadowActivity = shadowOf(activity)
val startedIntent = shadowActivity.nextStartedActivity val startedIntent = shadowActivity.nextStartedActivity
val shadowIntent: ShadowIntent = shadowOf(startedIntent) val shadowIntent: ShadowIntent = shadowOf(startedIntent)
@ -276,11 +276,11 @@ class MediaDetailFragmentUnitTests {
@Throws(Exception::class) @Throws(Exception::class)
fun testOnUpdateCoordinatesClickedCurrentLocationNotNull() { fun testOnUpdateCoordinatesClickedCurrentLocationNotNull() {
`when`(media.coordinates).thenReturn(null) `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") `when`(applicationKvStore.getString(lastLocation)).thenReturn("37.773972,-122.431297")
fragment.onUpdateCoordinatesClicked() fragment.onUpdateCoordinatesClicked()
Mockito.verify(locationManager, Mockito.times(3)).lastLocation Mockito.verify(locationManager, Mockito.times(3)).getLastLocation()
val shadowActivity: ShadowActivity = shadowOf(activity) val shadowActivity: ShadowActivity = shadowOf(activity)
val startedIntent = shadowActivity.nextStartedActivity val startedIntent = shadowActivity.nextStartedActivity
val shadowIntent: ShadowIntent = shadowOf(startedIntent) val shadowIntent: ShadowIntent = shadowOf(startedIntent)

View file

@ -54,8 +54,8 @@ class LanguagesAdapterTest {
.from(context) .from(context)
.inflate(R.layout.row_item_languages_spinner, null) as View .inflate(R.layout.row_item_languages_spinner, null) as View
languageNamesList = language.localizedNames languageNamesList = language.getLocalizedNames()
languageCodesList = language.codes languageCodesList = language.getCodes()
languagesAdapter = LanguagesAdapter(context, selectedLanguages) languagesAdapter = LanguagesAdapter(context, selectedLanguages)
} }
@ -124,12 +124,12 @@ class LanguagesAdapterTest {
var i = 0 var i = 0
var s = 0 var s = 0
while (i < length) { while (i < length) {
val key: String = language.codes[i] val key: String = language.getCodes()[i]
val value: String = language.localizedNames[i] val value: String = language.getLocalizedNames()[i]
if (value.contains(constraint, true) || if (value.contains(constraint, true) ||
Locale(key) Locale(key)
.getDisplayName( .getDisplayName(
Locale(language.codes[defaultlanguagecode!!]), Locale(language.getCodes()[defaultlanguagecode!!]),
).contains(constraint, true) ).contains(constraint, true)
) { ) {
s++ s++