Migrate location and language module from Java to Kotlin (#5988)

* Rename .java to .kt

* Migrated location and language module from Java to Kotlin

* Changed lastLocation visibility
This commit is contained in:
Saifuddin Adenwala 2024-12-03 11:57:11 +05:30 committed by GitHub
parent 771f370f9a
commit 8265cc6306
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 773 additions and 827 deletions

View file

@ -423,7 +423,7 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
* Moves map to GPS location
*/
private fun moveMapToGPSLocation() {
locationManager.lastLocation?.let {
locationManager.getLastLocation()?.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.getLastLocation()?.let {
addLocationMarker(GeoPoint(it.latitude, it.longitude))
markerImage.translationY = 0f
}

View file

@ -1,141 +0,0 @@
package fr.free.nrw.commons.language;
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 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.
@NonNull private final Resources 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);
// English names for all app supported languages in fixed order.
@NonNull private SoftReference<List<String>> canonicalNamesRef = new SoftReference<>(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();
}
/**
* @return Nonnull immutable list. The special code representing the dynamic system language is
* null.
*/
@NonNull
public List<String> getCodes() {
List<String> codes = codesRef.get();
if (codes == null) {
codes = getStringList(R.array.preference_language_keys);
codesRef = new SoftReference<>(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);
}
}
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);
}
}
return name;
}
public List<String> getCanonicalNames() {
List<String> names = canonicalNamesRef.get();
if (names == null) {
names = getStringList(R.array.preference_language_canonical_names);
canonicalNamesRef = new SoftReference<>(names);
}
return names;
}
public List<String> getLocalizedNames() {
List<String> names = localizedNamesRef.get();
if (names == null) {
names = getStringList(R.array.preference_language_local_names);
localizedNamesRef = new SoftReference<>(names);
}
return names;
}
public boolean isSupportedCode(@Nullable String code) {
return getCodes().contains(code);
}
private <T> T defaultIndex(List<T> list, int index, T defaultValue) {
return inBounds(list, index) ? list.get(index) : defaultValue;
}
/**
* Searches #codes for the specified language code and returns the index for use in
* #canonicalNames and #localizedNames.
*
* @param code The language code to search for. The special code representing the dynamic system
* 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);
}
/** @return Nonnull immutable list. */
@NonNull
private List<String> getStringList(int id) {
return Arrays.asList(getStringArray(id));
}
private boolean inBounds(List<?> list, int index) {
return index >= 0 && index < list.size();
}
public String[] getStringArray(@ArrayRes int id) {
return resources.getStringArray(id);
}
}

View file

@ -0,0 +1,135 @@
package fr.free.nrw.commons.language
import android.content.Context
import android.content.res.Resources
import android.text.TextUtils
import androidx.annotation.ArrayRes
import fr.free.nrw.commons.R
import java.lang.ref.SoftReference
import java.util.Arrays
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. */
class AppLanguageLookUpTable(context: Context) {
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.
private var codesRef = SoftReference<List<String>>(null)
// English names for all app supported languages in fixed order.
private var canonicalNamesRef = SoftReference<List<String>>(null)
// Native names for all app supported languages in fixed order.
private var localizedNamesRef = SoftReference<List<String>>(null)
/**
* @return Nonnull immutable list. The special code representing the dynamic system language is
* null.
*/
fun getCodes(): List<String> {
var codes = codesRef.get()
if (codes == null) {
codes = getStringList(R.array.preference_language_keys)
codesRef = SoftReference(codes)
}
return codes
}
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
}
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
}
fun getCanonicalNames(): List<String> {
var names = canonicalNamesRef.get()
if (names == null) {
names = getStringList(R.array.preference_language_canonical_names)
canonicalNamesRef = SoftReference(names)
}
return names
}
fun getLocalizedNames(): List<String> {
var names = localizedNamesRef.get()
if (names == null) {
names = getStringList(R.array.preference_language_local_names)
localizedNamesRef = SoftReference(names)
}
return names
}
fun isSupportedCode(code: String?): Boolean {
return getCodes().contains(code)
}
private fun <T> defaultIndex(list: List<T>, index: Int, defaultValue: T?): T? {
return if (inBounds(list, index)) list[index] else defaultValue
}
/**
* Searches #codes for the specified language code and returns the index for use in
* #canonicalNames and #localizedNames.
*
* @param code The language code to search for. The special code representing the dynamic system
* language is null.
* @return The index of the language code or -1 if the code is not supported.
*/
private fun indexOfCode(code: String?): Int {
return getCodes().indexOf(code)
}
/** @return Nonnull immutable list. */
private fun getStringList(id: Int): List<String> {
return getStringArray(id).toList()
}
private fun inBounds(list: List<*>, index: Int): Boolean {
return index in list.indices
}
fun getStringArray(@ArrayRes id: Int): Array<String> {
return resources.getStringArray(id)
}
}

