mirror of
				https://github.com/commons-app/apps-android-commons.git
				synced 2025-10-31 14:53:59 +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,197 +1,194 @@ | |||
| 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 | ||||
|     * 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; | ||||
|      * 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 | ||||
|      */ | ||||
|     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 = { | ||||
|                     IMAGE_DARK, | ||||
|                     IMAGE_BLURRY, | ||||
|                     IMAGE_DUPLICATE, | ||||
|                     IMAGE_OK, | ||||
|                     IMAGE_KEEP, | ||||
|                     IMAGE_WAIT, | ||||
|                     EMPTY_CAPTION, | ||||
|                     FILE_NAME_EXISTS, | ||||
|                     NO_CATEGORY_SELECTED, | ||||
|                     IMAGE_GEOLOCATION_DIFFERENT | ||||
|             } | ||||
|         flag = true, | ||||
|         value = [ | ||||
|             IMAGE_DARK, | ||||
|             IMAGE_BLURRY, | ||||
|             IMAGE_DUPLICATE, | ||||
|             IMAGE_OK, | ||||
|             IMAGE_KEEP, | ||||
|             IMAGE_WAIT, | ||||
|             EMPTY_CAPTION, | ||||
|             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 | ||||
|                 .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(); | ||||
|             compositeDisposable.add( | ||||
|                 okHttpJsonApiClient | ||||
|                     .setAvatar(username, url) | ||||
|                     .subscribeOn(Schedulers.io()) | ||||
|                     .observeOn(AndroidSchedulers.mainThread()) | ||||
|                     .subscribe( | ||||
|                         { 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)) | ||||
|                             progressDialogAvatar?.cancel() | ||||
|                         } | ||||
|                     }, | ||||
|                     t -> { | ||||
|                         Timber.e(t, "Setting Avatar Failed"); | ||||
|                         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() | ||||
|         } | ||||
|         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) { | ||||
|         createNotificationChannel(context); // Ensure the notification channel is created | ||||
|     @JvmStatic | ||||
|     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()) | ||||
|             .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(); | ||||
|         if (result <= 0 ) { | ||||
|             Timber.d("No issues to warn user is found"); | ||||
|     @JvmStatic | ||||
|     fun getErrorMessageForResult(context: Context, @Result result: Int): String { | ||||
|         val errorMessage = StringBuilder() | ||||
|         if (result <= 0) { | ||||
|             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(); | ||||
|         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; | ||||
|     } | ||||
| } | ||||
| } | ||||
|  | @ -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,39 +1,40 @@ | |||
| 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 | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     /** | ||||
|      * 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; | ||||
| import android.app.Activity | ||||
| import android.content.Context | ||||
| import android.util.DisplayMetrics | ||||
| import android.view.View | ||||
| 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 | ||||
|      * 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. | ||||
|      * @param rate Aspect ratios, ie 1 for 1:1. (width * rate = height) | ||||
|      * @param view view to change height | ||||
|      * @param rate Aspect ratios, i.e., 1 for 1:1 (width * rate = height) | ||||
|      * @param view View to change height | ||||
|      */ | ||||
|     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); | ||||
|     @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
	
	 Saifuddin
						Saifuddin