From dd7973cbbf0939e6c1c698688af8f0f1c7459f50 Mon Sep 17 00:00:00 2001 From: Saifuddin Date: Fri, 21 Feb 2025 19:39:32 +0530 Subject: [PATCH 1/3] Rename .java to .kt --- ...ataItemDetailsActivity.java => WikidataItemDetailsActivity.kt} | 0 .../explore/map/{ExploreMapCalls.java => ExploreMapCalls.kt} | 0 .../map/{ExploreMapContract.java => ExploreMapContract.kt} | 0 .../map/{ExploreMapController.java => ExploreMapController.kt} | 0 .../map/{ExploreMapFragment.java => ExploreMapFragment.kt} | 0 .../map/{ExploreMapPresenter.java => ExploreMapPresenter.kt} | 0 6 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/fr/free/nrw/commons/explore/depictions/{WikidataItemDetailsActivity.java => WikidataItemDetailsActivity.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/explore/map/{ExploreMapCalls.java => ExploreMapCalls.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/explore/map/{ExploreMapContract.java => ExploreMapContract.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/explore/map/{ExploreMapController.java => ExploreMapController.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/explore/map/{ExploreMapFragment.java => ExploreMapFragment.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/explore/map/{ExploreMapPresenter.java => ExploreMapPresenter.kt} (100%) diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java rename to app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.java rename to app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.java rename to app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.java rename to app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.java rename to app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.java rename to app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt From a4109eb37c4766b697c9e334fd73e9530e876dd5 Mon Sep 17 00:00:00 2001 From: Saifuddin Date: Fri, 21 Feb 2025 19:39:34 +0530 Subject: [PATCH 2/3] Refactor: Migrate WikidataItemDetailsActivity and explore/maps package to Kotlin The `WikidataItemDetailsActivity` has been migrated from Java to Kotlin. This includes updating the activity's structure, handling intents, setting up tabs and view pager, managing the media detail fragment, and implementing bookmark functionality. Additionally, the logic for displaying and interacting with depicted items' details, media, child classes, and parent classes has been adapted for the Kotlin implementation. --- .../contributions/ContributionsFragment.kt | 6 +- .../explore/ExploreMapRootFragment.java | 8 +- .../depictions/WikidataItemDetailsActivity.kt | 440 +++-- .../commons/explore/map/ExploreMapCalls.kt | 28 +- .../commons/explore/map/ExploreMapContract.kt | 70 +- .../explore/map/ExploreMapController.kt | 340 ++-- .../commons/explore/map/ExploreMapFragment.kt | 1437 ++++++++--------- .../explore/map/ExploreMapPresenter.kt | 282 ++-- .../location/LocationUpdateListener.kt | 6 +- .../NearbyParentFragmentPresenter.kt | 6 +- 10 files changed, 1261 insertions(+), 1362 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt index 0b7736bab..58bc68a52 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt @@ -697,12 +697,12 @@ class ContributionsFragment } } - override fun onLocationChangedSignificantly(latLng: LatLng) { + override fun onLocationChangedSignificantly(latLng: LatLng?) { // Will be called if location changed more than 1000 meter updateClosestNearbyCardViewInfo() } - override fun onLocationChangedSlightly(latLng: LatLng) { + override fun onLocationChangedSlightly(latLng: LatLng?) { /* Update closest nearby notification card onLocationChangedSlightly */ try { @@ -712,7 +712,7 @@ class ContributionsFragment } } - override fun onLocationChangedMedium(latLng: LatLng) { + override fun onLocationChangedMedium(latLng: LatLng?) { // Update closest nearby card view if location changed more than 500 meters updateClosestNearbyCardViewInfo() } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.java index abf02758d..bf37d019f 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreMapRootFragment.java @@ -144,8 +144,8 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme */ @Override public Media getMediaAtPosition(int i) { - if (mapFragment != null && mapFragment.mediaList != null) { - return mapFragment.mediaList.get(i); + if (mapFragment != null && mapFragment.getMediaList() != null) { + return mapFragment.getMediaList().get(i); } else { return null; } @@ -159,8 +159,8 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme */ @Override public int getTotalMediaCount() { - if (mapFragment != null && mapFragment.mediaList != null) { - return mapFragment.mediaList.size(); + if (mapFragment != null && mapFragment.getMediaList() != null) { + return mapFragment.getMediaList().size(); } else { return 0; } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt index cf7269123..109f7e199 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.kt @@ -1,161 +1,159 @@ -package fr.free.nrw.commons.explore.depictions; +package fr.free.nrw.commons.explore.depictions -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.FrameLayout; -import androidx.appcompat.widget.Toolbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.viewpager.widget.ViewPager; -import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.tabs.TabLayout; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; -import fr.free.nrw.commons.ViewPagerAdapter; -import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao; -import fr.free.nrw.commons.category.CategoryImagesCallback; -import fr.free.nrw.commons.databinding.ActivityWikidataItemDetailsBinding; -import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment; -import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment; -import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment; -import fr.free.nrw.commons.media.MediaDetailPagerFragment; -import fr.free.nrw.commons.theme.BaseActivity; -import fr.free.nrw.commons.upload.structure.depictions.DepictModel; -import fr.free.nrw.commons.upload.structure.depictions.DepictedItem; -import fr.free.nrw.commons.wikidata.WikidataConstants; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; -import java.util.ArrayList; -import java.util.List; -import javax.inject.Inject; +import android.content.Context +import android.content.Intent +import android.net.Uri +import android.os.Bundle +import android.view.Menu +import android.view.MenuItem +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import com.google.android.material.snackbar.Snackbar +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.ViewPagerAdapter +import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao +import fr.free.nrw.commons.category.CategoryImagesCallback +import fr.free.nrw.commons.databinding.ActivityWikidataItemDetailsBinding +import fr.free.nrw.commons.explore.depictions.child.ChildDepictionsFragment +import fr.free.nrw.commons.explore.depictions.media.DepictedImagesFragment +import fr.free.nrw.commons.explore.depictions.parent.ParentDepictionsFragment +import fr.free.nrw.commons.media.MediaDetailPagerFragment +import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.upload.structure.depictions.DepictModel +import fr.free.nrw.commons.upload.structure.depictions.DepictedItem +import fr.free.nrw.commons.wikidata.WikidataConstants +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import javax.inject.Inject /** * Activity to show depiction media, parent classes and child classes of depicted items in Explore */ -public class WikidataItemDetailsActivity extends BaseActivity implements MediaDetailPagerFragment.MediaDetailProvider, - CategoryImagesCallback { - private FragmentManager supportFragmentManager; - private DepictedImagesFragment depictionImagesListFragment; - private MediaDetailPagerFragment mediaDetailPagerFragment; +class WikidataItemDetailsActivity : BaseActivity(), + MediaDetailPagerFragment.MediaDetailProvider, CategoryImagesCallback { + + private lateinit var supportFragmentManager: FragmentManager + private lateinit var depictionImagesListFragment: DepictedImagesFragment + private var mediaDetailPagerFragment: MediaDetailPagerFragment? = null /** * Name of the depicted item * Ex: Rabbit */ - - @Inject BookmarkItemsDao bookmarkItemsDao; - private CompositeDisposable compositeDisposable; @Inject - DepictModel depictModel; - private String wikidataItemName; - private ActivityWikidataItemDetailsBinding binding; + lateinit var bookmarkItemsDao: BookmarkItemsDao - ViewPagerAdapter viewPagerAdapter; - private DepictedItem wikidataItem; + @Inject + lateinit var depictModel: DepictModel + private var wikidataItemName: String? = null + private lateinit var binding: ActivityWikidataItemDetailsBinding - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + private lateinit var viewPagerAdapter: ViewPagerAdapter + private var wikidataItem: DepictedItem? = null - binding = ActivityWikidataItemDetailsBinding.inflate(getLayoutInflater()); - setContentView(binding.getRoot()); - compositeDisposable = new CompositeDisposable(); - supportFragmentManager = getSupportFragmentManager(); - viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager()); - binding.viewPager.setAdapter(viewPagerAdapter); - binding.viewPager.setOffscreenPageLimit(2); - binding.tabLayout.setupWithViewPager(binding.viewPager); + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) - final DepictedItem depictedItem = getIntent().getParcelableExtra( - WikidataConstants.BOOKMARKS_ITEMS); - wikidataItem = depictedItem; - setSupportActionBar(binding.toolbarBinding.toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - setTabs(); - setPageTitle(); + binding = ActivityWikidataItemDetailsBinding.inflate(layoutInflater) + setContentView(binding.root) + + supportFragmentManager = getSupportFragmentManager() + viewPagerAdapter = ViewPagerAdapter(supportFragmentManager) + binding.viewPager.adapter = viewPagerAdapter + binding.viewPager.offscreenPageLimit = 2 + binding.tabLayout.setupWithViewPager(binding.viewPager) + + wikidataItem = intent.getParcelableExtra(WikidataConstants.BOOKMARKS_ITEMS) + setSupportActionBar(binding.toolbarBinding.toolbar) + supportActionBar?.setDisplayHomeAsUpEnabled(true) + + setTabs() + setPageTitle() } /** - * Gets the passed wikidataItemName from the intents and displays it as the page title + * Gets the passed wikidataItemName from the intent and displays it as the page title */ - private void setPageTitle() { - if (getIntent() != null && getIntent().getStringExtra("wikidataItemName") != null) { - setTitle(getIntent().getStringExtra("wikidataItemName")); + private fun setPageTitle() { + intent.getStringExtra("wikidataItemName")?.let { + title = it } } /** - * This method is called on success of API call for featured Images. - * The viewpager will notified that number of items have changed. + * This method is called on success of API call for featured images. + * The ViewPager will be notified that the number of items has changed. */ - @Override - public void viewPagerNotifyDataSetChanged() { - if (mediaDetailPagerFragment !=null){ - mediaDetailPagerFragment.notifyDataSetChanged(); - } + override fun viewPagerNotifyDataSetChanged() { + mediaDetailPagerFragment?.notifyDataSetChanged() } /** - * This activity contains 3 tabs and a viewpager. This method is used to set the titles of tab, - * Set the fragments according to the tab selected in the viewPager. + * This activity contains 3 tabs and a ViewPager. + * This method is used to set the titles of tabs and the fragments according to the selected tab */ - private void setTabs() { - List fragmentList = new ArrayList<>(); - List titleList = new ArrayList<>(); - depictionImagesListFragment = new DepictedImagesFragment(); - ChildDepictionsFragment childDepictionsFragment = new ChildDepictionsFragment(); - ParentDepictionsFragment parentDepictionsFragment = new ParentDepictionsFragment(); - wikidataItemName = getIntent().getStringExtra("wikidataItemName"); - String entityId = getIntent().getStringExtra("entityId"); - if (getIntent() != null && wikidataItemName != null) { - Bundle arguments = new Bundle(); - arguments.putString("wikidataItemName", wikidataItemName); - arguments.putString("entityId", entityId); - depictionImagesListFragment.setArguments(arguments); - parentDepictionsFragment.setArguments(arguments); - childDepictionsFragment.setArguments(arguments); + private fun setTabs() { + val fragmentList = mutableListOf() + val titleList = mutableListOf() + + depictionImagesListFragment = DepictedImagesFragment() + val childDepictionsFragment = ChildDepictionsFragment() + val parentDepictionsFragment = ParentDepictionsFragment() + + wikidataItemName = intent.getStringExtra("wikidataItemName") + val entityId = intent.getStringExtra("entityId") + + if (!wikidataItemName.isNullOrEmpty()) { + val arguments = Bundle().apply { + putString("wikidataItemName", wikidataItemName) + putString("entityId", entityId) + } + depictionImagesListFragment.arguments = arguments + parentDepictionsFragment.arguments = arguments + childDepictionsFragment.arguments = arguments } - fragmentList.add(depictionImagesListFragment); - titleList.add(getResources().getString(R.string.title_for_media)); - fragmentList.add(childDepictionsFragment); - titleList.add(getResources().getString(R.string.title_for_child_classes)); - fragmentList.add(parentDepictionsFragment); - titleList.add(getResources().getString(R.string.title_for_parent_classes)); - viewPagerAdapter.setTabData(fragmentList, titleList); - binding.viewPager.setOffscreenPageLimit(2); - viewPagerAdapter.notifyDataSetChanged(); + fragmentList.apply { + add(depictionImagesListFragment) + add(childDepictionsFragment) + add(parentDepictionsFragment) + } + + titleList.apply { + add(getString(R.string.title_for_media)) + add(getString(R.string.title_for_child_classes)) + add(getString(R.string.title_for_parent_classes)) + } + + viewPagerAdapter.setTabData(fragmentList, titleList) + binding.viewPager.offscreenPageLimit = 2 + viewPagerAdapter.notifyDataSetChanged() } - /** * Shows media detail fragment when user clicks on any image in the list */ - @Override - public void onMediaClicked(int position) { - binding.tabLayout.setVisibility(View.GONE); - binding.viewPager.setVisibility(View.GONE); - binding.mediaContainer.setVisibility(View.VISIBLE); - if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) { - // set isFeaturedImage true for featured images, to include author field on media detail - mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true); - FragmentManager supportFragmentManager = getSupportFragmentManager(); - supportFragmentManager - .beginTransaction() - .replace(R.id.mediaContainer, mediaDetailPagerFragment) - .addToBackStack(null) - .commit(); - supportFragmentManager.executePendingTransactions(); + override fun onMediaClicked(position: Int) { + binding.apply { + tabLayout.visibility = View.GONE + viewPager.visibility = View.GONE + mediaContainer.visibility = View.VISIBLE } - mediaDetailPagerFragment.showImage(position); + + if (mediaDetailPagerFragment == null || mediaDetailPagerFragment?.isVisible == false) { + mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true) + supportFragmentManager = getSupportFragmentManager() + supportFragmentManager.beginTransaction() + .replace(R.id.mediaContainer, mediaDetailPagerFragment!!) + .addToBackStack(null) + .commit() + supportFragmentManager.executePendingTransactions() + } + + mediaDetailPagerFragment?.showImage(position) } /** @@ -164,23 +162,23 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe * current index of viewPager. * @return Media Object */ - @Override - public Media getMediaAtPosition(int i) { - return depictionImagesListFragment.getMediaAtPosition(i); + override fun getMediaAtPosition(i: Int): Media? { + return depictionImagesListFragment.getMediaAtPosition(i) } /** * This method is called on backPressed of anyFragment in the activity. * If condition is called when mediaDetailFragment is opened. */ - @Override - public void onBackPressed() { - if (supportFragmentManager.getBackStackEntryCount() == 1){ - binding.tabLayout.setVisibility(View.VISIBLE); - binding.viewPager.setVisibility(View.VISIBLE); - binding.mediaContainer.setVisibility(View.GONE); + override fun onBackPressed() { + if (supportFragmentManager.backStackEntryCount == 1) { + binding.apply { + tabLayout.visibility = View.VISIBLE + viewPager.visibility = View.VISIBLE + mediaContainer.visibility = View.GONE + } } - super.onBackPressed(); + super.onBackPressed() } /** @@ -188,14 +186,12 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe * The viewpager will contain same number of media items as that of media elements in adapter. * @return Total Media count in the adapter */ - @Override - public int getTotalMediaCount() { - return depictionImagesListFragment.getTotalMediaCount(); + override fun getTotalMediaCount(): Int { + return depictionImagesListFragment.getTotalMediaCount() } - @Override - public Integer getContributionStateAt(int position) { - return null; + override fun getContributionStateAt(position: Int): Int? { + return null } /** @@ -203,107 +199,105 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe * * @param index item position that has been nominated */ - @Override - public void refreshNominatedMedia(int index) { - if (getSupportFragmentManager().getBackStackEntryCount() == 1) { - onBackPressed(); - onMediaClicked(index); + override fun refreshNominatedMedia(index: Int) { + if (supportFragmentManager.backStackEntryCount == 1) { + onBackPressed() + onMediaClicked(index) + } + } + + companion object { + /** + * Consumers should be simply using this method to use this activity. + * + * @param context a Context of the application package implementing this class. + * @param depictedItem Name of the depicts for displaying its details + */ + fun startYourself(context: Context, depictedItem: DepictedItem) { + val intent = Intent(context, WikidataItemDetailsActivity::class.java).apply { + putExtra("wikidataItemName", depictedItem.name) + putExtra("entityId", depictedItem.id) + putExtra(WikidataConstants.BOOKMARKS_ITEMS, depictedItem) + } + context.startActivity(intent) } } /** - * Consumers should be simply using this method to use this activity. - * - * @param context A Context of the application package implementing this class. - * @param depictedItem Name of the depicts for displaying its details + * Inflates the menu */ - public static void startYourself(Context context, DepictedItem depictedItem) { - Intent intent = new Intent(context, WikidataItemDetailsActivity.class); - intent.putExtra("wikidataItemName", depictedItem.getName()); - intent.putExtra("entityId", depictedItem.getId()); - intent.putExtra(WikidataConstants.BOOKMARKS_ITEMS, depictedItem); - context.startActivity(intent); - } - - /** - * This function inflates the menu - */ - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater menuInflater=getMenuInflater(); - menuInflater.inflate(R.menu.menu_wikidata_item,menu); - - updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_item)); - - return super.onCreateOptionsMenu(menu); + override fun onCreateOptionsMenu(menu: Menu): Boolean { + menuInflater.inflate(R.menu.menu_wikidata_item, menu) + updateBookmarkState(menu.findItem(R.id.menu_bookmark_current_item)) + return super.onCreateOptionsMenu(menu) } /** * This method handles the logic on item select in toolbar menu * Currently only 1 choice is available to open Wikidata item details page in browser */ - @Override - public boolean onOptionsItemSelected(MenuItem item) { - - switch (item.getItemId()){ - case R.id.browser_actions_menu_items: - String entityId=getIntent().getStringExtra("entityId"); - Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId); - Utils.handleWebUrl(this, uri); - return true; - case R.id.menu_bookmark_current_item: - - if(getIntent().getStringExtra("fragment") != null) { - compositeDisposable.add(depictModel.getDepictions( - getIntent().getStringExtra("entityId") - ).subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(depictedItems -> { - final boolean bookmarkExists = bookmarkItemsDao.updateBookmarkItem( - depictedItems.get(0)); - final Snackbar snackbar - = bookmarkExists ? Snackbar.make(findViewById(R.id.toolbar_layout), - R.string.add_bookmark, Snackbar.LENGTH_LONG) - : Snackbar.make(findViewById(R.id.toolbar_layout), - R.string.remove_bookmark, - Snackbar.LENGTH_LONG); - - snackbar.show(); - updateBookmarkState(item); - })); + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.browser_actions_menu_items -> { + val entityId = intent.getStringExtra("entityId") + val uri = Uri.parse("https://www.wikidata.org/wiki/$entityId") + Utils.handleWebUrl(this, uri) + return true + } + R.id.menu_bookmark_current_item -> { + val entityId = intent.getStringExtra("entityId") + if (intent.getStringExtra("fragment") != null) { + compositeDisposable.add( + depictModel.getDepictions(entityId!!) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { depictedItems -> + val bookmarkExists = bookmarkItemsDao + .updateBookmarkItem(depictedItems[0]) + val snackbarText = if (bookmarkExists) + R.string.add_bookmark + else + R.string.remove_bookmark + Snackbar.make( + findViewById(R.id.toolbar_layout), + snackbarText, + Snackbar.LENGTH_LONG + ).show() + updateBookmarkState(item) + } + ) } else { - final boolean bookmarkExists - = bookmarkItemsDao.updateBookmarkItem(wikidataItem); - final Snackbar snackbar - = bookmarkExists ? Snackbar.make(findViewById(R.id.toolbar_layout), - R.string.add_bookmark, Snackbar.LENGTH_LONG) - : Snackbar.make(findViewById(R.id.toolbar_layout), R.string.remove_bookmark, - Snackbar.LENGTH_LONG); - - snackbar.show(); - updateBookmarkState(item); + val bookmarkExists = bookmarkItemsDao.updateBookmarkItem(wikidataItem!!) + val snackbarText = if (bookmarkExists) + R.string.add_bookmark + else + R.string.remove_bookmark + Snackbar.make( + findViewById(R.id.toolbar_layout), + snackbarText, + Snackbar.LENGTH_LONG + ).show() + updateBookmarkState(item) } - return true; - case android.R.id.home: - onBackPressed(); - return true; - default: - return super.onOptionsItemSelected(item); + return true + } + android.R.id.home -> { + onBackPressed() + return true + } + else -> return super.onOptionsItemSelected(item) } } - private void updateBookmarkState(final MenuItem item) { - final boolean isBookmarked; - if(getIntent().getStringExtra("fragment") != null) { - isBookmarked - = bookmarkItemsDao.findBookmarkItem(getIntent().getStringExtra("entityId")); - } else { - isBookmarked = bookmarkItemsDao.findBookmarkItem(wikidataItem.getId()); - } - final int icon - = isBookmarked ? R.drawable.menu_ic_round_star_filled_24px - : R.drawable.menu_ic_round_star_border_24px; - item.setIcon(icon); + private fun updateBookmarkState(item: MenuItem) { + val isBookmarked = bookmarkItemsDao.findBookmarkItem( + intent.getStringExtra("entityId") ?: wikidataItem?.id + ) + val icon = if (isBookmarked) + R.drawable.menu_ic_round_star_filled_24px + else + R.drawable.menu_ic_round_star_border_24px + item.setIcon(icon) } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt index ec426942c..0738f1899 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapCalls.kt @@ -1,21 +1,16 @@ -package fr.free.nrw.commons.explore.map; +package fr.free.nrw.commons.explore.map -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.media.MediaClient; -import java.util.List; -import javax.inject.Inject; -import javax.inject.Singleton; +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.media.MediaClient +import javax.inject.Inject +import javax.inject.Singleton @Singleton -public class ExploreMapCalls { +class ExploreMapCalls @Inject constructor() { @Inject - MediaClient mediaClient; - - @Inject - public ExploreMapCalls() { - } + lateinit var mediaClient: MediaClient /** * Calls method to query Commons for uploads around a location @@ -23,9 +18,8 @@ public class ExploreMapCalls { * @param currentLatLng coordinates of search location * @return list of places obtained */ - List callCommonsQuery(final LatLng currentLatLng) { - String coordinates = currentLatLng.getLatitude() + "|" + currentLatLng.getLongitude(); - return mediaClient.getMediaListFromGeoSearch(coordinates).blockingGet(); + fun callCommonsQuery(currentLatLng: LatLng): List { + val coordinates = "${currentLatLng.latitude}|${currentLatLng.longitude}" + return mediaClient.getMediaListFromGeoSearch(coordinates).blockingGet() } - } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt index feb66bf55..256e621e9 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapContract.kt @@ -1,45 +1,43 @@ -package fr.free.nrw.commons.explore.map; +package fr.free.nrw.commons.explore.map -import android.content.Context; -import fr.free.nrw.commons.BaseMarker; -import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.location.LocationServiceManager; -import java.util.List; +import android.content.Context +import fr.free.nrw.commons.BaseMarker +import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.location.LocationServiceManager -public class ExploreMapContract { +interface ExploreMapContract { interface View { - boolean isNetworkConnectionEstablished(); - void populatePlaces(LatLng curlatLng); - void askForLocationPermission(); - void recenterMap(LatLng curLatLng); - void hideBottomDetailsSheet(); - LatLng getMapCenter(); - LatLng getMapFocus(); - LatLng getLastMapFocus(); - void addMarkersToMap(final List nearbyBaseMarkers); - void clearAllMarkers(); - void addSearchThisAreaButtonAction(); - void setSearchThisAreaButtonVisibility(boolean isVisible); - void setProgressBarVisibility(boolean isVisible); - boolean isDetailsBottomSheetVisible(); - boolean isSearchThisAreaButtonVisible(); - Context getContext(); - LatLng getLastLocation(); - void disableFABRecenter(); - void enableFABRecenter(); - void setFABRecenterAction(android.view.View.OnClickListener onClickListener); - boolean backButtonClicked(); + fun isNetworkConnectionEstablished(): Boolean + fun populatePlaces(curLatLng: LatLng?) + fun askForLocationPermission() + fun recenterMap(curLatLng: LatLng?) + fun hideBottomDetailsSheet() + fun getMapCenter(): LatLng + fun getMapFocus(): LatLng + fun getLastMapFocus(): LatLng + fun addMarkersToMap(nearbyBaseMarkers: List) + fun clearAllMarkers() + fun addSearchThisAreaButtonAction() + fun setSearchThisAreaButtonVisibility(isVisible: Boolean) + fun setProgressBarVisibility(isVisible: Boolean) + fun isDetailsBottomSheetVisible(): Boolean + fun isSearchThisAreaButtonVisible(): Boolean + fun getContext(): Context + fun getLastLocation(): LatLng + fun disableFABRecenter() + fun enableFABRecenter() + fun setFABRecenterAction(onClickListener: android.view.View.OnClickListener) + fun backButtonClicked(): Boolean } interface UserActions { - void updateMap(LocationServiceManager.LocationChangeType locationChangeType); - void lockUnlockNearby(boolean isNearbyLocked); - void attachView(View view); - void detachView(); - void setActionListeners(JsonKvStore applicationKvStore); - boolean backButtonClicked(); + fun updateMap(locationChangeType: LocationServiceManager.LocationChangeType) + fun lockUnlockNearby(isNearbyLocked: Boolean) + fun attachView(view: View) + fun detachView() + fun setActionListeners(applicationKvStore: JsonKvStore) + fun backButtonClicked(): Boolean } - } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt index c944f75a1..391d9d5c5 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapController.kt @@ -1,126 +1,120 @@ -package fr.free.nrw.commons.explore.map; +package fr.free.nrw.commons.explore.map -import static fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween; -import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; +import android.content.Context +import android.content.res.Resources +import android.graphics.Bitmap +import android.graphics.drawable.Drawable +import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.request.RequestOptions +import com.bumptech.glide.request.target.CustomTarget +import com.bumptech.glide.request.transition.Transition +import fr.free.nrw.commons.BaseMarker +import fr.free.nrw.commons.MapController +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.utils.ImageUtils +import fr.free.nrw.commons.utils.LengthUtils.computeDistanceBetween +import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween +import fr.free.nrw.commons.utils.LocationUtils +import fr.free.nrw.commons.utils.PlaceUtils +import timber.log.Timber +import javax.inject.Inject -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.bumptech.glide.request.target.CustomTarget; -import com.bumptech.glide.request.transition.Transition; -import fr.free.nrw.commons.BaseMarker; -import fr.free.nrw.commons.MapController; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.nearby.Place; -import fr.free.nrw.commons.utils.ImageUtils; -import fr.free.nrw.commons.utils.LocationUtils; -import fr.free.nrw.commons.utils.PlaceUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import timber.log.Timber; +class ExploreMapController @Inject constructor( + private val exploreMapCalls: ExploreMapCalls +) : MapController() { -public class ExploreMapController extends MapController { - - private final ExploreMapCalls exploreMapCalls; - public LatLng latestSearchLocation; // Can be current and camera target on search this area button is used - public LatLng currentLocation; // current location of user - public double latestSearchRadius = 0; // Any last search radius - public double currentLocationSearchRadius = 0; // Search radius of only searches around current location - - - @Inject - public ExploreMapController(ExploreMapCalls explorePlaces) { - this.exploreMapCalls = explorePlaces; - } + var latestSearchLocation: LatLng? = null // Can be current and camera target when search this + // area button is used + var currentLocation: LatLng? = null // Current location of user + var latestSearchRadius: Double = 0.0 // Any last search radius + var currentLocationSearchRadius: Double = 0.0 // Search radius of only searches around current + // location /** - * Takes location as parameter and returns ExplorePlaces info that holds currentLatLng, mediaList, - * explorePlaceList and boundaryCoordinates + * Takes location as parameter and returns ExplorePlaces info that holds currentLatLng, + * mediaList, explorePlaceList and boundaryCoordinates * - * @param currentLatLng is current geolocation - * @param searchLatLng is the location that we want to search around + * @param currentLatLng is current geolocation + * @param searchLatLng is the location that we want to search around * @param checkingAroundCurrentLocation is a boolean flag. True if we want to check around * current location, false if another location * @return explorePlacesInfo info that holds currentLatLng, mediaList, explorePlaceList and * boundaryCoordinates */ - public ExplorePlacesInfo loadAttractionsFromLocation(LatLng currentLatLng, LatLng searchLatLng, - boolean checkingAroundCurrentLocation) { + fun loadAttractionsFromLocation( + currentLatLng: LatLng, + searchLatLng: LatLng?, + checkingAroundCurrentLocation: Boolean + ): ExplorePlacesInfo? { if (searchLatLng == null) { - Timber.d("Loading attractions explore map, but search is null"); - return null; + Timber.d("Loading attractions explore map, but search is null") + return null } - ExplorePlacesInfo explorePlacesInfo = new ExplorePlacesInfo(); + val explorePlacesInfo = ExplorePlacesInfo() try { - explorePlacesInfo.currentLatLng = currentLatLng; - latestSearchLocation = searchLatLng; + explorePlacesInfo.currentLatLng = currentLatLng + latestSearchLocation = searchLatLng - List mediaList = exploreMapCalls.callCommonsQuery(searchLatLng); - LatLng[] boundaryCoordinates = {mediaList.get(0).getCoordinates(), // south - mediaList.get(0).getCoordinates(), // north - mediaList.get(0).getCoordinates(), // west - mediaList.get(0).getCoordinates()};// east, init with a random location + val mediaList = exploreMapCalls.callCommonsQuery(searchLatLng) + val boundaryCoordinates = arrayOf( + mediaList[0].coordinates, // south + mediaList[0].coordinates, // north + mediaList[0].coordinates, // west + mediaList[0].coordinates // east, init with a random location + ) - if (searchLatLng != null) { - Timber.d("Sorting places by distance..."); - final Map distances = new HashMap<>(); - for (Media media : mediaList) { - distances.put(media, - computeDistanceBetween(media.getCoordinates(), searchLatLng)); - // Find boundaries with basic find max approach - if (media.getCoordinates().getLatitude() - < boundaryCoordinates[0].getLatitude()) { - boundaryCoordinates[0] = media.getCoordinates(); - } - if (media.getCoordinates().getLatitude() - > boundaryCoordinates[1].getLatitude()) { - boundaryCoordinates[1] = media.getCoordinates(); - } - if (media.getCoordinates().getLongitude() - < boundaryCoordinates[2].getLongitude()) { - boundaryCoordinates[2] = media.getCoordinates(); - } - if (media.getCoordinates().getLongitude() - > boundaryCoordinates[3].getLongitude()) { - boundaryCoordinates[3] = media.getCoordinates(); - } + Timber.d("Sorting places by distance...") + val distances = mutableMapOf() + + for (media in mediaList) { + distances[media] = computeDistanceBetween(media.coordinates!!, searchLatLng) + + // Find boundaries with basic find max approach + if (media.coordinates!!.latitude < boundaryCoordinates[0]?.latitude!!) { + boundaryCoordinates[0] = media.coordinates + } + if (media.coordinates!!.latitude > boundaryCoordinates[1]?.latitude!!) { + boundaryCoordinates[1] = media.coordinates + } + if (media.coordinates!!.longitude < boundaryCoordinates[2]?.longitude!!) { + boundaryCoordinates[2] = media.coordinates + } + if (media.coordinates!!.longitude > boundaryCoordinates[3]?.longitude!!) { + boundaryCoordinates[3] = media.coordinates } } - explorePlacesInfo.mediaList = mediaList; - explorePlacesInfo.explorePlaceList = PlaceUtils.mediaToExplorePlace(mediaList); - explorePlacesInfo.boundaryCoordinates = boundaryCoordinates; + + explorePlacesInfo.mediaList = mediaList + explorePlacesInfo.explorePlaceList = PlaceUtils.mediaToExplorePlace(mediaList) + explorePlacesInfo.boundaryCoordinates = boundaryCoordinates // Sets latestSearchRadius to maximum distance among boundaries and search location - for (LatLng bound : boundaryCoordinates) { - double distance = LocationUtils.calculateDistance(bound.getLatitude(), - bound.getLongitude(), searchLatLng.getLatitude(), searchLatLng.getLongitude()); + for (bound in boundaryCoordinates) { + val distance = LocationUtils.calculateDistance( + bound?.latitude!!, bound.longitude, + searchLatLng.latitude, searchLatLng.longitude + ) if (distance > latestSearchRadius) { - latestSearchRadius = distance; + latestSearchRadius = distance } } - // Our radius searched around us, will be used to understand when user search their own location, we will follow them + // Our radius searched around us, will be used to understand when user search + // their own location, we will follow them if (checkingAroundCurrentLocation) { - currentLocationSearchRadius = latestSearchRadius; - currentLocation = currentLatLng; + currentLocationSearchRadius = latestSearchRadius + currentLocation = currentLatLng } - } catch (Exception e) { - e.printStackTrace(); + } catch (e: Exception) { + e.printStackTrace() } - return explorePlacesInfo; + return explorePlacesInfo } /** @@ -128,86 +122,96 @@ public class ExploreMapController extends MapController { * * @return baseMarkerOptions list that holds nearby places with their icons */ - public static List loadAttractionsFromLocationToBaseMarkerOptions( - LatLng currentLatLng, - final List placeList, - Context context, - NearbyBaseMarkerThumbCallback callback, - ExplorePlacesInfo explorePlacesInfo) { - List baseMarkerList = new ArrayList<>(); + companion object { + fun loadAttractionsFromLocationToBaseMarkerOptions( + currentLatLng: LatLng, + placeList: List?, + context: Context, + callback: NearbyBaseMarkerThumbCallback, + explorePlacesInfo: ExplorePlacesInfo + ): List { + val baseMarkerList = mutableListOf() - if (placeList == null) { - return baseMarkerList; - } - - VectorDrawableCompat vectorDrawable = null; - try { - vectorDrawable = VectorDrawableCompat.create( - context.getResources(), R.drawable.ic_custom_map_marker_dark, context.getTheme()); - - } catch (Resources.NotFoundException e) { - // ignore when running tests. - } - if (vectorDrawable != null) { - for (Place explorePlace : placeList) { - final BaseMarker baseMarker = new BaseMarker(); - String distance = formatDistanceBetween(currentLatLng, explorePlace.location); - explorePlace.setDistance(distance); - - baseMarker.setTitle( - explorePlace.name.substring(5, explorePlace.name.lastIndexOf("."))); - baseMarker.setPosition( - new fr.free.nrw.commons.location.LatLng( - explorePlace.location.getLatitude(), - explorePlace.location.getLongitude(), 0)); - baseMarker.setPlace(explorePlace); - - Glide.with(context) - .asBitmap() - .load(explorePlace.getThumb()) - .placeholder(R.drawable.image_placeholder_96) - .apply(new RequestOptions().override(96, 96).centerCrop()) - .into(new CustomTarget() { - // We add icons to markers when bitmaps are ready - @Override - public void onResourceReady(@NonNull Bitmap resource, - @Nullable Transition transition) { - baseMarker.setIcon( - ImageUtils.addRedBorder(resource, 6, context)); - baseMarkerList.add(baseMarker); - if (baseMarkerList.size() - == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerList, - explorePlacesInfo); - } - } - - @Override - public void onLoadCleared(@Nullable Drawable placeholder) { - } - - // We add thumbnail icon for images that couldn't be loaded - @Override - public void onLoadFailed(@Nullable final Drawable errorDrawable) { - super.onLoadFailed(errorDrawable); - baseMarker.fromResource(context, R.drawable.image_placeholder_96); - baseMarkerList.add(baseMarker); - if (baseMarkerList.size() - == placeList.size()) { // if true, we added all markers to list and can trigger thumbs ready callback - callback.onNearbyBaseMarkerThumbsReady(baseMarkerList, - explorePlacesInfo); - } - } - }); + if (placeList == null) { + return baseMarkerList } + + var vectorDrawable: VectorDrawableCompat? = null + try { + vectorDrawable = VectorDrawableCompat.create( + context.resources, R.drawable.ic_custom_map_marker_dark, context.theme + ) + } catch (e: Resources.NotFoundException) { + // Ignore when running tests + } + + vectorDrawable?.let { + for (explorePlace in placeList) { + val baseMarker = BaseMarker() + val distance = formatDistanceBetween(currentLatLng, explorePlace.location) + explorePlace.distance = distance + + baseMarker.title = explorePlace.name.substring( + 5, + explorePlace.name.lastIndexOf(".") + ) + baseMarker.position = LatLng( + explorePlace.location.latitude, + explorePlace.location.longitude, 0.0f + ) + baseMarker.place = explorePlace + + Glide.with(context) + .asBitmap() + .load(explorePlace.thumb) + .placeholder(R.drawable.image_placeholder_96) + .apply(RequestOptions().override(96, 96).centerCrop()) + .into(object : CustomTarget() { + // We add icons to markers when bitmaps are ready + override fun onResourceReady( + resource: Bitmap, + transition: Transition? + ) { + baseMarker.icon = ImageUtils.addRedBorder(resource, 6, context) + baseMarkerList.add(baseMarker) + if (baseMarkerList.size == placeList.size) { + // If true, we added all markers to list and can trigger thumbs + // ready callback + callback.onNearbyBaseMarkerThumbsReady( + baseMarkerList, + explorePlacesInfo + ) + } + } + + override fun onLoadCleared(placeholder: Drawable?) {} + + // We add thumbnail icon for images that couldn't be loaded + override fun onLoadFailed(errorDrawable: Drawable?) { + super.onLoadFailed(errorDrawable) + baseMarker.fromResource(context, R.drawable.image_placeholder_96) + baseMarkerList.add(baseMarker) + if (baseMarkerList.size == placeList.size) { + // If true, we added all markers to list and can trigger thumbs + // ready callback + callback.onNearbyBaseMarkerThumbsReady( + baseMarkerList, + explorePlacesInfo + ) + } + } + }) + } + } + return baseMarkerList } - return baseMarkerList; } interface NearbyBaseMarkerThumbCallback { - // Callback to notify thumbnails of explore markers are added as icons and ready - void onNearbyBaseMarkerThumbsReady(List baseMarkers, - ExplorePlacesInfo explorePlacesInfo); + fun onNearbyBaseMarkerThumbsReady( + baseMarkers: List, + explorePlacesInfo: ExplorePlacesInfo + ) } } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt index 1b1659182..87f768cee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapFragment.kt @@ -1,494 +1,478 @@ -package fr.free.nrw.commons.explore.map; +package fr.free.nrw.commons.explore.map -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.utils.MapUtils.ZOOM_LEVEL; +import android.Manifest +import android.Manifest.permission +import android.annotation.SuppressLint +import android.content.BroadcastReceiver +import android.content.Context +import android.content.Intent +import android.content.IntentFilter +import android.graphics.Color +import android.graphics.Paint +import android.graphics.drawable.BitmapDrawable +import android.location.Location +import android.location.LocationManager +import android.os.Build +import android.os.Bundle +import android.preference.PreferenceManager +import android.text.Html +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.result.contract.ActivityResultContracts +import androidx.core.content.ContextCompat +import com.google.android.material.bottomsheet.BottomSheetBehavior +import com.google.android.material.snackbar.Snackbar +import fr.free.nrw.commons.BaseMarker +import fr.free.nrw.commons.MapController +import fr.free.nrw.commons.Media +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.MainActivity +import fr.free.nrw.commons.databinding.FragmentExploreMapBinding +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.explore.ExploreMapRootFragment +import fr.free.nrw.commons.explore.paging.LiveDataConverter +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.LocationUpdateListener +import fr.free.nrw.commons.media.MediaClient +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.utils.DialogUtil +import fr.free.nrw.commons.utils.MapUtils +import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL +import fr.free.nrw.commons.utils.NetworkUtils +import fr.free.nrw.commons.utils.SystemThemeUtils +import fr.free.nrw.commons.utils.ViewUtil +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.schedulers.Schedulers +import java.util.ArrayList -import android.Manifest.permission; -import android.annotation.SuppressLint; -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.location.Location; -import android.location.LocationManager; -import android.os.Build; -import android.os.Bundle; -import android.preference.PreferenceManager; -import android.text.Html; -import android.view.LayoutInflater; -import android.view.View; -import android.view.View.OnClickListener; -import android.view.ViewGroup; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import com.google.android.material.bottomsheet.BottomSheetBehavior; -import com.google.android.material.snackbar.Snackbar; -import fr.free.nrw.commons.BaseMarker; -import fr.free.nrw.commons.MapController; -import fr.free.nrw.commons.Media; -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.MainActivity; -import fr.free.nrw.commons.databinding.FragmentExploreMapBinding; -import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; -import fr.free.nrw.commons.explore.ExploreMapRootFragment; -import fr.free.nrw.commons.explore.paging.LiveDataConverter; -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.LocationUpdateListener; -import fr.free.nrw.commons.media.MediaClient; -import fr.free.nrw.commons.nearby.Place; -import fr.free.nrw.commons.utils.DialogUtil; -import fr.free.nrw.commons.utils.MapUtils; -import fr.free.nrw.commons.utils.NetworkUtils; -import fr.free.nrw.commons.utils.SystemThemeUtils; -import fr.free.nrw.commons.utils.ViewUtil; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.schedulers.Schedulers; -import java.util.ArrayList; -import java.util.List; -import javax.inject.Inject; -import javax.inject.Named; -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; -import org.osmdroid.views.CustomZoomButtonsController; -import org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener; -import org.osmdroid.views.overlay.ItemizedOverlayWithFocus; -import org.osmdroid.views.overlay.MapEventsOverlay; -import org.osmdroid.views.overlay.Overlay; -import org.osmdroid.views.overlay.OverlayItem; -import org.osmdroid.views.overlay.ScaleBarOverlay; -import org.osmdroid.views.overlay.ScaleDiskOverlay; -import org.osmdroid.views.overlay.TilesOverlay; -import timber.log.Timber; +import javax.inject.Inject +import javax.inject.Named +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 +import org.osmdroid.views.CustomZoomButtonsController +import org.osmdroid.views.overlay.ItemizedIconOverlay.OnItemGestureListener +import org.osmdroid.views.overlay.ItemizedOverlayWithFocus +import org.osmdroid.views.overlay.MapEventsOverlay +import org.osmdroid.views.overlay.Marker +import org.osmdroid.views.overlay.OverlayItem +import org.osmdroid.views.overlay.ScaleBarOverlay +import org.osmdroid.views.overlay.ScaleDiskOverlay +import org.osmdroid.views.overlay.TilesOverlay +import timber.log.Timber -public class ExploreMapFragment extends CommonsDaggerSupportFragment - implements ExploreMapContract.View, LocationUpdateListener, LocationPermissionCallback { +class ExploreMapFragment : CommonsDaggerSupportFragment(), + ExploreMapContract.View, LocationUpdateListener, LocationPermissionCallback { - private BottomSheetBehavior bottomSheetDetailsBehavior; - private BroadcastReceiver broadcastReceiver; - private boolean isNetworkErrorOccurred; - private Snackbar snackbar; - private boolean isDarkTheme; - private boolean isPermissionDenied; - private fr.free.nrw.commons.location.LatLng lastKnownLocation; // last location of user - private fr.free.nrw.commons.location.LatLng lastFocusLocation; // last location that map is focused - public List mediaList; - private boolean recenterToUserLocation; // true is recenter is needed (ie. when current location is in visible map boundaries) - private BaseMarker clickedMarker; - private GeoPoint mapCenter; - private GeoPoint lastMapFocus; - IntentFilter intentFilter = new IntentFilter(MapUtils.NETWORK_INTENT_ACTION); + private lateinit var bottomSheetDetailsBehavior: BottomSheetBehavior<*> + private var broadcastReceiver: BroadcastReceiver? = null + private var isNetworkErrorOccurred = false + private var snackbar: Snackbar? = null + private var isDarkTheme = false + private var isPermissionDenied = false + private var lastKnownLocation: fr.free.nrw.commons.location.LatLng? = null // last location of user + private var lastFocusLocation: fr.free.nrw.commons.location.LatLng? = null // last focused location of the map + var mediaList: List? = null + private var recenterToUserLocation = false // true if recentering is needed + private var clickedMarker: BaseMarker? = null + private var mapCenter: GeoPoint? = null + private var lastMapFocus: GeoPoint? = null + private val intentFilter = IntentFilter(MapUtils.NETWORK_INTENT_ACTION) @Inject - LiveDataConverter liveDataConverter; + lateinit var liveDataConverter: LiveDataConverter + @Inject - MediaClient mediaClient; + lateinit var mediaClient: MediaClient + @Inject - LocationServiceManager locationManager; + lateinit var locationManager: LocationServiceManager + @Inject - ExploreMapController exploreMapController; + lateinit var exploreMapController: ExploreMapController + @Inject @Named("default_preferences") - JsonKvStore applicationKvStore; + lateinit var applicationKvStore: JsonKvStore + @Inject - BookmarkLocationsDao bookmarkLocationDao; // May be needed in future if we want to integrate bookmarking explore places + lateinit var bookmarkLocationDao: BookmarkLocationsDao // Future use for bookmarking explore places + @Inject - SystemThemeUtils systemThemeUtils; - LocationPermissionsHelper locationPermissionsHelper; + lateinit var systemThemeUtils: SystemThemeUtils + + private lateinit var locationPermissionsHelper: LocationPermissionsHelper // Nearby map state (if we came from Nearby) - 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 ExploreMapPresenter presenter; + private var presenter: ExploreMapPresenter? = null - public FragmentExploreMapBinding binding; + private lateinit var binding: FragmentExploreMapBinding - private ActivityResultLauncher activityResultLauncher = registerForActivityResult( - new ActivityResultContracts.RequestPermission(), isGranted -> { + private val 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_explore), - getActivity().getString(android.R.string.ok), - getActivity().getString(android.R.string.cancel), - () -> { - askForLocationPermission(); - }, - null, - null - ); + if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION)) { + activity?.let { + DialogUtil.showAlertDialog( + it, + getString(R.string.location_permission_title), + getString(R.string.location_permission_rationale_explore), + getString(android.R.string.ok), + getString(android.R.string.cancel), + { + askForLocationPermission() + }, + null, + null + ) + } } else { if (isPermissionDenied) { - locationPermissionsHelper.showAppSettingsDialog(getActivity(), - R.string.explore_map_needs_location); + locationPermissionsHelper.showAppSettingsDialog( + requireActivity(), + R.string.explore_map_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 } } - }); + } - @NonNull - public static ExploreMapFragment newInstance() { - ExploreMapFragment fragment = new ExploreMapFragment(); - fragment.setRetainInstance(true); - return fragment; + companion object { + @JvmStatic + fun newInstance(): ExploreMapFragment { + return ExploreMapFragment().apply { retainInstance = true } + } } - @Override - public View onCreateView( - @NonNull LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState - ) { - loadNearbyMapData(); - binding = FragmentExploreMapBinding.inflate(getLayoutInflater()); - return binding.getRoot(); + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + loadNearbyMapData() + binding = FragmentExploreMapBinding.inflate(inflater, container, false) + return binding.root } - @Override - public void onViewCreated(@NonNull final View view, @Nullable final Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - setSearchThisAreaButtonVisibility(false); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - binding.tvAttribution.setText( - Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + setSearchThisAreaButtonVisibility(false) + + binding.tvAttribution.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY) } else { - binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); + Html.fromHtml(getString(R.string.map_attribution)) } - initNetworkBroadCastReceiver(); - locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager, - this); + + initNetworkBroadCastReceiver() + locationPermissionsHelper = LocationPermissionsHelper( + requireActivity(), + locationManager, + this + ) + if (presenter == null) { - presenter = new ExploreMapPresenter(bookmarkLocationDao); + presenter = ExploreMapPresenter(bookmarkLocationDao) } - setHasOptionsMenu(true); + setHasOptionsMenu(true) - isDarkTheme = systemThemeUtils.isDeviceInNightMode(); - isPermissionDenied = false; - presenter.attachView(this); + isDarkTheme = systemThemeUtils.isDeviceInNightMode() + isPermissionDenied = false + presenter?.attachView(this) - initViews(); - presenter.setActionListeners(applicationKvStore); + initViews() + presenter?.setActionListeners(applicationKvStore) - org.osmdroid.config.Configuration.getInstance().load(this.getContext(), - PreferenceManager.getDefaultSharedPreferences(this.getContext())); + org.osmdroid.config.Configuration.getInstance().load( + requireContext(), + PreferenceManager.getDefaultSharedPreferences(requireContext()) + ) - binding.mapView.setTileSource(TileSourceFactory.WIKIMEDIA); - binding.mapView.setTilesScaledToDpi(true); + binding.mapView.apply { + setTileSource(TileSourceFactory.WIKIMEDIA) + setTilesScaledToDpi(true) + org.osmdroid.config.Configuration.getInstance() + .additionalHttpRequestProperties["Referer"] = "http://maps.wikimedia.org/" - org.osmdroid.config.Configuration.getInstance().getAdditionalHttpRequestProperties().put( - "Referer", "http://maps.wikimedia.org/" - ); - - ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(binding.mapView); - scaleBarOverlay.setScaleBarOffset(15, 25); - Paint barPaint = new Paint(); - barPaint.setARGB(200, 255, 250, 250); - scaleBarOverlay.setBackgroundPaint(barPaint); - scaleBarOverlay.enableScaleBar(); - binding.mapView.getOverlays().add(scaleBarOverlay); - binding.mapView.getZoomController() - .setVisibility(CustomZoomButtonsController.Visibility.NEVER); - binding.mapView.setMultiTouchControls(true); - - if (!isCameFromNearbyMap()) { - binding.mapView.getController().setZoom(ZOOM_LEVEL); - } - - performMapReadyActions(); - - binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { - @Override - public boolean singleTapConfirmedHelper(GeoPoint p) { - if (clickedMarker != null) { - removeMarker(clickedMarker); - addMarkerToMap(clickedMarker); - binding.mapView.invalidate(); - } else { - Timber.e("CLICKED MARKER IS NULL"); - } - if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_EXPANDED) { - // Back should first hide the bottom sheet if it is expanded - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - } else if (isDetailsBottomSheetVisible()) { - hideBottomDetailsSheet(); - } - return true; - } - - @Override - public boolean longPressHelper(GeoPoint p) { - return false; - } - })); - - binding.mapView.addMapListener(new MapListener() { - @Override - public boolean onScroll(ScrollEvent event) { - if (getLastMapFocus() != null) { - Location mylocation = new Location(""); - Location dest_location = new Location(""); - dest_location.setLatitude(binding.mapView.getMapCenter().getLatitude()); - dest_location.setLongitude(binding.mapView.getMapCenter().getLongitude()); - mylocation.setLatitude(getLastMapFocus().getLatitude()); - mylocation.setLongitude(getLastMapFocus().getLongitude()); - Float distance = mylocation.distanceTo(dest_location);//in meters - if (getLastMapFocus() != null) { - if (isNetworkConnectionEstablished() && (event.getX() > 0 - || event.getY() > 0)) { - if (distance > 2000.0) { - setSearchThisAreaButtonVisibility(true); - } else { - setSearchThisAreaButtonVisibility(false); - } - } - } else { - setSearchThisAreaButtonVisibility(false); + val scaleBarOverlay = ScaleBarOverlay(this).apply { + setScaleBarOffset(15, 25) + setBackgroundPaint( + Paint().apply { + setARGB(200, 255, 250, 250) } + ) + enableScaleBar() + } + overlays.add(scaleBarOverlay) + + zoomController.setVisibility(CustomZoomButtonsController.Visibility.NEVER) + setMultiTouchControls(true) + + if (!isCameFromNearbyMap()) { + controller.setZoom(ZOOM_LEVEL.toDouble()) + } + } + + performMapReadyActions() + + binding.mapView.overlays.add( + MapEventsOverlay(object : MapEventsReceiver { + override fun singleTapConfirmedHelper(p: GeoPoint?): Boolean { + clickedMarker?.let { + removeMarker(it) + addMarkerToMap(it) + binding.mapView.invalidate() + } ?: Timber.e("CLICKED MARKER IS NULL") + + if (bottomSheetDetailsBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN + } else if (isDetailsBottomSheetVisible()) { + hideBottomDetailsSheet() + } + return true } - return true; + override fun longPressHelper(p: GeoPoint?): Boolean = false + }) + ) + + + binding.mapView.addMapListener(object : MapListener { + override fun onScroll(event: ScrollEvent?): Boolean { + lastMapFocus?.let { + val myLocation = Location("").apply { + latitude = it.latitude + longitude = it.longitude + } + + val destLocation = Location("").apply { + latitude = binding.mapView.mapCenter.latitude + longitude = binding.mapView.mapCenter.longitude + } + + val distance = myLocation.distanceTo(destLocation) + + if ( + isNetworkConnectionEstablished() + && + (event?.x!! > 0 || event.y > 0) + ) { + setSearchThisAreaButtonVisibility(distance > 2000.0) + } + } ?: setSearchThisAreaButtonVisibility(false) + + return true } - @Override - public boolean onZoom(ZoomEvent event) { - return false; - } + override fun onZoom(event: ZoomEvent?): Boolean = false + }) - }); - if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { - askForLocationPermission(); + if (!locationPermissionsHelper.checkLocationPermission(requireActivity())) { + askForLocationPermission() } } - @Override - public void onResume() { - super.onResume(); - binding.mapView.onResume(); - presenter.attachView(this); - registerNetworkReceiver(); - if (isResumed()) { - if (locationPermissionsHelper.checkLocationPermission(getActivity())) { - performMapReadyActions(); + override fun onResume() { + super.onResume() + binding.mapView.onResume() + presenter?.attachView(this) + registerNetworkReceiver() + + if (isResumed) { + if (activity?.let { locationPermissionsHelper.checkLocationPermission(it) } == true) { + performMapReadyActions() } else { - startMapWithoutPermission(); + startMapWithoutPermission() } } } - @Override - public void onPause() { - super.onPause(); - // unregistering the broadcastReceiver, as it was causing an exception and a potential crash - unregisterNetworkReceiver(); + override fun onPause() { + super.onPause() + // Unregistering the broadcastReceiver to prevent crashes + unregisterNetworkReceiver() } - /** * Unregisters the networkReceiver */ - private void unregisterNetworkReceiver() { - if (getActivity() != null) { - getActivity().unregisterReceiver(broadcastReceiver); - } + private fun unregisterNetworkReceiver() { + activity?.unregisterReceiver(broadcastReceiver) } - private void startMapWithoutPermission() { - lastKnownLocation = MapUtils.getDefaultLatLng(); - moveCameraToPosition( - new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); - presenter.onMapReady(exploreMapController); + private fun startMapWithoutPermission() { + lastKnownLocation = MapUtils.defaultLatLng + moveCameraToPosition(GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude)) + presenter?.onMapReady(exploreMapController) } - private void registerNetworkReceiver() { - if (getActivity() != null) { - getActivity().registerReceiver(broadcastReceiver, intentFilter); - } + private fun registerNetworkReceiver() { + activity?.registerReceiver(broadcastReceiver, intentFilter) } - private void performMapReadyActions() { + private fun performMapReadyActions() { if (isDarkTheme) { - binding.mapView.getOverlayManager().getTilesOverlay() - .setColorFilter(TilesOverlay.INVERT_COLORS); + binding.mapView.overlayManager.tilesOverlay.setColorFilter(TilesOverlay.INVERT_COLORS) } if (applicationKvStore.getBoolean("doNotAskForLocationPermission", false) && - !locationPermissionsHelper.checkLocationPermission(getActivity())) { - isPermissionDenied = true; + !locationPermissionsHelper.checkLocationPermission(requireActivity()) + ) { + isPermissionDenied = true } - lastKnownLocation = MapUtils.getDefaultLatLng(); - // if we came from 'Show in Explore' in Nearby, load Nearby map center and zoom + lastKnownLocation = MapUtils.defaultLatLng + + // If user came from 'Show in Explore' in Nearby, load saved map center and zoom if (isCameFromNearbyMap()) { - moveCameraToPosition( - new GeoPoint(prevLatitude, prevLongitude), - prevZoom, - 1L - ); + moveCameraToPosition(GeoPoint(prevLatitude, prevLongitude), prevZoom, 1L) } else { moveCameraToPosition( - new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); + GeoPoint(lastKnownLocation!!.latitude, lastKnownLocation!!.longitude) + ) } - presenter.onMapReady(exploreMapController); + + presenter?.onMapReady(exploreMapController) } /** - * Fetch Nearby map camera data from fragment arguments if any. + * Fetch Nearby map camera data from fragment arguments if available. */ - public void loadNearbyMapData() { - // get fragment arguments - if (getArguments() != null) { - prevZoom = getArguments().getDouble("prev_zoom"); - prevLatitude = getArguments().getDouble("prev_latitude"); - prevLongitude = getArguments().getDouble("prev_longitude"); + fun loadNearbyMapData() { + arguments?.let { + prevZoom = it.getDouble("prev_zoom") + prevLatitude = it.getDouble("prev_latitude") + prevLongitude = it.getDouble("prev_longitude") } } /** - * Checks if fragment arguments contain data from Nearby map, indicating that the user navigated - * from Nearby using 'Show in Explore'. + * Checks if fragment arguments contain data from the Nearby map, + * indicating that the user navigated from Nearby using 'Show in Explore'. * * @return true if user navigated from Nearby map - **/ - public boolean isCameFromNearbyMap() { - return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0; + */ + fun isCameFromNearbyMap(): Boolean { + return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0 } - public void loadNearbyMapFromExplore() { - ((MainActivity) getContext()).loadNearbyMapFromExplore( - binding.mapView.getZoomLevelDouble(), - binding.mapView.getMapCenter().getLatitude(), - binding.mapView.getMapCenter().getLongitude() - ); + fun loadNearbyMapFromExplore() { + (requireContext() as MainActivity).loadNearbyMapFromExplore( + binding.mapView.zoomLevelDouble, + binding.mapView.mapCenter.latitude, + binding.mapView.mapCenter.longitude + ) } - private void initViews() { - Timber.d("init views called"); - initBottomSheets(); - setBottomSheetCallbacks(); + private fun initViews() { + Timber.d("init views called") + initBottomSheets() + setBottomSheetCallbacks() } /** - * a) Creates bottom sheet behaviours from bottom sheet, sets initial states and visibility + * a) Creates bottom sheet behaviors from bottom sheet, sets initial states and visibility. * b) Gets the touch event on the map to perform following actions: - * if bottom sheet details are expanded or collapsed hide the bottom sheet details. + * - If bottom sheet details are expanded or collapsed, hide the bottom sheet details. */ @SuppressLint("ClickableViewAccessibility") - private void initBottomSheets() { - bottomSheetDetailsBehavior = BottomSheetBehavior.from( - binding.bottomSheetDetailsBinding.getRoot()); - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - binding.bottomSheetDetailsBinding.getRoot().setVisibility(View.VISIBLE); + private fun initBottomSheets() { + bottomSheetDetailsBehavior = BottomSheetBehavior.from(binding.bottomSheetDetailsBinding.root) + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN + binding.bottomSheetDetailsBinding.root.visibility = View.VISIBLE } /** * Defines how bottom sheets will act on click */ - private void setBottomSheetCallbacks() { - binding.bottomSheetDetailsBinding.getRoot().setOnClickListener(v -> { - if (bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); - } else if (bottomSheetDetailsBehavior.getState() - == BottomSheetBehavior.STATE_EXPANDED) { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + private fun setBottomSheetCallbacks() { + binding.bottomSheetDetailsBinding.root.setOnClickListener { + bottomSheetDetailsBehavior.state = when (bottomSheetDetailsBehavior.state) { + BottomSheetBehavior.STATE_COLLAPSED -> BottomSheetBehavior.STATE_EXPANDED + BottomSheetBehavior.STATE_EXPANDED -> BottomSheetBehavior.STATE_COLLAPSED + else -> bottomSheetDetailsBehavior.state } - }); - } - - @Override - public void onLocationChangedSignificantly(LatLng latLng) { - Timber.d("Location significantly changed"); - if (latLng != null) { - handleLocationUpdate(latLng, LOCATION_SIGNIFICANTLY_CHANGED); } } - @Override - public void onLocationChangedSlightly(LatLng latLng) { - Timber.d("Location slightly changed"); - if (latLng != null) {//If the map has never ever shown the current location, lets do it know - handleLocationUpdate(latLng, LOCATION_SLIGHTLY_CHANGED); - } + override fun onLocationChangedSignificantly(latLng: LatLng?) { + Timber.d("Location significantly changed") + latLng?.let { handleLocationUpdate(it, LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) } } - private void handleLocationUpdate(final fr.free.nrw.commons.location.LatLng latLng, - final LocationServiceManager.LocationChangeType locationChangeType) { - lastKnownLocation = latLng; - exploreMapController.currentLocation = lastKnownLocation; - presenter.updateMap(locationChangeType); + override fun onLocationChangedSlightly(latLng: LatLng?) { + Timber.d("Location slightly changed") + latLng?.let { handleLocationUpdate(it, LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED) } } - @Override - public void onLocationChangedMedium(LatLng latLng) { - + private fun handleLocationUpdate( + latLng: fr.free.nrw.commons.location.LatLng, + locationChangeType: LocationServiceManager.LocationChangeType + ) { + lastKnownLocation = latLng + exploreMapController.currentLocation = lastKnownLocation + presenter?.updateMap(locationChangeType) } - @Override - public boolean isNetworkConnectionEstablished() { - return NetworkUtils.isInternetConnectionEstablished(getActivity()); + override fun onLocationChangedMedium(latLng: LatLng?) { + // No implementation required } - @Override - public void populatePlaces(LatLng currentLatLng) { - final Observable nearbyPlacesInfoObservable; - if (currentLatLng == null) { - return; - } - if (currentLatLng.equals( - getLastMapFocus())) { // Means we are checking around current location - nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(currentLatLng, - getLastMapFocus(), true); + override fun isNetworkConnectionEstablished(): Boolean { + return NetworkUtils.isInternetConnectionEstablished(activity) + } + + override fun populatePlaces(curLatLng: LatLng?) { + if (curLatLng == null) return + + val nearbyPlacesInfoObservable: Observable = + if (curLatLng == getLastMapFocus()) { + // Checking around current location + presenter!!.loadAttractionsFromLocation(curLatLng, getLastMapFocus(), true) } else { - nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(getLastMapFocus(), - currentLatLng, false); + presenter!!.loadAttractionsFromLocation(getLastMapFocus(), curLatLng, false) } - getCompositeDisposable().add(nearbyPlacesInfoObservable - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(explorePlacesInfo -> { - mediaList = explorePlacesInfo.mediaList; - if (mediaList == null) { - showResponseMessage(getString(R.string.no_pictures_in_this_area)); - } - updateMapMarkers(explorePlacesInfo); - lastMapFocus = new GeoPoint(currentLatLng.getLatitude(), - currentLatLng.getLongitude()); - }, - throwable -> { - Timber.d(throwable); - // Not showing the user, throwable localizedErrorMessage - showErrorMessage(getString(R.string.error_fetching_nearby_places)); - setProgressBarVisibility(false); - presenter.lockUnlockNearby(false); - })); + compositeDisposable.add( + nearbyPlacesInfoObservable + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ explorePlacesInfo -> + mediaList = explorePlacesInfo.mediaList + if (mediaList.isNullOrEmpty()) { + showResponseMessage(getString(R.string.no_pictures_in_this_area)) + } + updateMapMarkers(explorePlacesInfo) + lastMapFocus = GeoPoint(curLatLng.latitude, curLatLng.longitude) + }, { throwable -> + Timber.d(throwable) + showErrorMessage(getString(R.string.error_fetching_nearby_places)) + setProgressBarVisibility(false) + presenter?.lockUnlockNearby(false) + }) + ) + if (recenterToUserLocation) { - recenterToUserLocation = false; + recenterToUserLocation = false } } @@ -497,103 +481,102 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment * * @param explorePlacesInfo holds several information as current location, marker list etc. */ - private void updateMapMarkers(final MapController.ExplorePlacesInfo explorePlacesInfo) { - presenter.updateMapMarkers(explorePlacesInfo); + private fun updateMapMarkers(explorePlacesInfo: MapController.ExplorePlacesInfo) { + presenter?.updateMapMarkers(explorePlacesInfo) } - private void showErrorMessage(final String message) { - ViewUtil.showLongToast(getActivity(), message); + private fun showErrorMessage(message: String) { + ViewUtil.showLongToast(requireActivity(), message) } - private void showResponseMessage(final String message) { - ViewUtil.showLongSnackbar(getView(), message); + private fun showResponseMessage(message: String) { + ViewUtil.showLongSnackbar(requireView(), message) } - @Override - public void askForLocationPermission() { - Timber.d("Asking for location permission"); - activityResultLauncher.launch(permission.ACCESS_FINE_LOCATION); + override fun askForLocationPermission() { + Timber.d("Asking for location permission") + activityResultLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION) } - private void locationPermissionGranted() { - isPermissionDenied = false; - applicationKvStore.putBoolean("doNotAskForLocationPermission", false); - lastKnownLocation = locationManager.getLastLocation(); - fr.free.nrw.commons.location.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.mapView.getController().setCenter(targetP); - recenterMarkerToPosition(targetP); - moveCameraToPosition(targetP); - } else if (locationManager.isGPSProviderEnabled() - || locationManager.isNetworkProviderEnabled()) { - locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); - locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); - setProgressBarVisibility(true); + val targetP = GeoPoint(target!!.latitude, target.longitude) + mapCenter = targetP + binding.mapView.controller.setCenter(targetP) + recenterMarkerToPosition(targetP) + moveCameraToPosition(targetP) + } 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(), + R.string.ask_to_turn_location_on_text + ) } - presenter.onMapReady(exploreMapController); - registerUnregisterLocationListener(false); + presenter?.onMapReady(exploreMapController) + registerUnregisterLocationListener(false) } - public void registerUnregisterLocationListener(final boolean removeLocationListener) { - MapUtils.registerUnregisterLocationListener(removeLocationListener, locationManager, this); + fun registerUnregisterLocationListener(removeLocationListener: Boolean) { + MapUtils.registerUnregisterLocationListener(removeLocationListener, locationManager, this) } - @Override - public void recenterMap(LatLng currentLatLng) { - // if user has denied permission twice, then show dialog + override fun recenterMap(currentLatLng: LatLng?) { if (isPermissionDenied) { - if (locationPermissionsHelper.checkLocationPermission(getActivity())) { - // this will run when user has given permission by opening app's settings - isPermissionDenied = false; - recenterMap(currentLatLng); + if (locationPermissionsHelper.checkLocationPermission(requireActivity())) { + isPermissionDenied = false + recenterMap(currentLatLng) } else { - askForLocationPermission(); + askForLocationPermission() } } else { - if (!locationPermissionsHelper.checkLocationPermission(getActivity())) { - askForLocationPermission(); + if (!locationPermissionsHelper.checkLocationPermission(requireActivity())) { + askForLocationPermission() } else { - locationPermissionGranted(); + locationPermissionGranted() } } + if (currentLatLng == null) { - recenterToUserLocation = true; - return; + recenterToUserLocation = true + return } - recenterMarkerToPosition( - new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); - binding.mapView.getController() - .animateTo(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); - if (lastMapFocus != null) { - Location mylocation = new Location(""); - Location dest_location = new Location(""); - dest_location.setLatitude(binding.mapView.getMapCenter().getLatitude()); - dest_location.setLongitude(binding.mapView.getMapCenter().getLongitude()); - mylocation.setLatitude(lastMapFocus.getLatitude()); - mylocation.setLongitude(lastMapFocus.getLongitude()); - Float distance = mylocation.distanceTo(dest_location);//in meters - if (lastMapFocus != null) { - if (isNetworkConnectionEstablished()) { - if (distance > 2000.0) { - setSearchThisAreaButtonVisibility(true); - } else { - setSearchThisAreaButtonVisibility(false); - } - } - } else { - setSearchThisAreaButtonVisibility(false); + + recenterMarkerToPosition(GeoPoint(currentLatLng.latitude, currentLatLng.longitude)) + binding.mapView.controller.animateTo( + GeoPoint(currentLatLng.latitude, currentLatLng.longitude) + ) + + lastMapFocus?.let { + val myLocation = Location("").apply { + latitude = it.latitude + longitude = it.longitude } - } + val destLocation = Location("").apply { + latitude = binding.mapView.mapCenter.latitude + longitude = binding.mapView.mapCenter.longitude + } + val distance = myLocation.distanceTo(destLocation) + + if (isNetworkConnectionEstablished()) { + setSearchThisAreaButtonVisibility(distance > 2000.0) + } else { + setSearchThisAreaButtonVisibility(false) + } + } ?: setSearchThisAreaButtonVisibility(false) } - @Override - public void hideBottomDetailsSheet() { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + override fun hideBottomDetailsSheet() { + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN } /** @@ -602,97 +585,81 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment * * @param place Place of clicked nearby marker */ - private void passInfoToSheet(final Place place) { - binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener( - view -> Utils.handleGeoCoordinates(getActivity(), - place.getLocation(), binding.mapView.getZoomLevelDouble())); + private fun passInfoToSheet(place: Place) { + binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener { + Utils.handleGeoCoordinates(activity, place.location, binding.mapView.zoomLevelDouble) + } - binding.bottomSheetDetailsBinding.commonsButton.setVisibility( - place.hasCommonsLink() ? View.VISIBLE : View.GONE); - binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); + binding.bottomSheetDetailsBinding.commonsButton.visibility = + if (place.hasCommonsLink()) View.VISIBLE else View.GONE - int index = 0; - for (Media media : mediaList) { - if (media.getFilename().equals(place.name)) { - int finalIndex = index; - binding.bottomSheetDetailsBinding.mediaDetailsButton.setOnClickListener(view -> { - ((ExploreMapRootFragment) getParentFragment()).onMediaClicked(finalIndex); - }); + binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener { + Utils.handleWebUrl(context, place.siteLinks.commonsLink) + } + + mediaList?.indexOfFirst { it.filename == place.name }.takeIf { it!! >= 0 }?.let { index -> + binding.bottomSheetDetailsBinding.mediaDetailsButton.setOnClickListener { + (parentFragment as? ExploreMapRootFragment)?.onMediaClicked(index) } - index++; } - binding.bottomSheetDetailsBinding.title.setText( - place.name.substring(5, place.name.lastIndexOf("."))); - binding.bottomSheetDetailsBinding.category.setText(place.distance); - // Remove label since it is double information - String descriptionText = place.getLongDescription() - .replace(place.getName() + " (", ""); - descriptionText = (descriptionText.equals(place.getLongDescription()) ? descriptionText - : descriptionText.replaceFirst(".$", "")); - // Set the short description after we remove place name from long description - binding.bottomSheetDetailsBinding.description.setText(descriptionText); + + binding.bottomSheetDetailsBinding.title.text = place.name.substring( + 5, + place.name.lastIndexOf(".") + ) + binding.bottomSheetDetailsBinding.category.text = place.distance + + var descriptionText = place.longDescription.replace("${place.name} (", "") + descriptionText = if (descriptionText == place.longDescription) descriptionText + else descriptionText.dropLast(1) + + binding.bottomSheetDetailsBinding.description.text = descriptionText } - @Override - public void addSearchThisAreaButtonAction() { - binding.searchThisAreaButton.setOnClickListener(presenter.onSearchThisAreaClicked()); + override fun addSearchThisAreaButtonAction() { + binding.searchThisAreaButton.setOnClickListener { presenter?.onSearchThisAreaClicked() } } - @Override - public void setSearchThisAreaButtonVisibility(boolean isVisible) { - binding.searchThisAreaButton.setVisibility(isVisible ? View.VISIBLE : View.GONE); + override fun setSearchThisAreaButtonVisibility(isVisible: Boolean) { + binding.searchThisAreaButton.visibility = if (isVisible) View.VISIBLE else View.GONE } - @Override - public void setProgressBarVisibility(boolean isVisible) { - binding.mapProgressBar.setVisibility(isVisible ? View.VISIBLE : View.GONE); + override fun setProgressBarVisibility(isVisible: Boolean) { + binding.mapProgressBar.visibility = if (isVisible) View.VISIBLE else View.GONE } - @Override - public boolean isDetailsBottomSheetVisible() { - if (binding.bottomSheetDetailsBinding.getRoot().getVisibility() == View.VISIBLE) { - return true; - } else { - return false; - } + override fun isDetailsBottomSheetVisible(): Boolean { + return binding.bottomSheetDetailsBinding.root.visibility == View.VISIBLE } - @Override - public boolean isSearchThisAreaButtonVisible() { - return binding.bottomSheetDetailsBinding.getRoot().getVisibility() == View.VISIBLE; + override fun isSearchThisAreaButtonVisible(): Boolean { + return binding.bottomSheetDetailsBinding.root.visibility == View.VISIBLE } - @Override - public LatLng getLastLocation() { + override fun getLastLocation(): LatLng { if (lastKnownLocation == null) { - lastKnownLocation = locationManager.getLastLocation(); + lastKnownLocation = locationManager.getLastLocation() } - return lastKnownLocation; + return lastKnownLocation!! } - @Override - public void disableFABRecenter() { - binding.fabRecenter.setEnabled(false); + override fun disableFABRecenter() { + binding.fabRecenter.isEnabled = false } - @Override - public void enableFABRecenter() { - binding.fabRecenter.setEnabled(true); + override fun enableFABRecenter() { + binding.fabRecenter.isEnabled = true } /** - * Adds a markers to the map based on the list of NearbyBaseMarker. + * Adds markers to the map based on the list of NearbyBaseMarker. * * @param nearbyBaseMarkers The NearbyBaseMarker object representing the markers to be added. */ - @Override - public void addMarkersToMap(List nearbyBaseMarkers) { - clearAllMarkers(); - for (int i = 0; i < nearbyBaseMarkers.size(); i++) { - addMarkerToMap(nearbyBaseMarkers.get(i)); - } - binding.mapView.invalidate(); + override fun addMarkersToMap(nearbyBaseMarkers: List) { + clearAllMarkers() + nearbyBaseMarkers.forEach { addMarkerToMap(it) } + binding.mapView.invalidate() } /** @@ -700,312 +667,274 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment * * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be added. */ - private void addMarkerToMap(BaseMarker nearbyBaseMarker) { + private fun addMarkerToMap(nearbyBaseMarker: BaseMarker) { if (isAttachedToActivity()) { - ArrayList items = new ArrayList<>(); - Bitmap icon = nearbyBaseMarker.getIcon(); - Drawable d = new BitmapDrawable(getResources(), icon); - GeoPoint point = new GeoPoint( - nearbyBaseMarker.getPlace().location.getLatitude(), - nearbyBaseMarker.getPlace().location.getLongitude()); - OverlayItem item = new OverlayItem(nearbyBaseMarker.getPlace().name, null, - point); - item.setMarker(d); - items.add(item); - ItemizedOverlayWithFocus overlay = new ItemizedOverlayWithFocus(items, - new OnItemGestureListener() { - @Override - public boolean onItemSingleTapUp(int index, OverlayItem item) { - final Place place = nearbyBaseMarker.getPlace(); - if (clickedMarker != null) { - removeMarker(clickedMarker); - addMarkerToMap(clickedMarker); - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - bottomSheetDetailsBehavior.setState( - BottomSheetBehavior.STATE_COLLAPSED); - } - clickedMarker = nearbyBaseMarker; - passInfoToSheet(place); - return true; - } + val items = ArrayList() + val icon = nearbyBaseMarker.icon + val drawable = BitmapDrawable(resources, icon) + val point = GeoPoint( + nearbyBaseMarker.place.location.latitude, + nearbyBaseMarker.place.location.longitude + ) + val item = OverlayItem(nearbyBaseMarker.place.name, null, point).apply { + setMarker(drawable) + } + items.add(item) - @Override - public boolean onItemLongPress(int index, OverlayItem item) { - return false; + val overlay = ItemizedOverlayWithFocus(items, object : OnItemGestureListener { + override fun onItemSingleTapUp(index: Int, item: OverlayItem): Boolean { + val place = nearbyBaseMarker.place + clickedMarker?.let { + removeMarker(it) + addMarkerToMap(it) + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_COLLAPSED } - }, getContext()); + clickedMarker = nearbyBaseMarker + passInfoToSheet(place) + return true + } - overlay.setFocusItemsOnTap(true); - binding.mapView.getOverlays().add(overlay); // Add the overlay to the map + override fun onItemLongPress(index: Int, item: OverlayItem): Boolean { + return false + } + }, context) + + overlay.setFocusItemsOnTap(true) + binding.mapView.overlays.add(overlay) // Add the overlay to the map } } - /** - * Removes a marker from the map based on the specified NearbyBaseMarker. - * - * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be removed. - */ - private void removeMarker(BaseMarker nearbyBaseMarker) { - Place place = nearbyBaseMarker.getPlace(); - List overlays = binding.mapView.getOverlays(); - ItemizedOverlayWithFocus item; + private fun removeMarker(nearbyBaseMarker: BaseMarker) { + val place = nearbyBaseMarker.place + val overlays = binding.mapView.overlays + var item: ItemizedOverlayWithFocus - for (int i = 0; i < overlays.size(); i++) { - if (overlays.get(i) instanceof ItemizedOverlayWithFocus) { - item = (ItemizedOverlayWithFocus) overlays.get(i); - OverlayItem overlayItem = item.getItem(0); + for (i in overlays.indices) { + if (overlays[i] is ItemizedOverlayWithFocus<*>) { + item = overlays[i] as ItemizedOverlayWithFocus + val overlayItem = item.getItem(0) - if (place.location.getLatitude() == overlayItem.getPoint().getLatitude() - && place.location.getLongitude() == overlayItem.getPoint().getLongitude()) { - binding.mapView.getOverlays().remove(i); - binding.mapView.invalidate(); - break; + if (place.location.latitude == overlayItem.point.latitude && + place.location.longitude == overlayItem.point.longitude + ) { + binding.mapView.overlays.removeAt(i) + binding.mapView.invalidate() + break } } } } - /** - * Clears all markers from the map and resets certain map overlays and gestures. After clearing - * markers, it re-adds a scale bar overlay and rotation gesture overlay to the map. - */ - @Override - public void clearAllMarkers() { + override fun clearAllMarkers() { if (isAttachedToActivity()) { - binding.mapView.getOverlayManager().clear(); - GeoPoint geoPoint = mapCenter; - if (geoPoint != null) { - List overlays = binding.mapView.getOverlays(); - ScaleDiskOverlay diskOverlay = - new ScaleDiskOverlay(this.getContext(), - geoPoint, 2000, GeoConstants.UnitOfMeasure.foot); - Paint circlePaint = new Paint(); - circlePaint.setColor(Color.rgb(128, 128, 128)); - circlePaint.setStyle(Paint.Style.STROKE); - circlePaint.setStrokeWidth(2f); - diskOverlay.setCirclePaint2(circlePaint); - Paint diskPaint = new Paint(); - diskPaint.setColor(Color.argb(40, 128, 128, 128)); - diskPaint.setStyle(Paint.Style.FILL_AND_STROKE); - diskOverlay.setCirclePaint1(diskPaint); - diskOverlay.setDisplaySizeMin(900); - diskOverlay.setDisplaySizeMax(1700); - binding.mapView.getOverlays().add(diskOverlay); - org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker( - binding.mapView); - startMarker.setPosition(geoPoint); - startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, - org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM); - startMarker.setIcon( - ContextCompat.getDrawable(this.getContext(), - R.drawable.current_location_marker)); - startMarker.setTitle("Your Location"); - startMarker.setTextLabelFontSize(24); - binding.mapView.getOverlays().add(startMarker); - } - ScaleBarOverlay scaleBarOverlay = new ScaleBarOverlay(binding.mapView); - scaleBarOverlay.setScaleBarOffset(15, 25); - Paint barPaint = new Paint(); - barPaint.setARGB(200, 255, 250, 250); - scaleBarOverlay.setBackgroundPaint(barPaint); - scaleBarOverlay.enableScaleBar(); - binding.mapView.getOverlays().add(scaleBarOverlay); - binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { - @Override - public boolean singleTapConfirmedHelper(GeoPoint p) { - if (clickedMarker != null) { - removeMarker(clickedMarker); - addMarkerToMap(clickedMarker); - binding.mapView.invalidate(); - } else { - Timber.e("CLICKED MARKER IS NULL"); + binding.mapView.overlayManager.clear() + mapCenter?.let { geoPoint -> + val overlays = binding.mapView.overlays + val diskOverlay = ScaleDiskOverlay( + context, geoPoint, 2000, GeoConstants.UnitOfMeasure.foot + ).apply { + val circlePaint = Paint().apply { + color = Color.rgb(128, 128, 128) + style = Paint.Style.STROKE + strokeWidth = 2f } - if (bottomSheetDetailsBehavior.getState() - == BottomSheetBehavior.STATE_EXPANDED) { - // Back should first hide the bottom sheet if it is expanded - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); + setCirclePaint2(circlePaint) + + val diskPaint = Paint().apply { + color = Color.argb(40, 128, 128, 128) + style = Paint.Style.FILL_AND_STROKE + } + setCirclePaint1(diskPaint) + + setDisplaySizeMin(900) + setDisplaySizeMax(1700) + } + binding.mapView.overlays.add(diskOverlay) + + val startMarker = Marker(binding.mapView).apply { + position = geoPoint + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + icon = ContextCompat.getDrawable( + requireContext(), + R.drawable.current_location_marker + ) + title = "Your Location" + textLabelFontSize = 24 + } + binding.mapView.overlays.add(startMarker) + } + + val scaleBarOverlay = ScaleBarOverlay(binding.mapView).apply { + setScaleBarOffset(15, 25) + setBackgroundPaint( + Paint().apply { + setARGB(200, 255, 250, 250) + } + ) + enableScaleBar() + } + binding.mapView.overlays.add(scaleBarOverlay) + + binding.mapView.overlays.add(object : MapEventsOverlay(object : MapEventsReceiver { + override fun singleTapConfirmedHelper(p: GeoPoint?): Boolean { + clickedMarker?.let { + removeMarker(it) + addMarkerToMap(it) + binding.mapView.invalidate() + } ?: Timber.e("CLICKED MARKER IS NULL") + + if (bottomSheetDetailsBehavior.state == BottomSheetBehavior.STATE_EXPANDED) { + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN } else if (isDetailsBottomSheetVisible()) { - hideBottomDetailsSheet(); + hideBottomDetailsSheet() } - return true; + return true } - @Override - public boolean longPressHelper(GeoPoint p) { - return false; + override fun longPressHelper(p: GeoPoint?): Boolean { + return false } - })); - binding.mapView.setMultiTouchControls(true); + }) {}) + + binding.mapView.setMultiTouchControls(true) } } - /** - * Recenters the map view to the specified GeoPoint and updates the marker to indicate the new - * position. - * - * @param geoPoint The GeoPoint representing the new center position for the map. - */ - private void recenterMarkerToPosition(GeoPoint geoPoint) { - if (geoPoint != null) { - binding.mapView.getController().setCenter(geoPoint); - List overlays = binding.mapView.getOverlays(); - for (int i = 0; i < overlays.size(); i++) { - if (overlays.get(i) instanceof org.osmdroid.views.overlay.Marker) { - binding.mapView.getOverlays().remove(i); - } else if (overlays.get(i) instanceof ScaleDiskOverlay) { - binding.mapView.getOverlays().remove(i); + private fun recenterMarkerToPosition(geoPoint: GeoPoint?) { + geoPoint?.let { + binding.mapView.controller.setCenter(it) + val overlays = binding.mapView.overlays + overlays.removeAll { overlay -> overlay is Marker || overlay is ScaleDiskOverlay } + + val diskOverlay = ScaleDiskOverlay( + context, it, 2000, GeoConstants.UnitOfMeasure.foot + ).apply { + val circlePaint = Paint().apply { + color = Color.rgb(128, 128, 128) + style = Paint.Style.STROKE + strokeWidth = 2f } + setCirclePaint2(circlePaint) + + val diskPaint = Paint().apply { + color = Color.argb(40, 128, 128, 128) + style = Paint.Style.FILL_AND_STROKE + } + setCirclePaint1(diskPaint) + + setDisplaySizeMin(900) + setDisplaySizeMax(1700) } - ScaleDiskOverlay diskOverlay = - new ScaleDiskOverlay(this.getContext(), - geoPoint, 2000, GeoConstants.UnitOfMeasure.foot); - Paint circlePaint = new Paint(); - circlePaint.setColor(Color.rgb(128, 128, 128)); - circlePaint.setStyle(Paint.Style.STROKE); - circlePaint.setStrokeWidth(2f); - diskOverlay.setCirclePaint2(circlePaint); - Paint diskPaint = new Paint(); - diskPaint.setColor(Color.argb(40, 128, 128, 128)); - diskPaint.setStyle(Paint.Style.FILL_AND_STROKE); - diskOverlay.setCirclePaint1(diskPaint); - diskOverlay.setDisplaySizeMin(900); - diskOverlay.setDisplaySizeMax(1700); - binding.mapView.getOverlays().add(diskOverlay); - org.osmdroid.views.overlay.Marker startMarker = new org.osmdroid.views.overlay.Marker( - binding.mapView); - startMarker.setPosition(geoPoint); - startMarker.setAnchor(org.osmdroid.views.overlay.Marker.ANCHOR_CENTER, - org.osmdroid.views.overlay.Marker.ANCHOR_BOTTOM); - startMarker.setIcon( - ContextCompat.getDrawable(this.getContext(), R.drawable.current_location_marker)); - startMarker.setTitle("Your Location"); - startMarker.setTextLabelFontSize(24); - binding.mapView.getOverlays().add(startMarker); + binding.mapView.overlays.add(diskOverlay) + + val startMarker = Marker(binding.mapView).apply { + position = it + setAnchor(Marker.ANCHOR_CENTER, Marker.ANCHOR_BOTTOM) + icon = ContextCompat.getDrawable( + requireContext(), + R.drawable.current_location_marker + ) + title = "Your Location" + textLabelFontSize = 24 + } + binding.mapView.overlays.add(startMarker) } } - /** - * Moves the camera of the map view to the specified GeoPoint using an animation. - * - * @param geoPoint The GeoPoint representing the new camera position for the map. - */ - private void moveCameraToPosition(GeoPoint geoPoint) { - binding.mapView.getController().animateTo(geoPoint); + private fun moveCameraToPosition(geoPoint: GeoPoint) { + binding.mapView.controller.animateTo(geoPoint) } - /** - * Moves the camera of the map view to the specified GeoPoint at specified zoom level and speed - * using an animation. - * - * @param geoPoint The GeoPoint representing the new camera position for the map. - * @param zoom Zoom level of the map camera - * @param speed Speed of animation - */ - private void moveCameraToPosition(GeoPoint geoPoint, double zoom, long speed) { - binding.mapView.getController().animateTo(geoPoint, zoom, speed); + private fun moveCameraToPosition(geoPoint: GeoPoint, zoom: Double, speed: Long) { + binding.mapView.controller.animateTo(geoPoint, zoom, speed) } - @Override - public fr.free.nrw.commons.location.LatLng getLastMapFocus() { - return lastMapFocus == null ? getMapCenter() : new fr.free.nrw.commons.location.LatLng( - lastMapFocus.getLatitude(), lastMapFocus.getLongitude(), 100); + override fun getLastMapFocus(): LatLng { + return lastMapFocus?.let { + LatLng(it.latitude, it.longitude, 100f) + } ?: getMapCenter() } - @Override - public fr.free.nrw.commons.location.LatLng getMapCenter() { - fr.free.nrw.commons.location.LatLng latLnge = null; + override fun getMapCenter(): LatLng { + var latLng: LatLng? = null + if (mapCenter != null) { - latLnge = new fr.free.nrw.commons.location.LatLng( - mapCenter.getLatitude(), mapCenter.getLongitude(), 100); + latLng = LatLng(mapCenter!!.latitude, mapCenter!!.longitude, 100f) } else { - if (applicationKvStore.getString("LastLocation") != null) { - final String[] locationLatLng - = applicationKvStore.getString("LastLocation").split(","); - lastKnownLocation - = new fr.free.nrw.commons.location.LatLng(Double.parseDouble(locationLatLng[0]), - Double.parseDouble(locationLatLng[1]), 1f); - latLnge = lastKnownLocation; - } else { - latLnge = new fr.free.nrw.commons.location.LatLng(51.506255446947776, - -0.07483536015053005, 1f); + applicationKvStore.getString("LastLocation")?.let { lastLocation -> + val locationLatLng = lastLocation.split(",").map { it.toDouble() } + lastKnownLocation = LatLng(locationLatLng[0], locationLatLng[1], 1f) + latLng = lastKnownLocation + } ?: run { + latLng = LatLng(51.506255446947776, -0.07483536015053005, 1f) } } + if (!isCameFromNearbyMap()) { - moveCameraToPosition(new GeoPoint(latLnge.getLatitude(), latLnge.getLongitude())); + moveCameraToPosition(GeoPoint(latLng?.latitude!!, latLng?.longitude!!)) } - return latLnge; + return latLng!! } - @Override - public fr.free.nrw.commons.location.LatLng getMapFocus() { - fr.free.nrw.commons.location.LatLng mapFocusedLatLng = new fr.free.nrw.commons.location.LatLng( - binding.mapView.getMapCenter().getLatitude(), - binding.mapView.getMapCenter().getLongitude(), 100); - return mapFocusedLatLng; + override fun getMapFocus(): LatLng { + return LatLng( + binding.mapView.mapCenter.latitude, + binding.mapView.mapCenter.longitude, + 100f + ) } - @Override - public void setFABRecenterAction(OnClickListener onClickListener) { - binding.fabRecenter.setOnClickListener(onClickListener); + override fun setFABRecenterAction(onClickListener: View.OnClickListener) { + binding.fabRecenter.setOnClickListener(onClickListener) } - @Override - public boolean backButtonClicked() { - if (!(bottomSheetDetailsBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN)) { - bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); - return true; + override fun backButtonClicked(): Boolean { + return if (bottomSheetDetailsBehavior.state != BottomSheetBehavior.STATE_HIDDEN) { + bottomSheetDetailsBehavior.state = BottomSheetBehavior.STATE_HIDDEN + true } else { - return false; + false } } - /** - * Adds network broadcast receiver to recognize connection established - */ - private void initNetworkBroadCastReceiver() { - broadcastReceiver = new BroadcastReceiver() { - @Override - public void onReceive(final Context context, final Intent intent) { - if (getActivity() != null) { - if (NetworkUtils.isInternetConnectionEstablished(getActivity())) { + private fun initNetworkBroadCastReceiver() { + broadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + activity?.let { + if (NetworkUtils.isInternetConnectionEstablished(it)) { if (isNetworkErrorOccurred) { - presenter.updateMap(LOCATION_SIGNIFICANTLY_CHANGED); - isNetworkErrorOccurred = false; - } - - if (snackbar != null) { - snackbar.dismiss(); - snackbar = null; + presenter?.updateMap( + LocationServiceManager + .LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED + ) + isNetworkErrorOccurred = false } + snackbar?.dismiss() + snackbar = null } else { if (snackbar == null) { - snackbar = Snackbar.make(getView(), R.string.no_internet, - Snackbar.LENGTH_INDEFINITE); - setSearchThisAreaButtonVisibility(false); - setProgressBarVisibility(false); + snackbar = Snackbar.make( + requireView(), + R.string.no_internet, + Snackbar.LENGTH_INDEFINITE + ) + setSearchThisAreaButtonVisibility(false) + setProgressBarVisibility(false) } - - isNetworkErrorOccurred = true; - snackbar.show(); + isNetworkErrorOccurred = true + snackbar?.show() } } } - }; + } } - /** - * helper function to confirm that this fragment has been attached. - **/ - public boolean isAttachedToActivity() { - boolean attached = isVisible() && getActivity() != null; - return attached; + fun isAttachedToActivity(): Boolean { + return isVisible && activity != null } - @Override - public void onLocationPermissionDenied(String toastMessage) { - } + override fun onLocationPermissionDenied(toastMessage: String) {} - @Override - public void onLocationPermissionGranted() { - } + override fun onLocationPermissionGranted() {} } diff --git a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt index 94b9cf5ad..bc92ff6ee 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/explore/map/ExploreMapPresenter.kt @@ -1,152 +1,139 @@ -package fr.free.nrw.commons.explore.map; +package fr.free.nrw.commons.explore.map -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; -import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.SEARCH_CUSTOM_AREA; +import android.location.Location +import android.view.View +import fr.free.nrw.commons.BaseMarker +import fr.free.nrw.commons.MapController +import fr.free.nrw.commons.MapController.ExplorePlacesInfo +import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao +import fr.free.nrw.commons.explore.map.ExploreMapController.NearbyBaseMarkerThumbCallback +import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType +import io.reactivex.Observable +import java.lang.reflect.Proxy +import timber.log.Timber +class ExploreMapPresenter( + private val bookmarkLocationDao: BookmarkLocationsDao +) : ExploreMapContract.UserActions, ExploreMapController.NearbyBaseMarkerThumbCallback { -import android.location.Location; -import android.view.View; -import fr.free.nrw.commons.BaseMarker; -import fr.free.nrw.commons.MapController; -import fr.free.nrw.commons.MapController.ExplorePlacesInfo; -import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; -import fr.free.nrw.commons.explore.map.ExploreMapController.NearbyBaseMarkerThumbCallback; -import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType; -import io.reactivex.Observable; -import java.lang.reflect.Proxy; -import java.util.List; -import timber.log.Timber; + private var isNearbyLocked: Boolean = false + private var currentLatLng: LatLng? = null + private var exploreMapController: ExploreMapController? = null -public class ExploreMapPresenter - implements ExploreMapContract.UserActions, - NearbyBaseMarkerThumbCallback { - - BookmarkLocationsDao bookmarkLocationDao; - private boolean isNearbyLocked; - private LatLng currentLatLng; - private ExploreMapController exploreMapController; - - private static final ExploreMapContract.View DUMMY = (ExploreMapContract.View) Proxy - .newProxyInstance( - ExploreMapContract.View.class.getClassLoader(), - new Class[]{ExploreMapContract.View.class}, (proxy, method, args) -> { - if (method.getName().equals("onMyEvent")) { - return null; - } else if (String.class == method.getReturnType()) { - return ""; - } else if (Integer.class == method.getReturnType()) { - return Integer.valueOf(0); - } else if (int.class == method.getReturnType()) { - return 0; - } else if (Boolean.class == method.getReturnType()) { - return Boolean.FALSE; - } else if (boolean.class == method.getReturnType()) { - return false; - } else { - return null; - } + companion object { + private val DUMMY: ExploreMapContract.View = Proxy.newProxyInstance( + ExploreMapContract.View::class.java.classLoader, + arrayOf(ExploreMapContract.View::class.java) + ) { _, method, _ -> + when (method.returnType) { + String::class.java -> "" + Integer::class.java -> 0 + Int::class.java -> 0 + Boolean::class.java -> false + Boolean::class.javaPrimitiveType -> false + else -> null } - ); - private ExploreMapContract.View exploreMapFragmentView = DUMMY; - - public ExploreMapPresenter(BookmarkLocationsDao bookmarkLocationDao) { - this.bookmarkLocationDao = bookmarkLocationDao; + } as ExploreMapContract.View } - @Override - public void updateMap(LocationChangeType locationChangeType) { - Timber.d("Presenter updates map and list" + locationChangeType.toString()); + private var exploreMapFragmentView: ExploreMapContract.View = DUMMY + + override fun updateMap(locationChangeType: LocationChangeType) { + Timber.d("Presenter updates map and list $locationChangeType") if (isNearbyLocked) { - Timber.d("Nearby is locked, so updateMapAndList returns"); - return; + Timber.d("Nearby is locked, so updateMapAndList returns") + return } if (!exploreMapFragmentView.isNetworkConnectionEstablished()) { - Timber.d("Network connection is not established"); - return; + Timber.d("Network connection is not established") + return } /** * Significant changed - Markers and current location will be updated together * Slightly changed - Only current position marker will be updated */ - if (locationChangeType.equals(LOCATION_SIGNIFICANTLY_CHANGED)) { - Timber.d("LOCATION_SIGNIFICANTLY_CHANGED"); - lockUnlockNearby(true); - exploreMapFragmentView.setProgressBarVisibility(true); - exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapCenter()); - } else if (locationChangeType.equals(SEARCH_CUSTOM_AREA)) { - Timber.d("SEARCH_CUSTOM_AREA"); - lockUnlockNearby(true); - exploreMapFragmentView.setProgressBarVisibility(true); - exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapFocus()); - } else { // Means location changed slightly, ie user is walking or driving. - Timber.d("Means location changed slightly"); + when (locationChangeType) { + LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED -> { + Timber.d("LOCATION_SIGNIFICANTLY_CHANGED") + lockUnlockNearby(true) + exploreMapFragmentView.setProgressBarVisibility(true) + exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapCenter()) + } + + LocationChangeType.SEARCH_CUSTOM_AREA -> { + Timber.d("SEARCH_CUSTOM_AREA") + lockUnlockNearby(true) + exploreMapFragmentView.setProgressBarVisibility(true) + exploreMapFragmentView.populatePlaces(exploreMapFragmentView.getMapFocus()) + } + + else -> { + Timber.d("Means location changed slightly") + } } } /** - * Nearby updates takes time, since they are network operations. During update time, we don't - * want to get any other calls from user. So locking nearby. + * Nearby updates take time since they are network operations. During update time, we don't + * want to get any other calls from the user. So locking nearby. * * @param isNearbyLocked true means lock, false means unlock */ - @Override - public void lockUnlockNearby(boolean isNearbyLocked) { - this.isNearbyLocked = isNearbyLocked; + override fun lockUnlockNearby(isNearbyLocked: Boolean) { + this.isNearbyLocked = isNearbyLocked if (isNearbyLocked) { - exploreMapFragmentView.disableFABRecenter(); + exploreMapFragmentView.disableFABRecenter() } else { - exploreMapFragmentView.enableFABRecenter(); + exploreMapFragmentView.enableFABRecenter() } } - @Override - public void attachView(ExploreMapContract.View view) { - exploreMapFragmentView = view; + override fun attachView(view: ExploreMapContract.View) { + exploreMapFragmentView = view } - @Override - public void detachView() { - exploreMapFragmentView = DUMMY; + override fun detachView() { + exploreMapFragmentView = DUMMY } /** * Sets click listener of FAB */ - @Override - public void setActionListeners(JsonKvStore applicationKvStore) { - exploreMapFragmentView.setFABRecenterAction(v -> { - exploreMapFragmentView.recenterMap(currentLatLng); - }); - - } - - @Override - public boolean backButtonClicked() { - return exploreMapFragmentView.backButtonClicked(); - } - - public void onMapReady(ExploreMapController exploreMapController) { - this.exploreMapController = exploreMapController; - if (null != exploreMapFragmentView) { - exploreMapFragmentView.addSearchThisAreaButtonAction(); - initializeMapOperations(); + override fun setActionListeners(applicationKvStore: JsonKvStore) { + exploreMapFragmentView.setFABRecenterAction { + currentLatLng?.let { it1 -> exploreMapFragmentView.recenterMap(it1) } } } - public void initializeMapOperations() { - lockUnlockNearby(false); - updateMap(LOCATION_SIGNIFICANTLY_CHANGED); + override fun backButtonClicked(): Boolean { + return exploreMapFragmentView.backButtonClicked() } - public Observable loadAttractionsFromLocation(LatLng currentLatLng, - LatLng searchLatLng, boolean checkingAroundCurrent) { - return Observable - .fromCallable(() -> exploreMapController - .loadAttractionsFromLocation(currentLatLng, searchLatLng, checkingAroundCurrent)); + fun onMapReady(exploreMapController: ExploreMapController) { + this.exploreMapController = exploreMapController + exploreMapFragmentView.addSearchThisAreaButtonAction() + initializeMapOperations() + } + + fun initializeMapOperations() { + lockUnlockNearby(false) + updateMap(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) + } + + fun loadAttractionsFromLocation( + currentLatLng: LatLng, + searchLatLng: LatLng?, + checkingAroundCurrent: Boolean + ): Observable { + return Observable.fromCallable { + exploreMapController?.loadAttractionsFromLocation( + currentLatLng, searchLatLng, checkingAroundCurrent + ) + } } /** @@ -155,47 +142,45 @@ public class ExploreMapPresenter * * @param explorePlacesInfo This variable has placeToCenter list information and distances. */ - public void updateMapMarkers( - MapController.ExplorePlacesInfo explorePlacesInfo) { + fun updateMapMarkers(explorePlacesInfo: MapController.ExplorePlacesInfo) { if (explorePlacesInfo.mediaList != null) { - prepareNearbyBaseMarkers(explorePlacesInfo); + prepareNearbyBaseMarkers(explorePlacesInfo) } else { - lockUnlockNearby(false); // So that new location updates wont come - exploreMapFragmentView.setProgressBarVisibility(false); + lockUnlockNearby(false) // So that new location updates won't come + exploreMapFragmentView.setProgressBarVisibility(false) } } - void prepareNearbyBaseMarkers(MapController.ExplorePlacesInfo explorePlacesInfo) { - exploreMapController - .loadAttractionsFromLocationToBaseMarkerOptions(explorePlacesInfo.currentLatLng, - // Curlatlang will be used to calculate distances - explorePlacesInfo.explorePlaceList, - exploreMapFragmentView.getContext(), - this, - explorePlacesInfo); + private fun prepareNearbyBaseMarkers(explorePlacesInfo: MapController.ExplorePlacesInfo) { + ExploreMapController.loadAttractionsFromLocationToBaseMarkerOptions( + explorePlacesInfo.currentLatLng, + explorePlacesInfo.explorePlaceList, + exploreMapFragmentView.getContext(), + this, + explorePlacesInfo + ) } - @Override - public void onNearbyBaseMarkerThumbsReady(List baseMarkers, - ExplorePlacesInfo explorePlacesInfo) { - if (null != exploreMapFragmentView) { - exploreMapFragmentView.addMarkersToMap(baseMarkers); - lockUnlockNearby(false); // So that new location updates wont come - exploreMapFragmentView.setProgressBarVisibility(false); - } + override fun onNearbyBaseMarkerThumbsReady( + baseMarkers: List, + explorePlacesInfo: ExplorePlacesInfo + ) { + exploreMapFragmentView.addMarkersToMap(baseMarkers) + lockUnlockNearby(false) // So that new location updates won't come + exploreMapFragmentView.setProgressBarVisibility(false) } - public View.OnClickListener onSearchThisAreaClicked() { - return v -> { + fun onSearchThisAreaClicked(): View.OnClickListener { + return View.OnClickListener { // Lock map operations during search this area operation - exploreMapFragmentView.setSearchThisAreaButtonVisibility(false); + exploreMapFragmentView.setSearchThisAreaButtonVisibility(false) if (searchCloseToCurrentLocation()) { - updateMap(LOCATION_SIGNIFICANTLY_CHANGED); + updateMap(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) } else { - updateMap(SEARCH_CUSTOM_AREA); + updateMap(LocationChangeType.SEARCH_CUSTOM_AREA) } - }; + } } /** @@ -204,24 +189,19 @@ public class ExploreMapPresenter * * @return Returns true if search this area button is used around our current location */ - public boolean searchCloseToCurrentLocation() { - if (null == exploreMapFragmentView.getLastMapFocus()) { - return true; + fun searchCloseToCurrentLocation(): Boolean { + val lastMapFocus = exploreMapFragmentView.getLastMapFocus() ?: return true + + val myLocation = Location("").apply { + latitude = lastMapFocus.latitude + longitude = lastMapFocus.longitude } - Location mylocation = new Location(""); - Location dest_location = new Location(""); - dest_location.setLatitude(exploreMapFragmentView.getMapFocus().getLatitude()); - dest_location.setLongitude(exploreMapFragmentView.getMapFocus().getLongitude()); - mylocation.setLatitude(exploreMapFragmentView.getLastMapFocus().getLatitude()); - mylocation.setLongitude(exploreMapFragmentView.getLastMapFocus().getLongitude()); - Float distance = mylocation.distanceTo(dest_location); - - if (distance > 2000.0 * 3 / 4) { - return false; - } else { - return true; + val destLocation = Location("").apply { + latitude = exploreMapFragmentView.getMapFocus().latitude + longitude = exploreMapFragmentView.getMapFocus().longitude } + + return myLocation.distanceTo(destLocation) <= 2000.0 * 3 / 4 } - } diff --git a/app/src/main/java/fr/free/nrw/commons/location/LocationUpdateListener.kt b/app/src/main/java/fr/free/nrw/commons/location/LocationUpdateListener.kt index e90cc1224..1dd3420f2 100644 --- a/app/src/main/java/fr/free/nrw/commons/location/LocationUpdateListener.kt +++ b/app/src/main/java/fr/free/nrw/commons/location/LocationUpdateListener.kt @@ -2,11 +2,11 @@ package fr.free.nrw.commons.location interface LocationUpdateListener { // Will be used to update all nearby markers on the map - fun onLocationChangedSignificantly(latLng: LatLng) + fun onLocationChangedSignificantly(latLng: LatLng?) // Will be used to track users motion - fun onLocationChangedSlightly(latLng: LatLng) + fun onLocationChangedSlightly(latLng: LatLng?) // Will be used updating nearby card view notification - fun onLocationChangedMedium(latLng: LatLng) + fun onLocationChangedMedium(latLng: LatLng?) } \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt index ce268f7a3..9722600ee 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/presenter/NearbyParentFragmentPresenter.kt @@ -465,17 +465,17 @@ class NearbyParentFragmentPresenter updateMapAndList(LocationChangeType.MAP_UPDATED) } - override fun onLocationChangedSignificantly(latLng: LatLng) { + override fun onLocationChangedSignificantly(latLng: LatLng?) { Timber.d("Location significantly changed") updateMapAndList(LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED) } - override fun onLocationChangedSlightly(latLng: LatLng) { + override fun onLocationChangedSlightly(latLng: LatLng?) { Timber.d("Location significantly changed") updateMapAndList(LocationChangeType.LOCATION_SLIGHTLY_CHANGED) } - override fun onLocationChangedMedium(latLng: LatLng) { + override fun onLocationChangedMedium(latLng: LatLng?) { Timber.d("Location changed medium") } From 7175b7021f9b9ac28b7e246532a7e952b94937bd Mon Sep 17 00:00:00 2001 From: Saifuddin Date: Sat, 22 Feb 2025 16:17:51 +0530 Subject: [PATCH 3/3] Refactor: Update `onLocationChanged` methods to accept nullable `LatLng` The `onLocationChangedSignificantly`, `onLocationChangedSlightly`, and `onLocationChangedMedium` methods in `NearbyParentFragment.java` have been updated to accept a nullable `LatLng` parameter. This change allows for cases where the location may not be available or valid. The methods previously required a non-null `LatLng` object. If the location is found to be valid then the location is handled by calling `handleLocationUpdate` method. --- .../nrw/commons/nearby/fragments/NearbyParentFragment.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java index 2b64f0e37..0bef6c894 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.java @@ -1699,7 +1699,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } @Override - public void onLocationChangedSignificantly(final LatLng latLng) { + public void onLocationChangedSignificantly(@Nullable final LatLng latLng) { Timber.d("Location significantly changed"); if (latLng != null) { handleLocationUpdate(latLng, LOCATION_SIGNIFICANTLY_CHANGED); @@ -1707,7 +1707,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } @Override - public void onLocationChangedSlightly(final LatLng latLng) { + public void onLocationChangedSlightly(@Nullable final LatLng latLng) { Timber.d("Location slightly changed"); if (latLng != null) {//If the map has never ever shown the current location, lets do it know handleLocationUpdate(latLng, LOCATION_SLIGHTLY_CHANGED); @@ -1715,7 +1715,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } @Override - public void onLocationChangedMedium(final LatLng latLng) { + public void onLocationChangedMedium(@Nullable final LatLng latLng) { Timber.d("Location changed medium"); if (latLng != null) {//If the map has never ever shown the current location, lets do it know handleLocationUpdate(latLng, LOCATION_SIGNIFICANTLY_CHANGED);