View file

@ -1,198 +0,0 @@
package fr.free.nrw.commons.location;
import android.location.Location;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
/**
* 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;
/**
* 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;
}
this.latitude = Math.max(-90.0D, Math.min(90.0D, latitude));
this.accuracy = accuracy;
}
/**
* An alternate constructor for this class.
* @param in A parcelable which contains the latitude, longitude, and accuracy
*/
public LatLng(Parcel in) {
latitude = in.readDouble();
longitude = in.readDouble();
accuracy = in.readFloat();
}
/**
* 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
*/
public static LatLng from(@NonNull Location location) {
return new LatLng(location.getLatitude(), location.getLongitude(), location.getAccuracy());
}
/**
* creates a hash code for the longitude 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";
}
/**
* Returns a nicely formatted coordinate string. Used e.g. in
* the detail view.
*
* @return The formatted string.
*/
public String getPrettyCoordinateString() {
return formatCoordinate(this.latitude) + " " + this.getNorthSouth() + ", "
+ formatCoordinate(this.longitude) + " " + this.getEastWest();
}
/**
* Return the location accuracy in meter.
*
* @return float
*/
public float getAccuracy() {
return accuracy;
}
/**
* Return the longitude in degrees.
*
* @return double
*/
public double getLongitude() {
return longitude;
}
/**
* Return the latitude in degrees.
*
* @return double
*/
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];
}
};
}

View file

@ -0,0 +1,150 @@
package fr.free.nrw.commons.location
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
/**
* A latitude and longitude point with accuracy information, often of a picture.
*/
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°
*
* 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.
*/
init {
val adjustedLongitude = when {
longitude in -180.0..180.0 -> longitude
else -> ((longitude - 180.0) % 360.0 + 360.0) % 360.0 - 180.0
}
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 parcel A parcelable which contains the latitude, longitude, and accuracy
*/
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
}
/**
* Checks for equality of two LatLng objects.
* @param other the second LatLng object
*/
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()
}
/**
* Returns a string representation of the latitude and longitude.
*/
override fun toString(): String {
return "lat/lng: ($latitude,$longitude)"
}
/**
* Returns a nicely formatted coordinate string. Used e.g. in
* the detail view.
*
* @return The formatted string.
*/
fun getPrettyCoordinateString(): String {
return "${formatCoordinate(latitude)} ${getNorthSouth()}, " +
"${formatCoordinate(longitude)} ${getEastWest()}"
}
/**
* Gets a URI for a Google Maps intent at the location.
*/
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()
}
/**
* Returns "N" or "S" depending on the latitude.
*
* @return "N" or "S".
*/
private fun getNorthSouth(): String = if (latitude < 0) "S" else "N"
/**
* Returns "E" or "W" depending on the longitude.
*
* @return "E" or "W".
*/
private fun getEastWest(): String = if (longitude in 0.0..179.999) "E" else "W"
}

View file

@ -1,186 +0,0 @@
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;
/**
* Helper class to handle location permissions.
*
* Location flow for fragments containing a map is as follows:
* Case 1: When location permission has never been asked for or denied before
* Check if permission is already granted or not.
* If not already granted, ask for it (if it isn't denied twice before).
* If now user grants permission, go to Case 3/4, else go to Case 2.
*
* Case 2: When location permission is just asked but has been denied
* Shows a toast to tell the user why location permission is needed.
* Also shows a rationale to the user, on agreeing to which, we go back to Case 1.
* Show current location / nearby pins / nearby images according to the default location.
*
* Case 3: When location permission are already granted, but location services are off
* Asks the user to turn on the location service, using a dialog.
* If the user rejects, checks for the last known location and shows stuff using that location.
* Also displays a toast telling the user why location should be turned on.
*
* Case 4: When location permission has been granted and location services are also on
* 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;
}
/**
* Ask for location permission if the user agrees on attaching location with pictures and the
* app does not have the access to location
*
* @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
) {
if (checkLocationPermission(activity)) {
callback.onLocationPermissionGranted();
} else {
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);
},
() -> callback.onLocationPermissionDenied(
activity.getString(R.string.upload_map_location_access)),
null,
false);
} else {
ActivityCompat.requestPermissions(activity,
new String[]{permission.ACCESS_FINE_LOCATION},
RequestCodes.LOCATION);
}
}
}
/**
* Shows a dialog for user to open the settings page and turn on location services
*
* @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()
);
}
/**
* Opens the location access page in settings, for user to turn on location services
*
* @param activity Activtiy object
*/
public void openLocationSettings(Activity activity) {
final Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
final PackageManager packageManager = activity.getPackageManager();
if (intent.resolveActivity(packageManager) != null) {
activity.startActivity(intent);
} else {
Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG)
.show();
}
}
/**
* Shows a dialog for user to open the app's settings page and give location permission
*
* @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()
);
}
/**
* Opens detailed settings page of the app for the user to turn on location services
*
* @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);
}
/**
* 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());
}
/**
* Checks if location permission is already granted or not
*
* @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});
}
/**
* Handle onPermissionDenied within individual classes based on the requirements
*/
public interface LocationPermissionCallback {
void onLocationPermissionDenied(String toastMessage);
void onLocationPermissionGranted();
}
}

View file

@ -0,0 +1,200 @@
package fr.free.nrw.commons.location
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.
*
* Location flow for fragments containing a map is as follows:
* Case 1: When location permission has never been asked for or denied before
* Check if permission is already granted or not.
* If not already granted, ask for it (if it isn't denied twice before).
* If now user grants permission, go to Case 3/4, else go to Case 2.
*
* Case 2: When location permission is just asked but has been denied
* Shows a toast to tell the user why location permission is needed.
* Also shows a rationale to the user, on agreeing to which, we go back to Case 1.
* Show current location / nearby pins / nearby images according to the default location.
*
* Case 3: When location permission are already granted, but location services are off
* Asks the user to turn on the location service, using a dialog.
* If the user rejects, checks for the last known location and shows stuff using that location.
* Also displays a toast telling the user why location should be turned on.
*
* Case 4: When location permission has been granted and location services are also on
* Do whatever is required by that particular activity / fragment using current location.
*
*/
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
* app does not have the access to location
*
* @param dialogTitleResource Resource id of the title of the dialog
* @param dialogTextResource Resource id of the text of the dialog
*/
fun requestForLocationAccess(
dialogTitleResource: Int,
dialogTextResource: Int
) {
if (checkLocationPermission(activity)) {
callback?.onLocationPermissionGranted()
} else {
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,
arrayOf(permission.ACCESS_FINE_LOCATION),
1
)
},
{
callback?.onLocationPermissionDenied(
activity.getString(R.string.upload_map_location_access)
)
},
null,
false
)
} else {
ActivityCompat.requestPermissions(
activity,
arrayOf(permission.ACCESS_FINE_LOCATION),
RequestCodes.LOCATION
)
}
}
}
/**
* Shows a dialog for user to open the settings page and turn on location services
*
* @param activity Activity object
* @param dialogTextResource int id of the required string resource
*/
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 Activity object
*/
fun openLocationSettings(activity: Activity) {
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
val packageManager = activity.packageManager
if (intent.resolveActivity(packageManager) != null) {
activity.startActivity(intent)
} else {
Toast.makeText(activity, R.string.cannot_open_location_settings, Toast.LENGTH_LONG)
.show()
}
}
/**
* Shows a dialog for user to open the app's settings page and give location permission
*
* @param activity Activity object
* @param dialogTextResource int id of the required string resource
*/
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()
}
)
}
/**
* Opens detailed settings page of the app for the user to turn on location services
*
* @param activity Activity object
*/
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
*/
fun isLocationAccessToAppsTurnedOn(): Boolean {
return locationManager.isNetworkProviderEnabled() || locationManager.isGPSProviderEnabled()
}
/**
* Checks if location permission is already granted or not
*
* @param activity Activity object
* @return Returns true if location permission is granted and false otherwise
*/
fun checkLocationPermission(activity: Activity): Boolean {
return PermissionUtils.hasPermission(
activity,
arrayOf(permission.ACCESS_FINE_LOCATION)
)
}
/**
* Handle onPermissionDenied within individual classes based on the requirements
*/
interface LocationPermissionCallback {
fun onLocationPermissionDenied(toastMessage: String)
fun onLocationPermissionGranted()
}
}

