mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-11-02 15:53:55 +01:00
Migrated the following files in util module to Kotlin
- ImageUtils - ImageUtilsWrapper - LangCodeUtils - LayoutUtils - LengthUtils - LocationUtils - MapUtils
This commit is contained in:
parent
3126ad947d
commit
4b95f47b29
12 changed files with 453 additions and 421 deletions
|
|
@ -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.nearby.fragments.NearbyParentFragment.WLM_URL;
|
||||
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.formatDistanceBetween;
|
||||
|
||||
|
|
@ -23,12 +22,10 @@ import android.view.LayoutInflater;
|
|||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.MenuItem.OnMenuItemClickListener;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
|
|
@ -39,7 +36,6 @@ import androidx.annotation.Nullable;
|
|||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager.OnBackStackChangedListener;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import fr.free.nrw.commons.CommonsApplication;
|
||||
import fr.free.nrw.commons.Utils;
|
||||
import fr.free.nrw.commons.auth.SessionManager;
|
||||
import fr.free.nrw.commons.databinding.FragmentContributionsBinding;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
package fr.free.nrw.commons.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.content.Context;
|
||||
import static fr.free.nrw.commons.utils.LangCodeUtils.getLocalizedResources;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
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.notification.NotificationHelper;
|
||||
import fr.free.nrw.commons.review.ReviewController;
|
||||
import fr.free.nrw.commons.utils.LangCodeUtils;
|
||||
import fr.free.nrw.commons.utils.ViewUtilWrapper;
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
|
|
|||
|
|
@ -310,7 +310,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
|
|||
}
|
||||
|
||||
private void startMapWithoutPermission() {
|
||||
lastKnownLocation = MapUtils.defaultLatLng;
|
||||
lastKnownLocation = MapUtils.getDefaultLatLng();
|
||||
moveCameraToPosition(
|
||||
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
|
||||
presenter.onMapReady(exploreMapController);
|
||||
|
|
@ -331,7 +331,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
|
|||
!locationPermissionsHelper.checkLocationPermission(getActivity())) {
|
||||
isPermissionDenied = true;
|
||||
}
|
||||
lastKnownLocation = MapUtils.defaultLatLng;
|
||||
lastKnownLocation = MapUtils.getDefaultLatLng();
|
||||
moveCameraToPosition(
|
||||
new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude()));
|
||||
presenter.onMapReady(exploreMapController);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,6 @@ import android.view.ViewGroup;
|
|||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.view.animation.Animation;
|
||||
import android.view.animation.AnimationUtils;
|
||||
import android.widget.Button;
|
||||
import android.widget.Toast;
|
||||
import androidx.activity.result.ActivityResultCallback;
|
||||
import androidx.activity.result.ActivityResultLauncher;
|
||||
|
|
@ -701,7 +700,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
= new LatLng(Double.parseDouble(locationLatLng[0]),
|
||||
Double.parseDouble(locationLatLng[1]), 1f);
|
||||
} else {
|
||||
lastKnownLocation = MapUtils.defaultLatLng;
|
||||
lastKnownLocation = MapUtils.getDefaultLatLng();
|
||||
}
|
||||
if (binding.map != null) {
|
||||
moveCameraToPosition(
|
||||
|
|
@ -793,7 +792,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
hideBottomSheet();
|
||||
binding.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener(
|
||||
(v, hasFocus) -> {
|
||||
LayoutUtils.setLayoutHeightAllignedToWidth(1.25,
|
||||
LayoutUtils.setLayoutHeightAlignedToWidth(1.25,
|
||||
binding.nearbyFilterList.getRoot());
|
||||
if (hasFocus) {
|
||||
binding.nearbyFilterList.getRoot().setVisibility(View.VISIBLE);
|
||||
|
|
@ -834,7 +833,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment
|
|||
.getLayoutParams().width = (int) LayoutUtils.getScreenWidth(getActivity(),
|
||||
0.75);
|
||||
binding.nearbyFilterList.searchListView.setAdapter(nearbyFilterSearchRecyclerViewAdapter);
|
||||
LayoutUtils.setLayoutHeightAllignedToWidth(1.25, binding.nearbyFilterList.getRoot());
|
||||
LayoutUtils.setLayoutHeightAlignedToWidth(1.25, binding.nearbyFilterList.getRoot());
|
||||
compositeDisposable.add(
|
||||
RxSearchView.queryTextChanges(binding.nearbyFilter.searchViewLayout.searchView)
|
||||
.takeUntil(RxView.detaches(binding.nearbyFilter.searchViewLayout.searchView))
|
||||
|
|
|
|||
|
|
@ -11,13 +11,10 @@ import static fr.free.nrw.commons.nearby.CheckBoxTriStates.UNKNOWN;
|
|||
import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT;
|
||||
|
||||
import android.location.Location;
|
||||
import android.view.View;
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.work.ExistingWorkPolicy;
|
||||
import fr.free.nrw.commons.BaseMarker;
|
||||
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.location.LatLng;
|
||||
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.MarkerPlaceGroup;
|
||||
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.PlaceDao;
|
||||
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.wikidata.WikidataEditListener;
|
||||
import io.reactivex.disposables.CompositeDisposable;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.List;
|
||||
import timber.log.Timber;
|
||||
|
|
|
|||
|
|
@ -1,90 +1,85 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.app.NotificationChannel;
|
||||
import android.app.NotificationManager;
|
||||
import android.app.ProgressDialog;
|
||||
import android.app.WallpaperManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import androidx.annotation.IntDef;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.exifinterface.media.ExifInterface;
|
||||
import androidx.work.Data;
|
||||
import androidx.work.OneTimeWorkRequest;
|
||||
import androidx.work.WorkManager;
|
||||
import com.facebook.common.executors.CallerThreadExecutor;
|
||||
import com.facebook.common.references.CloseableReference;
|
||||
import com.facebook.datasource.DataSource;
|
||||
import com.facebook.drawee.backends.pipeline.Fresco;
|
||||
import com.facebook.imagepipeline.core.ImagePipeline;
|
||||
import com.facebook.imagepipeline.datasource.BaseBitmapDataSubscriber;
|
||||
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;
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.ProgressDialog
|
||||
import android.content.Context
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
import android.graphics.Canvas
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import androidx.annotation.IntDef
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.exifinterface.media.ExifInterface
|
||||
import androidx.work.Data
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
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 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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
*/
|
||||
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
|
||||
* 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;
|
||||
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;
|
||||
const val FILE_NO_EXIF = 1 shl 5 // 32
|
||||
|
||||
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(
|
||||
flag = true,
|
||||
value = {
|
||||
value = [
|
||||
IMAGE_DARK,
|
||||
IMAGE_BLURRY,
|
||||
IMAGE_DUPLICATE,
|
||||
|
|
@ -95,103 +90,105 @@ public class ImageUtils {
|
|||
FILE_NAME_EXISTS,
|
||||
NO_CATEGORY_SELECTED,
|
||||
IMAGE_GEOLOCATION_DIFFERENT
|
||||
}
|
||||
]
|
||||
)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface Result {
|
||||
}
|
||||
@Retention
|
||||
annotation class Result
|
||||
|
||||
/**
|
||||
* @return IMAGE_OK if image is not too dark
|
||||
* IMAGE_DARK if image is too dark
|
||||
*/
|
||||
static @Result int checkIfImageIsTooDark(String imagePath) {
|
||||
long millis = System.currentTimeMillis();
|
||||
try {
|
||||
Bitmap bmp = new ExifInterface(imagePath).getThumbnailBitmap();
|
||||
@JvmStatic
|
||||
fun checkIfImageIsTooDark(imagePath: String): Int {
|
||||
val millis = System.currentTimeMillis()
|
||||
return try {
|
||||
var bmp = ExifInterface(imagePath).thumbnailBitmap
|
||||
if (bmp == null) {
|
||||
bmp = BitmapFactory.decodeFile(imagePath);
|
||||
bmp = BitmapFactory.decodeFile(imagePath)
|
||||
}
|
||||
|
||||
if (checkIfImageIsDark(bmp)) {
|
||||
return IMAGE_DARK;
|
||||
IMAGE_DARK
|
||||
} else {
|
||||
IMAGE_OK
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Timber.d(e, "Error while checking image darkness.");
|
||||
} catch (e: Exception) {
|
||||
Timber.d(e, "Error while checking image darkness.")
|
||||
IMAGE_OK
|
||||
} 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
|
||||
* @return false if image is neither dark nor blurry or if the input bitmapRegionDecoder provided is null
|
||||
* true if geolocation of the image and wikidata item are different
|
||||
* @return false if image is neither dark nor blurry or if the input bitmapRegionDecoder provide
|
||||
* d is null true if geolocation of the image and wikidata item are different
|
||||
*/
|
||||
static boolean checkImageGeolocationIsDifferent(String geolocationOfFileString, LatLng latLng) {
|
||||
Timber.d("Comparing geolocation of file with nearby place location");
|
||||
@JvmStatic
|
||||
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
|
||||
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("\\|");
|
||||
Double distance = LengthUtils.computeDistanceBetween(
|
||||
new LatLng(Double.parseDouble(geolocationOfFile[0]),Double.parseDouble(geolocationOfFile[1]),0)
|
||||
, latLng);
|
||||
val geolocationOfFile = geolocationOfFileString.split("|")
|
||||
val distance = LengthUtils.computeDistanceBetween(
|
||||
LatLng(geolocationOfFile[0].toDouble(), geolocationOfFile[1].toDouble(), 0.0F),
|
||||
latLng
|
||||
)
|
||||
// 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) {
|
||||
Timber.e("Expected bitmap was null");
|
||||
return true;
|
||||
Timber.e("Expected bitmap was null")
|
||||
return true
|
||||
}
|
||||
|
||||
int bitmapWidth = bitmap.getWidth();
|
||||
int bitmapHeight = bitmap.getHeight();
|
||||
val bitmapWidth = bitmap.width
|
||||
val bitmapHeight = bitmap.height
|
||||
|
||||
int allPixelsCount = bitmapWidth * bitmapHeight;
|
||||
int numberOfBrightPixels = 0;
|
||||
int numberOfMediumBrightnessPixels = 0;
|
||||
double brightPixelThreshold = 0.025 * allPixelsCount;
|
||||
double mediumBrightPixelThreshold = 0.3 * allPixelsCount;
|
||||
val allPixelsCount = bitmapWidth * bitmapHeight
|
||||
var numberOfBrightPixels = 0
|
||||
var numberOfMediumBrightnessPixels = 0
|
||||
val brightPixelThreshold = 0.025 * allPixelsCount
|
||||
val mediumBrightPixelThreshold = 0.3 * allPixelsCount
|
||||
|
||||
for (int x = 0; x < bitmapWidth; x++) {
|
||||
for (int y = 0; y < bitmapHeight; y++) {
|
||||
int pixel = bitmap.getPixel(x, y);
|
||||
int r = Color.red(pixel);
|
||||
int g = Color.green(pixel);
|
||||
int b = Color.blue(pixel);
|
||||
for (x in 0 until bitmapWidth) {
|
||||
for (y in 0 until bitmapHeight) {
|
||||
val pixel = bitmap.getPixel(x, y)
|
||||
val r = Color.red(pixel)
|
||||
val g = Color.green(pixel)
|
||||
val b = Color.blue(pixel)
|
||||
|
||||
int secondMax = r > g ? r : g;
|
||||
double max = (secondMax > b ? secondMax : b) / 255.0;
|
||||
val max = maxOf(r, g, b) / 255.0
|
||||
val min = minOf(r, g, b) / 255.0
|
||||
|
||||
int secondMin = r < g ? r : g;
|
||||
double min = (secondMin < b ? secondMin : b) / 255.0;
|
||||
val luminance = ((max + min) / 2.0) * 100
|
||||
|
||||
double luminance = ((max + min) / 2.0) * 100;
|
||||
|
||||
int highBrightnessLuminance = 40;
|
||||
int mediumBrightnessLuminance = 26;
|
||||
val highBrightnessLuminance = 40
|
||||
val mediumBrightnessLuminance = 26
|
||||
|
||||
if (luminance < highBrightnessLuminance) {
|
||||
if (luminance > mediumBrightnessLuminance) {
|
||||
numberOfMediumBrightnessPixels++;
|
||||
numberOfMediumBrightnessPixels++
|
||||
}
|
||||
} else {
|
||||
numberOfBrightPixels++;
|
||||
numberOfBrightPixels++
|
||||
}
|
||||
|
||||
if (numberOfBrightPixels >= brightPixelThreshold || numberOfMediumBrightnessPixels >= mediumBrightPixelThreshold) {
|
||||
return false;
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,25 +198,25 @@ public class ImageUtils {
|
|||
* @param context context
|
||||
* @param imageUrl Url of the image
|
||||
*/
|
||||
public static void setWallpaperFromImageUrl(Context context, Uri imageUrl) {
|
||||
|
||||
enqueueSetWallpaperWork(context, imageUrl);
|
||||
|
||||
@JvmStatic
|
||||
fun setWallpaperFromImageUrl(context: Context, imageUrl: Uri) {
|
||||
enqueueSetWallpaperWork(context, imageUrl)
|
||||
}
|
||||
|
||||
private static void createNotificationChannel(Context context) {
|
||||
@JvmStatic
|
||||
private fun createNotificationChannel(context: Context) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
CharSequence name = "Wallpaper Setting";
|
||||
String description = "Notifications for wallpaper setting progress";
|
||||
int importance = NotificationManager.IMPORTANCE_DEFAULT;
|
||||
NotificationChannel channel = new NotificationChannel("set_wallpaper_channel", name, importance);
|
||||
channel.setDescription(description);
|
||||
NotificationManager notificationManager = context.getSystemService(NotificationManager.class);
|
||||
notificationManager.createNotificationChannel(channel);
|
||||
val name = "Wallpaper Setting"
|
||||
val description = "Notifications for wallpaper setting progress"
|
||||
val importance = NotificationManager.IMPORTANCE_DEFAULT
|
||||
val channel = NotificationChannel("set_wallpaper_channel", name, importance).apply {
|
||||
this.description = description
|
||||
}
|
||||
val notificationManager = context.getSystemService(NotificationManager::class.java)
|
||||
notificationManager.createNotificationChannel(channel)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calls the set avatar api to set the image url as user's avatar
|
||||
* @param context
|
||||
|
|
@ -228,66 +225,96 @@ public class ImageUtils {
|
|||
* @param okHttpJsonApiClient
|
||||
* @param compositeDisposable
|
||||
*/
|
||||
public static void setAvatarFromImageUrl(Context context, String url, String username,
|
||||
OkHttpJsonApiClient okHttpJsonApiClient, CompositeDisposable compositeDisposable) {
|
||||
showSettingAvatarProgressBar(context);
|
||||
@JvmStatic
|
||||
fun setAvatarFromImageUrl(
|
||||
context: Context,
|
||||
url: String,
|
||||
username: String,
|
||||
okHttpJsonApiClient: OkHttpJsonApiClient,
|
||||
compositeDisposable: CompositeDisposable
|
||||
) {
|
||||
showSettingAvatarProgressBar(context)
|
||||
|
||||
try {
|
||||
compositeDisposable.add(okHttpJsonApiClient
|
||||
compositeDisposable.add(
|
||||
okHttpJsonApiClient
|
||||
.setAvatar(username, url)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
response -> {
|
||||
if (response != null && response.getStatus().equals("200")) {
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_successfully));
|
||||
if (progressDialogAvatar != null && progressDialogAvatar.isShowing()) {
|
||||
progressDialogAvatar.dismiss();
|
||||
}
|
||||
{ response ->
|
||||
if (response?.status == "200") {
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_successfully))
|
||||
progressDialogAvatar?.dismiss()
|
||||
}
|
||||
},
|
||||
t -> {
|
||||
Timber.e(t, "Setting Avatar Failed");
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully));
|
||||
if (progressDialogAvatar != null) {
|
||||
progressDialogAvatar.cancel();
|
||||
{ t ->
|
||||
Timber.e(t, "Setting Avatar Failed")
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully))
|
||||
progressDialogAvatar?.cancel()
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
catch (Exception e){
|
||||
Timber.d(e+"success");
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully));
|
||||
if (progressDialogAvatar != null) {
|
||||
progressDialogAvatar.cancel();
|
||||
)
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
Timber.d("$e success")
|
||||
ViewUtil.showLongToast(context, context.getString(R.string.avatar_set_unsuccessfully))
|
||||
progressDialogAvatar?.cancel()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@JvmStatic
|
||||
fun enqueueSetWallpaperWork(context: Context, imageUrl: Uri) {
|
||||
createNotificationChannel(context) // Ensure the notification channel is created
|
||||
|
||||
public static void enqueueSetWallpaperWork(Context context, Uri imageUrl) {
|
||||
createNotificationChannel(context); // Ensure the notification channel is created
|
||||
|
||||
Data inputData = new Data.Builder()
|
||||
val inputData = Data.Builder()
|
||||
.putString("imageUrl", imageUrl.toString())
|
||||
.build();
|
||||
.build()
|
||||
|
||||
OneTimeWorkRequest setWallpaperWork = new OneTimeWorkRequest.Builder(SetWallpaperWorker.class)
|
||||
val setWallpaperWork = OneTimeWorkRequest.Builder(SetWallpaperWorker::class.java)
|
||||
.setInputData(inputData)
|
||||
.build();
|
||||
.build()
|
||||
|
||||
WorkManager.getInstance(context).enqueue(setWallpaperWork);
|
||||
WorkManager.getInstance(context).enqueue(setWallpaperWork)
|
||||
}
|
||||
|
||||
|
||||
private static void showSettingWallpaperProgressBar(Context context) {
|
||||
progressDialogWallpaper = ProgressDialog.show(context, context.getString(R.string.setting_wallpaper_dialog_title),
|
||||
context.getString(R.string.setting_wallpaper_dialog_message), true);
|
||||
@JvmStatic
|
||||
private fun showSettingWallpaperProgressBar(context: Context) {
|
||||
progressDialogWallpaper = ProgressDialog.show(
|
||||
context,
|
||||
context.getString(R.string.setting_wallpaper_dialog_title),
|
||||
context.getString(R.string.setting_wallpaper_dialog_message),
|
||||
true
|
||||
)
|
||||
}
|
||||
|
||||
private static void showSettingAvatarProgressBar(Context context) {
|
||||
progressDialogAvatar = ProgressDialog.show(context, context.getString(R.string.setting_avatar_dialog_title),
|
||||
context.getString(R.string.setting_avatar_dialog_message), true);
|
||||
@JvmStatic
|
||||
private fun showSettingAvatarProgressBar(context: Context) {
|
||||
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
|
||||
* if result is 1100 IMAGE_DUPLICATE and IMAGE_GEOLOCATION_DIFFERENT
|
||||
*/
|
||||
public static String getErrorMessageForResult(Context context, @Result int result) {
|
||||
StringBuilder errorMessage = new StringBuilder();
|
||||
@JvmStatic
|
||||
fun getErrorMessageForResult(context: Context, @Result result: Int): String {
|
||||
val errorMessage = StringBuilder()
|
||||
if (result <= 0) {
|
||||
Timber.d("No issues to warn user is found");
|
||||
Timber.d("No issues to warn user are found")
|
||||
} 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 ((IMAGE_DARK & result) != 0 ) { // We are checking image dark bit to see if that bit is set or not
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_dark));
|
||||
if (result and IMAGE_DARK != 0) {
|
||||
errorMessage.append("\n - ")
|
||||
.append(context.getString(R.string.upload_problem_image_dark))
|
||||
}
|
||||
|
||||
if ((IMAGE_BLURRY & result) != 0 ) {
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_blurry));
|
||||
if (result and IMAGE_BLURRY != 0) {
|
||||
errorMessage.append("\n - ")
|
||||
.append(context.getString(R.string.upload_problem_image_blurry))
|
||||
}
|
||||
|
||||
if ((IMAGE_DUPLICATE & result) != 0 ) {
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_image_duplicate));
|
||||
if (result and IMAGE_DUPLICATE != 0) {
|
||||
errorMessage.append("\n - ").
|
||||
append(context.getString(R.string.upload_problem_image_duplicate))
|
||||
}
|
||||
|
||||
if ((IMAGE_GEOLOCATION_DIFFERENT & result) != 0 ) {
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_different_geolocation));
|
||||
if (result and IMAGE_GEOLOCATION_DIFFERENT != 0) {
|
||||
errorMessage.append("\n - ")
|
||||
.append(context.getString(R.string.upload_problem_different_geolocation))
|
||||
}
|
||||
|
||||
if ((FILE_FBMD & result) != 0) {
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.upload_problem_fbmd));
|
||||
if (result and FILE_FBMD != 0) {
|
||||
errorMessage.append("\n - ")
|
||||
.append(context.getString(R.string.upload_problem_fbmd))
|
||||
}
|
||||
|
||||
if ((FILE_NO_EXIF & result) != 0){
|
||||
errorMessage.append("\n - ").append(context.getResources().getString(R.string.internet_downloaded));
|
||||
if (result and FILE_NO_EXIF != 0) {
|
||||
errorMessage.append("\n - ")
|
||||
.append(context.getString(R.string.internet_downloaded))
|
||||
}
|
||||
|
||||
errorMessage.append("\n\n").append(context.getResources().getString(R.string.upload_problem_do_you_continue));
|
||||
errorMessage.append("\n\n")
|
||||
.append(context.getString(R.string.upload_problem_do_you_continue))
|
||||
}
|
||||
|
||||
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;
|
||||
return errorMessage.toString()
|
||||
}
|
||||
}
|
||||
|
|
@ -1,30 +1,29 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
package fr.free.nrw.commons.utils
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import io.reactivex.Single;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
public class ImageUtilsWrapper {
|
||||
class ImageUtilsWrapper @Inject constructor() {
|
||||
|
||||
@Inject
|
||||
public ImageUtilsWrapper() {
|
||||
|
||||
}
|
||||
|
||||
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))
|
||||
fun checkIfImageIsTooDark(bitmapPath: String): Single<Int> {
|
||||
return Single.fromCallable { ImageUtils.checkIfImageIsTooDark(bitmapPath) }
|
||||
.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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,27 +1,27 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.content.res.Resources;
|
||||
import java.util.Locale;
|
||||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.content.res.Resources
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @param code Language code you want to update.
|
||||
* @return Updated language code. If not in the "deprecated list" returns the same code.
|
||||
*/
|
||||
public static String fixLanguageCode(String code) {
|
||||
if (code.equalsIgnoreCase("iw")) {
|
||||
return "he";
|
||||
} else if (code.equalsIgnoreCase("in")) {
|
||||
return "id";
|
||||
} else if (code.equalsIgnoreCase("ji")) {
|
||||
return "yi";
|
||||
} else {
|
||||
return code;
|
||||
@JvmStatic
|
||||
fun fixLanguageCode(code: String): String {
|
||||
return when (code.lowercase()) {
|
||||
"iw" -> "he"
|
||||
"in" -> "id"
|
||||
"ji" -> "yi"
|
||||
else -> code
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -29,11 +29,12 @@ public class LangCodeUtils {
|
|||
* Returns configuration for locale of
|
||||
* our choice regardless of user's device settings
|
||||
*/
|
||||
public static Resources getLocalizedResources(Context context, Locale desiredLocale) {
|
||||
Configuration conf = context.getResources().getConfiguration();
|
||||
conf = new Configuration(conf);
|
||||
conf.setLocale(desiredLocale);
|
||||
Context localizedContext = context.createConfigurationContext(conf);
|
||||
return localizedContext.getResources();
|
||||
@JvmStatic
|
||||
fun getLocalizedResources(context: Context, desiredLocale: Locale): Resources {
|
||||
val conf = Configuration(context.resources.configuration).apply {
|
||||
setLocale(desiredLocale)
|
||||
}
|
||||
val localizedContext = context.createConfigurationContext(conf)
|
||||
return localizedContext.resources
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,38 +1,47 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
package fr.free.nrw.commons.utils
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
|
||||
public class LayoutUtils {
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.DisplayMetrics
|
||||
import android.view.View
|
||||
import android.view.ViewTreeObserver
|
||||
|
||||
/**
|
||||
* Can be used for keeping aspect radios suggested by material guidelines. See:
|
||||
* 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
|
||||
* width and sets height by multiplying the width with height.
|
||||
* @param rate Aspect ratios, ie 1 for 1:1. (width * rate = height)
|
||||
* @param view view to change height
|
||||
* Utility class for layout-related operations.
|
||||
*/
|
||||
public static void setLayoutHeightAllignedToWidth(double rate, View view) {
|
||||
ViewTreeObserver vto = view.getViewTreeObserver();
|
||||
vto.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
|
||||
@Override
|
||||
public void onGlobalLayout() {
|
||||
view.getViewTreeObserver().removeOnGlobalLayoutListener(this);
|
||||
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
|
||||
layoutParams.height = (int) (view.getWidth() * rate);
|
||||
view.setLayoutParams(layoutParams);
|
||||
object LayoutUtils {
|
||||
|
||||
/**
|
||||
* Can be used for keeping aspect ratios suggested by material guidelines. See:
|
||||
* https://material.io/design/layout/spacing-methods.html#containers-aspect-ratios
|
||||
* 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.
|
||||
* @param rate Aspect ratios, i.e., 1 for 1:1 (width * rate = height)
|
||||
* @param view View to change height
|
||||
*/
|
||||
@JvmStatic
|
||||
fun setLayoutHeightAlignedToWidth(rate: Double, view: View) {
|
||||
val vto = view.viewTreeObserver
|
||||
vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
view.viewTreeObserver.removeOnGlobalLayoutListener(this)
|
||||
val layoutParams = view.layoutParams
|
||||
layoutParams.height = (view.width * rate).toInt()
|
||||
view.layoutParams = layoutParams
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
public static double getScreenWidth(Context context, double rate) {
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
((Activity)context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
|
||||
return displayMetrics.widthPixels * rate;
|
||||
/**
|
||||
* Calculates and returns the screen width multiplied by the provided rate.
|
||||
* @param context Context used to access display metrics.
|
||||
* @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
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
|
||||
public class LengthUtils {
|
||||
object LengthUtils {
|
||||
/**
|
||||
* Returns a formatted distance string between two points.
|
||||
*
|
||||
|
|
@ -14,13 +17,14 @@ public class LengthUtils {
|
|||
* @param point2 LatLng type point2
|
||||
* @return string distance
|
||||
*/
|
||||
public static String formatDistanceBetween(LatLng point1, LatLng point2) {
|
||||
@JvmStatic
|
||||
fun formatDistanceBetween(point1: LatLng?, point2: LatLng?): String? {
|
||||
if (point1 == null || point2 == null) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
int distance = (int) Math.round(computeDistanceBetween(point1, point2));
|
||||
return formatDistance(distance);
|
||||
val distance = computeDistanceBetween(point1, point2).roundToInt()
|
||||
return formatDistance(distance)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -32,21 +36,21 @@ public class LengthUtils {
|
|||
* @return A string representing the distance
|
||||
* @throws IllegalArgumentException If distance is negative
|
||||
*/
|
||||
public static String formatDistance(int distance) {
|
||||
@JvmStatic
|
||||
fun formatDistance(distance: Int): String {
|
||||
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)
|
||||
if (distance >= 1000) {
|
||||
numberFormat.setMaximumFractionDigits(1);
|
||||
return numberFormat.format(distance / 1000.0) + "km";
|
||||
return if (distance >= 1000) {
|
||||
numberFormat.maximumFractionDigits = 1
|
||||
"${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
|
||||
* @throws NullPointerException if one or both the points are null
|
||||
*/
|
||||
public static double computeDistanceBetween(@NonNull LatLng point1, @NonNull LatLng point2) {
|
||||
return computeAngleBetween(point1, point2) * 6371009.0D; // Earth's radius in meter
|
||||
@JvmStatic
|
||||
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 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
|
||||
*/
|
||||
private static double computeAngleBetween(@NonNull LatLng point1, @NonNull LatLng point2) {
|
||||
@JvmStatic
|
||||
private fun computeAngleBetween(point1: LatLng, point2: LatLng): Double {
|
||||
return distanceRadians(
|
||||
Math.toRadians(point1.getLatitude()),
|
||||
Math.toRadians(point1.getLongitude()),
|
||||
Math.toRadians(point2.getLatitude()),
|
||||
Math.toRadians(point2.getLongitude())
|
||||
);
|
||||
Math.toRadians(point1.latitude),
|
||||
Math.toRadians(point1.longitude),
|
||||
Math.toRadians(point2.latitude),
|
||||
Math.toRadians(point2.longitude)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -87,8 +93,9 @@ public class LengthUtils {
|
|||
* @param lng2 Longitude of point B
|
||||
* @return Arc length between the points
|
||||
*/
|
||||
private static double distanceRadians(double lat1, double lng1, double lat2, double lng2) {
|
||||
return arcHav(havDistance(lat1, lat2, lng1 - lng2));
|
||||
@JvmStatic
|
||||
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
|
||||
* @return Inverse of haversine
|
||||
*/
|
||||
private static double arcHav(double x) {
|
||||
return 2.0D * Math.asin(Math.sqrt(x));
|
||||
@JvmStatic
|
||||
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
|
||||
* @return Arc length between points
|
||||
*/
|
||||
private static double havDistance(double lat1, double lat2, double longitude) {
|
||||
return hav(lat1 - lat2) + hav(longitude) * Math.cos(lat1) * Math.cos(lat2);
|
||||
@JvmStatic
|
||||
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
|
||||
* @return Haversine of x
|
||||
*/
|
||||
private static double hav(double x) {
|
||||
double sinHalf = Math.sin(x * 0.5D);
|
||||
return sinHalf * sinHalf;
|
||||
@JvmStatic
|
||||
private fun hav(x: Double): Double {
|
||||
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
|
||||
* @throws NullPointerException if one or both the points are null
|
||||
*/
|
||||
public static double computeBearing(@NonNull LatLng point1, @NonNull LatLng point2) {
|
||||
double diffLongitute = Math.toRadians(point2.getLongitude() - point1.getLongitude());
|
||||
double lat1 = Math.toRadians(point1.getLatitude());
|
||||
double lat2 = Math.toRadians(point2.getLatitude());
|
||||
double y = Math.sin(diffLongitute) * Math.cos(lat2);
|
||||
double x = Math.cos(lat1) * Math.sin(lat2) - Math.sin(lat1) * Math.cos(lat2) * Math.cos(diffLongitute);
|
||||
double bearing = Math.atan2(y, x);
|
||||
return (Math.toDegrees(bearing) + 360) % 360;
|
||||
@JvmStatic
|
||||
fun computeBearing(point1: LatLng, point2: LatLng): Double {
|
||||
val diffLongitude = Math.toRadians(point2.longitude - point1.longitude)
|
||||
val lat1 = Math.toRadians(point1.latitude)
|
||||
val lat2 = Math.toRadians(point2.latitude)
|
||||
val y = sin(diffLongitude) * cos(lat2)
|
||||
val x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(diffLongitude)
|
||||
val bearing = atan2(y, x)
|
||||
return (Math.toDegrees(bearing) + 360) % 360
|
||||
}
|
||||
}
|
||||
|
|
@ -1,58 +1,63 @@
|
|||
package fr.free.nrw.commons.utils;
|
||||
package fr.free.nrw.commons.utils
|
||||
|
||||
import fr.free.nrw.commons.location.LatLng;
|
||||
import timber.log.Timber;
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import timber.log.Timber
|
||||
import kotlin.math.atan2
|
||||
import kotlin.math.cos
|
||||
import kotlin.math.sin
|
||||
import kotlin.math.sqrt
|
||||
|
||||
public class LocationUtils {
|
||||
public static final double RADIUS_OF_EARTH_KM = 6371.0; // Earth's radius in kilometers
|
||||
object LocationUtils {
|
||||
const val RADIUS_OF_EARTH_KM = 6371.0 // Earth's radius in kilometers
|
||||
|
||||
public static LatLng deriveUpdatedLocationFromSearchQuery(String customQuery) {
|
||||
LatLng latLng = null;
|
||||
final int indexOfPrefix = customQuery.indexOf("Point(");
|
||||
@JvmStatic
|
||||
fun deriveUpdatedLocationFromSearchQuery(customQuery: String): LatLng? {
|
||||
var latLng: LatLng? = null
|
||||
val indexOfPrefix = customQuery.indexOf("Point(")
|
||||
if (indexOfPrefix == -1) {
|
||||
Timber.e("Invalid prefix index - Seems like user has entered an invalid query");
|
||||
return latLng;
|
||||
Timber.e("Invalid prefix index - Seems like user has entered an invalid query")
|
||||
return latLng
|
||||
}
|
||||
final int indexOfSuffix = customQuery.indexOf(")\"", indexOfPrefix);
|
||||
val indexOfSuffix = customQuery.indexOf(")\"", indexOfPrefix)
|
||||
if (indexOfSuffix == -1) {
|
||||
Timber.e("Invalid suffix index - Seems like user has entered an invalid query");
|
||||
return latLng;
|
||||
Timber.e("Invalid suffix index - Seems like user has entered an invalid query")
|
||||
return latLng
|
||||
}
|
||||
String latLngString = customQuery.substring(indexOfPrefix+"Point(".length(), indexOfSuffix);
|
||||
val latLngString = customQuery.substring(indexOfPrefix + "Point(".length, indexOfSuffix)
|
||||
if (latLngString.isEmpty()) {
|
||||
return null;
|
||||
return null
|
||||
}
|
||||
|
||||
String latLngArray[] = latLngString.split(" ");
|
||||
if (latLngArray.length != 2) {
|
||||
return null;
|
||||
val latLngArray = latLngString.split(" ")
|
||||
if (latLngArray.size != 2) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
latLng = new LatLng(Double.parseDouble(latLngArray[1].trim()),
|
||||
Double.parseDouble(latLngArray[0].trim()), 1f);
|
||||
}catch (Exception e){
|
||||
Timber.e("Error while parsing user entered lat long: %s", e);
|
||||
latLng = LatLng(latLngArray[1].trim().toDouble(),
|
||||
latLngArray[0].trim().toDouble(), 1f)
|
||||
} catch (e: Exception) {
|
||||
Timber.e("Error while parsing user entered lat long: %s", e)
|
||||
}
|
||||
|
||||
return latLng;
|
||||
return latLng
|
||||
}
|
||||
|
||||
|
||||
public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
||||
double lat1Rad = Math.toRadians(lat1);
|
||||
double lon1Rad = Math.toRadians(lon1);
|
||||
double lat2Rad = Math.toRadians(lat2);
|
||||
double lon2Rad = Math.toRadians(lon2);
|
||||
@JvmStatic
|
||||
fun calculateDistance(lat1: Double, lon1: Double, lat2: Double, lon2: Double): Double {
|
||||
val lat1Rad = Math.toRadians(lat1)
|
||||
val lon1Rad = Math.toRadians(lon1)
|
||||
val lat2Rad = Math.toRadians(lat2)
|
||||
val lon2Rad = Math.toRadians(lon2)
|
||||
|
||||
// Haversine formula
|
||||
double dlon = lon2Rad - lon1Rad;
|
||||
double 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);
|
||||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||
val dlon = lon2Rad - lon1Rad
|
||||
val dlat = lat2Rad - lat1Rad
|
||||
val a = Math.pow(
|
||||
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 distance;
|
||||
return RADIUS_OF_EARTH_KM * c
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.LocationServiceManager;
|
||||
import fr.free.nrw.commons.location.LocationUpdateListener;
|
||||
import timber.log.Timber;
|
||||
import fr.free.nrw.commons.location.LatLng
|
||||
import fr.free.nrw.commons.location.LocationServiceManager
|
||||
import fr.free.nrw.commons.location.LocationUpdateListener
|
||||
import timber.log.Timber
|
||||
|
||||
public class MapUtils {
|
||||
public static final float ZOOM_LEVEL = 14f;
|
||||
public static final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005;
|
||||
public static final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004;
|
||||
public static final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE";
|
||||
public static final float ZOOM_OUT = 0f;
|
||||
object MapUtils {
|
||||
const val ZOOM_LEVEL = 14f
|
||||
const val CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005
|
||||
const val CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004
|
||||
const val NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"
|
||||
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 {
|
||||
if (removeLocationListener) {
|
||||
locationManager.unregisterLocationManager();
|
||||
locationManager.removeLocationListener(locationUpdateListener);
|
||||
Timber.d("Location service manager unregistered and removed");
|
||||
locationManager.unregisterLocationManager()
|
||||
locationManager.removeLocationListener(locationUpdateListener)
|
||||
Timber.d("Location service manager unregistered and removed")
|
||||
} else {
|
||||
locationManager.addLocationListener(locationUpdateListener);
|
||||
locationManager.registerLocationManager();
|
||||
Timber.d("Location service manager added and registered");
|
||||
locationManager.addLocationListener(locationUpdateListener)
|
||||
locationManager.registerLocationManager()
|
||||
Timber.d("Location service manager added and registered")
|
||||
}
|
||||
}catch (final Exception e){
|
||||
Timber.e(e);
|
||||
//Broadcasts are tricky, should be catchedonR
|
||||
} catch (e: Exception) {
|
||||
Timber.e(e)
|
||||
// Broadcasts are tricky, should be caught on onR
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue