Migrated the following files in util module to Kotlin

- ImageUtils
- ImageUtilsWrapper
- LangCodeUtils
- LayoutUtils
- LengthUtils
- LocationUtils
- MapUtils
This commit is contained in:
Saifuddin 2024-11-17 21:20:36 +05:30
parent 3126ad947d
commit 4b95f47b29
12 changed files with 453 additions and 421 deletions

View file

@ -5,7 +5,6 @@ import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED;
import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED; import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED;
import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL; import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL;
import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME; import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME;
import static fr.free.nrw.commons.utils.ImageUtils.IMAGE_OK;
import static fr.free.nrw.commons.utils.LengthUtils.computeBearing; import static fr.free.nrw.commons.utils.LengthUtils.computeBearing;
import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween;
@ -23,12 +22,10 @@ import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.MenuItem.OnMenuItemClickListener;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultCallback;
@ -39,7 +36,6 @@ import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
import androidx.fragment.app.FragmentTransaction; import androidx.fragment.app.FragmentTransaction;
import fr.free.nrw.commons.CommonsApplication;
import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.auth.SessionManager;
import fr.free.nrw.commons.databinding.FragmentContributionsBinding; import fr.free.nrw.commons.databinding.FragmentContributionsBinding;

View file

@ -1,10 +1,10 @@
package fr.free.nrw.commons.delete; package fr.free.nrw.commons.delete;
import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE; import static fr.free.nrw.commons.notification.NotificationHelper.NOTIFICATION_DELETE;
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Context; import android.content.Context;
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@ -16,6 +16,7 @@ import fr.free.nrw.commons.actions.PageEditClient;
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException; import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException;
import fr.free.nrw.commons.notification.NotificationHelper; import fr.free.nrw.commons.notification.NotificationHelper;
import fr.free.nrw.commons.review.ReviewController; import fr.free.nrw.commons.review.ReviewController;
import fr.free.nrw.commons.utils.LangCodeUtils;
import fr.free.nrw.commons.utils.ViewUtilWrapper; import fr.free.nrw.commons.utils.ViewUtilWrapper;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Single; import io.reactivex.Single;

View file