View file

@ -1,274 +0,0 @@
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 timber.log.Timber;
public class LocationServiceManager implements 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;
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;
/**
* 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);
}
public LatLng getLastLocation() {
if (lastLocation == null) {
lastLocation = getLastKnownLocation();
if(lastLocation != null) {
return LatLng.from(lastLocation);
}
else {
return null;
}
}
return LatLng.from(lastLocation);
}
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 (bestLocation == null) {
return null;
}
return bestLocation;
}
/**
* Registers a LocationManager to listen for current location.
*/
public void registerLocationManager() {
if (!isLocationManagerRegistered) {
isLocationManagerRegistered = requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER)
&& requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER);
}
}
/**
* Requests location updates from the specified provider.
*
* @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,
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;
}
}
/**
* Returns whether a given location is better than the current best location.
*
* @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) {
if (currentBestLocation == null) {
// A new location is always better than no location
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;
// 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];
Location.distanceBetween(
currentBestLocation.getLatitude(),
currentBestLocation.getLongitude(),
location.getLatitude(),
location.getLongitude(),
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;
}
} else{
return 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);
}
/**
* Unregisters location manager.
*/
public void unregisterLocationManager() {
isLocationManagerRegistered = false;
locationExplanationDisplayed.clear();
try {
locationManager.removeUpdates(this);
} catch (SecurityException e) {
Timber.e(e, "Security exception");
}
}
/**
* Adds a new listener to the list of location listeners.
*
* @param listener the new listener
*/
public void addLocationListener(LocationUpdateListener listener) {
if (!locationListeners.contains(listener)) {
locationListeners.add(listener);
}
}
/**
* Removes a listener from the list of location listeners.
*
* @param listener the listener to be removed
*/
public void removeLocationListener(LocationUpdateListener listener) {
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
public void onStatusChanged(String provider, int status, Bundle extras) {
Timber.d("%s's status changed to %d", provider, status);
}
@Override
public void onProviderEnabled(String provider) {
Timber.d("Provider %s enabled", provider);
}
@Override
public void onProviderDisabled(String provider) {
Timber.d("Provider %s disabled", provider);
}
public boolean isNetworkProviderEnabled() {
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
}
public boolean isGPSProviderEnabled() {
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.
LOCATION_NOT_CHANGED,
PERMISSION_JUST_GRANTED,
MAP_UPDATED,
SEARCH_CUSTOM_AREA,
CUSTOM_QUERY
}
}

View file

