From a602344ebcab763e8c677be978baa24b3513113c Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Fri, 7 Feb 2025 12:05:33 +0530 Subject: [PATCH] Migrated --- .../nearby/fragments/NearbyParentFragment.kt | 3580 +++++++++-------- 1 file changed, 1889 insertions(+), 1691 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt index 2b64f0e37..1e98c0067 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt @@ -1,591 +1,588 @@ -package fr.free.nrw.commons.nearby.fragments; +package fr.free.nrw.commons.nearby.fragments -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.CUSTOM_QUERY; -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.MAP_UPDATED; -import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; - -import android.Manifest.permission; -import android.annotation.SuppressLint; -import android.app.ProgressDialog; -import android.content.ActivityNotFoundException; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.content.pm.PackageManager; -import android.content.res.Configuration; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.Paint.Style; -import android.graphics.drawable.Drawable; -import android.location.Location; -import android.location.LocationManager; -import android.net.Uri; -import android.os.Build; -import android.os.Bundle; -import android.os.Environment; -import android.os.Handler; -import android.preference.PreferenceManager; -import android.provider.Settings; -import android.text.Html; -import android.text.method.LinkMovementMethod; -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.View.OnClickListener; -import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; -import android.view.animation.Animation; -import android.view.animation.AnimationUtils; -import android.widget.Toast; -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts.RequestMultiplePermissions; -import androidx.activity.result.contract.ActivityResultContracts.RequestPermission; -import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.appcompat.app.AlertDialog.Builder; -import androidx.constraintlayout.widget.ConstraintLayout; -import androidx.core.content.ContextCompat; -import androidx.core.content.FileProvider; -import androidx.lifecycle.LifecycleCoroutineScope; -import androidx.lifecycle.LifecycleOwnerKt; -import androidx.recyclerview.widget.DividerItemDecoration; -import androidx.recyclerview.widget.GridLayoutManager; -import androidx.recyclerview.widget.LinearLayoutManager; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback; -import com.google.android.material.snackbar.Snackbar; -import com.jakewharton.rxbinding2.view.RxView; -import com.jakewharton.rxbinding3.appcompat.RxSearchView; -import fr.free.nrw.commons.CommonsApplication; -import fr.free.nrw.commons.CommonsApplication.BaseLogoutListener; -import fr.free.nrw.commons.MapController.NearbyPlacesInfo; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; -import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; -import fr.free.nrw.commons.contributions.ContributionController; -import fr.free.nrw.commons.contributions.MainActivity; -import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment; -import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding; -import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; -import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.location.LocationPermissionsHelper; -import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback; -import fr.free.nrw.commons.location.LocationServiceManager; -import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType; -import fr.free.nrw.commons.location.LocationUpdateListener; -import fr.free.nrw.commons.nearby.BottomSheetAdapter; -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.NearbyFilterSearchRecyclerViewAdapter; -import fr.free.nrw.commons.nearby.NearbyFilterState; -import fr.free.nrw.commons.nearby.Place; -import fr.free.nrw.commons.nearby.PlacesRepository; -import fr.free.nrw.commons.nearby.Sitelinks; -import fr.free.nrw.commons.nearby.WikidataFeedback; -import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract; -import fr.free.nrw.commons.nearby.fragments.AdvanceQueryFragment.Callback; -import fr.free.nrw.commons.nearby.model.BottomSheetItem; -import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter; -import fr.free.nrw.commons.upload.FileUtils; -import fr.free.nrw.commons.utils.DialogUtil; -import fr.free.nrw.commons.utils.ExecutorUtils; -import fr.free.nrw.commons.utils.LayoutUtils; -import fr.free.nrw.commons.utils.MapUtils; -import fr.free.nrw.commons.utils.NearbyFABUtils; -import fr.free.nrw.commons.utils.NetworkUtils; -import fr.free.nrw.commons.utils.SystemThemeUtils; -import fr.free.nrw.commons.utils.ViewUtil; -import fr.free.nrw.commons.wikidata.WikidataEditListener; -import io.reactivex.Completable; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.TimeUnit; -import javax.inject.Inject; -import javax.inject.Named; -import kotlin.Unit; -import org.jetbrains.annotations.NotNull; -import org.osmdroid.api.IGeoPoint; -import org.osmdroid.events.MapEventsReceiver; -import org.osmdroid.events.MapListener; -import org.osmdroid.events.ScrollEvent; -import org.osmdroid.events.ZoomEvent; -import org.osmdroid.tileprovider.tilesource.TileSourceFactory; -import org.osmdroid.util.GeoPoint; -import org.osmdroid.util.constants.GeoConstants.UnitOfMeasure; -import org.osmdroid.views.CustomZoomButtonsController.Visibility; -import org.osmdroid.views.overlay.MapEventsOverlay; -import org.osmdroid.views.overlay.Marker; -import org.osmdroid.views.overlay.Overlay; -import org.osmdroid.views.overlay.ScaleBarOverlay; -import org.osmdroid.views.overlay.ScaleDiskOverlay; -import org.osmdroid.views.overlay.TilesOverlay; -import timber.log.Timber; +import android.Manifest.permission +import android.annotation.SuppressLint +import android.app.ProgressDialog +import android.content.ActivityNotFoundException +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.content.res.Configuration +import android.graphics.Color +import android.graphics.Paint +import android.location.Location +import android.location.LocationManager +import android.net.Uri +import android.os.Build +import android.os.Bundle +import android.os.Environment +import android.os.Handler +import android.preference.PreferenceManager +import android.provider.Settings +import android.text.Html +import android.text.method.LinkMovementMethod +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.View +import android.view.ViewGroup +import android.view.animation.Animation +import android.view.animation.AnimationUtils +import android.widget.Toast +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.annotation.DrawableRes +import androidx.appcompat.app.AlertDialog +import androidx.constraintlayout.widget.ConstraintLayout +import androidx.core.content.ContextCompat +import androidx.core.content.FileProvider +import androidx.lifecycle.LifecycleCoroutineScope +import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.DividerItemDecoration +import androidx.recyclerview.widget.GridLayoutManager +import androidx.recyclerview.widget.LinearLayoutManager +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.bottomsheet.BottomSheetBehavior.BottomSheetCallback +import com.google.android.material.snackbar.Snackbar +import com.jakewharton.rxbinding2.view.RxView +import com.jakewharton.rxbinding3.appcompat.queryTextChanges +import fr.free.nrw.commons.CommonsApplication +import fr.free.nrw.commons.MapController.NearbyPlacesInfo +import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao +import fr.free.nrw.commons.contributions.ContributionController +import fr.free.nrw.commons.contributions.MainActivity +import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment +import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.filepicker.FilePicker +import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.location.LocationPermissionsHelper +import fr.free.nrw.commons.location.LocationPermissionsHelper.LocationPermissionCallback +import fr.free.nrw.commons.location.LocationServiceManager +import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType +import fr.free.nrw.commons.location.LocationUpdateListener +import fr.free.nrw.commons.nearby.BottomSheetAdapter +import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener +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.NearbyFilterSearchRecyclerViewAdapter +import fr.free.nrw.commons.nearby.NearbyFilterState +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.nearby.PlacesRepository +import fr.free.nrw.commons.nearby.WikidataFeedback +import fr.free.nrw.commons.nearby.contract.NearbyParentFragmentContract +import fr.free.nrw.commons.nearby.model.BottomSheetItem +import fr.free.nrw.commons.nearby.presenter.NearbyParentFragmentPresenter +import fr.free.nrw.commons.upload.FileUtils +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.ExecutorUtils.get +import fr.free.nrw.commons.utils.LayoutUtils.getScreenWidth +import fr.free.nrw.commons.utils.LayoutUtils.setLayoutHeightAlignedToWidth +import fr.free.nrw.commons.utils.MapUtils.defaultLatLng +import fr.free.nrw.commons.utils.NearbyFABUtils.addAnchorToBigFABs +import fr.free.nrw.commons.utils.NearbyFABUtils.addAnchorToSmallFABs +import fr.free.nrw.commons.utils.NearbyFABUtils.removeAnchorFromFAB +import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished +import fr.free.nrw.commons.utils.SystemThemeUtils +import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.wikidata.WikidataConstants +import fr.free.nrw.commons.wikidata.WikidataEditListener +import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener +import io.reactivex.Completable +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import org.osmdroid.events.MapEventsReceiver +import org.osmdroid.events.MapListener +import org.osmdroid.events.ScrollEvent +import org.osmdroid.events.ZoomEvent +import org.osmdroid.tileprovider.tilesource.TileSourceFactory +import org.osmdroid.util.GeoPoint +import org.osmdroid.util.constants.GeoConstants.UnitOfMeasure +import org.osmdroid.views.CustomZoomButtonsController +import org.osmdroid.views.MapView +import org.osmdroid.views.overlay.MapEventsOverlay +import org.osmdroid.views.overlay.Marker +import org.osmdroid.views.overlay.ScaleBarOverlay +import org.osmdroid.views.overlay.ScaleDiskOverlay +import org.osmdroid.views.overlay.TilesOverlay +import timber.log.Timber +import java.io.File +import java.io.FileOutputStream +import java.io.IOException +import java.text.SimpleDateFormat +import java.util.Date +import java.util.Locale +import java.util.concurrent.TimeUnit +import javax.inject.Inject +import javax.inject.Named +import kotlin.concurrent.Volatile -public class NearbyParentFragment extends CommonsDaggerSupportFragment - implements NearbyParentFragmentContract.View, - WikidataEditListener.WikidataP18EditListener, LocationUpdateListener, - LocationPermissionCallback, BottomSheetAdapter.ItemClickListener { +class NearbyParentFragment : CommonsDaggerSupportFragment(), NearbyParentFragmentContract.View, + WikidataP18EditListener, LocationUpdateListener, LocationPermissionCallback, ItemClickListener { + var binding: FragmentNearbyParentBinding? = null - FragmentNearbyParentBinding binding; - - public final MapEventsOverlay mapEventsOverlay = new MapEventsOverlay(new MapEventsReceiver() { - @Override - public boolean singleTapConfirmedHelper(GeoPoint p) { + val mapEventsOverlay: MapEventsOverlay = MapEventsOverlay(object : MapEventsReceiver { + override fun singleTapConfirmedHelper(p: GeoPoint): Boolean { if (clickedMarker != null) { - clickedMarker.closeInfoWindow(); + clickedMarker!!.closeInfoWindow() } else { - Timber.e("CLICKED MARKER IS NULL"); + Timber.e("CLICKED MARKER IS NULL") } - if (isListBottomSheetExpanded()) { + if (isListBottomSheetExpanded) { // Back should first hide the bottom sheet if it is expanded - hideBottomSheet(); - } else if (isDetailsBottomSheetVisible()) { - hideBottomDetailsSheet(); + hideBottomSheet() + } else if (isDetailsBottomSheetVisible) { + hideBottomDetailsSheet() } - return true; + return true } - @Override - public boolean longPressHelper(GeoPoint p) { - return false; + override fun longPressHelper(p: GeoPoint): Boolean { + return false } - }); + }) @Inject - LocationServiceManager locationManager; + lateinit var locationManager: LocationServiceManager + + @Inject - NearbyController nearbyController; + lateinit var nearbyController: NearbyController + @Inject @Named("default_preferences") - JsonKvStore applicationKvStore; - @Inject - BookmarkLocationsDao bookmarkLocationDao; - @Inject - PlacesRepository placesRepository; - @Inject - ContributionController controller; - @Inject - WikidataEditListener wikidataEditListener; - @Inject - SystemThemeUtils systemThemeUtils; - @Inject - CommonPlaceClickActions commonPlaceClickActions; + lateinit var applicationKvStore: JsonKvStore - private LocationPermissionsHelper locationPermissionsHelper; - private NearbyFilterSearchRecyclerViewAdapter nearbyFilterSearchRecyclerViewAdapter; - private BottomSheetBehavior bottomSheetListBehavior; - private BottomSheetBehavior bottomSheetDetailsBehavior; - private Animation rotate_backward; - private Animation fab_close; - private Animation fab_open; - private Animation rotate_forward; - private static final float ZOOM_LEVEL = 15f; - private final String NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE"; - private BroadcastReceiver broadcastReceiver; - private boolean isNetworkErrorOccurred; - private Snackbar snackbar; - private View view; - private LifecycleCoroutineScope scope; - private NearbyParentFragmentPresenter presenter; - private boolean isDarkTheme; - private boolean isFABsExpanded; - private Place selectedPlace; - private Marker clickedMarker; - private ProgressDialog progressDialog; - private final double CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005; - private final double CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004; - private boolean isPermissionDenied; - private boolean recenterToUserLocation; - private GeoPoint mapCenter; - IntentFilter intentFilter = new IntentFilter(NETWORK_INTENT_ACTION); - private Place lastPlaceToCenter; - private LatLng lastKnownLocation; - private boolean isVisibleToUser; - private LatLng lastFocusLocation; - private PlaceAdapter adapter; - private GeoPoint lastMapFocus; - private NearbyParentFragmentInstanceReadyCallback nearbyParentFragmentInstanceReadyCallback; - private boolean isAdvancedQueryFragmentVisible = false; - private Place nearestPlace; - private volatile boolean stopQuery; + @Inject + lateinit var bookmarkLocationDao: BookmarkLocationsDao + + @Inject + lateinit var placesRepository: PlacesRepository + + @Inject + lateinit var controller: ContributionController + + @Inject + lateinit var wikidataEditListener: WikidataEditListener + + @Inject + lateinit var systemThemeUtils: SystemThemeUtils + + @Inject + lateinit var commonPlaceClickActions: CommonPlaceClickActions + + private var locationPermissionsHelper: LocationPermissionsHelper? = null + private var nearbyFilterSearchRecyclerViewAdapter: NearbyFilterSearchRecyclerViewAdapter? = null + private var bottomSheetListBehavior: BottomSheetBehavior<*>? = null + private var bottomSheetDetailsBehavior: BottomSheetBehavior<*>? = null + private var rotate_backward: Animation? = null + private var fab_close: Animation? = null + private var fab_open: Animation? = null + private var rotate_forward: Animation? = null + private val NETWORK_INTENT_ACTION = "android.net.conn.CONNECTIVITY_CHANGE" + private var broadcastReceiver: BroadcastReceiver? = null + private var isNetworkErrorOccurred = false + private var snackbar: Snackbar? = null + private var view: View? = null + private var scope: LifecycleCoroutineScope? = null + private var presenter: NearbyParentFragmentPresenter? = null + private var isDarkTheme = false + private var isFABsExpanded = false + private var selectedPlace: Place? = null + private var clickedMarker: Marker? = null + private var progressDialog: ProgressDialog? = null + private val CAMERA_TARGET_SHIFT_FACTOR_PORTRAIT = 0.005 + private val CAMERA_TARGET_SHIFT_FACTOR_LANDSCAPE = 0.004 + private var isPermissionDenied = false + private var recenterToUserLocation = false + private var mapCenter: GeoPoint? = null + var intentFilter: IntentFilter = IntentFilter(NETWORK_INTENT_ACTION) + private var lastPlaceToCenter: Place? = null + private var lastKnownLocation: LatLng? = null + private var isVisibleToUser = false + private var lastFocusLocation: LatLng? = null + private var adapter: PlaceAdapter? = null + private var lastMapFocus: GeoPoint? = null + private var nearbyParentFragmentInstanceReadyCallback: NearbyParentFragmentInstanceReadyCallback? = + null + private var isAdvancedQueryFragmentVisible = false + private var nearestPlace: Place? = null + + @Volatile + private var stopQuery = false // Explore map data (for if we came from Explore) - private double prevZoom; - private double prevLatitude; - private double prevLongitude; + private var prevZoom = 0.0 + private var prevLatitude = 0.0 + private var prevLongitude = 0.0 - private final Handler searchHandler = new Handler(); - private Runnable searchRunnable; + private val searchHandler = Handler() + private val searchRunnable: Runnable? = null - private LatLng updatedLatLng; - private boolean searchable; + private val updatedLatLng: LatLng? = null + private var searchable = false - private ConstraintLayout nearbyLegend; + private val nearbyLegend: ConstraintLayout? = null - private GridLayoutManager gridLayoutManager; - private List dataList; - private BottomSheetAdapter bottomSheetAdapter; + private var gridLayoutManager: GridLayoutManager? = null + private var dataList: MutableList? = null + private var bottomSheetAdapter: BottomSheetAdapter? = null - private final ActivityResultLauncher galleryPickLauncherForResult = - registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); - }); - }); - - private final ActivityResultLauncher customSelectorLauncherForResult = - registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCustomSelector(result, requireActivity(), - callbacks); - }); - }); - - private final ActivityResultLauncher cameraPickLauncherForResult = - registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); - }); - }); - - private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( - new RequestMultiplePermissions(), - new ActivityResultCallback>() { - @Override - public void onActivityResult(Map result) { - boolean areAllGranted = true; - for (final boolean b : result.values()) { - areAllGranted = areAllGranted && b; - } - - if (areAllGranted) { - controller.locationPermissionCallback.onLocationPermissionGranted(); - } else { - if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - controller.handleShowRationaleFlowCameraLocation(getActivity(), - inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); - } else { - controller.locationPermissionCallback.onLocationPermissionDenied( - getActivity().getString( - R.string.in_app_camera_location_permission_denied)); + private val galleryPickLauncherForResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + controller?.handleActivityResultWithCallback( + requireActivity(), + object : FilePicker.HandleActivityResult { + override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) { + // Handle the result from the gallery + controller?.onPictureReturnedFromGallery( + result, + requireActivity(), + callbacks + ) } - } - } - }); + }) + } - private ActivityResultLauncher locationPermissionLauncher = registerForActivityResult( - new RequestPermission(), isGranted -> { + + private val customSelectorLauncherForResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult? -> + controller?.handleActivityResultWithCallback( + requireActivity(), + object : FilePicker.HandleActivityResult { + override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) { + if (result != null) { + controller?.onPictureReturnedFromCustomSelector( + result, + requireActivity(), + callbacks + ) + } + } + }) + } + + + private val cameraPickLauncherForResult = + registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult? -> + controller?.handleActivityResultWithCallback( + requireActivity(), + object : FilePicker.HandleActivityResult { + override fun onHandleActivityResult(callbacks: FilePicker.Callbacks) { + if (result != null) { + controller?.onPictureReturnedFromCamera( + result, + requireActivity(), + callbacks + ) + } + } + }) + } + + + private lateinit var inAppCameraLocationPermissionLauncher: ActivityResultLauncher> + + private val locationPermissionLauncher: ActivityResultLauncher = + registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { - locationPermissionGranted(); + locationPermissionGranted() } else { if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { - DialogUtil.showAlertDialog(getActivity(), - getActivity().getString(R.string.location_permission_title), - getActivity().getString(R.string.location_permission_rationale_nearby), - getActivity().getString(android.R.string.ok), - getActivity().getString(android.R.string.cancel), - () -> { - askForLocationPermission(); + showAlertDialog( + requireActivity(), + getString(fr.free.nrw.commons.R.string.location_permission_title), + getString(fr.free.nrw.commons.R.string.location_permission_rationale_nearby), + getString(fr.free.nrw.commons.R.string.ok), + getString(fr.free.nrw.commons.R.string.cancel), + { + askForLocationPermission() }, null, null - ); + ) } else { if (isPermissionDenied) { - locationPermissionsHelper.showAppSettingsDialog(getActivity(), - R.string.nearby_needs_location); + locationPermissionsHelper?.showAppSettingsDialog( + requireActivity(), + fr.free.nrw.commons.R.string.nearby_needs_location + ) } - Timber.d("The user checked 'Don't ask again' or denied the permission twice"); - isPermissionDenied = true; + Timber.d("The user checked 'Don't ask again' or denied the permission twice") + isPermissionDenied = true } } - }); + } /** * WLM URL */ - public static final String WLM_URL = "https://commons.wikimedia.org/wiki/Commons:Mobile_app/Contributing_to_WLM_using_the_app"; + val WLM_URL = + "https://commons.wikimedia.org/wiki/Commons:Mobile_app/Contributing_to_WLM_using_the_app" - @NonNull - public static NearbyParentFragment newInstance() { - NearbyParentFragment fragment = new NearbyParentFragment(); - fragment.setRetainInstance(true); - return fragment; - } - @Override - public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, - final Bundle savedInstanceState) { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { loadExploreMapData(); - binding = FragmentNearbyParentBinding.inflate(inflater, container, false); - view = binding.getRoot(); + binding = FragmentNearbyParentBinding.inflate(inflater, container, false) + view = binding!!.root - initNetworkBroadCastReceiver(); - scope = LifecycleOwnerKt.getLifecycleScope(getViewLifecycleOwner()); - presenter = new NearbyParentFragmentPresenter(bookmarkLocationDao, placesRepository, - nearbyController); - progressDialog = new ProgressDialog(getActivity()); - progressDialog.setCancelable(false); - progressDialog.setMessage("Saving in progress..."); - setHasOptionsMenu(true); + initNetworkBroadCastReceiver() + scope = viewLifecycleOwner.lifecycleScope + presenter = NearbyParentFragmentPresenter( + bookmarkLocationDao!!, + placesRepository!!, nearbyController!! + ) + progressDialog = ProgressDialog(activity) + progressDialog!!.setCancelable(false) + progressDialog!!.setMessage("Saving in progress...") + setHasOptionsMenu(true) // Inflate the layout for this fragment - return view; - + return view } - @Override - public void onCreateOptionsMenu(@NonNull final Menu menu, - @NonNull final MenuInflater inflater) { - inflater.inflate(R.menu.nearby_fragment_menu, menu); - MenuItem refreshButton = menu.findItem(R.id.item_refresh); - MenuItem listMenu = menu.findItem(R.id.list_sheet); - MenuItem showInExploreButton = menu.findItem(R.id.list_item_show_in_explore); - MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx); - MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml); - refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - try { - emptyCache(); - } catch (Exception e) { - throw new RuntimeException(e); - } - return false; + override fun onCreateOptionsMenu( + menu: Menu, + inflater: MenuInflater + ) { + inflater.inflate(fr.free.nrw.commons.R.menu.nearby_fragment_menu, menu) + val refreshButton = menu.findItem(fr.free.nrw.commons.R.id.item_refresh) + val listMenu = menu.findItem(fr.free.nrw.commons.R.id.list_sheet) + val showInExploreButton = menu.findItem(fr.free.nrw.commons.R.id.list_item_show_in_explore) + val saveAsGPXButton = menu.findItem(fr.free.nrw.commons.R.id.list_item_gpx) + val saveAsKMLButton = menu.findItem(fr.free.nrw.commons.R.id.list_item_kml) + refreshButton.setOnMenuItemClickListener { + try { + emptyCache() + } catch (e: Exception) { + throw RuntimeException(e) } - }); - listMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(MenuItem item) { - listOptionMenuItemClicked(); - return false; - } - }); - showInExploreButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { - @Override - public boolean onMenuItemClick(@NonNull MenuItem item) { - ((MainActivity) getContext()).loadExploreMapFromNearby( - binding.map.getZoomLevelDouble(), - binding.map.getMapCenter().getLatitude(), - binding.map.getMapCenter().getLongitude() - ); - return false; - } - }); - saveAsGPXButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { - - @Override - public boolean onMenuItemClick(@NonNull MenuItem item) { - try { - progressDialog.setTitle(getString(R.string.saving_gpx_file)); - progressDialog.show(); - savePlacesAsGPX(); - } catch (Exception e) { - throw new RuntimeException(e); - } - return false; - } - }); - saveAsKMLButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { - - @Override - public boolean onMenuItemClick(@NonNull MenuItem item) { - try { - progressDialog.setTitle(getString(R.string.saving_kml_file)); - progressDialog.show(); - savePlacesAsKML(); - } catch (Exception e) { - throw new RuntimeException(e); - } - return false; - } - }); - } - - @Override - public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - isDarkTheme = systemThemeUtils.isDeviceInNightMode(); - if (Utils.isMonumentsEnabled(new Date())) { - binding.rlContainerWlmMonthMessage.setVisibility(View.VISIBLE); - } else { - binding.rlContainerWlmMonthMessage.setVisibility(View.GONE); + false } - locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager, - this); + listMenu.setOnMenuItemClickListener { + listOptionMenuItemClicked() + false + } + + showInExploreButton.setOnMenuItemClickListener { item -> + (context as MainActivity).loadExploreMapFromNearby( + binding?.map?.zoomLevelDouble ?: 0.0, // Using safe calls to avoid NPE + binding?.map?.mapCenter?.latitude ?: 0.0, + binding?.map?.mapCenter?.longitude ?: 0.0 + ) + return@setOnMenuItemClickListener true + } + + + saveAsGPXButton.setOnMenuItemClickListener { + try { + progressDialog!!.setTitle(getString(fr.free.nrw.commons.R.string.saving_gpx_file)) + progressDialog!!.show() + savePlacesAsGPX() + } catch (e: Exception) { + throw RuntimeException(e) + } + false + } + saveAsKMLButton.setOnMenuItemClickListener { + try { + progressDialog!!.setTitle(getString(fr.free.nrw.commons.R.string.saving_kml_file)) + progressDialog!!.show() + savePlacesAsKML() + } catch (e: Exception) { + throw RuntimeException(e) + } + false + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + // Initialize the launcher in the appropriate lifecycle method (e.g., onViewCreated) + inAppCameraLocationPermissionLauncher = + registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { result -> + val areAllGranted = result.values.all { it } + + if (areAllGranted) { + controller?.locationPermissionCallback?.onLocationPermissionGranted() + } else { + if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION)) { + controller?.handleShowRationaleFlowCameraLocation( + requireActivity(), + inAppCameraLocationPermissionLauncher, // Reference it directly + cameraPickLauncherForResult + ) + } else { + controller?.locationPermissionCallback?.onLocationPermissionDenied( + getString(fr.free.nrw.commons.R.string.in_app_camera_location_permission_denied) + ) + } + } + } + isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true + if (Utils.isMonumentsEnabled(Date())) { + binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE + } else { + binding?.rlContainerWlmMonthMessage?.visibility = View.GONE + } + locationPermissionsHelper = + locationManager?.let { LocationPermissionsHelper(requireActivity(), it, this) } // Set up the floating activity button to toggle the visibility of the legend - binding.fabLegend.setOnClickListener(v -> { - if (binding.nearbyLegendLayout.getRoot().getVisibility() == View.VISIBLE) { - binding.nearbyLegendLayout.getRoot().setVisibility(View.GONE); + binding?.fabLegend?.setOnClickListener { + if (binding?.nearbyLegendLayout?.root?.visibility == View.VISIBLE) { + binding?.nearbyLegendLayout?.root?.visibility = View.GONE } else { - binding.nearbyLegendLayout.getRoot().setVisibility(View.VISIBLE); + binding?.nearbyLegendLayout?.root?.visibility = View.VISIBLE } - }); + } - presenter.attachView(this); - isPermissionDenied = false; - recenterToUserLocation = false; - initThemePreferences(); - initViews(); - presenter.setActionListeners(applicationKvStore); - org.osmdroid.config.Configuration.getInstance().load(this.getContext(), - PreferenceManager.getDefaultSharedPreferences(this.getContext())); + presenter?.attachView(this) + isPermissionDenied = false + recenterToUserLocation = false + initThemePreferences() + initViews() + presenter?.setActionListeners(applicationKvStore) + org.osmdroid.config.Configuration.getInstance() + .load(requireContext(), PreferenceManager.getDefaultSharedPreferences(requireContext())) - // Use the Wikimedia tile server, rather than OpenStreetMap (Mapnik) which has various - // restrictions that we do not satisfy. - binding.map.setTileSource(TileSourceFactory.WIKIMEDIA); - binding.map.setTilesScaledToDpi(true); + // Use the Wikimedia tile server, rather than OpenStreetMap (Mapnik) + binding?.map?.setTileSource(TileSourceFactory.WIKIMEDIA) + binding?.map?.setTilesScaledToDpi(true) // Add referer HTTP header because the Wikimedia tile server requires it. - // This was suggested by Dmitry Brant within an email thread between us and WMF. - org.osmdroid.config.Configuration.getInstance().getAdditionalHttpRequestProperties().put( - "Referer", "http://maps.wikimedia.org/" - ); + org.osmdroid.config.Configuration.getInstance().getAdditionalHttpRequestProperties() + .put("Referer", "http://maps.wikimedia.org/") - if (applicationKvStore.getString("LastLocation") - != null) { // Checking for last searched location - String[] locationLatLng = applicationKvStore.getString("LastLocation").split(","); - lastMapFocus = new GeoPoint(Double.valueOf(locationLatLng[0]), - Double.valueOf(locationLatLng[1])); + if (applicationKvStore?.getString("LastLocation") != null) { // Checking for last searched location + val locationLatLng = applicationKvStore!!.getString("LastLocation")!!.split(",") + lastMapFocus = GeoPoint(locationLatLng[0].toDouble(), locationLatLng[1].toDouble()) } else { - lastMapFocus = new GeoPoint(51.50550, -0.07520); + lastMapFocus = GeoPoint(51.50550, -0.07520) } - ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(binding.map); - scaleBarOverlay.setScaleBarOffset(15, 25); - Paint barPaint = new Paint(); - barPaint.setARGB(200, 255, 250, 250); - scaleBarOverlay.setBackgroundPaint(barPaint); - scaleBarOverlay.enableScaleBar(); - binding.map.getOverlays().add(scaleBarOverlay); - binding.map.getZoomController().setVisibility(Visibility.NEVER); - binding.map.getController().setZoom(ZOOM_LEVEL); + + val scaleBarOverlay = ScaleBarOverlay(binding?.map) + scaleBarOverlay.setScaleBarOffset(15, 25) + val barPaint = Paint().apply { + setARGB(200, 255, 250, 250) + } + scaleBarOverlay.setBackgroundPaint(barPaint) + scaleBarOverlay.enableScaleBar() + binding?.map?.overlays?.add(scaleBarOverlay) + binding?.map?.getZoomController() + ?.setVisibility(CustomZoomButtonsController.Visibility.NEVER) + binding?.map?.controller?.setZoom(ZOOM_LEVEL.toInt()) + // if we came from Explore map using 'Show in Nearby', load Explore map camera position if (isCameFromExploreMap()) { moveCameraToPosition( - new GeoPoint(prevLatitude, prevLongitude), + GeoPoint(prevLatitude, prevLongitude), prevZoom, 1L - ); + ) } - binding.map.getOverlays().add(mapEventsOverlay); + binding?.map?.getOverlays()?.add(mapEventsOverlay) - binding.map.addMapListener(new MapListener() { - @Override - public boolean onScroll(ScrollEvent event) { - presenter.handleMapScrolled(scope, !isNetworkErrorOccurred); - return true; + binding?.map?.addMapListener(object : MapListener { + override fun onScroll(event: ScrollEvent): Boolean { + presenter?.handleMapScrolled(scope, !isNetworkErrorOccurred) + return true } - @Override - public boolean onZoom(ZoomEvent event) { - return false; + override fun onZoom(event: ZoomEvent): Boolean { + return false } + }) - }); - - binding.map.setMultiTouchControls(true); - if (nearbyParentFragmentInstanceReadyCallback != null) { - nearbyParentFragmentInstanceReadyCallback.onReady(); - } - initNearbyFilter(); - addCheckBoxCallback(); + binding?.map?.setMultiTouchControls(true) + nearbyParentFragmentInstanceReadyCallback?.onReady() + initNearbyFilter() + addCheckBoxCallback() if (!isCameFromExploreMap()) { - moveCameraToPosition(lastMapFocus); + moveCameraToPosition(lastMapFocus!!); } - initRvNearbyList(); - onResume(); + moveCameraToPosition(lastMapFocus!!) + initRvNearbyList() + onResume() + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - binding.tvAttribution.setText( - Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); + binding?.tvAttribution?.text = Html.fromHtml( + getString(fr.free.nrw.commons.R.string.map_attribution), + Html.FROM_HTML_MODE_LEGACY + ) } else { - //noinspection deprecation - binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); + @Suppress("DEPRECATION") + binding?.tvAttribution?.text = + Html.fromHtml(getString(fr.free.nrw.commons.R.string.map_attribution)) } - binding.tvAttribution.setMovementMethod(LinkMovementMethod.getInstance()); - binding.nearbyFilterList.btnAdvancedOptions.setOnClickListener(v -> { - binding.nearbyFilter.searchViewLayout.searchView.clearFocus(); - showHideAdvancedQueryFragment(true); - final AdvanceQueryFragment fragment = new AdvanceQueryFragment(); - final Bundle bundle = new Bundle(); + binding?.tvAttribution?.movementMethod = LinkMovementMethod.getInstance() + + binding?.nearbyFilterList?.btnAdvancedOptions?.setOnClickListener { + binding?.nearbyFilter?.searchViewLayout?.searchView?.clearFocus() + showHideAdvancedQueryFragment(true) + val fragment = AdvanceQueryFragment() + val bundle = Bundle() try { - bundle.putString("query", - FileUtils.INSTANCE.readFromResource( - "/queries/radius_query_for_upload_wizard.rq") - ); - } catch (IOException e) { - Timber.e(e); + bundle.putString( + "query", + FileUtils.readFromResource("/queries/radius_query_for_upload_wizard.rq") + ) + } catch (e: IOException) { + Timber.e(e) } - fragment.setArguments(bundle); - fragment.callback = new Callback() { - @Override - public void close() { - showHideAdvancedQueryFragment(false); + fragment.arguments = bundle + fragment.callback = object : AdvanceQueryFragment.Callback { + override fun close() { + showHideAdvancedQueryFragment(false) } - @Override - public void reset() { - presenter.setAdvancedQuery(null); - presenter.updateMapAndList(LOCATION_SIGNIFICANTLY_CHANGED); - showHideAdvancedQueryFragment(false); + override fun reset() { + presenter?.setAdvancedQuery(null.toString()) + presenter?.updateMapAndList(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) + showHideAdvancedQueryFragment(false) } - @Override - public void apply(@NotNull final String query) { - presenter.setAdvancedQuery(query); - presenter.updateMapAndList(CUSTOM_QUERY); - showHideAdvancedQueryFragment(false); + override fun apply(query: String) { + presenter?.setAdvancedQuery(query) + presenter?.updateMapAndList(LocationChangeType.CUSTOM_QUERY) + showHideAdvancedQueryFragment(false) } - }; - getChildFragmentManager().beginTransaction() - .replace(R.id.fl_container_nearby_children, fragment) - .commit(); - }); + } + childFragmentManager.beginTransaction() + .replace(fr.free.nrw.commons.R.id.fl_container_nearby_children, fragment) + .commit() + } - binding.tvLearnMore.setOnClickListener(v -> onLearnMoreClicked()); + binding?.tvLearnMore?.setOnClickListener { + onLearnMoreClicked() + } - if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { - askForLocationPermission(); + if (!locationPermissionsHelper?.checkLocationPermission(requireActivity())!!) { + askForLocationPermission() } } /** * Fetch Explore map camera data from fragment arguments if any. */ - public void loadExploreMapData() { + fun loadExploreMapData() { // get fragment arguments - if (getArguments() != null) { - prevZoom = getArguments().getDouble("prev_zoom"); - prevLatitude = getArguments().getDouble("prev_latitude"); - prevLongitude = getArguments().getDouble("prev_longitude"); + if (arguments != null) { + prevZoom = requireArguments().getDouble("prev_zoom") + prevLatitude = requireArguments().getDouble("prev_latitude") + prevLongitude = requireArguments().getDouble("prev_longitude") } } @@ -594,118 +591,135 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment * navigated from Explore using 'Show in Nearby'. * * @return true if user navigated from Explore map - **/ - public boolean isCameFromExploreMap() { - return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0; + */ + fun isCameFromExploreMap(): Boolean { + return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0 } /** * Initialise background based on theme, this should be doe ideally via styles, that would need * another refactor */ - private void initThemePreferences() { + private fun initThemePreferences() { if (isDarkTheme) { - binding.bottomSheetNearby.rvNearbyList.setBackgroundColor( - getContext().getResources().getColor(R.color.contributionListDarkBackground)); - binding.nearbyFilterList.checkboxTriStates.setTextColor( - getContext().getResources().getColor(android.R.color.white)); - binding.nearbyFilterList.checkboxTriStates.setTextColor( - getContext().getResources().getColor(android.R.color.white)); - binding.nearbyFilterList.getRoot().setBackgroundColor( - getContext().getResources().getColor(R.color.contributionListDarkBackground)); - binding.map.getOverlayManager().getTilesOverlay() - .setColorFilter(TilesOverlay.INVERT_COLORS); + binding!!.bottomSheetNearby.rvNearbyList.setBackgroundColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.contributionListDarkBackground) + ) + binding!!.nearbyFilterList.checkboxTriStates.setTextColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.white) + ) + binding!!.nearbyFilterList.checkboxTriStates.setTextColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.white) + ) + binding!!.nearbyFilterList.root.setBackgroundColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.contributionListDarkBackground) + ) + binding!!.map.overlayManager.tilesOverlay + .setColorFilter(TilesOverlay.INVERT_COLORS) } else { - binding.bottomSheetNearby.rvNearbyList.setBackgroundColor( - getContext().getResources().getColor(android.R.color.white)); - binding.nearbyFilterList.checkboxTriStates.setTextColor( - getContext().getResources().getColor(R.color.contributionListDarkBackground)); - binding.nearbyFilterList.getRoot().setBackgroundColor( - getContext().getResources().getColor(android.R.color.white)); - binding.nearbyFilterList.getRoot().setBackgroundColor( - getContext().getResources().getColor(android.R.color.white)); + binding!!.bottomSheetNearby.rvNearbyList.setBackgroundColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.white) + ) + binding!!.nearbyFilterList.checkboxTriStates.setTextColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.contributionListDarkBackground) + ) + binding!!.nearbyFilterList.root.setBackgroundColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.white) + ) + binding!!.nearbyFilterList.root.setBackgroundColor( + requireContext().resources.getColor(fr.free.nrw.commons.R.color.white) + ) } } - private void initRvNearbyList() { - binding.bottomSheetNearby.rvNearbyList.setLayoutManager( - new LinearLayoutManager(getContext())); - adapter = new PlaceAdapter(bookmarkLocationDao, - place -> { + private fun initRvNearbyList() { + binding!!.bottomSheetNearby.rvNearbyList.layoutManager = LinearLayoutManager(context) + adapter = PlaceAdapter( + bookmarkLocationDao!!, + { place: Place -> moveCameraToPosition( - new GeoPoint(place.location.getLatitude(), place.location.getLongitude())); - return Unit.INSTANCE; + GeoPoint(place.location.latitude, place.location.longitude) + ) + Unit }, - (place, isBookmarked) -> { - presenter.toggleBookmarkedStatus(place); - return Unit.INSTANCE; + { place: Place?, isBookmarked: Boolean? -> + presenter!!.toggleBookmarkedStatus(place) + Unit }, - commonPlaceClickActions, + commonPlaceClickActions!!, inAppCameraLocationPermissionLauncher, galleryPickLauncherForResult, cameraPickLauncherForResult - ); - binding.bottomSheetNearby.rvNearbyList.setAdapter(adapter); + ) + binding!!.bottomSheetNearby.rvNearbyList.adapter = adapter } - private void addCheckBoxCallback() { - binding.nearbyFilterList.checkboxTriStates.setCallback( - (o, state, b, b1) -> presenter.filterByMarkerType(o, state, b, b1)); + private fun addCheckBoxCallback() { + binding!!.nearbyFilterList.checkboxTriStates.setCallback { o, state, b, b1 -> + presenter!!.filterByMarkerType( + o, + state, + b, + b1 + ) + } } - private void performMapReadyActions() { - if (((MainActivity) getActivity()).activeFragment == ActiveFragment.NEARBY) { - if (applicationKvStore.getBoolean("doNotAskForLocationPermission", false) && - !locationPermissionsHelper.checkLocationPermission(getActivity())) { - isPermissionDenied = true; + private fun performMapReadyActions() { + if ((activity as MainActivity).activeFragment == ActiveFragment.NEARBY) { + if (applicationKvStore!!.getBoolean("doNotAskForLocationPermission", false) && + !locationPermissionsHelper!!.checkLocationPermission(requireActivity()) + ) { + isPermissionDenied = true } } - presenter.onMapReady(); + presenter!!.onMapReady() } - @Override - public void askForLocationPermission() { - Timber.d("Asking for location permission"); - locationPermissionLauncher.launch(permission.ACCESS_FINE_LOCATION); + override fun askForLocationPermission() { + Timber.d("Asking for location permission") + locationPermissionLauncher.launch(permission.ACCESS_FINE_LOCATION) } - private void locationPermissionGranted() { - isPermissionDenied = false; - applicationKvStore.putBoolean("doNotAskForLocationPermission", false); - lastKnownLocation = locationManager.getLastLocation(); - LatLng target = lastKnownLocation; + private fun locationPermissionGranted() { + isPermissionDenied = false + applicationKvStore!!.putBoolean("doNotAskForLocationPermission", false) + lastKnownLocation = locationManager!!.getLastLocation() + val target = lastKnownLocation if (lastKnownLocation != null) { - GeoPoint targetP = new GeoPoint(target.getLatitude(), target.getLongitude()); - mapCenter = targetP; - binding.map.getController().setCenter(targetP); - recenterMarkerToPosition(targetP); + val targetP = GeoPoint(target!!.latitude, target.longitude) + mapCenter = targetP + binding!!.map.controller.setCenter(targetP) + recenterMarkerToPosition(targetP) if (!isCameFromExploreMap()) { moveCameraToPosition(targetP); } - } else if (locationManager.isGPSProviderEnabled() - || locationManager.isNetworkProviderEnabled()) { - locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); - locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); - setProgressBarVisibility(true); + } else if (locationManager!!.isGPSProviderEnabled() + || locationManager!!.isNetworkProviderEnabled() + ) { + locationManager!!.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER) + locationManager!!.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER) + setProgressBarVisibility(true) } else { - locationPermissionsHelper.showLocationOffDialog(getActivity(), - R.string.ask_to_turn_location_on_text); + locationPermissionsHelper!!.showLocationOffDialog( + requireActivity(), + fr.free.nrw.commons.R.string.ask_to_turn_location_on_text + ) } - presenter.onMapReady(); - registerUnregisterLocationListener(false); + presenter!!.onMapReady() + registerUnregisterLocationListener(false) } - @Override - public void onResume() { - super.onResume(); - binding.map.onResume(); - presenter.attachView(this); - registerNetworkReceiver(); - if (isResumed() && ((MainActivity) getActivity()).activeFragment == ActiveFragment.NEARBY) { - if (locationPermissionsHelper.checkLocationPermission(getActivity())) { - locationPermissionGranted(); + override fun onResume() { + super.onResume() + binding!!.map.onResume() + presenter!!.attachView(this) + registerNetworkReceiver() + if (isResumed && (activity as MainActivity).activeFragment == ActiveFragment.NEARBY) { + if (locationPermissionsHelper!!.checkLocationPermission(requireActivity())) { + locationPermissionGranted() } else { - startMapWithoutPermission(); + startMapWithoutPermission() } } } @@ -715,66 +729,75 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment * coordinates, other than that it points to the last known location which can be get by the key * "LastLocation" from applicationKvStore */ - private void startMapWithoutPermission() { - if (applicationKvStore.getString("LastLocation") != null) { - final String[] locationLatLng - = applicationKvStore.getString("LastLocation").split(","); - lastKnownLocation - = new LatLng(Double.parseDouble(locationLatLng[0]), - Double.parseDouble(locationLatLng[1]), 1f); + private fun startMapWithoutPermission() { + if (applicationKvStore!!.getString("LastLocation") != null) { + val locationLatLng = + applicationKvStore!!.getString("LastLocation")!!.split(",".toRegex()) + .dropLastWhile { it.isEmpty() }.toTypedArray() + lastKnownLocation = LatLng( + locationLatLng[0].toDouble(), + locationLatLng[1].toDouble(), 1f + ) } else { - lastKnownLocation = MapUtils.getDefaultLatLng(); + lastKnownLocation = defaultLatLng } - if (binding.map != null && !isCameFromExploreMap()) { + if (binding!!.map != null && !isCameFromExploreMap()) { moveCameraToPosition( - new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); + GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude) + ) } - presenter.onMapReady(); + presenter!!.onMapReady() } - private void registerNetworkReceiver() { - if (getActivity() != null) { - getActivity().registerReceiver(broadcastReceiver, intentFilter); + private fun registerNetworkReceiver() { + if (activity != null) { + requireActivity().registerReceiver(broadcastReceiver, intentFilter) } } - @Override - public void onPause() { - super.onPause(); - binding.map.onPause(); - getCompositeDisposable().clear(); - presenter.detachView(); - registerUnregisterLocationListener(true); + override fun onPause() { + super.onPause() + binding!!.map.onPause() + compositeDisposable.clear() + presenter!!.detachView() + registerUnregisterLocationListener(true) try { - if (broadcastReceiver != null && getActivity() != null) { - getContext().unregisterReceiver(broadcastReceiver); + if (broadcastReceiver != null && activity != null) { + requireContext().unregisterReceiver(broadcastReceiver) } if (locationManager != null && presenter != null) { - locationManager.removeLocationListener(presenter); - locationManager.unregisterLocationManager(); + locationManager!!.removeLocationListener(presenter!!) + locationManager!!.unregisterLocationManager() } - } catch (final Exception e) { - Timber.e(e); + } catch (e: Exception) { + Timber.e(e) //Broadcast receivers should always be unregistered inside catch, you never know if they were already registered or not } } - @Override - public void onDestroyView() { - super.onDestroyView(); - searchHandler.removeCallbacks(searchRunnable); - presenter.removeNearbyPreferences(applicationKvStore); + override fun onDestroyView() { + super.onDestroyView() + searchRunnable?.let { + searchHandler.removeCallbacks(it) + } ?: run { + Timber.w("NearbyParentFragment: searchRunnable is null") + } + + if (presenter == null) Timber.w("NearbyParentFragment: presenter is null") + if (applicationKvStore == null) Timber.w("NearbyParentFragment: applicationKvStore is null") + + presenter?.removeNearbyPreferences(applicationKvStore ?: return) } - private void initViews() { - Timber.d("init views called"); - initBottomSheets(); - loadAnimations(); - setBottomSheetCallbacks(); - addActionToTitle(); - if (!Utils.isMonumentsEnabled(new Date())) { - NearbyFilterState.setWlmSelected(false); + private fun initViews() { + Timber.d("init views called") + initBottomSheets() + loadAnimations() + setBottomSheetCallbacks() + addActionToTitle() + if (!Utils.isMonumentsEnabled(Date())) { + NearbyFilterState.setWlmSelected(false) } } @@ -786,181 +809,191 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment * hide the list bottom sheet. */ @SuppressLint("ClickableViewAccessibility") - private void initBottomSheets() { - bottomSheetListBehavior = BottomSheetBehavior.from(binding.bottomSheetNearby.bottomSheet); - bottomSheetDetailsBehavior = BottomSheetBehavior.from(binding.bottomSheetDetails.getRoot()); - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - binding.bottomSheetDetails.getRoot().setVisibility(View.VISIBLE); - bottomSheetListBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + private fun initBottomSheets() { + bottomSheetListBehavior = + BottomSheetBehavior.from(binding!!.bottomSheetNearby.bottomSheet) + bottomSheetDetailsBehavior = BottomSheetBehavior.from(binding!!.bottomSheetDetails.root) + bottomSheetDetailsBehavior?.setState(BottomSheetBehavior.STATE_HIDDEN) + binding!!.bottomSheetDetails.root.visibility = View.VISIBLE + bottomSheetListBehavior?.setState(BottomSheetBehavior.STATE_HIDDEN) } - /** - * Determines the number of spans (columns) in the RecyclerView based on device orientation and - * adapter item count. - * - * @return The number of spans to be used in the RecyclerView. - */ - private int getSpanCount() { - int orientation = getResources().getConfiguration().orientation; - if (bottomSheetAdapter != null) { - return (orientation == Configuration.ORIENTATION_PORTRAIT) ? 3 - : bottomSheetAdapter.getItemCount(); - } else { - return (orientation == Configuration.ORIENTATION_PORTRAIT) ? 3 : 6; + private val spanCount: Int + /** + * Determines the number of spans (columns) in the RecyclerView based on device orientation + * and adapter item count. + * + * @return The number of spans to be used in the RecyclerView. + */ + get() { + val orientation = resources.configuration.orientation + return if (bottomSheetAdapter != null) { + if ((orientation == Configuration.ORIENTATION_PORTRAIT)) + 3 + else + bottomSheetAdapter!!.itemCount + } else { + if ((orientation == Configuration.ORIENTATION_PORTRAIT)) 3 else 6 + } } - } - public void initNearbyFilter() { - binding.nearbyFilterList.getRoot().setVisibility(View.GONE); - hideBottomSheet(); - binding.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener( - (v, hasFocus) -> { - LayoutUtils.setLayoutHeightAlignedToWidth(1.25, - binding.nearbyFilterList.getRoot()); - if (hasFocus) { - binding.nearbyFilterList.getRoot().setVisibility(View.VISIBLE); - presenter.searchViewGainedFocus(); - } else { - binding.nearbyFilterList.getRoot().setVisibility(View.GONE); - } - }); - binding.nearbyFilterList.searchListView.setHasFixedSize(true); - binding.nearbyFilterList.searchListView.addItemDecoration( - new DividerItemDecoration(getContext(), - DividerItemDecoration.VERTICAL)); - final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); - linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); - binding.nearbyFilterList.searchListView.setLayoutManager(linearLayoutManager); - nearbyFilterSearchRecyclerViewAdapter = new NearbyFilterSearchRecyclerViewAdapter( - getContext(), new ArrayList<>(Label.valuesAsList()), - binding.nearbyFilterList.searchListView); - nearbyFilterSearchRecyclerViewAdapter.setCallback( - new NearbyFilterSearchRecyclerViewAdapter.Callback() { - @Override - public void setCheckboxUnknown() { - presenter.setCheckboxUnknown(); + fun initNearbyFilter() { + binding!!.nearbyFilterList.root.visibility = View.GONE + hideBottomSheet() + binding!!.nearbyFilter.searchViewLayout.searchView.setOnQueryTextFocusChangeListener { v, hasFocus -> + setLayoutHeightAlignedToWidth( + 1.25, + binding!!.nearbyFilterList.root + ) + if (hasFocus) { + binding!!.nearbyFilterList.root.visibility = View.VISIBLE + presenter!!.searchViewGainedFocus() + } else { + binding!!.nearbyFilterList.root.visibility = View.GONE + } + } + binding!!.nearbyFilterList.searchListView.setHasFixedSize(true) + binding!!.nearbyFilterList.searchListView.addItemDecoration( + DividerItemDecoration( + context, + DividerItemDecoration.VERTICAL + ) + ) + val linearLayoutManager = LinearLayoutManager(activity) + linearLayoutManager.orientation = LinearLayoutManager.VERTICAL + binding!!.nearbyFilterList.searchListView.layoutManager = linearLayoutManager + nearbyFilterSearchRecyclerViewAdapter = NearbyFilterSearchRecyclerViewAdapter( + context, ArrayList(Label.valuesAsList()), + binding!!.nearbyFilterList.searchListView + ) + nearbyFilterSearchRecyclerViewAdapter!!.setCallback( + object : NearbyFilterSearchRecyclerViewAdapter.Callback { + override fun setCheckboxUnknown() { + presenter!!.setCheckboxUnknown() } - @Override - public void filterByMarkerType(final ArrayList