@ -310,7 +310,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
} }
private void startMapWithoutPermission() { private void startMapWithoutPermission() {
lastKnownLocation = MapUtils.defaultLatLng; lastKnownLocation = MapUtils.getDefaultLatLng();
moveCameraToPosition( moveCameraToPosition(
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
presenter.onMapReady(exploreMapController); presenter.onMapReady(exploreMapController);
@ -331,7 +331,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
!locationPermissionsHelper.checkLocationPermission(getActivity())) { !locationPermissionsHelper.checkLocationPermission(getActivity())) {
isPermissionDenied = true; isPermissionDenied = true;
} }
lastKnownLocation = MapUtils.defaultLatLng; lastKnownLocation = MapUtils.getDefaultLatLng();
moveCameraToPosition( moveCameraToPosition(
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
presenter.onMapReady(exploreMapController); presenter.onMapReady(exploreMapController);

View file

@ -43,7 +43,6 @@ import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams; import android.view.ViewGroup.LayoutParams;
import android.view.animation.Animation; import android.view.animation.Animation;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.Toast; import android.widget.Toast;
import androidx.activity.result.ActivityResultCallback; import androidx.activity.result.ActivityResultCallback;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
@ -701,7 +700,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
= new LatLng(Double.parseDouble(locationLatLng[0]), = new LatLng(Double.parseDouble(locationLatLng[0]),
Double.parseDouble(locationLatLng[1]), 1f); Double.parseDouble(locationLatLng[1]), 1f);
} else { } else {
lastKnownLocation = MapUtils.defaultLatLng; lastKnownLocation = MapUtils.getDefaultLatLng();
} }
if (binding.map != null) { if (binding.map != null) {
moveCameraToPosition( moveCameraToPosition(
@ -793,7 +792,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
hideBottomSheet(); hideBottomSheet();
binding.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener( binding.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener(
(v, hasFocus) -> { (v, hasFocus) -> {
LayoutUtils.setLayoutHeightAllignedToWidth(1.25, LayoutUtils.setLayoutHeightAlignedToWidth(1.25,
binding.nearbyFilterList.getRoot()); binding.nearbyFilterList.getRoot());
if (hasFocus) { if (hasFocus) {
binding.nearbyFilterList.getRoot().setVisibility(View.VISIBLE); binding.nearbyFilterList.getRoot().setVisibility(View.VISIBLE);
@ -834,7 +833,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
.getLayoutParams().width = (int) LayoutUtils.getScreenWidth(getActivity(), .getLayoutParams().width = (int) LayoutUtils.getScreenWidth(getActivity(),
0.75); 0.75);
binding.nearbyFilterList.searchListView.setAdapter(nearbyFilterSearchRecyclerViewAdapter); binding.nearbyFilterList.searchListView.setAdapter(nearbyFilterSearchRecyclerViewAdapter);
LayoutUtils.setLayoutHeightAllignedToWidth(1.25, binding.nearbyFilterList.getRoot()); LayoutUtils.setLayoutHeightAlignedToWidth(1.25, binding.nearbyFilterList.getRoot());
compositeDisposable.add( compositeDisposable.add(
RxSearchView.queryTextChanges(binding.nearbyFilter.searchViewLayout.searchView) RxSearchView.queryTextChanges(binding.nearbyFilter.searchViewLayout.searchView)
.takeUntil(RxView.detaches(binding.nearbyFilter.searchViewLayout.searchView)) .takeUntil(RxView.detaches(binding.nearbyFilter.searchViewLayout.searchView))

View file

@ -11,13 +11,10 @@ import static fr.free.nrw.commons.nearby.CheckBoxTriStates.UNKNOWN;
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
import android.location.Location; import android.location.Location;
import android.view.View;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.work.ExistingWorkPolicy;
import fr.free.nrw.commons.BaseMarker; import fr.free.nrw.commons.BaseMarker;
import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.Contribution;
import fr.free.nrw.commons.kvstore.JsonKvStore; import fr.free.nrw.commons.kvstore.JsonKvStore;
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType; import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType;
@ -26,14 +23,10 @@ import fr.free.nrw.commons.nearby.CheckBoxTriStates;
import fr.free.nrw.commons.nearby.Label; import fr.free.nrw.commons.nearby.Label;
import fr.free.nrw.commons.nearby.MarkerPlaceGroup; import fr.free.nrw.commons.nearby.MarkerPlaceGroup;
import fr.free.nrw.commons.nearby.NearbyController; import fr.free.nrw.commons.nearby.NearbyController;
import fr.free.nrw.commons.nearby.NearbyFilterState;
import fr.free.nrw.commons.nearby.Place; import fr.free.nrw.commons.nearby.Place;
import fr.free.nrw.commons.nearby.PlaceDao;
import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract;
import fr.free.nrw.commons.upload.worker.WorkRequestHelper;
import fr.free.nrw.commons.utils.LocationUtils; import fr.free.nrw.commons.utils.LocationUtils;
import fr.free.nrw.commons.wikidata.WikidataEditListener; import fr.free.nrw.commons.wikidata.WikidataEditListener;
import io.reactivex.disposables.CompositeDisposable;
import java.lang.reflect.Proxy; import java.lang.reflect.Proxy;
import java.util.List; import java.util.List;
import timber.log.Timber; import timber.log.Timber;

View file

@ -1,197 +1,194 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import android.app.NotificationChannel; import android.app.NotificationChannel
import android.app.NotificationManager; import android.app.NotificationManager
import android.app.ProgressDialog; import android.app.ProgressDialog
import android.app.WallpaperManager; import android.content.Context
import android.content.Context; import android.graphics.Bitmap
import android.graphics.Bitmap; import android.graphics.BitmapFactory
import android.graphics.BitmapFactory; import android.graphics.Canvas
import android.graphics.Canvas; import android.graphics.Color
import android.graphics.Color; import android.net.Uri
import android.net.Uri; import android.os.Build
import android.os.Build; import androidx.annotation.IntDef
import androidx.annotation.IntDef; import androidx.core.content.ContextCompat
import androidx.annotation.Nullable; import androidx.exifinterface.media.ExifInterface
import androidx.core.content.ContextCompat; import androidx.work.Data
import androidx.exifinterface.media.ExifInterface; import androidx.work.OneTimeWorkRequest
import androidx.work.Data; import androidx.work.WorkManager
import androidx.work.OneTimeWorkRequest; import fr.free.nrw.commons.R
import androidx.work.WorkManager; import fr.free.nrw.commons.contributions.SetWallpaperWorker
import com.facebook.common.executors.CallerThreadExecutor; import fr.free.nrw.commons.location.LatLng
import com.facebook.common.references.CloseableReference; import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient
import com.facebook.datasource.DataSource; import io.reactivex.android.schedulers.AndroidSchedulers
import com.facebook.drawee.backends.pipeline.Fresco; import io.reactivex.disposables.CompositeDisposable
import com.facebook.imagepipeline.core.ImagePipeline; import io.reactivex.schedulers.Schedulers
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber; import timber.log.Timber
import com.facebook.imagepipeline.image.CloseableImage;
import com.facebook.imagepipeline.request.ImageRequest;
import com.facebook.imagepipeline.request.ImageRequestBuilder;
import fr.free.nrw.commons.R;
import fr.free.nrw.commons.contributions.SetWallpaperWorker;
import fr.free.nrw.commons.location.LatLng;
import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.schedulers.Schedulers;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import timber.log.Timber;
/** /**
* Created by bluesir9 on 3/10/17. * Created by blueSir9 on 3/10/17.
*/ */
public class ImageUtils {
object ImageUtils {
/** /**
* Set 0th bit as 1 for dark image ie. 0001 * Set 0th bit as 1 for dark image ie. 0001
*/ */
public static final int IMAGE_DARK = 1 << 0; // 1 const val IMAGE_DARK = 1 shl 0 // 1
/** /**
* Set 1st bit as 1 for blurry image ie. 0010 * Set 1st bit as 1 for blurry image ie. 0010
*/ */
public static final int IMAGE_BLURRY = 1 << 1; // 2 const val IMAGE_BLURRY = 1 shl 1 // 2
/** /**
* Set 2nd bit as 1 for duplicate image ie. 0100 * Set 2nd bit as 1 for duplicate image ie. 0100
*/ */
public static final int IMAGE_DUPLICATE = 1 << 2; //4 const val IMAGE_DUPLICATE = 1 shl 2 // 4
/** /**
* Set 3rd bit as 1 for image with different geo location ie. 1000 * Set 3rd bit as 1 for image with different geo location ie. 1000
*/ */
public static final int IMAGE_GEOLOCATION_DIFFERENT = 1 << 3; //8 const val IMAGE_GEOLOCATION_DIFFERENT = 1 shl 3 // 8
/** /**
* The parameter FILE_FBMD is returned from the class ReadFBMD if the uploaded image contains FBMD data else returns IMAGE_OK * The parameter FILE_FBMD is returned from the class ReadFBMD if the uploaded image contains
* FBMD data else returns IMAGE_OK
* ie. 10000 * ie. 10000
*/ */
public static final int FILE_FBMD = 1 << 4; const val FILE_FBMD = 1 shl 4 // 16
/** /**
* The parameter FILE_NO_EXIF is returned from the class EXIFReader if the uploaded image does not contains EXIF data else returns IMAGE_OK * The parameter FILE_NO_EXIF is returned from the class EXIFReader if the uploaded image does
* ie. 100000 * not contains EXIF data else returns IMAGE_OK
*/ * ie. 100000
public static final int FILE_NO_EXIF = 1 << 5; */
public static final int IMAGE_OK = 0; const val FILE_NO_EXIF = 1 shl 5 // 32
public static final int IMAGE_KEEP = -1;
public static final int IMAGE_WAIT = -2;
public static final int EMPTY_CAPTION = -3;
public static final int FILE_NAME_EXISTS = 1 << 6;
static final int NO_CATEGORY_SELECTED = -5;
private static ProgressDialog progressDialogWallpaper; const val IMAGE_OK = 0
const val IMAGE_KEEP = -1
const val IMAGE_WAIT = -2
const val EMPTY_CAPTION = -3
const val FILE_NAME_EXISTS = 1 shl 6 // 64
const val NO_CATEGORY_SELECTED = -5
private static ProgressDialog progressDialogAvatar; private var progressDialogWallpaper: ProgressDialog? = null
private var progressDialogAvatar: ProgressDialog? = null
@IntDef( @IntDef(
flag = true, flag = true,
value = { value = [
IMAGE_DARK, IMAGE_DARK,
IMAGE_BLURRY, IMAGE_BLURRY,
IMAGE_DUPLICATE, IMAGE_DUPLICATE,
IMAGE_OK, IMAGE_OK,
IMAGE_KEEP, IMAGE_KEEP,
IMAGE_WAIT, IMAGE_WAIT,
EMPTY_CAPTION, EMPTY_CAPTION,
FILE_NAME_EXISTS, FILE_NAME_EXISTS,
NO_CATEGORY_SELECTED, NO_CATEGORY_SELECTED,
IMAGE_GEOLOCATION_DIFFERENT IMAGE_GEOLOCATION_DIFFERENT
} ]
) )
@Retention(RetentionPolicy.SOURCE) @Retention
public @interface Result { annotation class Result
}
/** /**
* @return IMAGE_OK if image is not too dark * @return IMAGE_OK if image is not too dark
* IMAGE_DARK if image is too dark * IMAGE_DARK if image is too dark
*/ */
static @Result int checkIfImageIsTooDark(String imagePath) { @JvmStatic
long millis = System.currentTimeMillis(); fun checkIfImageIsTooDark(imagePath: String): Int {
try { val millis = System.currentTimeMillis()
Bitmap bmp = new ExifInterface(imagePath).getThumbnailBitmap(); return try {
var bmp = ExifInterface(imagePath).thumbnailBitmap
if (bmp == null) { if (bmp == null) {
bmp = BitmapFactory.decodeFile(imagePath); bmp = BitmapFactory.decodeFile(imagePath)
} }
if (checkIfImageIsDark(bmp)) { if (checkIfImageIsDark(bmp)) {
return IMAGE_DARK; IMAGE_DARK
} else {
IMAGE_OK
} }
} catch (e: Exception) {
} catch (Exception e) { Timber.d(e, "Error while checking image darkness.")
Timber.d(e, "Error while checking image darkness."); IMAGE_OK
} finally { } finally {
Timber.d("Checking image darkness took " + (System.currentTimeMillis() - millis) + " ms."); Timber.d("Checking image darkness took ${System.currentTimeMillis() - millis} ms.")
} }
return IMAGE_OK;
} }
/** /**
* @param geolocationOfFileString Geolocation of image. If geotag doesn't exists, then this will be an empty string * @param geolocationOfFileString Geolocation of image. If geotag doesn't exists, then this will
* be an empty string
* @param latLng Location of wikidata item will be edited after upload * @param latLng Location of wikidata item will be edited after upload
* @return false if image is neither dark nor blurry or if the input bitmapRegionDecoder provided is null * @return false if image is neither dark nor blurry or if the input bitmapRegionDecoder provide
* true if geolocation of the image and wikidata item are different * d is null true if geolocation of the image and wikidata item are different
*/ */
static boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) { @JvmStatic
Timber.d("Comparing geolocation of file with nearby place location"); fun checkImageGeolocationIsDifferent(geolocationOfFileString: String, latLng: LatLng?): Boolean {
Timber.d("Comparing geolocation of file with nearby place location")
if (latLng == null) { // Means that geolocation for this image is not given if (latLng == null) { // Means that geolocation for this image is not given
return false; // Since we don't know geolocation of file, we choose letting upload return false // Since we don't know geolocation of file, we choose letting upload
} }
String[] geolocationOfFile = geolocationOfFileString.split("\\|"); val geolocationOfFile = geolocationOfFileString.split("|")
Double distance = LengthUtils.computeDistanceBetween( val distance = LengthUtils.computeDistanceBetween(
new LatLng(Double.parseDouble(geolocationOfFile[0]),Double.parseDouble(geolocationOfFile[1]),0) LatLng(geolocationOfFile[0].toDouble(), geolocationOfFile[1].toDouble(), 0.0F),
, latLng); latLng
)
// Distance is more than 1 km, means that geolocation is wrong // Distance is more than 1 km, means that geolocation is wrong
return distance >= 1000; return distance >= 1000
} }
private static boolean checkIfImageIsDark(Bitmap bitmap) { @JvmStatic
private fun checkIfImageIsDark(bitmap: Bitmap?): Boolean {
if (bitmap == null) { if (bitmap == null) {
Timber.e("Expected bitmap was null"); Timber.e("Expected bitmap was null")
return true; return true
} }
int bitmapWidth = bitmap.getWidth(); val bitmapWidth = bitmap.width
int bitmapHeight = bitmap.getHeight(); val bitmapHeight = bitmap.height
int allPixelsCount = bitmapWidth * bitmapHeight; val allPixelsCount = bitmapWidth * bitmapHeight
int numberOfBrightPixels = 0; var numberOfBrightPixels = 0
int numberOfMediumBrightnessPixels = 0; var numberOfMediumBrightnessPixels = 0
double brightPixelThreshold = 0.025 * allPixelsCount; val brightPixelThreshold = 0.025 * allPixelsCount
double mediumBrightPixelThreshold = 0.3 * allPixelsCount; val mediumBrightPixelThreshold = 0.3 * allPixelsCount
for (int x = 0; x < bitmapWidth; x++) { for (x in 0 until bitmapWidth) {
for (int y = 0; y < bitmapHeight; y++) { for (y in 0 until bitmapHeight) {
int pixel = bitmap.getPixel(x, y); val pixel = bitmap.getPixel(x, y)
int r = Color.red(pixel); val r = Color.red(pixel)
int g = Color.green(pixel); val g = Color.green(pixel)
int b = Color.blue(pixel); val b = Color.blue(pixel)
int secondMax = r > g ? r : g; val max = maxOf(r, g, b) / 255.0
double max = (secondMax > b ? secondMax : b) / 255.0; val min = minOf(r, g, b) / 255.0
int secondMin = r < g ? r : g; val luminance = ((max + min) / 2.0) * 100
double min = (secondMin < b ? secondMin : b) / 255.0;
double luminance = ((max + min) / 2.0) * 100; val highBrightnessLuminance = 40
val mediumBrightnessLuminance = 26
int highBrightnessLuminance = 40;
int mediumBrightnessLuminance = 26;
if (luminance < highBrightnessLuminance) { if (luminance < highBrightnessLuminance) {
if (luminance > mediumBrightnessLuminance) { if (luminance > mediumBrightnessLuminance) {
numberOfMediumBrightnessPixels++; numberOfMediumBrightnessPixels++
} }
} else { } else {
numberOfBrightPixels++; numberOfBrightPixels++
} }
if (numberOfBrightPixels >= brightPixelThreshold || numberOfMediumBrightnessPixels >= mediumBrightPixelThreshold) { if (numberOfBrightPixels >= brightPixelThreshold || numberOfMediumBrightnessPixels >= mediumBrightPixelThreshold) {
return false; return false
} }
} }
} }
return true; return true
} }
/** /**
@ -201,25 +198,25 @@ public class ImageUtils {
* @param context context * @param context context
* @param imageUrl Url of the image * @param imageUrl Url of the image
*/ */
public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) { @JvmStatic
fun setWallpaperFromImageUrl(context: Context, imageUrl: Uri) {
enqueueSetWallpaperWork(context, imageUrl); enqueueSetWallpaperWork(context, imageUrl)
} }
private static void createNotificationChannel(Context context) { @JvmStatic
private fun createNotificationChannel(context: Context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = "Wallpaper Setting"; val name = "Wallpaper Setting"
String description = "Notifications for wallpaper setting progress"; val description = "Notifications for wallpaper setting progress"
int importance = NotificationManager.IMPORTANCE_DEFAULT; val importance = NotificationManager.IMPORTANCE_DEFAULT
NotificationChannel channel = new NotificationChannel("set_wallpaper_channel", name, importance); val channel = NotificationChannel("set_wallpaper_channel", name, importance).apply {
channel.setDescription(description); this.description = description
NotificationManager notificationManager = context.getSystemService(NotificationManager.class); }
notificationManager.createNotificationChannel(channel); val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
} }
} }
/** /**
* Calls the set avatar api to set the image url as user's avatar * Calls the set avatar api to set the image url as user's avatar
* @param context * @param context
@ -228,66 +225,96 @@ public class ImageUtils {
* @param okHttpJsonApiClient * @param okHttpJsonApiClient
* @param compositeDisposable * @param compositeDisposable
*/ */
public static void setAvatarFromImageUrl(Context context, String url, String username, @JvmStatic
OkHttpJsonApiClient okHttpJsonApiClient, CompositeDisposable compositeDisposable) { fun setAvatarFromImageUrl(
showSettingAvatarProgressBar(context); context: Context,
url: String,
username: String,
okHttpJsonApiClient: OkHttpJsonApiClient,
compositeDisposable: CompositeDisposable
) {
showSettingAvatarProgressBar(context)
try { try {
compositeDisposable.add(okHttpJsonApiClient compositeDisposable.add(
.setAvatar(username, url) okHttpJsonApiClient
.subscribeOn(Schedulers.io()) .setAvatar(username, url)
.observeOn(AndroidSchedulers.mainThread()) .subscribeOn(Schedulers.io())
.subscribe( .observeOn(AndroidSchedulers.mainThread())
response -> { .subscribe(
if (response != null && response.getStatus().equals("200")) { { response ->
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_successfully)); if (response?.status == "200") {
if (progressDialogAvatar != null && progressDialogAvatar.isShowing()) { ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_successfully))
progressDialogAvatar.dismiss(); progressDialogAvatar?.dismiss()
} }
},
{ t ->
Timber.e(t, "Setting Avatar Failed")
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully))
progressDialogAvatar?.cancel()
} }
}, )
t -> { )
Timber.e(t, "Setting Avatar Failed"); } catch (e: Exception) {
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully)); Timber.d("$e success")
if (progressDialogAvatar != null) { ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully))
progressDialogAvatar.cancel(); progressDialogAvatar?.cancel()
}
}
));
} }
catch (Exception e){
Timber.d(e+"success");
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully));
if (progressDialogAvatar != null) {
progressDialogAvatar.cancel();
}
}
} }
public static void enqueueSetWallpaperWork(Context context, Uri imageUrl) { @JvmStatic
createNotificationChannel(context); // Ensure the notification channel is created fun enqueueSetWallpaperWork(context: Context, imageUrl: Uri) {
createNotificationChannel(context) // Ensure the notification channel is created
Data inputData = new Data.Builder() val inputData = Data.Builder()
.putString("imageUrl", imageUrl.toString()) .putString("imageUrl", imageUrl.toString())
.build(); .build()
OneTimeWorkRequest setWallpaperWork = new OneTimeWorkRequest.Builder(SetWallpaperWorker.class) val setWallpaperWork = OneTimeWorkRequest.Builder(SetWallpaperWorker::class.java)
.setInputData(inputData) .setInputData(inputData)
.build(); .build()
WorkManager.getInstance(context).enqueue(setWallpaperWork); WorkManager.getInstance(context).enqueue(setWallpaperWork)
} }
@JvmStatic
private static void showSettingWallpaperProgressBar(Context context) { private fun showSettingWallpaperProgressBar(context: Context) {
progressDialogWallpaper = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title), progressDialogWallpaper = ProgressDialog.show(
context.getString(R.string.setting_wallpaper_dialog_message), true); context,
context.getString(R.string.setting_wallpaper_dialog_title),
context.getString(R.string.setting_wallpaper_dialog_message),
true
)
} }
private static void showSettingAvatarProgressBar(Context context) { @JvmStatic
progressDialogAvatar = ProgressDialog.show(context, context.getString(R.string.setting_avatar_dialog_title), private fun showSettingAvatarProgressBar(context: Context) {
context.getString(R.string.setting_avatar_dialog_message), true); progressDialogAvatar = ProgressDialog.show(
context,
context.getString(R.string.setting_avatar_dialog_title),
context.getString(R.string.setting_avatar_dialog_message),
true
)
}
/**
* Adds red border to bitmap with specified border size
* * @param bitmap
* * @param borderSize
* * @param context
* * @return
*/
@JvmStatic
fun addRedBorder(bitmap: Bitmap, borderSize: Int, context: Context): Bitmap {
val bmpWithBorder = Bitmap.createBitmap(
bitmap.width + borderSize * 2,
bitmap.height + borderSize * 2,
bitmap.config
)
val canvas = Canvas(bmpWithBorder)
canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed))
canvas.drawBitmap(bitmap, borderSize.toFloat(), borderSize.toFloat(), null)
return bmpWithBorder
} }
/** /**
@ -295,57 +322,42 @@ public class ImageUtils {
* is 0001 means IMAGE_DARK * is 0001 means IMAGE_DARK
* if result is 1100 IMAGE_DUPLICATE and IMAGE_GEOLOCATION_DIFFERENT * if result is 1100 IMAGE_DUPLICATE and IMAGE_GEOLOCATION_DIFFERENT
*/ */
public static String getErrorMessageForResult(Context context, @Result int result) { @JvmStatic
StringBuilder errorMessage = new StringBuilder(); fun getErrorMessageForResult(context: Context, @Result result: Int): String {
if (result <= 0 ) { val errorMessage = StringBuilder()
Timber.d("No issues to warn user is found"); if (result <= 0) {
Timber.d("No issues to warn user are found")
} else { } else {
Timber.d("Issues found to warn user"); Timber.d("Issues found to warn user")
errorMessage.append(context.getString(R.string.upload_problem_exist))
errorMessage.append(context.getResources().getString(R.string.upload_problem_exist)); if (result and IMAGE_DARK != 0) {
errorMessage.append("\n - ")
if ((IMAGE_DARK & result) != 0 ) { // We are checking image dark bit to see if that bit is set or not .append(context.getString(R.string.upload_problem_image_dark))
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_dark));
} }
if (result and IMAGE_BLURRY != 0) {
if ((IMAGE_BLURRY & result) != 0 ) { errorMessage.append("\n - ")
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_blurry)); .append(context.getString(R.string.upload_problem_image_blurry))
} }
if (result and IMAGE_DUPLICATE != 0) {
if ((IMAGE_DUPLICATE & result) != 0 ) { errorMessage.append("\n - ").
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_duplicate)); append(context.getString(R.string.upload_problem_image_duplicate))
} }
if (result and IMAGE_GEOLOCATION_DIFFERENT != 0) {
if ((IMAGE_GEOLOCATION_DIFFERENT & result) != 0 ) { errorMessage.append("\n - ")
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_different_geolocation)); .append(context.getString(R.string.upload_problem_different_geolocation))
} }
if (result and FILE_FBMD != 0) {
if ((FILE_FBMD & result) != 0) { errorMessage.append("\n - ")
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_fbmd)); .append(context.getString(R.string.upload_problem_fbmd))
} }
if (result and FILE_NO_EXIF != 0) {
if ((FILE_NO_EXIF & result) != 0){ errorMessage.append("\n - ")
errorMessage.append("\n - ").append(context.getResources().getString(R.string.internet_downloaded)); .append(context.getString(R.string.internet_downloaded))
} }
errorMessage.append("\n\n")
errorMessage.append("\n\n").append(context.getResources().getString(R.string.upload_problem_do_you_continue)); .append(context.getString(R.string.upload_problem_do_you_continue))
} }
return errorMessage.toString()
return errorMessage.toString();
} }
}
/**
* Adds red border to a bitmap
* @param bitmap
* @param borderSize
* @param context
* @return
*/
public static Bitmap addRedBorder(Bitmap bitmap, int borderSize, Context context) {
Bitmap bmpWithBorder = Bitmap.createBitmap(bitmap.getWidth() + borderSize * 2, bitmap.getHeight() + borderSize * 2, bitmap.getConfig());
Canvas canvas = new Canvas(bmpWithBorder);
canvas.drawColor(ContextCompat.getColor(context, R.color.deleteRed));
canvas.drawBitmap(bitmap, borderSize, borderSize, null);
return bmpWithBorder;
}
}