@ -0,0 +1,255 @@
package fr.free.nrw.commons.location
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
class LocationServiceManager(private val context: Context) : LocationListener {
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 val locationManager: LocationManager =
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
private var lastLocationVar: Location? = null
private val locationListeners = CopyOnWriteArrayList<LocationUpdateListener>()
private var isLocationManagerRegistered = false
private val locationExplanationDisplayed = mutableSetOf<Activity>()
/**
* Constructs a new instance of LocationServiceManager.
*
*/
fun getLastLocation(): LatLng? {
if (lastLocationVar == null) {
lastLocationVar = getLastKnownLocation()
return lastLocationVar?.let { LatLng.from(it) }
}
return LatLng.from(lastLocationVar!!)
}
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
}
if (
location != null
&&
(bestLocation == null || location.accuracy < bestLocation.accuracy)
) {
bestLocation = location
}
}
return bestLocation
}
/**
* Registers a LocationManager to listen for current location.
*/
fun registerLocationManager() {
if (!isLocationManagerRegistered) {
isLocationManagerRegistered =
requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) &&
requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER)
}
}
/**
* Requests location updates from the specified provider.
*
* @param locationProvider the location provider
* @return true if successful
*/
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
)
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 currentBestLocation the current best location
* @return LOCATION_SIGNIFICANTLY_CHANGED if location changed significantly
* LOCATION_SLIGHTLY_CHANGED if location changed slightly
*/
private fun isBetterLocation(location: Location, currentBestLocation: Location?): LocationChangeType {
if (currentBestLocation == null) {
return LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED
}
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)
val results = FloatArray(5)
Location.distanceBetween(
currentBestLocation.latitude, currentBestLocation.longitude,
location.latitude, location.longitude,
results
)
return when {
isSignificantlyNewer
||
isMoreAccurate
||
(isNewer && !isSignificantlyLessAccurate && isFromSameProvider) -> {
if (results[0] < 1000) LocationChangeType.LOCATION_SLIGHTLY_CHANGED
else LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED
}
else -> LocationChangeType.LOCATION_NOT_CHANGED
}
}
/**
* Checks whether two providers are the same
*/
private fun isSameProvider(provider1: String?, provider2: String?): Boolean {
return provider1 == provider2
}
/**
* Unregisters location manager.
*/
fun unregisterLocationManager() {
isLocationManagerRegistered = false
locationExplanationDisplayed.clear()
try {
locationManager.removeUpdates(this)
} catch (e: SecurityException) {
Timber.e(e, "Security exception")
}
}
/**
* Adds a new listener to the list of location listeners.
*
* @param listener the new listener
*/
fun addLocationListener(listener: LocationUpdateListener) {
if (!locationListeners.contains(listener)) {
locationListeners.add(listener)
}
}
/**
* Removes a listener from the list of location listeners.
*
* @param listener the listener to be removed
*/
fun removeLocationListener(listener: LocationUpdateListener) {
locationListeners.remove(listener)
}
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)) }
}
}
@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 fun onProviderEnabled(provider: String) {
Timber.d("Provider %s enabled", provider)
}
override fun onProviderDisabled(provider: String) {
Timber.d("Provider %s disabled", provider)
}
fun isNetworkProviderEnabled(): Boolean {
return locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)
}
fun isGPSProviderEnabled(): Boolean {
return locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER)
}
enum class LocationChangeType {
LOCATION_SIGNIFICANTLY_CHANGED,
LOCATION_SLIGHTLY_CHANGED,
LOCATION_MEDIUM_CHANGED,
LOCATION_NOT_CHANGED,
PERMISSION_JUST_GRANTED,
MAP_UPDATED,
SEARCH_CUSTOM_AREA,
CUSTOM_QUERY
}
}

View file

@ -1,7 +0,0 @@
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
}

View file

@ -0,0 +1,12 @@
package fr.free.nrw.commons.location
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)
}

View file

@ -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

View file

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

View file

@ -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)

View file

@ -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++