View file

@ -1,30 +1,29 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng
import io.reactivex.Single; import io.reactivex.Single
import io.reactivex.schedulers.Schedulers; import io.reactivex.schedulers.Schedulers
import javax.inject.Inject; import javax.inject.Inject
import javax.inject.Singleton; import javax.inject.Singleton
@Singleton @Singleton
public class ImageUtilsWrapper { class ImageUtilsWrapper @Inject constructor() {
@Inject fun checkIfImageIsTooDark(bitmapPath: String): Single<Int> {
public ImageUtilsWrapper() { return Single.fromCallable { ImageUtils.checkIfImageIsTooDark(bitmapPath) }
}
public Single<Integer> checkIfImageIsTooDark(String bitmapPath) {
return Single.fromCallable(() -> ImageUtils.checkIfImageIsTooDark(bitmapPath))
.subscribeOn(Schedulers.computation());
}
public Single<Integer> checkImageGeolocationIsDifferent(String geolocationOfFileString,
LatLng latLng) {
return Single.fromCallable(
() -> ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng))
.subscribeOn(Schedulers.computation()) .subscribeOn(Schedulers.computation())
.map(isDifferent -> isDifferent ? ImageUtils.IMAGE_GEOLOCATION_DIFFERENT
: ImageUtils.IMAGE_OK);
} }
}
fun checkImageGeolocationIsDifferent(
geolocationOfFileString: String,
latLng: LatLng
): Single<Int> {
return Single.fromCallable {
ImageUtils.checkImageGeolocationIsDifferent(geolocationOfFileString, latLng)
}
.subscribeOn(Schedulers.computation())
.map { isDifferent ->
if (isDifferent) ImageUtils.IMAGE_GEOLOCATION_DIFFERENT else ImageUtils.IMAGE_OK
}
}
}

View file

@ -1,39 +1,40 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import android.content.Context;
import android.content.res.Configuration; import android.content.Context
import android.content.res.Resources; import android.content.res.Configuration
import java.util.Locale; import android.content.res.Resources
import java.util.Locale
/** /**
* Utilities class for miscellaneous strings * Utilities class for miscellaneous strings
*/ */
public class LangCodeUtils { object LangCodeUtils {
/** /**
* Replaces the deprecated ISO-639 language codes used by Android with the updated ISO-639-1. * Replaces the deprecated ISO-639 language codes used by Android with the updated ISO-639-1.
* @param code Language code you want to update. * @param code Language code you want to update.
* @return Updated language code. If not in the "deprecated list" returns the same code. * @return Updated language code. If not in the "deprecated list" returns the same code.
*/ */
public static String fixLanguageCode(String code) { @JvmStatic
if (code.equalsIgnoreCase("iw")) { fun fixLanguageCode(code: String): String {
return "he"; return when (code.lowercase()) {
} else if (code.equalsIgnoreCase("in")) { "iw" -> "he"
return "id"; "in" -> "id"
} else if (code.equalsIgnoreCase("ji")) { "ji" -> "yi"
return "yi"; else -> code
} else {
return code;
} }
} }
/** /**
* Returns configuration for locale of * Returns configuration for locale of
* our choice regardless of user's device settings * our choice regardless of user's device settings
*/ */
public static Resources getLocalizedResources(Context context, Locale desiredLocale) { @JvmStatic
Configuration conf = context.getResources().getConfiguration(); fun getLocalizedResources(context: Context, desiredLocale: Locale): Resources {
conf = new Configuration(conf); val conf = Configuration(context.resources.configuration).apply {
conf.setLocale(desiredLocale); setLocale(desiredLocale)
Context localizedContext = context.createConfigurationContext(conf); }
return localizedContext.getResources(); val localizedContext = context.createConfigurationContext(conf)
return localizedContext.resources
} }
} }

View file

@ -1,38 +1,47 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import android.app.Activity; import android.app.Activity
import android.content.Context; import android.content.Context
import android.util.DisplayMetrics; import android.util.DisplayMetrics
import android.view.View; import android.view.View
import android.view.ViewGroup; import android.view.ViewTreeObserver
import android.view.ViewTreeObserver;
public class LayoutUtils { /**
* Utility class for layout-related operations.
*/
object LayoutUtils {
/** /**
* Can be used for keeping aspect radios suggested by material guidelines. See: * Can be used for keeping aspect ratios suggested by material guidelines. See:
* https://material.io/design/layout/spacing-methods.html#containers-aspect-ratios * https://material.io/design/layout/spacing-methods.html#containers-aspect-ratios
* In some cases we don't know exact width, for such cases this method measures * In some cases, we don't know the exact width, for such cases this method measures
* width and sets height by multiplying the width with height. * width and sets height by multiplying the width with height.
* @param rate Aspect ratios, ie 1 for 1:1. (width * rate = height) * @param rate Aspect ratios, i.e., 1 for 1:1 (width * rate = height)
* @param view view to change height * @param view View to change height
*/ */
public static void setLayoutHeightAllignedToWidth(double rate, View view) { @JvmStatic
ViewTreeObserver vto = view.getViewTreeObserver(); fun setLayoutHeightAlignedToWidth(rate: Double, view: View) {
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { val vto = view.viewTreeObserver
@Override vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
public void onGlobalLayout() { override fun onGlobalLayout() {
view.getViewTreeObserver().removeOnGlobalLayoutListener(this); view.viewTreeObserver.removeOnGlobalLayoutListener(this)
ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); val layoutParams = view.layoutParams
layoutParams.height = (int) (view.getWidth() * rate); layoutParams.height = (view.width * rate).toInt()
view.setLayoutParams(layoutParams); view.layoutParams = layoutParams
} }
}); })
} }
public static double getScreenWidth(Context context, double rate) { /**
DisplayMetrics displayMetrics = new DisplayMetrics(); * Calculates and returns the screen width multiplied by the provided rate.
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics); * @param context Context used to access display metrics.
return displayMetrics.widthPixels * rate; * @param rate Multiplier for screen width.
* @return Calculated screen width multiplied by the rate.
*/
@JvmStatic
fun getScreenWidth(context: Context, rate: Double): Double {
val displayMetrics = DisplayMetrics()
(context as Activity).windowManager.defaultDisplay.getMetrics(displayMetrics)
return displayMetrics.widthPixels * rate
} }
} }

View file

@ -1,12 +1,15 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import androidx.annotation.NonNull; import java.text.NumberFormat
import fr.free.nrw.commons.location.LatLng
import kotlin.math.asin
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin
import kotlin.math.sqrt
import java.text.NumberFormat; object LengthUtils {
import fr.free.nrw.commons.location.LatLng;
public class LengthUtils {
/** /**
* Returns a formatted distance string between two points. * Returns a formatted distance string between two points.
* *
@ -14,13 +17,14 @@ public class LengthUtils {
* @param point2 LatLng type point2 * @param point2 LatLng type point2
* @return string distance * @return string distance
*/ */
public static String formatDistanceBetween(LatLng point1, LatLng point2) { @JvmStatic
fun formatDistanceBetween(point1: LatLng?, point2: LatLng?): String? {
if (point1 == null || point2 == null) { if (point1 == null || point2 == null) {
return null; return null
} }
int distance = (int) Math.round(computeDistanceBetween(point1, point2)); val distance = computeDistanceBetween(point1, point2).roundToInt()
return formatDistance(distance); return formatDistance(distance)
} }
/** /**
@ -32,21 +36,21 @@ public class LengthUtils {
* @return A string representing the distance * @return A string representing the distance
* @throws IllegalArgumentException If distance is negative * @throws IllegalArgumentException If distance is negative
*/ */
public static String formatDistance(int distance) { @JvmStatic
fun formatDistance(distance: Int): String {
if (distance < 0) { if (distance < 0) {
throw new IllegalArgumentException("Distance must be non-negative"); throw IllegalArgumentException("Distance must be non-negative")
} }
NumberFormat numberFormat = NumberFormat.getNumberInstance(); val numberFormat = NumberFormat.getNumberInstance()
// Adjust to km if distance is over 1000m (1km) // Adjust to km if distance is over 1000m (1km)
if (distance >= 1000) { return if (distance >= 1000) {
numberFormat.setMaximumFractionDigits(1); numberFormat.maximumFractionDigits = 1
return numberFormat.format(distance / 1000.0) + "km"; "${numberFormat.format(distance / 1000.0)}km"
} else {
"${numberFormat.format(distance)}m"
} }
// Otherwise just return in meters
return numberFormat.format(distance) + "m";
} }
/** /**
@ -57,8 +61,9 @@ public class LengthUtils {
* @return distance between the points in meters * @return distance between the points in meters
* @throws NullPointerException if one or both the points are null * @throws NullPointerException if one or both the points are null
*/ */
public static double computeDistanceBetween(@NonNull LatLng point1, @NonNull LatLng point2) { @JvmStatic
return computeAngleBetween(point1, point2) * 6371009.0D; // Earth's radius in meter fun computeDistanceBetween(point1: LatLng, point2: LatLng): Double {
return computeAngleBetween(point1, point2) * 6371009.0 // Earth's radius in meters
} }
/** /**
@ -66,16 +71,17 @@ public class LengthUtils {
* *
* @param point1 one of the two end points * @param point1 one of the two end points
* @param point2 one of the two end points * @param point2 one of the two end points
* @return Angle in radius * @return Angle in radians
* @throws NullPointerException if one or both the points are null * @throws NullPointerException if one or both the points are null
*/ */
private static double computeAngleBetween(@NonNull LatLng point1, @NonNull LatLng point2) { @JvmStatic
private fun computeAngleBetween(point1: LatLng, point2: LatLng): Double {
return distanceRadians( return distanceRadians(
Math.toRadians(point1.getLatitude()), Math.toRadians(point1.latitude),
Math.toRadians(point1.getLongitude()), Math.toRadians(point1.longitude),
Math.toRadians(point2.getLatitude()), Math.toRadians(point2.latitude),
Math.toRadians(point2.getLongitude()) Math.toRadians(point2.longitude)
); )
} }
/** /**
@ -87,8 +93,9 @@ public class LengthUtils {
* @param lng2 Longitude of point B * @param lng2 Longitude of point B
* @return Arc length between the points * @return Arc length between the points
*/ */
private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) { @JvmStatic
return arcHav(havDistance(lat1, lat2, lng1 - lng2)); private fun distanceRadians(lat1: Double, lng1: Double, lat2: Double, lng2: Double): Double {
return arcHav(havDistance(lat1, lat2, lng1 - lng2))
} }
/** /**
@ -97,8 +104,9 @@ public class LengthUtils {
* @param x Angle in radian * @param x Angle in radian
* @return Inverse of haversine * @return Inverse of haversine
*/ */
private static double arcHav(double x) { @JvmStatic
return 2.0D * Math.asin(Math.sqrt(x)); private fun arcHav(x: Double): Double {
return 2.0 * asin(sqrt(x))
} }
/** /**
@ -109,8 +117,9 @@ public class LengthUtils {
* @param longitude Longitude on which they lie * @param longitude Longitude on which they lie
* @return Arc length between points * @return Arc length between points
*/ */
private static double havDistance(double lat1, double lat2, double longitude) { @JvmStatic
return hav(lat1 - lat2) + hav(longitude) * Math.cos(lat1) * Math.cos(lat2); private fun havDistance(lat1: Double, lat2: Double, longitude: Double): Double {
return hav(lat1 - lat2) + hav(longitude) * cos(lat1) * cos(lat2)
} }
/** /**
@ -119,9 +128,10 @@ public class LengthUtils {
* @param x Angle in radians * @param x Angle in radians
* @return Haversine of x * @return Haversine of x
*/ */
private static double hav(double x) { @JvmStatic
double sinHalf = Math.sin(x * 0.5D); private fun hav(x: Double): Double {
return sinHalf * sinHalf; val sinHalf = sin(x * 0.5)
return sinHalf * sinHalf
} }
/** /**
@ -133,13 +143,14 @@ public class LengthUtils {
* @return Bearing between the two end points in degrees * @return Bearing between the two end points in degrees
* @throws NullPointerException if one or both the points are null * @throws NullPointerException if one or both the points are null
*/ */
public static double computeBearing(@NonNull LatLng point1, @NonNull LatLng point2) { @JvmStatic
double diffLongitute = Math.toRadians(point2.getLongitude() - point1.getLongitude()); fun computeBearing(point1: LatLng, point2: LatLng): Double {
double lat1 = Math.toRadians(point1.getLatitude()); val diffLongitude = Math.toRadians(point2.longitude - point1.longitude)
double lat2 = Math.toRadians(point2.getLatitude()); val lat1 = Math.toRadians(point1.latitude)
double y = Math.sin(diffLongitute) * Math.cos(lat2); val lat2 = Math.toRadians(point2.latitude)
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLongitute); val y = sin(diffLongitude) * cos(lat2)
double bearing = Math.atan2(y, x); val x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(diffLongitude)
return (Math.toDegrees(bearing) + 360) % 360; val bearing = atan2(y, x)
return (Math.toDegrees(bearing) + 360) % 360
} }
} }

View file

@ -1,58 +1,63 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng
import timber.log.Timber; import timber.log.Timber
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.sin
import kotlin.math.sqrt
public class LocationUtils { object LocationUtils {
public static final double RADIUS_OF_EARTH_KM = 6371.0; // Earth's radius in kilometers const val RADIUS_OF_EARTH_KM = 6371.0 // Earth's radius in kilometers
public static LatLng deriveUpdatedLocationFromSearchQuery(String customQuery) { @JvmStatic
LatLng latLng = null; fun deriveUpdatedLocationFromSearchQuery(customQuery: String): LatLng? {
final int indexOfPrefix = customQuery.indexOf("Point("); var latLng: LatLng? = null
val indexOfPrefix = customQuery.indexOf("Point(")
if (indexOfPrefix == -1) { if (indexOfPrefix == -1) {
Timber.e("Invalid prefix index - Seems like user has entered an invalid query"); Timber.e("Invalid prefix index - Seems like user has entered an invalid query")
return latLng; return latLng
} }
final int indexOfSuffix = customQuery.indexOf(")\"", indexOfPrefix); val indexOfSuffix = customQuery.indexOf(")\"", indexOfPrefix)
if (indexOfSuffix == -1) { if (indexOfSuffix == -1) {
Timber.e("Invalid suffix index - Seems like user has entered an invalid query"); Timber.e("Invalid suffix index - Seems like user has entered an invalid query")
return latLng; return latLng
} }
String latLngString = customQuery.substring(indexOfPrefix+"Point(".length(), indexOfSuffix); val latLngString = customQuery.substring(indexOfPrefix + "Point(".length, indexOfSuffix)
if (latLngString.isEmpty()) { if (latLngString.isEmpty()) {
return null; return null
} }
String latLngArray[] = latLngString.split(" "); val latLngArray = latLngString.split(" ")
if (latLngArray.length != 2) { if (latLngArray.size != 2) {
return null; return null
} }
try { try {
latLng = new LatLng(Double.parseDouble(latLngArray[1].trim()), latLng = LatLng(latLngArray[1].trim().toDouble(),
Double.parseDouble(latLngArray[0].trim()), 1f); latLngArray[0].trim().toDouble(), 1f)
}catch (Exception e){ } catch (e: Exception) {
Timber.e("Error while parsing user entered lat long: %s", e); Timber.e("Error while parsing user entered lat long: %s", e)
} }
return latLng; return latLng
} }
@JvmStatic
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) { fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
double lat1Rad = Math.toRadians(lat1); val lat1Rad = Math.toRadians(lat1)
double lon1Rad = Math.toRadians(lon1); val lon1Rad = Math.toRadians(lon1)
double lat2Rad = Math.toRadians(lat2); val lat2Rad = Math.toRadians(lat2)
double lon2Rad = Math.toRadians(lon2); val lon2Rad = Math.toRadians(lon2)
// Haversine formula // Haversine formula
double dlon = lon2Rad - lon1Rad; val dlon = lon2Rad - lon1Rad
double dlat = lat2Rad - lat1Rad; val dlat = lat2Rad - lat1Rad
double a = Math.pow(Math.sin(dlat / 2), 2) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.pow(Math.sin(dlon / 2), 2); val a = Math.pow(
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); sin(dlat / 2), 2.0) + cos(lat1Rad) * cos(lat2Rad) * Math.pow(sin(dlon / 2), 2.0
)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
double distance = RADIUS_OF_EARTH_KM * c; return RADIUS_OF_EARTH_KM * c
return distance;
} }
} }

View file

@ -1,33 +1,39 @@
package fr.free.nrw.commons.utils; package fr.free.nrw.commons.utils
import fr.free.nrw.commons.location.LatLng; import fr.free.nrw.commons.location.LatLng
import fr.free.nrw.commons.location.LocationServiceManager; import fr.free.nrw.commons.location.LocationServiceManager
import fr.free.nrw.commons.location.LocationUpdateListener; import fr.free.nrw.commons.location.LocationUpdateListener
import timber.log.Timber; import timber.log.Timber
public class MapUtils { object MapUtils {
public static final float ZOOM_LEVEL = 14f; const val ZOOM_LEVEL = 14f
public static final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005; const val CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005
public static final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004; const val CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004
public static final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; const val NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"
public static final float ZOOM_OUT = 0f; const val ZOOM_OUT = 0f
public static final LatLng defaultLatLng = new fr.free.nrw.commons.location.LatLng(51.50550,-0.07520,1f); @JvmStatic
val defaultLatLng = LatLng(51.50550, -0.07520, 1f)
public static void registerUnregisterLocationListener(final boolean removeLocationListener, LocationServiceManager locationManager, LocationUpdateListener locationUpdateListener) { @JvmStatic
fun registerUnregisterLocationListener(
removeLocationListener: Boolean,
locationManager: LocationServiceManager,
locationUpdateListener: LocationUpdateListener
) {
try { try {
if (removeLocationListener) { if (removeLocationListener) {
locationManager.unregisterLocationManager(); locationManager.unregisterLocationManager()
locationManager.removeLocationListener(locationUpdateListener); locationManager.removeLocationListener(locationUpdateListener)
Timber.d("Location service manager unregistered and removed"); Timber.d("Location service manager unregistered and removed")
} else { } else {
locationManager.addLocationListener(locationUpdateListener); locationManager.addLocationListener(locationUpdateListener)
locationManager.registerLocationManager(); locationManager.registerLocationManager()
Timber.d("Location service manager added and registered"); Timber.d("Location service manager added and registered")
} }
}catch (final Exception e){ } catch (e: Exception) {
Timber.e(e); Timber.e(e)
//Broadcasts are tricky, should be catchedonR // Broadcasts are tricky, should be caught on onR
} }
} }
} }