From 36f844a709dc40944644723917c0b1622380edf6 Mon Sep 17 00:00:00 2001 From: Sujal Date: Wed, 29 Jan 2025 19:20:29 +0530 Subject: [PATCH 01/14] Show placeholder and display depiction section when no depictions are available (#6163) (#6165) * corrected * corrected * Update MediaDetailFragment.kt Spelling correction --- .../nrw/commons/media/MediaDetailFragment.kt | 23 +++++++++++++++---- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt index 4c993fb80..d714b094a 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt @@ -650,10 +650,8 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C } private fun onDepictionsLoaded(idAndCaptions: List) { - binding.depictsLayout.visibility = - if (idAndCaptions.isEmpty()) View.GONE else View.VISIBLE - binding.depictionsEditButton.visibility = - if (idAndCaptions.isEmpty()) View.GONE else View.VISIBLE + binding.depictsLayout.visibility = View.VISIBLE + binding.depictionsEditButton.visibility = View.VISIBLE buildDepictionList(idAndCaptions) } @@ -863,8 +861,22 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C */ private fun buildDepictionList(idAndCaptions: List) { binding.mediaDetailDepictionContainer.removeAllViews() + + // Create a mutable list from the original list + val mutableIdAndCaptions = idAndCaptions.toMutableList() + + if (mutableIdAndCaptions.isEmpty()) { + // Create a placeholder IdAndCaptions object and add it to the list + mutableIdAndCaptions.add( + IdAndCaptions( + id = media?.pageId ?: "", // Use an empty string if media?.pageId is null + captions = mapOf(Locale.getDefault().language to getString(R.string.detail_panel_cats_none)) // Create a Map with the language as the key and the message as the value + ) + ) + } + val locale: String = Locale.getDefault().language - for (idAndCaption: IdAndCaptions in idAndCaptions) { + for (idAndCaption: IdAndCaptions in mutableIdAndCaptions) { binding.mediaDetailDepictionContainer.addView( buildDepictLabel( getDepictionCaption(idAndCaption, locale), @@ -875,6 +887,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C } } + private fun getDepictionCaption(idAndCaption: IdAndCaptions, locale: String): String? { // Check if the Depiction Caption is available in user's locale // if not then check for english, else show any available. From 5d4474ead65649377929f8fc37a50521afb30767 Mon Sep 17 00:00:00 2001 From: Akshay Komar <146421342+Akshaykomar890@users.noreply.github.com> Date: Thu, 30 Jan 2025 13:11:27 +0530 Subject: [PATCH 02/14] Migrated AboutActivity from Java to Kotlin (#6158) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Rename Constants to Follow Kotlin Naming Conventions >This PR refactors constant names in the project to adhere to Kotlin's UPPERCASE_SNAKE_CASE naming convention, improving code readability and maintaining consistency across the codebase. >Renamed the following constants in LoginActivity: >saveProgressDialog → SAVE_PROGRESS_DIALOG >saveErrorMessage → SAVE_ERROR_MESSAGE >saveUsername → SAVE_USERNAME >savePassword → SAVE_PASSWORD >Updated all references to these constants throughout the project. * Update Project_Default.xml * Refactor variable names to adhere to naming conventions Renamed variables to use camel case: -UPLOAD_COUNT_THRESHOLD → uploadCountThreshold -REVERT_PERCENTAGE_FOR_MESSAGE → revertPercentageForMessage -REVERT_SHARED_PREFERENCE → revertSharedPreference -UPLOAD_SHARED_PREFERENCE → uploadSharedPreference Renamed variables with uppercase initials to lowercase for alignment with Kotlin conventions: -Latitude → latitude -Longitude → longitude -Accuracy → accuracy Refactored the following variable names: -NUMBER_OF_QUESTIONS → numberOfQuestions -MULTIPLIER_TO_GET_PERCENTAGE → multiplierToGetPercentage * Refactor Dialog View Initialization with Null-Safe Calls This PR refactors the dialog setup code in CustomSelectorActivity to improve safety and readability by replacing explicit casts with null-safe generic calls for findViewById. >Replaced explicit casting (as Button and as TextView) with the generic findViewById() method for improved type safety. >Added null-safety (?.) to avoid potential crashes if a view is not found in the dialog layout. why changed:- >Prevents runtime crashes caused by NullPointerException when a view is missing in the layout. * Refactor Unit Test: Replace Unsafe Casting with Type-Safe Mocking for findViewById >PR refactors the unit test for NearbyParentFragment by replacing unsafe casting in the findViewById mocking statements with type-safe >Ensured all findViewById mocks now use a consistent, type-safe format (findViewById(...)) to reduce verbosity and potential casting errors. >Verified the functionality of prepareViewsForSheetPosition remains unchanged, ensuring no regression in test behavior. * Update NearbyParentFragmentUnitTest.kt * Refactor: Rename Constants to Follow CamelCase Naming Convention >Updated all constant variable names to follow the camelCase naming convention, removing underscores in the middle or end. >Ensured variable names remain descriptive and align with code readability best practices. * Replace private val with const val for URL constants in QuizController * Renaming the constant to use UPPER_SNAKE_CASE * Renaming the constant to use UPPER_SNAKE_CASE * Update Done * **Refactor: Convert `minimumThresholdForSwipe` to a compile-time constant** * Convert AboutActivity from Java to Kotlin This PR converts the AboutActivity class from Java to Kotlin >Testing: >Verified all functionalities of the AboutActivity, including toolbar setup, intent launches, and dialog interactions, to ensure behavior remains consistent post-conversion. >Successfully ran unit tests for AboutActivity to confirm the correctness of methods and logic. * Thank you for the suggestion! Since these methods all take a single View parameter, replacing them with method references is a great way to simplify the code and improve readability. I'll updated the code accordingly. Added a TODO in the code as a reminder to refactor this in the future. --------- Co-authored-by: Nicolas Raoul --- .../fr/free/nrw/commons/AboutActivity.java | 187 ---------------- .../java/fr/free/nrw/commons/AboutActivity.kt | 209 ++++++++++++++++++ 2 files changed, 209 insertions(+), 187 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/AboutActivity.java create mode 100644 app/src/main/java/fr/free/nrw/commons/AboutActivity.kt diff --git a/app/src/main/java/fr/free/nrw/commons/AboutActivity.java b/app/src/main/java/fr/free/nrw/commons/AboutActivity.java deleted file mode 100644 index dcc9bfd43..000000000 --- a/app/src/main/java/fr/free/nrw/commons/AboutActivity.java +++ /dev/null @@ -1,187 +0,0 @@ -package fr.free.nrw.commons; - -import android.annotation.SuppressLint; -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.ArrayAdapter; -import android.widget.LinearLayout; -import android.widget.Spinner; -import androidx.annotation.NonNull; -import fr.free.nrw.commons.databinding.ActivityAboutBinding; -import fr.free.nrw.commons.theme.BaseActivity; -import fr.free.nrw.commons.utils.ConfigUtils; -import fr.free.nrw.commons.utils.DialogUtil; -import java.util.Collections; -import java.util.List; - -/** - * Represents about screen of this app - */ -public class AboutActivity extends BaseActivity { - - /* - This View Binding class is auto-generated for each xml file. The format is usually the name - of the file with PascalCasing (The underscore characters will be ignored). - More information is available at https://developer.android.com/topic/libraries/view-binding - */ - private ActivityAboutBinding binding; - - /** - * This method helps in the creation About screen - * - * @param savedInstanceState Data bundle - */ - @Override - @SuppressLint("StringFormatInvalid") - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - /* - Instead of just setting the view with the xml file. We need to use View Binding class. - */ - binding = ActivityAboutBinding.inflate(getLayoutInflater()); - final View view = binding.getRoot(); - setContentView(view); - - setSupportActionBar(binding.toolbarBinding.toolbar); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - final String aboutText = getString(R.string.about_license); - /* - We can then access all the views by just using the id names like this. - camelCasing is used with underscore characters being ignored. - */ - binding.aboutLicense.setHtmlText(aboutText); - - @SuppressLint("StringFormatMatches") - String improveText = String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL); - binding.aboutImprove.setHtmlText(improveText); - binding.aboutVersion.setText(ConfigUtils.getVersionNameWithSha(getApplicationContext())); - - Utils.setUnderlinedText(binding.aboutFaq, R.string.about_faq, getApplicationContext()); - Utils.setUnderlinedText(binding.aboutRateUs, R.string.about_rate_us, getApplicationContext()); - Utils.setUnderlinedText(binding.aboutUserGuide, R.string.user_guide, getApplicationContext()); - Utils.setUnderlinedText(binding.aboutPrivacyPolicy, R.string.about_privacy_policy, getApplicationContext()); - Utils.setUnderlinedText(binding.aboutTranslate, R.string.about_translate, getApplicationContext()); - Utils.setUnderlinedText(binding.aboutCredits, R.string.about_credits, getApplicationContext()); - - /* - To set listeners, we can create a separate method and use lambda syntax. - */ - binding.facebookLaunchIcon.setOnClickListener(this::launchFacebook); - binding.githubLaunchIcon.setOnClickListener(this::launchGithub); - binding.websiteLaunchIcon.setOnClickListener(this::launchWebsite); - binding.aboutRateUs.setOnClickListener(this::launchRatings); - binding.aboutCredits.setOnClickListener(this::launchCredits); - binding.aboutPrivacyPolicy.setOnClickListener(this::launchPrivacyPolicy); - binding.aboutUserGuide.setOnClickListener(this::launchUserGuide); - binding.aboutFaq.setOnClickListener(this::launchFrequentlyAskedQuesions); - binding.aboutTranslate.setOnClickListener(this::launchTranslate); - } - - @Override - public boolean onSupportNavigateUp() { - onBackPressed(); - return true; - } - - public void launchFacebook(View view) { - Intent intent; - try { - intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL)); - intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME); - startActivity(intent); - } catch (Exception e) { - Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL)); - } - } - - public void launchGithub(View view) { - Intent intent; - try { - intent = new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL)); - intent.setPackage(Urls.GITHUB_PACKAGE_NAME); - startActivity(intent); - } catch (Exception e) { - Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL)); - } - } - - public void launchWebsite(View view) { - Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL)); - } - - public void launchRatings(View view){ - Utils.rateApp(this); - } - - public void launchCredits(View view) { - Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL)); - } - - public void launchUserGuide(View view) { - Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL)); - } - - public void launchPrivacyPolicy(View view) { - Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)); - } - - public void launchFrequentlyAskedQuesions(View view) { - Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL)); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.menu_about, menu); - return super.onCreateOptionsMenu(menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case R.id.share_app_icon: - String shareText = String.format(getString(R.string.share_text), Urls.PLAY_STORE_URL_PREFIX + this.getPackageName()); - Intent sendIntent = new Intent(); - sendIntent.setAction(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_TEXT, shareText); - sendIntent.setType("text/plain"); - startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via))); - return true; - default: - return super.onOptionsItemSelected(item); - } - } - - public void launchTranslate(View view) { - @NonNull List sortedLocalizedNamesRef = CommonsApplication.getInstance().getLanguageLookUpTable().getCanonicalNames(); - Collections.sort(sortedLocalizedNamesRef); - final ArrayAdapter languageAdapter = new ArrayAdapter<>(AboutActivity.this, - android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef); - final Spinner spinner = new Spinner(AboutActivity.this); - spinner.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)); - spinner.setAdapter(languageAdapter); - spinner.setGravity(17); - spinner.setPadding(50,0,0,0); - - Runnable positiveButtonRunnable = () -> { - String langCode = CommonsApplication.getInstance().getLanguageLookUpTable().getCodes().get(spinner.getSelectedItemPosition()); - Utils.handleWebUrl(AboutActivity.this, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode)); - }; - DialogUtil.showAlertDialog(this, - getString(R.string.about_translate_title), - getString(R.string.about_translate_message), - getString(R.string.about_translate_proceed), - getString(R.string.about_translate_cancel), - positiveButtonRunnable, - () -> {}, - spinner - ); - } - -} diff --git a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt new file mode 100644 index 000000000..143f5e569 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt @@ -0,0 +1,209 @@ +package fr.free.nrw.commons + +import android.annotation.SuppressLint +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 android.widget.ArrayAdapter +import android.widget.LinearLayout +import android.widget.Spinner +import fr.free.nrw.commons.CommonsApplication.Companion.instance +import fr.free.nrw.commons.databinding.ActivityAboutBinding +import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import java.util.Collections + +/** + * Represents about screen of this app + */ +class AboutActivity : BaseActivity() { + /* + This View Binding class is auto-generated for each xml file. The format is usually the name + of the file with PascalCasing (The underscore characters will be ignored). + More information is available at https://developer.android.com/topic/libraries/view-binding + */ + private var binding: ActivityAboutBinding? = null + + /** + * This method helps in the creation About screen + * + * @param savedInstanceState Data bundle + */ + @SuppressLint("StringFormatInvalid") //TODO: + public override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + /* + Instead of just setting the view with the xml file. We need to use View Binding class. + */ + binding = ActivityAboutBinding.inflate(layoutInflater) + val view: View = binding!!.root + setContentView(view) + + setSupportActionBar(binding!!.toolbarBinding.toolbar) + supportActionBar!!.setDisplayHomeAsUpEnabled(true) + val aboutText = getString(R.string.about_license) + /* + We can then access all the views by just using the id names like this. + camelCasing is used with underscore characters being ignored. + */ + binding!!.aboutLicense.setHtmlText(aboutText) + + @SuppressLint("StringFormatMatches") // TODO: + val improveText = + String.format(getString(R.string.about_improve), Urls.NEW_ISSUE_URL) + binding!!.aboutImprove.setHtmlText(improveText) + binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha() + + Utils.setUnderlinedText( + binding!!.aboutFaq, R.string.about_faq, + applicationContext + ) + Utils.setUnderlinedText( + binding!!.aboutRateUs, R.string.about_rate_us, + applicationContext + ) + Utils.setUnderlinedText( + binding!!.aboutUserGuide, R.string.user_guide, + applicationContext + ) + Utils.setUnderlinedText( + binding!!.aboutPrivacyPolicy, R.string.about_privacy_policy, + applicationContext + ) + Utils.setUnderlinedText( + binding!!.aboutTranslate, R.string.about_translate, + applicationContext + ) + Utils.setUnderlinedText( + binding!!.aboutCredits, R.string.about_credits, + applicationContext + ) + + /* + To set listeners, we can create a separate method and use lambda syntax. + */ + binding!!.facebookLaunchIcon.setOnClickListener(::launchFacebook) + binding!!.githubLaunchIcon.setOnClickListener(::launchGithub) + binding!!.websiteLaunchIcon.setOnClickListener(::launchWebsite) + binding!!.aboutRateUs.setOnClickListener(::launchRatings) + binding!!.aboutCredits.setOnClickListener(::launchCredits) + binding!!.aboutPrivacyPolicy.setOnClickListener(::launchPrivacyPolicy) + binding!!.aboutUserGuide.setOnClickListener(::launchUserGuide) + binding!!.aboutFaq.setOnClickListener(::launchFrequentlyAskedQuesions) + binding!!.aboutTranslate.setOnClickListener(::launchTranslate) + } + + override fun onSupportNavigateUp(): Boolean { + onBackPressed() + return true + } + + fun launchFacebook(view: View?) { + val intent: Intent + try { + intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL)) + intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME) + startActivity(intent) + } catch (e: Exception) { + Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL)) + } + } + + fun launchGithub(view: View?) { + val intent: Intent + try { + intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL)) + intent.setPackage(Urls.GITHUB_PACKAGE_NAME) + startActivity(intent) + } catch (e: Exception) { + Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL)) + } + } + + fun launchWebsite(view: View?) { + Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL)) + } + + fun launchRatings(view: View?) { + Utils.rateApp(this) + } + + fun launchCredits(view: View?) { + Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL)) + } + + fun launchUserGuide(view: View?) { + Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL)) + } + + fun launchPrivacyPolicy(view: View?) { + Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) + } + + fun launchFrequentlyAskedQuesions(view: View?) { + Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL)) + } + + override fun onCreateOptionsMenu(menu: Menu): Boolean { + val inflater = menuInflater + inflater.inflate(R.menu.menu_about, menu) + return super.onCreateOptionsMenu(menu) + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { + R.id.share_app_icon -> { + val shareText = String.format( + getString(R.string.share_text), + Urls.PLAY_STORE_URL_PREFIX + this.packageName + ) + val sendIntent = Intent() + sendIntent.setAction(Intent.ACTION_SEND) + sendIntent.putExtra(Intent.EXTRA_TEXT, shareText) + sendIntent.setType("text/plain") + startActivity(Intent.createChooser(sendIntent, getString(R.string.share_via))) + return true + } + + else -> return super.onOptionsItemSelected(item) + } + } + + fun launchTranslate(view: View?) { + val sortedLocalizedNamesRef = instance.languageLookUpTable!!.getCanonicalNames() + Collections.sort(sortedLocalizedNamesRef) + val languageAdapter = ArrayAdapter( + this@AboutActivity, + android.R.layout.simple_spinner_dropdown_item, sortedLocalizedNamesRef + ) + val spinner = Spinner(this@AboutActivity) + spinner.layoutParams = + LinearLayout.LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ) + spinner.adapter = languageAdapter + spinner.gravity = 17 + spinner.setPadding(50, 0, 0, 0) + + val positiveButtonRunnable = Runnable { + val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition] + Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode)) + } + showAlertDialog( + this, + getString(R.string.about_translate_title), + getString(R.string.about_translate_message), + getString(R.string.about_translate_proceed), + getString(R.string.about_translate_cancel), + positiveButtonRunnable, + {}, + spinner + ) + } +} From 9dc9a3b8abc696e46d941b7ea1046c1a9e3f7cbd Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Thu, 30 Jan 2025 13:01:33 +0100 Subject: [PATCH 03/14] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index 2b54dfaa6..f3b7d8467 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -20,6 +20,7 @@ * NancyMilad * OsamaK * Tala Ali +* XIDME * أيوب * أَحمد * ترجمان05 @@ -408,7 +409,7 @@ خطأ في جلب المعالم القريبة. لا توجد عمليات بحث حديثة هل أنت متأكد من أنك تريد مسح سجل بحثك؟ - هل انت متأكد انك تريد الغاء هذا التحميل + هل أنت متأكد أنك تريد إلغاء هذا التحميل؟ هل تريد حذف هذا البحث؟ تم حذف سجل البحث ترشيح للحذف From 7b291535e0a16e16ba1455a87cff97fc2b07af87 Mon Sep 17 00:00:00 2001 From: Ifeoluwa Andrew Omole Date: Thu, 30 Jan 2025 13:58:00 +0100 Subject: [PATCH 04/14] Feat: Make it smoother to switch between nearby and explore maps (#6164) * Nearby: Add 'Show in Explore' 3-dots menu item * MainActivity: Add methods to pass extras between Nearby and Explore * MainActivity: Extend loadFragment() to support passing fragment arguments * Nearby: Add ability to navigate to Explore fragment on 'Show in Explore' click * Explore: Read fragment arguments for Nearby map data and update Explore map if present * Explore: Add 'Show in Nearby' 3-dots menu item. Only visible when Map tab is selected * Explore: On 'Show in Nearby' click, navigate to Nearby fragment, passing map data as fragment args * Nearby: Read fragment arguments for Explore map data and update Nearby map if present * MainActivity: Fix memory leaks when navigating between bottom nav destinations * Explore: Fix crashes caused by unattached map fragment * Refactor code to pass unit tests * Explore: Format javadocs --------- Co-authored-by: Nicolas Raoul --- .gitignore | 3 +- .../commons/contributions/MainActivity.java | 64 ++++ .../nrw/commons/explore/ExploreFragment.java | 85 ++++- .../explore/ExploreMapRootFragment.java | 19 +- .../explore/map/ExploreMapFragment.java | 318 +++++++++++------- .../fragments/NearbyParentFragment.java | 132 ++++++-- .../main/res/menu/explore_fragment_menu.xml | 19 ++ .../main/res/menu/nearby_fragment_menu.xml | 6 + app/src/main/res/values/strings.xml | 2 + .../explore/ExploreFragmentUnitTest.kt | 14 +- 10 files changed, 510 insertions(+), 152 deletions(-) create mode 100644 app/src/main/res/menu/explore_fragment_menu.xml diff --git a/.gitignore b/.gitignore index e54ea2551..7fa4767a7 100644 --- a/.gitignore +++ b/.gitignore @@ -46,4 +46,5 @@ captures/* # Test and other output app/jacoco.exec -app/CommonsContributions \ No newline at end of file +app/CommonsContributions +app/.* diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java index 03027f287..047943721 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java @@ -207,6 +207,9 @@ public class MainActivity extends BaseActivity private boolean loadFragment(Fragment fragment, boolean showBottom) { //showBottom so that we do not show the bottom tray again when constructing //from the saved instance state. + + freeUpFragments(); + if (fragment instanceof ContributionsFragment) { if (activeFragment == ActiveFragment.CONTRIBUTIONS) { // scroll to top if already on the Contributions tab @@ -256,6 +259,31 @@ public class MainActivity extends BaseActivity return false; } + /** + * loadFragment() overload that supports passing extras to fragments + **/ + private boolean loadFragment(Fragment fragment, boolean showBottom, Bundle args) { + if (fragment != null && args != null) { + fragment.setArguments(args); + } + + return loadFragment(fragment, showBottom); + } + + /** + * Old implementation of loadFragment() was causing memory leaks, due to MainActivity holding + * references to cleared fragments. This function frees up all fragment references. + *

+ * Called in loadFragment() before doing the actual loading. + **/ + public void freeUpFragments() { + // free all fragments except contributionsFragment because several tests depend on it. + // hence, contributionsFragment is probably still a leak + nearbyParentFragment = null; + exploreFragment = null; + bookmarkFragment = null; + } + public void hideTabs() { binding.fragmentMainNavTabLayout.setVisibility(View.GONE); } @@ -432,6 +460,42 @@ public class MainActivity extends BaseActivity }); } + /** + * Launch the Explore fragment from Nearby fragment. This method is called when a user clicks + * the 'Show in Explore' option in the 3-dots menu in Nearby. + * + * @param zoom current zoom of Nearby map + * @param latitude current latitude of Nearby map + * @param longitude current longitude of Nearby map + **/ + public void loadExploreMapFromNearby(double zoom, double latitude, double longitude) { + Bundle bundle = new Bundle(); + bundle.putDouble("prev_zoom", zoom); + bundle.putDouble("prev_latitude", latitude); + bundle.putDouble("prev_longitude", longitude); + + loadFragment(ExploreFragment.newInstance(), false, bundle); + setSelectedItemId(NavTab.EXPLORE.code()); + } + + /** + * Launch the Nearby fragment from Explore fragment. This method is called when a user clicks + * the 'Show in Nearby' option in the 3-dots menu in Explore. + * + * @param zoom current zoom of Explore map + * @param latitude current latitude of Explore map + * @param longitude current longitude of Explore map + **/ + public void loadNearbyMapFromExplore(double zoom, double latitude, double longitude) { + Bundle bundle = new Bundle(); + bundle.putDouble("prev_zoom", zoom); + bundle.putDouble("prev_latitude", latitude); + bundle.putDouble("prev_longitude", longitude); + + loadFragment(NearbyParentFragment.newInstance(), false, bundle); + setSelectedItemId(NavTab.NEARBY.code()); + } + @Override protected void onResume() { super.onResume(); diff --git a/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.java b/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.java index d444148d4..223d028dc 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/ExploreFragment.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons.explore; +import static androidx.viewpager.widget.ViewPager.SCROLL_STATE_IDLE; + import android.os.Bundle; import android.view.LayoutInflater; import android.view.Menu; @@ -42,9 +44,13 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { @Named("default_preferences") public JsonKvStore applicationKvStore; - public void setScroll(boolean canScroll){ - if (binding != null) - { + // Nearby map state (for if we came from Nearby fragment) + private double prevZoom; + private double prevLatitude; + private double prevLongitude; + + public void setScroll(boolean canScroll) { + if (binding != null) { binding.viewPager.setCanScroll(canScroll); } } @@ -60,6 +66,7 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); + loadNearbyMapData(); binding = FragmentExploreBinding.inflate(inflater, container, false); viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager()); @@ -89,6 +96,11 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { }); setTabs(); setHasOptionsMenu(true); + + // if we came from 'Show in Explore' in Nearby, jump to Map tab + if (isCameFromNearbyMap()) { + binding.viewPager.setCurrentItem(2); + } return binding.getRoot(); } @@ -108,6 +120,13 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { Bundle mapArguments = new Bundle(); mapArguments.putString("categoryName", EXPLORE_MAP); + // if we came from 'Show in Explore' in Nearby, pass on zoom and center to Explore map root + if (isCameFromNearbyMap()) { + mapArguments.putDouble("prev_zoom", prevZoom); + mapArguments.putDouble("prev_latitude", prevLatitude); + mapArguments.putDouble("prev_longitude", prevLongitude); + } + featuredRootFragment = new ExploreListRootFragment(featuredArguments); mobileRootFragment = new ExploreListRootFragment(mobileArguments); mapRootFragment = new ExploreMapRootFragment(mapArguments); @@ -120,13 +139,35 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { fragmentList.add(mapRootFragment); titleList.add(getString(R.string.explore_tab_title_map).toUpperCase(Locale.ROOT)); - ((MainActivity)getActivity()).showTabs(); + ((MainActivity) getActivity()).showTabs(); ((BaseActivity) getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(false); viewPagerAdapter.setTabData(fragmentList, titleList); viewPagerAdapter.notifyDataSetChanged(); } + /** + * Fetch Nearby map camera data from fragment arguments if any. + */ + public void loadNearbyMapData() { + // get fragment arguments + if (getArguments() != null) { + prevZoom = getArguments().getDouble("prev_zoom"); + prevLatitude = getArguments().getDouble("prev_latitude"); + prevLongitude = getArguments().getDouble("prev_longitude"); + } + } + + /** + * Checks if fragment arguments contain data from Nearby map. if present, then 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; + } + public boolean onBackPressed() { if (binding.tabLayout.getSelectedTabPosition() == 0) { if (featuredRootFragment.backPressed()) { @@ -155,7 +196,38 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { */ @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_search, menu); + // if logged in 'Show in Nearby' menu item is visible + if (applicationKvStore.getBoolean("login_skipped") == false) { + inflater.inflate(R.menu.explore_fragment_menu, menu); + + MenuItem others = menu.findItem(R.id.list_item_show_in_nearby); + + if (binding.viewPager.getCurrentItem() == 2) { + others.setVisible(true); + } + + // if on Map tab, show all menu options, else only show search + binding.viewPager.addOnPageChangeListener(new OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, + int positionOffsetPixels) { + } + + @Override + public void onPageSelected(int position) { + others.setVisible((position == 2)); + } + + @Override + public void onPageScrollStateChanged(int state) { + if (state == SCROLL_STATE_IDLE && binding.viewPager.getCurrentItem() == 2) { + onPageSelected(2); + } + } + }); + } else { + inflater.inflate(R.menu.menu_search, menu); + } super.onCreateOptionsMenu(menu, inflater); } @@ -171,6 +243,9 @@ public class ExploreFragment extends CommonsDaggerSupportFragment { case R.id.action_search: ActivityUtils.startActivityWithFlags(getActivity(), SearchActivity.class); return true; + case R.id.list_item_show_in_nearby: + mapRootFragment.loadNearbyMapFromExplore(); + return true; default: return super.onOptionsItemSelected(item); } 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 2653b4409..abf02758d 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 @@ -39,10 +39,22 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme } public ExploreMapRootFragment(Bundle bundle) { + // get fragment arguments String title = bundle.getString("categoryName"); + double zoom = bundle.getDouble("prev_zoom"); + double latitude = bundle.getDouble("prev_latitude"); + double longitude = bundle.getDouble("prev_longitude"); + mapFragment = new ExploreMapFragment(); Bundle featuredArguments = new Bundle(); featuredArguments.putString("categoryName", title); + + // if we came from 'Show in Explore' in Nearby, pass on zoom and center + if (zoom != 0.0 || latitude != 0.0 || longitude != 0.0) { + featuredArguments.putDouble("prev_zoom", zoom); + featuredArguments.putDouble("prev_latitude", latitude); + featuredArguments.putDouble("prev_longitude", longitude); + } mapFragment.setArguments(featuredArguments); } @@ -198,7 +210,8 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme ((MainActivity) getActivity()).showTabs(); return true; - } if (mapFragment != null && mapFragment.isVisible()) { + } + if (mapFragment != null && mapFragment.isVisible()) { if (mapFragment.backButtonClicked()) { // Explore map fragment handled the event no further action required. return true; @@ -213,6 +226,10 @@ public class ExploreMapRootFragment extends CommonsDaggerSupportFragment impleme return false; } + public void loadNearbyMapFromExplore() { + mapFragment.loadNearbyMapFromExplore(); + } + @Override public void onDestroy() { super.onDestroy(); 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.java index ad42d9518..e64b96190 100644 --- 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.java @@ -38,6 +38,7 @@ 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; @@ -115,6 +116,11 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment SystemThemeUtils systemThemeUtils; LocationPermissionsHelper locationPermissionsHelper; + // Nearby map state (if we came from Nearby) + private double prevZoom; + private double prevLatitude; + private double prevLongitude; + private ExploreMapPresenter presenter; public FragmentExploreMapBinding binding; @@ -160,6 +166,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment ViewGroup container, Bundle savedInstanceState ) { + loadNearbyMapData(); binding = FragmentExploreMapBinding.inflate(getLayoutInflater()); return binding.getRoot(); } @@ -169,12 +176,14 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment 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)); + binding.tvAttribution.setText( + Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); } else { binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); } initNetworkBroadCastReceiver(); - locationPermissionsHelper = new LocationPermissionsHelper(getActivity(),locationManager,this); + locationPermissionsHelper = new LocationPermissionsHelper(getActivity(), locationManager, + this); if (presenter == null) { presenter = new ExploreMapPresenter(bookmarkLocationDao); } @@ -204,9 +213,14 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment scaleBarOverlay.setBackgroundPaint(barPaint); scaleBarOverlay.enableScaleBar(); binding.mapView.getOverlays().add(scaleBarOverlay); - binding.mapView.getZoomController().setVisibility(CustomZoomButtonsController.Visibility.NEVER); + binding.mapView.getZoomController() + .setVisibility(CustomZoomButtonsController.Visibility.NEVER); binding.mapView.setMultiTouchControls(true); - binding.mapView.getController().setZoom(ZOOM_LEVEL); + + if (!isCameFromNearbyMap()) { + binding.mapView.getController().setZoom(ZOOM_LEVEL); + } + performMapReadyActions(); binding.mapView.getOverlays().add(new MapEventsOverlay(new MapEventsReceiver() { @@ -295,7 +309,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment unregisterNetworkReceiver(); } - + /** * Unregisters the networkReceiver */ @@ -328,11 +342,51 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment isPermissionDenied = true; } lastKnownLocation = MapUtils.getDefaultLatLng(); - moveCameraToPosition( - new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); + + // if we came from 'Show in Explore' in Nearby, load Nearby map center and zoom + if (isCameFromNearbyMap()) { + moveCameraToPosition( + new GeoPoint(prevLatitude, prevLongitude), + prevZoom, + 1L + ); + } else { + moveCameraToPosition( + new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); + } presenter.onMapReady(exploreMapController); } + /** + * Fetch Nearby map camera data from fragment arguments if any. + */ + public void loadNearbyMapData() { + // get fragment arguments + if (getArguments() != null) { + prevZoom = getArguments().getDouble("prev_zoom"); + prevLatitude = getArguments().getDouble("prev_latitude"); + prevLongitude = getArguments().getDouble("prev_longitude"); + } + } + + /** + * Checks if fragment arguments contain data from 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; + } + + public void loadNearbyMapFromExplore() { + ((MainActivity) getContext()).loadNearbyMapFromExplore( + binding.mapView.getZoomLevelDouble(), + binding.mapView.getMapCenter().getLatitude(), + binding.mapView.getMapCenter().getLongitude() + ); + } + private void initViews() { Timber.d("init views called"); initBottomSheets(); @@ -346,7 +400,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment */ @SuppressLint("ClickableViewAccessibility") private void initBottomSheets() { - bottomSheetDetailsBehavior = BottomSheetBehavior.from(binding.bottomSheetDetailsBinding.getRoot()); + bottomSheetDetailsBehavior = BottomSheetBehavior.from( + binding.bottomSheetDetailsBinding.getRoot()); bottomSheetDetailsBehavior.setState(BottomSheetBehavior.STATE_HIDDEN); binding.bottomSheetDetailsBinding.getRoot().setVisibility(View.VISIBLE); } @@ -404,7 +459,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment if (currentLatLng == null) { return; } - if (currentLatLng.equals(getLastMapFocus())) { // Means we are checking around current location + if (currentLatLng.equals( + getLastMapFocus())) { // Means we are checking around current location nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(currentLatLng, getLastMapFocus(), true); } else { @@ -416,11 +472,12 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment .observeOn(AndroidSchedulers.mainThread()) .subscribe(explorePlacesInfo -> { mediaList = explorePlacesInfo.mediaList; - if(mediaList == null) { + if (mediaList == null) { showResponseMessage(getString(R.string.no_pictures_in_this_area)); } updateMapMarkers(explorePlacesInfo); - lastMapFocus = new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude()); + lastMapFocus = new GeoPoint(currentLatLng.getLatitude(), + currentLatLng.getLongitude()); }, throwable -> { Timber.d(throwable); @@ -474,9 +531,9 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); locationManager.requestLocationUpdatesFromProvider(LocationManager.GPS_PROVIDER); setProgressBarVisibility(true); - } - else { - locationPermissionsHelper.showLocationOffDialog(getActivity(), R.string.ask_to_turn_location_on_text); + } else { + locationPermissionsHelper.showLocationOffDialog(getActivity(), + R.string.ask_to_turn_location_on_text); } presenter.onMapReady(exploreMapController); registerUnregisterLocationListener(false); @@ -508,7 +565,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment recenterToUserLocation = true; return; } - recenterMarkerToPosition(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); + recenterMarkerToPosition( + new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); binding.mapView.getController() .animateTo(new GeoPoint(currentLatLng.getLatitude(), currentLatLng.getLongitude())); if (lastMapFocus != null) { @@ -549,7 +607,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment view -> Utils.handleGeoCoordinates(getActivity(), place.getLocation(), binding.mapView.getZoomLevelDouble())); - binding.bottomSheetDetailsBinding.commonsButton.setVisibility(place.hasCommonsLink() ? View.VISIBLE : View.GONE); + binding.bottomSheetDetailsBinding.commonsButton.setVisibility( + place.hasCommonsLink() ? View.VISIBLE : View.GONE); binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); @@ -563,7 +622,8 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment } index++; } - binding.bottomSheetDetailsBinding.title.setText(place.name.substring(5, place.name.lastIndexOf("."))); + 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() @@ -641,40 +701,43 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment * @param nearbyBaseMarker The NearbyBaseMarker object representing the marker to be added. */ private void addMarkerToMap(BaseMarker nearbyBaseMarker) { - 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); + 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; } - clickedMarker = nearbyBaseMarker; - passInfoToSheet(place); - return true; - } - @Override - public boolean onItemLongPress(int index, OverlayItem item) { - return false; - } - }, getContext()); + @Override + public boolean onItemLongPress(int index, OverlayItem item) { + return false; + } + }, getContext()); - overlay.setFocusItemsOnTap(true); - binding.mapView.getOverlays().add(overlay); // Add the overlay to the map + overlay.setFocusItemsOnTap(true); + binding.mapView.getOverlays().add(overlay); // Add the overlay to the map + } } /** @@ -708,68 +771,72 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment */ @Override public void clearAllMarkers() { - 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"); - } - 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; + 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"); + } + 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.setMultiTouchControls(true); + @Override + public boolean longPressHelper(GeoPoint p) { + return false; + } + })); + binding.mapView.setMultiTouchControls(true); + } } /** @@ -826,6 +893,18 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment binding.mapView.getController().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); + } + @Override public fr.free.nrw.commons.location.LatLng getLastMapFocus() { return lastMapFocus == null ? getMapCenter() : new fr.free.nrw.commons.location.LatLng( @@ -851,14 +930,17 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment -0.07483536015053005, 1f); } } - moveCameraToPosition(new GeoPoint(latLnge.getLatitude(),latLnge.getLongitude())); + if (!isCameFromNearbyMap()) { + moveCameraToPosition(new GeoPoint(latLnge.getLatitude(), latLnge.getLongitude())); + } return latLnge; } @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); + binding.mapView.getMapCenter().getLatitude(), + binding.mapView.getMapCenter().getLongitude(), 100); return mapFocusedLatLng; } @@ -911,9 +993,19 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment }; } - @Override - public void onLocationPermissionDenied(String toastMessage) {} + /** + * helper function to confirm that this fragment has been attached. + **/ + public boolean isAttachedToActivity() { + boolean attached = isVisible() && getActivity() != null; + return attached; + } @Override - public void onLocationPermissionGranted() {} + public void onLocationPermissionDenied(String toastMessage) { + } + + @Override + public void onLocationPermissionGranted() { + } } 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 ee15d13ac..95f19f699 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 @@ -233,6 +233,11 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment private Place nearestPlace; private volatile boolean stopQuery; + // Explore map data (for if we came from Explore) + private double prevZoom; + private double prevLatitude; + private double prevLongitude; + private final Handler searchHandler = new Handler(); private Runnable searchRunnable; @@ -247,27 +252,28 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment private final ActivityResultLauncher galleryPickLauncherForResult = registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromGallery(result, requireActivity(), callbacks); + }); }); - }); private final ActivityResultLauncher customSelectorLauncherForResult = registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCustomSelector(result, requireActivity(), callbacks); + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCustomSelector(result, requireActivity(), + callbacks); + }); }); - }); private final ActivityResultLauncher cameraPickLauncherForResult = registerForActivityResult(new StartActivityForResult(), - result -> { - controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { - controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + result -> { + controller.handleActivityResultWithCallback(requireActivity(), callbacks -> { + controller.onPictureReturnedFromCamera(result, requireActivity(), callbacks); + }); }); - }); private ActivityResultLauncher inAppCameraLocationPermissionLauncher = registerForActivityResult( new RequestMultiplePermissions(), @@ -337,12 +343,15 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment @Override public View onCreateView(@NonNull final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { + loadExploreMapData(); + binding = FragmentNearbyParentBinding.inflate(inflater, container, false); view = binding.getRoot(); initNetworkBroadCastReceiver(); scope = LifecycleOwnerKt.getLifecycleScope(getViewLifecycleOwner()); - presenter = new NearbyParentFragmentPresenter(bookmarkLocationDao, placesRepository, nearbyController); + presenter = new NearbyParentFragmentPresenter(bookmarkLocationDao, placesRepository, + nearbyController); progressDialog = new ProgressDialog(getActivity()); progressDialog.setCancelable(false); progressDialog.setMessage("Saving in progress..."); @@ -359,6 +368,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment inflater.inflate(R.menu.nearby_fragment_menu, menu); MenuItem refreshButton = menu.findItem(R.id.item_refresh); MenuItem listMenu = menu.findItem(R.id.list_sheet); + MenuItem showInExploreButton = menu.findItem(R.id.list_item_show_in_explore); MenuItem saveAsGPXButton = menu.findItem(R.id.list_item_gpx); MenuItem saveAsKMLButton = menu.findItem(R.id.list_item_kml); refreshButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { @@ -379,6 +389,17 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment return false; } }); + showInExploreButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { + @Override + public boolean onMenuItemClick(@NonNull MenuItem item) { + ((MainActivity) getContext()).loadExploreMapFromNearby( + binding.map.getZoomLevelDouble(), + binding.map.getMapCenter().getLatitude(), + binding.map.getMapCenter().getLongitude() + ); + return false; + } + }); saveAsGPXButton.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override @@ -467,6 +488,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment binding.map.getOverlays().add(scaleBarOverlay); binding.map.getZoomController().setVisibility(Visibility.NEVER); binding.map.getController().setZoom(ZOOM_LEVEL); + // if we came from Explore map using 'Show in Nearby', load Explore map camera position + if (isCameFromExploreMap()) { + moveCameraToPosition( + new GeoPoint(prevLatitude, prevLongitude), + prevZoom, + 1L + ); + } binding.map.getOverlays().add(mapEventsOverlay); binding.map.addMapListener(new MapListener() { @@ -489,11 +518,14 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } initNearbyFilter(); addCheckBoxCallback(); - moveCameraToPosition(lastMapFocus); + if (!isCameFromExploreMap()) { + moveCameraToPosition(lastMapFocus); + } initRvNearbyList(); onResume(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); + binding.tvAttribution.setText( + Html.fromHtml(getString(R.string.map_attribution), Html.FROM_HTML_MODE_LEGACY)); } else { //noinspection deprecation binding.tvAttribution.setText(Html.fromHtml(getString(R.string.map_attribution))); @@ -545,6 +577,28 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } } + /** + * Fetch Explore map camera data from fragment arguments if any. + */ + public void loadExploreMapData() { + // get fragment arguments + if (getArguments() != null) { + prevZoom = getArguments().getDouble("prev_zoom"); + prevLatitude = getArguments().getDouble("prev_latitude"); + prevLongitude = getArguments().getDouble("prev_longitude"); + } + } + + /** + * Checks if fragment arguments contain data from Explore map. if present, then the user + * navigated from Explore using 'Show in Nearby'. + * + * @return true if user navigated from Explore map + **/ + public boolean isCameFromExploreMap() { + return prevZoom != 0.0 || prevLatitude != 0.0 || prevLongitude != 0.0; + } + /** * Initialise background based on theme, this should be doe ideally via styles, that would need * another refactor @@ -625,7 +679,9 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment mapCenter = targetP; binding.map.getController().setCenter(targetP); recenterMarkerToPosition(targetP); - moveCameraToPosition(targetP); + if (!isCameFromExploreMap()) { + moveCameraToPosition(targetP); + } } else if (locationManager.isGPSProviderEnabled() || locationManager.isNetworkProviderEnabled()) { locationManager.requestLocationUpdatesFromProvider(LocationManager.NETWORK_PROVIDER); @@ -669,7 +725,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } else { lastKnownLocation = MapUtils.getDefaultLatLng(); } - if (binding.map != null) { + if (binding.map != null && !isCameFromExploreMap()) { moveCameraToPosition( new GeoPoint(lastKnownLocation.getLatitude(), lastKnownLocation.getLongitude())); } @@ -739,8 +795,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } /** - * Determines the number of spans (columns) in the RecyclerView based on device orientation - * and adapter item count. + * Determines the number of spans (columns) in the RecyclerView based on device orientation and + * adapter item count. * * @return The number of spans to be used in the RecyclerView. */ @@ -1175,7 +1231,6 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment /** * Clears the Nearby local cache and then calls for pin details to be fetched afresh. - * */ private void emptyCache() { // reload the map once the cache is cleared @@ -1338,7 +1393,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } /** - * Fetches and updates the data for a specific place, then updates the corresponding marker on the map. + * Fetches and updates the data for a specific place, then updates the corresponding marker on + * the map. * * @param entity The entity ID of the place. * @param place The Place object containing the initial place data. @@ -1469,9 +1525,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } /** - * Stops any ongoing queries and clears all disposables. - * This method sets the stopQuery flag to true and clears the compositeDisposable - * to prevent any further processing. + * Stops any ongoing queries and clears all disposables. This method sets the stopQuery flag to + * true and clears the compositeDisposable to prevent any further processing. */ @Override public void stopQuery() { @@ -1624,7 +1679,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment new Builder(getContext()) .setMessage(R.string.login_alert_message) .setCancelable(false) - .setNegativeButton(R.string.cancel, (dialog, which) -> {}) + .setNegativeButton(R.string.cancel, (dialog, which) -> { + }) .setPositiveButton(R.string.login, (dialog, which) -> { // logout of the app BaseLogoutListener logoutListener = new BaseLogoutListener(getActivity()); @@ -1743,7 +1799,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment final boolean filterForPlaceState, final boolean filterForAllNoneType) { final boolean displayExists = false; - final boolean displayNeedsPhoto= false; + final boolean displayNeedsPhoto = false; final boolean displayWlm = false; if (selectedLabels == null || selectedLabels.size() == 0) { replaceMarkerOverlays(NearbyController.markerLabelList); @@ -1903,8 +1959,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment /** * Adds multiple markers representing places to the map and handles item gestures. * - * @param markerPlaceGroups The list of marker place groups containing the places and - * their bookmarked status + * @param markerPlaceGroups The list of marker place groups containing the places and their + * bookmarked status */ @Override public void replaceMarkerOverlays(final List markerPlaceGroups) { @@ -1913,7 +1969,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment for (int i = markerPlaceGroups.size() - 1; i >= 0; i--) { newMarkers.add( convertToMarker(markerPlaceGroups.get(i).getPlace(), - markerPlaceGroups.get(i).getIsBookmarked()) + markerPlaceGroups.get(i).getIsBookmarked()) ); } clearAllMarkers(); @@ -2103,7 +2159,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment if (binding.fabCamera.isShown()) { Timber.d("Camera button tapped. Place: %s", selectedPlace.toString()); storeSharedPrefs(selectedPlace); - controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, cameraPickLauncherForResult); + controller.initiateCameraPick(getActivity(), inAppCameraLocationPermissionLauncher, + cameraPickLauncherForResult); } }); @@ -2121,7 +2178,8 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment if (binding.fabCustomGallery.isShown()) { Timber.d("Gallery button tapped. Place: %s", selectedPlace.toString()); storeSharedPrefs(selectedPlace); - controller.initiateCustomGalleryPickWithPermission(getActivity(), customSelectorLauncherForResult); + controller.initiateCustomGalleryPickWithPermission(getActivity(), + customSelectorLauncherForResult); } }); } @@ -2296,6 +2354,18 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment binding.map.getController().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.map.getController().animateTo(geoPoint, zoom, speed); + } + @Override public void onBottomSheetItemClick(@Nullable View view, int position) { BottomSheetItem item = dataList.get(position); diff --git a/app/src/main/res/menu/explore_fragment_menu.xml b/app/src/main/res/menu/explore_fragment_menu.xml new file mode 100644 index 000000000..4dce1c57c --- /dev/null +++ b/app/src/main/res/menu/explore_fragment_menu.xml @@ -0,0 +1,19 @@ + +

+ + + + \ No newline at end of file diff --git a/app/src/main/res/menu/nearby_fragment_menu.xml b/app/src/main/res/menu/nearby_fragment_menu.xml index fe049cde4..e7c23ed89 100644 --- a/app/src/main/res/menu/nearby_fragment_menu.xml +++ b/app/src/main/res/menu/nearby_fragment_menu.xml @@ -12,6 +12,12 @@ android:icon="@drawable/ic_list_white_24dp" /> + + Caption copied to clipboard Congratulations, all pictures in this album have been either uploaded or marked as not for upload. + Show in Explore + Show in Nearby diff --git a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt index 41c999791..e2ef3bb92 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/explore/ExploreFragmentUnitTest.kt @@ -11,16 +11,19 @@ import androidx.fragment.app.FragmentManager import androidx.fragment.app.FragmentTransaction import androidx.test.core.app.ApplicationProvider import com.google.android.material.tabs.TabLayout +import com.nhaarman.mockitokotlin2.eq import fr.free.nrw.commons.OkHttpConnectionFactory import fr.free.nrw.commons.R import fr.free.nrw.commons.TestCommonsApplication import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.createTestClient import org.junit.Assert +import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Ignore import org.junit.Test import org.junit.runner.RunWith +import org.mockito.ArgumentCaptor import org.mockito.Mock import org.mockito.Mockito.verify import org.mockito.Mockito.`when` @@ -34,6 +37,7 @@ import org.robolectric.annotation.LooperMode import org.robolectric.fakes.RoboMenu import org.robolectric.fakes.RoboMenuItem + @RunWith(RobolectricTestRunner::class) @Config(sdk = [21], application = TestCommonsApplication::class) @LooperMode(LooperMode.Mode.PAUSED) @@ -151,6 +155,14 @@ class ExploreFragmentUnitTest { Shadows.shadowOf(getMainLooper()).idle() val menu: Menu = RoboMenu(context) fragment.onCreateOptionsMenu(menu, inflater) - verify(inflater).inflate(R.menu.menu_search, menu) + + val captor = ArgumentCaptor.forClass( + Int::class.java + ) + verify(inflater).inflate(captor.capture(), eq(menu)) + + val capturedLayout = captor.value + assertTrue(capturedLayout == R.menu.menu_search || capturedLayout == R.menu.explore_fragment_menu) + } } From e6538574374267b833f5a436e21389d84ea38562 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Fri, 31 Jan 2025 09:49:38 +0100 Subject: [PATCH 05/14] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-ar/strings.xml | 3 +++ app/src/main/res/values-da/strings.xml | 2 ++ app/src/main/res/values-ko/strings.xml | 1 + app/src/main/res/values-ru/strings.xml | 3 +++ 4 files changed, 9 insertions(+) diff --git a/app/src/main/res/values-ar/strings.xml b/app/src/main/res/values-ar/strings.xml index f3b7d8467..577b031cb 100644 --- a/app/src/main/res/values-ar/strings.xml +++ b/app/src/main/res/values-ar/strings.xml @@ -5,6 +5,7 @@ * Asma * Azouz.anis * ButterflyOfFire +* Cigaryno * Claw eg * Dr-Taher * Dr. Mohammed @@ -884,4 +885,6 @@ الشرح تم نسخ التسمية التوضيحية إلى الحافظة مبروك، جميع الصور الموجودة في هذا الألبوم تم تحميلها أو تم وضع علامة عليها بأنها غير قابلة للتحميل. + عرض في استكشاف + عرض في المناطق القريبة diff --git a/app/src/main/res/values-da/strings.xml b/app/src/main/res/values-da/strings.xml index e7f66a69e..232f56c49 100644 --- a/app/src/main/res/values-da/strings.xml +++ b/app/src/main/res/values-da/strings.xml @@ -821,4 +821,6 @@ Billedtekst Billedtekst kopieret til udklipsholder Tillykke, alle billeder i dette album er enten blevet uploadet eller markeret som ikke til upload. + Vis i Udforsk + Vis i I nærheden diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index cd974dc30..1e482148e 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -709,6 +709,7 @@ 이 파일을 사용하는 문서 계정 계정 버리기 + 버리기는 <b>최후의 수단</b>이며 <b>영원히 편집을 중단하고 싶을 때</b>와 가능한 한 많은 과거 연관성을 숨기고 싶을 때만 사용해야 합니다.<br/><br/>위키미디어 공용에서 계정을 삭제하려면 계정 이름을 변경하여 다른 사람이 기여한 내용을 알아볼 수 없도록 하는 계정 버리기라는 프로세스를 거쳐야 합니다. <b>버리기는 완전한 익명성을 보장하지 않으며 프로젝트에 수행한 기여를 제거하지 않습니다</b>. 캡션 캡션이 클립보드에 복사되었습니다 diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 0131fef5d..1c01761e0 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -333,6 +333,7 @@ Копирование викикода в буфер обмена Викикод скопирован в буфер обмена Функция «Поблизости» может работать некорректно, определение местоположения недоступно. + Интернет недоступен. Показаны только кэшированные места. Доступ к местоположению запрещён. Чтобы использовать эту функцию, укажите своё местоположение вручную. Необходимо разрешение для отображения списка мест поблизости Необходимо разрешение для отображения списка мест поблизости @@ -869,7 +870,9 @@ Викисклад Другие вики Использование файла + SingleWebViewActivity Учётная запись Подпись Подпись скопирована в буфер обмена + Поздравляем, все фотографии в этом альбоме либо загружены, либо помечены как не предназначенные для загрузки. From 7566ddf529c4b7cbcbb771c05b6fe3655f92e0bf Mon Sep 17 00:00:00 2001 From: Parneet Singh <111801812+parneet-guraya@users.noreply.github.com> Date: Sat, 1 Feb 2025 05:43:17 +0530 Subject: [PATCH 06/14] enhance spammy category filter (#6167) Signed-off-by: parneet-guraya --- app/.attach_pid781771 | 0 .../nrw/commons/category/CategoriesModel.kt | 46 +++++++++---------- .../commons/category/CategoriesModelTest.kt | 39 ++++++++++++++++ 3 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 app/.attach_pid781771 diff --git a/app/.attach_pid781771 b/app/.attach_pid781771 new file mode 100644 index 000000000..e69de29bb diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt index fd90be95f..47147944c 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoriesModel.kt @@ -36,37 +36,35 @@ class CategoriesModel * @return */ fun isSpammyCategory(item: String): Boolean { - // Check for current and previous year to exclude these categories from removal - val now = Calendar.getInstance() - val curYear = now[Calendar.YEAR] - val curYearInString = curYear.toString() - val prevYear = curYear - 1 - val prevYearInString = prevYear.toString() - Timber.d("Previous year: %s", prevYearInString) - - val mentionsDecade = item.matches(".*0s.*".toRegex()) - val recentDecade = item.matches(".*20[0-2]0s.*".toRegex()) - val spammyCategory = - item.matches("(.*)needing(.*)".toRegex()) || - item.matches("(.*)taken on(.*)".toRegex()) // always skip irrelevant categories such as Media_needing_categories_as_of_16_June_2017(Issue #750) + val spammyCategory = item.matches("(.*)needing(.*)".toRegex()) + || item.matches("(.*)taken on(.*)".toRegex()) + + // checks for + // dd/mm/yyyy or yy + // yyyy or yy/mm/dd + // yyyy or yy/mm + // mm/yyyy or yy + // for `yy` it is assumed that 20XX is implicit. + // with separators [., /, -] + val isIrrelevantCategory = + item.contains("""\d{1,2}[-/.]\d{1,2}[-/.]\d{2,4}|\d{2,4}[-/.]\d{1,2}[-/.]\d{1,2}|\d{2,4}[-/.]\d{1,2}|\d{1,2}[-/.]\d{2,4}""".toRegex()) + + if (spammyCategory) { return true } - if (mentionsDecade) { - // Check if the year in the form of XX(X)0s is recent/relevant, i.e. in the 2000s or 2010s/2020s as stated in Issue #1029 - // Example: "2020s" is OK, but "1920s" is not (and should be skipped) - return !recentDecade - } else { - // If it is not an year in decade form (e.g. 19xxs/20xxs), then check if item contains a 4-digit year - // anywhere within the string (.* is wildcard) (Issue #47) - // And that item does not equal the current year or previous year - return item.matches(".*(19|20)\\d{2}.*".toRegex()) && - !item.contains(curYearInString) && - !item.contains(prevYearInString) + if(isIrrelevantCategory){ + return true } + + val hasYear = item.matches("(.*\\d{4}.*)".toRegex()) + val validYearsRange = item.matches(".*(20[0-9]{2}).*".toRegex()) + + // finally if there's 4 digits year exists in XXXX it should only be in 20XX range. + return hasYear && !validYearsRange } /** diff --git a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt index 8c336470a..21fdba2f5 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/category/CategoriesModelTest.kt @@ -11,6 +11,7 @@ import fr.free.nrw.commons.upload.GpsCategoryModel import io.reactivex.Single import io.reactivex.subjects.BehaviorSubject import media +import org.junit.Assert import org.junit.Before import org.junit.Test import org.mockito.ArgumentMatchers @@ -331,4 +332,42 @@ class CategoriesModelTest { media(), ) } + + @Test + fun `test valid input with XXXX in it between the expected range 20XX`() { + val input = categoriesModel.isSpammyCategory("Amavenita (ship, 2014)") + Assert.assertFalse(input) + } + + @Test + fun `test valid input with XXXXs in it between the expected range 20XXs`() { + val input = categoriesModel.isSpammyCategory("Amavenita (ship, 2014s)") + Assert.assertFalse(input) + } + + @Test + fun `test invalid category when have needing in the input`() { + val input = categoriesModel.isSpammyCategory("Media needing categories as of 30 March 2017") + Assert.assertTrue(input) + } + + @Test + fun `test invalid category when have taken on in the input`() { + val input = categoriesModel.isSpammyCategory("Photographs taken on 2015-12-08") + Assert.assertTrue(input) + } + + @Test + fun `test invalid category when have yy mm or yy mm dd in the input`() { + // filtering based on [., /, -] separators between the dates. + val input = categoriesModel.isSpammyCategory("Image class 09.14") + Assert.assertTrue(input) + } + + @Test + fun `test invalid category when have years not in 20XX range`() { + val input = categoriesModel.isSpammyCategory("Japan in the 1400s") + Assert.assertTrue(input) + } + } From 0293b865b42da9507199cc25d456353aaf39df40 Mon Sep 17 00:00:00 2001 From: "translatewiki.net" Date: Mon, 3 Feb 2025 13:01:34 +0100 Subject: [PATCH 07/14] Localisation updates from https://translatewiki.net. --- app/src/main/res/values-fr/strings.xml | 2 ++ app/src/main/res/values-iw/strings.xml | 12 +++++++----- app/src/main/res/values-lt/strings.xml | 21 ++++++++++++++++++++- app/src/main/res/values-mk/strings.xml | 2 ++ app/src/main/res/values-pms/strings.xml | 2 ++ app/src/main/res/values-ps/strings.xml | 2 +- 6 files changed, 34 insertions(+), 7 deletions(-) diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 07021189f..5aeda5299 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -855,4 +855,6 @@ Utilisations du fichier Légende Légende copiée dans le presse-papier + Afficher dans Explorer + Afficher à proximité diff --git a/app/src/main/res/values-iw/strings.xml b/app/src/main/res/values-iw/strings.xml index 61381c407..7f2d9a8f1 100644 --- a/app/src/main/res/values-iw/strings.xml +++ b/app/src/main/res/values-iw/strings.xml @@ -85,7 +85,7 @@ מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך מתקבל תוכן שיתופי. עיבוד התמונות עשוי לארוך זמן מה כתלות בגודל התמונות והמכשיר שלך - לחקור + סיור מראה כללי משוב @@ -311,7 +311,7 @@ האינטרנט אינו זמין. מוצגים רק מקומות שמורים. הגישה למיקום נדחתה. נא להגדיר את המקום שלך ידנית כדי להשתמש ביכולת הזאת. נדרשת הרשאה כדי להציג רשימה של מקומות בסביבה - נדרשת הרשאה להצגת רשימת התמונות בסביבתך + נדרשת הרשאה כדי להציג רשימה של תמונות בסביבה כיוונים ויקינתונים ויקיפדיה @@ -605,12 +605,12 @@ קיים זקוק לתצלום סוג המקום: - גשר, מוזאון, מלון, וכו\'. + גשר, מוזאון, מלון וכו\'. משהו השתבש בכניסה למערכת, עליך לאפס את הסיסמה שלך! מדיה מחלקות יורשות מחלקות מורישות - נמצא בקרבת מקום + נמצא מקום בסביבה האם אלו תמונות של %1$s? האם זאת תמונה של %1$s? סימניות @@ -628,7 +628,7 @@ להפעיל מיקום? נא להפעיל את שירותי המיקום כדי שהיישום יוכל להציג את מיקומך הנוכחי פעולת \"בסביבה\" זקוקה לשירותי מיקומי פועלים כדי לעבוד כמו שצריך - חקירת המפה דורשת הרשאות מיקום כדי להציג תמונות בסביבתך + מפת \"סיור\" דורשת הרשאות מיקום כדי להציג תמונות בסביבתך יש להעניק הרשאת מיקום כדי להגדיר את המיקום אוטומטית. האם צילמת את שתי התמונות באותו המקום? האם ברצונך להשתמש בקו הרוחב וקו האורך של התמונה משמאל? לטעון עוד @@ -852,4 +852,6 @@ כותרת הכותרת הועתקה ללוח ברכותינו, כל התמונות באלבום הזה הועלו או שסומנו לא להעלאה. + בתצוגת סיור + בתצוגת בסביבה diff --git a/app/src/main/res/values-lt/strings.xml b/app/src/main/res/values-lt/strings.xml index 89a5af3ad..04c1f931f 100644 --- a/app/src/main/res/values-lt/strings.xml +++ b/app/src/main/res/values-lt/strings.xml @@ -96,6 +96,8 @@ Nufotografuoti Netoliese Mano įkėlimai + Kopijuoti nuorodą + Nuoroda nukopijuota į mainų sritį. Dalintis Žiūrėti failo puslapį Antraštė (būtina) @@ -345,11 +347,13 @@ Ištrinti Pasiekimai Profilis + Ženkliukai Statistika Gauta padėka Rinktiniai paveikslėliai Vaizdai per „Netoliese esančios vietos“ - Lygis + Lygis %d + %s (%s lygis) Vaizdai įkelti Paveikslėliai negrąžinti Naudoti vaizdai @@ -381,6 +385,7 @@ Jūsų įrenginyje nepavyko rasti suderinamos žemėlapio programos. Norėdami naudotis šia funkcija, įdiekite žemėlapio programą. Nuotraukos Vietos + Kategorijos Pridėti prie / pašalinti iš žymių Žymės Jūs nepridėjote jokių žymių @@ -751,4 +756,18 @@ Laukiama Nepavyko Nepavyko įkelti vietos duomenų + Trinti aplanką + Patvirtinti ištrynimą + Ar tikrai norite ištrinti aplanką %1$s, kuriame yra %2$d elementų? + Ištrinti + Atšaukti + Aplankas %1$s sėkmingai ištrintas + Nepavyko ištrinti aplanko %1$s + Klaida išsiunčiant į šiukšliadėžę aplanko turinį: %1$s + Įkeliant įvyko klaida + Naudojimo būdų nerasta + Vikiteka + Kiti viki + Failo naudojimas + Paskyra diff --git a/app/src/main/res/values-mk/strings.xml b/app/src/main/res/values-mk/strings.xml index 15d6789fd..94e8f638b 100644 --- a/app/src/main/res/values-mk/strings.xml +++ b/app/src/main/res/values-mk/strings.xml @@ -818,4 +818,6 @@ Толкување Толкувањето е ставено во меѓускладот Честитаме. Сите слики од овој албум се подигнати или обележани за неподигање. + Прикажи во „Истражи“ + Прикажи во „Во близина“ diff --git a/app/src/main/res/values-pms/strings.xml b/app/src/main/res/values-pms/strings.xml index 0e3063ac7..76a0be425 100644 --- a/app/src/main/res/values-pms/strings.xml +++ b/app/src/main/res/values-pms/strings.xml @@ -814,4 +814,6 @@ Legenda Legenda copià an sla taulëtta Congratulassion, tute le fòto ëd s\'àlbom a son ëstàita carià opura marcà coma da nen carié. + Smon-e andrinta a Explore + Smon-e andrinta a Nearby diff --git a/app/src/main/res/values-ps/strings.xml b/app/src/main/res/values-ps/strings.xml index ca52b10d8..2dc2366a8 100644 --- a/app/src/main/res/values-ps/strings.xml +++ b/app/src/main/res/values-ps/strings.xml @@ -15,7 +15,7 @@ دا انځور به د %1$s په منښتليک سمبال وي. ويکي خونديځ امستنې - کارن-نوم + کارن‌نوم پټنوم ننوتل نومليکنه From bdcc02be1a3e434ad320a7b16b60716d2b9dd6fe Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Wed, 29 Jan 2025 15:35:18 +0530 Subject: [PATCH 08/14] Rename .java to .kt --- .../{ContributionController.java => ContributionController.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionController.java => ContributionController.kt} (100%) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt From 7efed21241d000f56ccb9e2fc23b21c35d1dbc25 Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Wed, 29 Jan 2025 15:35:21 +0530 Subject: [PATCH 09/14] Migrated ContributionController --- .../contributions/ContributionController.kt | 561 ++++++++++-------- .../free/nrw/commons/upload/UploadActivity.kt | 2 +- 2 files changed, 316 insertions(+), 247 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt index 65604a7e0..296391c6d 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionController.kt @@ -1,94 +1,104 @@ -package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import static fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT; - -import android.Manifest.permission; -import android.app.Activity; -import android.content.Context; -import android.content.Intent; -import android.widget.Toast; -import androidx.activity.result.ActivityResult; -import androidx.activity.result.ActivityResultLauncher; -import androidx.annotation.NonNull; -import androidx.lifecycle.LiveData; -import androidx.paging.DataSource.Factory; -import androidx.paging.LivePagedListBuilder; -import androidx.paging.PagedList; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.filepicker.DefaultCallback; -import fr.free.nrw.commons.filepicker.FilePicker; -import fr.free.nrw.commons.filepicker.FilePicker.ImageSource; -import fr.free.nrw.commons.filepicker.UploadableFile; -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.nearby.Place; -import fr.free.nrw.commons.upload.UploadActivity; -import fr.free.nrw.commons.utils.DialogUtil; -import fr.free.nrw.commons.utils.PermissionUtils; -import fr.free.nrw.commons.utils.ViewUtil; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import javax.inject.Inject; -import javax.inject.Named; -import javax.inject.Singleton; +import android.Manifest.permission +import android.app.Activity +import android.content.Context +import android.content.Intent +import android.widget.Toast +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.lifecycle.LiveData +import androidx.paging.LivePagedListBuilder +import androidx.paging.PagedList +import fr.free.nrw.commons.R +import fr.free.nrw.commons.filepicker.DefaultCallback +import fr.free.nrw.commons.filepicker.FilePicker +import fr.free.nrw.commons.filepicker.FilePicker.HandleActivityResult +import fr.free.nrw.commons.filepicker.FilePicker.configuration +import fr.free.nrw.commons.filepicker.FilePicker.handleExternalImagesPicked +import fr.free.nrw.commons.filepicker.FilePicker.onPictureReturnedFromDocuments +import fr.free.nrw.commons.filepicker.FilePicker.openCameraForImage +import fr.free.nrw.commons.filepicker.FilePicker.openCustomSelector +import fr.free.nrw.commons.filepicker.FilePicker.openGallery +import fr.free.nrw.commons.filepicker.UploadableFile +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.nearby.Place +import fr.free.nrw.commons.upload.UploadActivity +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.PermissionUtils.PERMISSIONS_STORAGE +import fr.free.nrw.commons.utils.PermissionUtils.checkPermissionsAndPerformAction +import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.ViewUtil.showShortToast +import fr.free.nrw.commons.wikidata.WikidataConstants.PLACE_OBJECT +import java.util.Arrays +import javax.inject.Inject +import javax.inject.Named +import javax.inject.Singleton @Singleton -public class ContributionController { +class ContributionController @Inject constructor(@param:Named("default_preferences") private val defaultKvStore: JsonKvStore) { + private var locationBeforeImageCapture: LatLng? = null + private var isInAppCameraUpload = false + @JvmField + var locationPermissionCallback: LocationPermissionCallback? = null + private var locationPermissionsHelper: LocationPermissionsHelper? = null - public static final String ACTION_INTERNAL_UPLOADS = "internalImageUploads"; - private final JsonKvStore defaultKvStore; - private LatLng locationBeforeImageCapture; - private boolean isInAppCameraUpload; - public LocationPermissionCallback locationPermissionCallback; - private LocationPermissionsHelper locationPermissionsHelper; // Temporarily disabled, see issue [https://github.com/commons-app/apps-android-commons/issues/5847] // LiveData> failedAndPendingContributionList; - LiveData> pendingContributionList; - LiveData> failedContributionList; + @JvmField + var pendingContributionList: LiveData>? = null + @JvmField + var failedContributionList: LiveData>? = null + @JvmField @Inject - LocationServiceManager locationManager; + var locationManager: LocationServiceManager? = null + @JvmField @Inject - ContributionsRepository repository; - - @Inject - public ContributionController(@Named("default_preferences") JsonKvStore defaultKvStore) { - this.defaultKvStore = defaultKvStore; - } + var repository: ContributionsRepository? = null /** * Check for permissions and initiate camera click */ - public void initiateCameraPick(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher, - ActivityResultLauncher resultLauncher) { - boolean useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true); + fun initiateCameraPick( + activity: Activity, + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, + resultLauncher: ActivityResultLauncher + ) { + val useExtStorage = defaultKvStore.getBoolean("useExternalStorage", true) if (!useExtStorage) { - initiateCameraUpload(activity, resultLauncher); - return; + initiateCameraUpload(activity, resultLauncher) + return } - PermissionUtils.checkPermissionsAndPerformAction(activity, - () -> { + checkPermissionsAndPerformAction( + activity, + { if (defaultKvStore.getBoolean("inAppCameraFirstRun")) { - defaultKvStore.putBoolean("inAppCameraFirstRun", false); - askUserToAllowLocationAccess(activity, inAppCameraLocationPermissionLauncher, resultLauncher); + defaultKvStore.putBoolean("inAppCameraFirstRun", false) + askUserToAllowLocationAccess( + activity, + inAppCameraLocationPermissionLauncher, + resultLauncher + ) } else if (defaultKvStore.getBoolean("inAppCameraLocationPref")) { - createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher, resultLauncher); + createDialogsAndHandleLocationPermissions( + activity, + inAppCameraLocationPermissionLauncher, resultLauncher + ) } else { - initiateCameraUpload(activity, resultLauncher); + initiateCameraUpload(activity, resultLauncher) } }, R.string.storage_permission_title, R.string.write_storage_permission_rationale, - PermissionUtils.getPERMISSIONS_STORAGE()); + *PERMISSIONS_STORAGE + ) } /** @@ -96,310 +106,369 @@ public class ContributionController { * * @param activity */ - private void createDialogsAndHandleLocationPermissions(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher, - ActivityResultLauncher resultLauncher) { - locationPermissionCallback = new LocationPermissionCallback() { - @Override - public void onLocationPermissionDenied(String toastMessage) { + private fun createDialogsAndHandleLocationPermissions( + activity: Activity, + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>?, + resultLauncher: ActivityResultLauncher + ) { + locationPermissionCallback = object : LocationPermissionCallback { + override fun onLocationPermissionDenied(toastMessage: String) { Toast.makeText( activity, toastMessage, Toast.LENGTH_LONG - ).show(); - initiateCameraUpload(activity, resultLauncher); + ).show() + initiateCameraUpload(activity, resultLauncher) } - @Override - public void onLocationPermissionGranted() { - if (!locationPermissionsHelper.isLocationAccessToAppsTurnedOn()) { - showLocationOffDialog(activity, R.string.in_app_camera_needs_location, - R.string.in_app_camera_location_unavailable, resultLauncher); + override fun onLocationPermissionGranted() { + if (!locationPermissionsHelper!!.isLocationAccessToAppsTurnedOn()) { + showLocationOffDialog( + activity, R.string.in_app_camera_needs_location, + R.string.in_app_camera_location_unavailable, resultLauncher + ) } else { - initiateCameraUpload(activity, resultLauncher); + initiateCameraUpload(activity, resultLauncher) } } - }; - - locationPermissionsHelper = new LocationPermissionsHelper( - activity, locationManager, locationPermissionCallback); - if (inAppCameraLocationPermissionLauncher != null) { - inAppCameraLocationPermissionLauncher.launch( - new String[]{permission.ACCESS_FINE_LOCATION}); } + locationPermissionsHelper = LocationPermissionsHelper( + activity, locationManager!!, locationPermissionCallback + ) + inAppCameraLocationPermissionLauncher?.launch( + arrayOf(permission.ACCESS_FINE_LOCATION) + ) } /** * Shows a dialog alerting the user about location services being off and asking them to turn it * on * TODO: Add a seperate callback in LocationPermissionsHelper for this. - * Ref: https://github.com/commons-app/apps-android-commons/pull/5494/files#r1510553114 + * Ref: https://github.com/commons-app/apps-android-commons/pull/5494/files#r1510553114 * * @param activity Activity reference * @param dialogTextResource Resource id of text to be shown in dialog * @param toastTextResource Resource id of text to be shown in toast * @param resultLauncher */ - private void showLocationOffDialog(Activity activity, int dialogTextResource, - int toastTextResource, ActivityResultLauncher resultLauncher) { - DialogUtil - .showAlertDialog(activity, - activity.getString(R.string.ask_to_turn_location_on), - activity.getString(dialogTextResource), - activity.getString(R.string.title_app_shortcut_setting), - activity.getString(R.string.cancel), - () -> locationPermissionsHelper.openLocationSettings(activity), - () -> { - Toast.makeText(activity, activity.getString(toastTextResource), - Toast.LENGTH_LONG).show(); - initiateCameraUpload(activity, resultLauncher); - } - ); + private fun showLocationOffDialog( + activity: Activity, dialogTextResource: Int, + toastTextResource: Int, resultLauncher: ActivityResultLauncher + ) { + showAlertDialog(activity, + activity.getString(R.string.ask_to_turn_location_on), + activity.getString(dialogTextResource), + activity.getString(R.string.title_app_shortcut_setting), + activity.getString(R.string.cancel), + { locationPermissionsHelper!!.openLocationSettings(activity) }, + { + Toast.makeText( + activity, activity.getString(toastTextResource), + Toast.LENGTH_LONG + ).show() + initiateCameraUpload(activity, resultLauncher) + } + ) } - public void handleShowRationaleFlowCameraLocation(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher, - ActivityResultLauncher resultLauncher) { - DialogUtil.showAlertDialog(activity, activity.getString(R.string.location_permission_title), + fun handleShowRationaleFlowCameraLocation( + activity: Activity, + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>?, + resultLauncher: ActivityResultLauncher + ) { + showAlertDialog( + activity, activity.getString(R.string.location_permission_title), activity.getString(R.string.in_app_camera_location_permission_rationale), activity.getString(android.R.string.ok), activity.getString(android.R.string.cancel), - () -> { - createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher, resultLauncher); + { + createDialogsAndHandleLocationPermissions( + activity, + inAppCameraLocationPermissionLauncher, resultLauncher + ) + }, + { + locationPermissionCallback!!.onLocationPermissionDenied( + activity.getString(R.string.in_app_camera_location_permission_denied) + ) }, - () -> locationPermissionCallback.onLocationPermissionDenied( - activity.getString(R.string.in_app_camera_location_permission_denied)), null - ); + ) } /** * Suggest user to attach location information with pictures. If the user selects "Yes", then: - *

+ * + * * Location is taken from the EXIF if the default camera application does not redact location * tags. - *

+ * + * * Otherwise, if the EXIF metadata does not have location information, then location captured by * the app is used * * @param activity */ - private void askUserToAllowLocationAccess(Activity activity, - ActivityResultLauncher inAppCameraLocationPermissionLauncher, - ActivityResultLauncher resultLauncher) { - DialogUtil.showAlertDialog(activity, + private fun askUserToAllowLocationAccess( + activity: Activity, + inAppCameraLocationPermissionLauncher: ActivityResultLauncher>, + resultLauncher: ActivityResultLauncher + ) { + showAlertDialog( + activity, activity.getString(R.string.in_app_camera_location_permission_title), activity.getString(R.string.in_app_camera_location_access_explanation), activity.getString(R.string.option_allow), activity.getString(R.string.option_dismiss), - () -> { - defaultKvStore.putBoolean("inAppCameraLocationPref", true); - createDialogsAndHandleLocationPermissions(activity, - inAppCameraLocationPermissionLauncher, resultLauncher); + { + defaultKvStore.putBoolean("inAppCameraLocationPref", true) + createDialogsAndHandleLocationPermissions( + activity, + inAppCameraLocationPermissionLauncher, resultLauncher + ) }, - () -> { - ViewUtil.showLongToast(activity, R.string.in_app_camera_location_permission_denied); - defaultKvStore.putBoolean("inAppCameraLocationPref", false); - initiateCameraUpload(activity, resultLauncher); + { + showLongToast(activity, R.string.in_app_camera_location_permission_denied) + defaultKvStore.putBoolean("inAppCameraLocationPref", false) + initiateCameraUpload(activity, resultLauncher) }, null - ); + ) } /** * Initiate gallery picker */ - public void initiateGalleryPick(final Activity activity, ActivityResultLauncher resultLauncher, final boolean allowMultipleUploads) { - initiateGalleryUpload(activity, resultLauncher, allowMultipleUploads); + fun initiateGalleryPick( + activity: Activity, + resultLauncher: ActivityResultLauncher, + allowMultipleUploads: Boolean + ) { + initiateGalleryUpload(activity, resultLauncher, allowMultipleUploads) } /** * Initiate gallery picker with permission */ - public void initiateCustomGalleryPickWithPermission(final Activity activity, ActivityResultLauncher resultLauncher) { - setPickerConfiguration(activity, true); + fun initiateCustomGalleryPickWithPermission( + activity: Activity, + resultLauncher: ActivityResultLauncher + ) { + setPickerConfiguration(activity, true) - PermissionUtils.checkPermissionsAndPerformAction(activity, - () -> FilePicker.openCustomSelector(activity, resultLauncher, 0), + checkPermissionsAndPerformAction( + activity, + { openCustomSelector(activity, resultLauncher, 0) }, R.string.storage_permission_title, R.string.write_storage_permission_rationale, - PermissionUtils.getPERMISSIONS_STORAGE()); + *PERMISSIONS_STORAGE + ) } /** * Open chooser for gallery uploads */ - private void initiateGalleryUpload(final Activity activity, ActivityResultLauncher resultLauncher, - final boolean allowMultipleUploads) { - setPickerConfiguration(activity, allowMultipleUploads); - FilePicker.openGallery(activity, resultLauncher, 0, isDocumentPhotoPickerPreferred()); + private fun initiateGalleryUpload( + activity: Activity, resultLauncher: ActivityResultLauncher, + allowMultipleUploads: Boolean + ) { + setPickerConfiguration(activity, allowMultipleUploads) + openGallery(activity, resultLauncher, 0, isDocumentPhotoPickerPreferred) } /** * Sets configuration for file picker */ - private void setPickerConfiguration(Activity activity, - boolean allowMultipleUploads) { - boolean copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true); - FilePicker.configuration(activity) + private fun setPickerConfiguration( + activity: Activity, + allowMultipleUploads: Boolean + ) { + val copyToExternalStorage = defaultKvStore.getBoolean("useExternalStorage", true) + configuration(activity) .setCopyTakenPhotosToPublicGalleryAppFolder(copyToExternalStorage) - .setAllowMultiplePickInGallery(allowMultipleUploads); + .setAllowMultiplePickInGallery(allowMultipleUploads) } /** * Initiate camera upload by opening camera */ - private void initiateCameraUpload(Activity activity, ActivityResultLauncher resultLauncher) { - setPickerConfiguration(activity, false); + private fun initiateCameraUpload( + activity: Activity, + resultLauncher: ActivityResultLauncher + ) { + setPickerConfiguration(activity, false) if (defaultKvStore.getBoolean("inAppCameraLocationPref", false)) { - locationBeforeImageCapture = locationManager.getLastLocation(); + locationBeforeImageCapture = locationManager!!.getLastLocation() } - isInAppCameraUpload = true; - FilePicker.openCameraForImage(activity, resultLauncher, 0); + isInAppCameraUpload = true + openCameraForImage(activity, resultLauncher, 0) } - private boolean isDocumentPhotoPickerPreferred(){ - return defaultKvStore.getBoolean( - "openDocumentPhotoPickerPref", true); - } + private val isDocumentPhotoPickerPreferred: Boolean + get() = defaultKvStore.getBoolean( + "openDocumentPhotoPickerPref", true + ) - public void onPictureReturnedFromGallery(ActivityResult result, Activity activity, FilePicker.Callbacks callbacks){ - - if(isDocumentPhotoPickerPreferred()){ - FilePicker.onPictureReturnedFromDocuments(result, activity, callbacks); + fun onPictureReturnedFromGallery( + result: ActivityResult, + activity: Activity, + callbacks: FilePicker.Callbacks + ) { + if (isDocumentPhotoPickerPreferred) { + onPictureReturnedFromDocuments(result, activity, callbacks) } else { - FilePicker.onPictureReturnedFromGallery(result, activity, callbacks); + FilePicker.onPictureReturnedFromGallery(result, activity, callbacks) } } - public void onPictureReturnedFromCustomSelector(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - FilePicker.onPictureReturnedFromCustomSelector(result, activity, callbacks); + fun onPictureReturnedFromCustomSelector( + result: ActivityResult, + activity: Activity, + callbacks: FilePicker.Callbacks + ) { + FilePicker.onPictureReturnedFromCustomSelector(result, activity, callbacks) } - public void onPictureReturnedFromCamera(ActivityResult result, Activity activity, @NonNull FilePicker.Callbacks callbacks) { - FilePicker.onPictureReturnedFromCamera(result, activity, callbacks); + fun onPictureReturnedFromCamera( + result: ActivityResult, + activity: Activity, + callbacks: FilePicker.Callbacks + ) { + FilePicker.onPictureReturnedFromCamera(result, activity, callbacks) } /** * Attaches callback for file picker. */ - public void handleActivityResultWithCallback(Activity activity, FilePicker.HandleActivityResult handleActivityResult) { + fun handleActivityResultWithCallback( + activity: Activity, + handleActivityResult: HandleActivityResult + ) { + handleActivityResult.onHandleActivityResult(object : DefaultCallback() { + override fun onCanceled(source: FilePicker.ImageSource, type: Int) { + super.onCanceled(source, type) + defaultKvStore.remove(PLACE_OBJECT) + } - handleActivityResult.onHandleActivityResult(new DefaultCallback() { + override fun onImagePickerError( + e: Exception, source: FilePicker.ImageSource, + type: Int + ) { + showShortToast(activity, R.string.error_occurred_in_picking_images) + } - @Override - public void onCanceled(final ImageSource source, final int type) { - super.onCanceled(source, type); - defaultKvStore.remove(PLACE_OBJECT); - } - - @Override - public void onImagePickerError(Exception e, FilePicker.ImageSource source, - int type) { - ViewUtil.showShortToast(activity, R.string.error_occurred_in_picking_images); - } - - @Override - public void onImagesPicked(@NonNull List imagesFiles, - FilePicker.ImageSource source, int type) { - Intent intent = handleImagesPicked(activity, imagesFiles); - activity.startActivity(intent); - } - }); + override fun onImagesPicked( + imagesFiles: List, + source: FilePicker.ImageSource, type: Int + ) { + val intent = handleImagesPicked(activity, imagesFiles) + activity.startActivity(intent) + } + }) } - public List handleExternalImagesPicked(Activity activity, - Intent data) { - return FilePicker.handleExternalImagesPicked(data, activity); + fun handleExternalImagesPicked( + activity: Activity, + data: Intent? + ): List { + return handleExternalImagesPicked(data, activity) } /** * Returns intent to be passed to upload activity Attaches place object for nearby uploads and * location before image capture if in-app camera is used */ - private Intent handleImagesPicked(Context context, - List imagesFiles) { - Intent shareIntent = new Intent(context, UploadActivity.class); - shareIntent.setAction(ACTION_INTERNAL_UPLOADS); + private fun handleImagesPicked( + context: Context, + imagesFiles: List + ): Intent { + val shareIntent = Intent(context, UploadActivity::class.java) + shareIntent.setAction(ACTION_INTERNAL_UPLOADS) shareIntent - .putParcelableArrayListExtra(UploadActivity.EXTRA_FILES, new ArrayList<>(imagesFiles)); - Place place = defaultKvStore.getJson(PLACE_OBJECT, Place.class); + .putParcelableArrayListExtra(UploadActivity.EXTRA_FILES, ArrayList(imagesFiles)) + val place = defaultKvStore.getJson(PLACE_OBJECT, Place::class.java) if (place != null) { - shareIntent.putExtra(PLACE_OBJECT, place); + shareIntent.putExtra(PLACE_OBJECT, place) } if (locationBeforeImageCapture != null) { shareIntent.putExtra( UploadActivity.LOCATION_BEFORE_IMAGE_CAPTURE, - locationBeforeImageCapture); + locationBeforeImageCapture + ) } shareIntent.putExtra( UploadActivity.IN_APP_CAMERA_UPLOAD, isInAppCameraUpload - ); - isInAppCameraUpload = false; // reset the flag for next use - return shareIntent; + ) + isInAppCameraUpload = false // reset the flag for next use + return shareIntent } - /** - * Fetches the contributions with the state "IN_PROGRESS", "QUEUED" and "PAUSED" and then it - * populates the `pendingContributionList`. - **/ - void getPendingContributions() { - final PagedList.Config pagedListConfig = - (new PagedList.Config.Builder()) - .setPrefetchDistance(50) - .setPageSize(10).build(); - Factory factory; - factory = repository.fetchContributionsWithStates( - Arrays.asList(Contribution.STATE_IN_PROGRESS, Contribution.STATE_QUEUED, - Contribution.STATE_PAUSED)); + val pendingContributions: Unit + /** + * Fetches the contributions with the state "IN_PROGRESS", "QUEUED" and "PAUSED" and then it + * populates the `pendingContributionList`. + */ + get() { + val pagedListConfig = + (PagedList.Config.Builder()) + .setPrefetchDistance(50) + .setPageSize(10).build() + val factory = repository!!.fetchContributionsWithStates( + Arrays.asList( + Contribution.STATE_IN_PROGRESS, Contribution.STATE_QUEUED, + Contribution.STATE_PAUSED + ) + ) - LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, - pagedListConfig); - pendingContributionList = livePagedListBuilder.build(); - } + val livePagedListBuilder = LivePagedListBuilder(factory, pagedListConfig) + pendingContributionList = livePagedListBuilder.build() + } - /** - * Fetches the contributions with the state "FAILED" and populates the - * `failedContributionList`. - **/ - void getFailedContributions() { - final PagedList.Config pagedListConfig = - (new PagedList.Config.Builder()) - .setPrefetchDistance(50) - .setPageSize(10).build(); - Factory factory; - factory = repository.fetchContributionsWithStates( - Collections.singletonList(Contribution.STATE_FAILED)); + val failedContributions: Unit + /** + * Fetches the contributions with the state "FAILED" and populates the + * `failedContributionList`. + */ + get() { + val pagedListConfig = + (PagedList.Config.Builder()) + .setPrefetchDistance(50) + .setPageSize(10).build() + val factory = repository!!.fetchContributionsWithStates( + listOf(Contribution.STATE_FAILED) + ) - LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, - pagedListConfig); - failedContributionList = livePagedListBuilder.build(); - } + val livePagedListBuilder = LivePagedListBuilder(factory, pagedListConfig) + failedContributionList = livePagedListBuilder.build() + } /** * Temporarily disabled, see issue [https://github.com/commons-app/apps-android-commons/issues/5847] * Fetches the contributions with the state "IN_PROGRESS", "QUEUED", "PAUSED" and "FAILED" and * then it populates the `failedAndPendingContributionList`. - **/ -// void getFailedAndPendingContributions() { -// final PagedList.Config pagedListConfig = -// (new PagedList.Config.Builder()) -// .setPrefetchDistance(50) -// .setPageSize(10).build(); -// Factory factory; -// factory = repository.fetchContributionsWithStates( -// Arrays.asList(Contribution.STATE_IN_PROGRESS, Contribution.STATE_QUEUED, -// Contribution.STATE_PAUSED, Contribution.STATE_FAILED)); -// -// LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, -// pagedListConfig); -// failedAndPendingContributionList = livePagedListBuilder.build(); -// } + */ + // void getFailedAndPendingContributions() { + // final PagedList.Config pagedListConfig = + // (new PagedList.Config.Builder()) + // .setPrefetchDistance(50) + // .setPageSize(10).build(); + // Factory factory; + // factory = repository.fetchContributionsWithStates( + // Arrays.asList(Contribution.STATE_IN_PROGRESS, Contribution.STATE_QUEUED, + // Contribution.STATE_PAUSED, Contribution.STATE_FAILED)); + // + // LivePagedListBuilder livePagedListBuilder = new LivePagedListBuilder(factory, + // pagedListConfig); + // failedAndPendingContributionList = livePagedListBuilder.build(); + // } + + companion object { + const val ACTION_INTERNAL_UPLOADS: String = "internalImageUploads" + } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt index 56ad9dd84..020284934 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadActivity.kt @@ -679,7 +679,7 @@ class UploadActivity : BaseActivity(), UploadContract.View, UploadBaseFragment.C } private fun receiveExternalSharedItems() { - uploadableFiles = contributionController!!.handleExternalImagesPicked(this, intent) + uploadableFiles = contributionController!!.handleExternalImagesPicked(this, intent).toMutableList() } private fun receiveInternalSharedItems() { From 506588d2640f017ea0e2ca19236c0c38dcc7b385 Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Wed, 29 Jan 2025 16:25:48 +0530 Subject: [PATCH 10/14] Rename .java to .kt --- .../contributions/{ContributionDao.java => ContributionDao.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionDao.java => ContributionDao.kt} (100%) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt From 8b8f70c338dbbf06b74380a0b7900989b4b257b8 Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Wed, 29 Jan 2025 16:25:50 +0530 Subject: [PATCH 11/14] Migrated ContributionDao --- .../commons/contributions/ContributionDao.kt | 121 +++++++++--------- .../nrw/commons/upload/worker/UploadWorker.kt | 2 +- 2 files changed, 63 insertions(+), 60 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt index 2e375145c..50faa1340 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionDao.kt @@ -1,52 +1,51 @@ -package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import android.database.sqlite.SQLiteException; -import androidx.paging.DataSource; -import androidx.room.Dao; -import androidx.room.Delete; -import androidx.room.Insert; -import androidx.room.OnConflictStrategy; -import androidx.room.Query; -import androidx.room.Transaction; -import androidx.room.Update; -import io.reactivex.Completable; -import io.reactivex.Single; -import java.util.Calendar; -import java.util.List; -import timber.log.Timber; +import android.database.sqlite.SQLiteException +import androidx.paging.DataSource +import androidx.room.Dao +import androidx.room.Delete +import androidx.room.Insert +import androidx.room.OnConflictStrategy +import androidx.room.Query +import androidx.room.Transaction +import androidx.room.Update +import io.reactivex.Completable +import io.reactivex.Single +import java.util.Calendar @Dao -public abstract class ContributionDao { - +abstract class ContributionDao { @Query("SELECT * FROM contribution order by media_dateUploaded DESC") - abstract DataSource.Factory fetchContributions(); + abstract fun fetchContributions(): DataSource.Factory @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract void saveSynchronous(Contribution contribution); + abstract fun saveSynchronous(contribution: Contribution) - public Completable save(final Contribution contribution) { + fun save(contribution: Contribution): Completable { return Completable - .fromAction(() -> { - contribution.setDateModified(Calendar.getInstance().getTime()); - if (contribution.getDateUploadStarted() == null) { - contribution.setDateUploadStarted(Calendar.getInstance().getTime()); + .fromAction { + contribution.dateModified = Calendar.getInstance().time + if (contribution.dateUploadStarted == null) { + contribution.dateUploadStarted = Calendar.getInstance().time } - saveSynchronous(contribution); - }); + saveSynchronous(contribution) + } } @Transaction - public void deleteAndSaveContribution(final Contribution oldContribution, - final Contribution newContribution) { - deleteSynchronous(oldContribution); - saveSynchronous(newContribution); + open fun deleteAndSaveContribution( + oldContribution: Contribution, + newContribution: Contribution + ) { + deleteSynchronous(oldContribution) + saveSynchronous(newContribution) } @Insert(onConflict = OnConflictStrategy.REPLACE) - public abstract Single> save(List contribution); + abstract fun save(contribution: List): Single> @Delete - public abstract void deleteSynchronous(Contribution contribution); + abstract fun deleteSynchronous(contribution: Contribution) /** * Deletes contributions with specific states from the database. @@ -55,12 +54,12 @@ public abstract class ContributionDao { * @throws SQLiteException If an SQLite error occurs. */ @Query("DELETE FROM contribution WHERE state IN (:states)") - public abstract void deleteContributionsWithStatesSynchronous(List states) - throws SQLiteException; + @Throws(SQLiteException::class) + abstract fun deleteContributionsWithStatesSynchronous(states: List) - public Completable delete(final Contribution contribution) { + fun delete(contribution: Contribution): Completable { return Completable - .fromAction(() -> deleteSynchronous(contribution)); + .fromAction { deleteSynchronous(contribution) } } /** @@ -69,19 +68,19 @@ public abstract class ContributionDao { * @param states The states of the contributions to delete. * @return A Completable indicating the result of the operation. */ - public Completable deleteContributionsWithStates(List states) { + fun deleteContributionsWithStates(states: List): Completable { return Completable - .fromAction(() -> deleteContributionsWithStatesSynchronous(states)); + .fromAction { deleteContributionsWithStatesSynchronous(states) } } @Query("SELECT * from contribution WHERE media_filename=:fileName") - public abstract List getContributionWithTitle(String fileName); + abstract fun getContributionWithTitle(fileName: String): List @Query("SELECT * from contribution WHERE pageId=:pageId") - public abstract Contribution getContribution(String pageId); + abstract fun getContribution(pageId: String): Contribution @Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC") - public abstract Single> getContribution(List states); + abstract fun getContribution(states: List): Single> /** * Gets contributions with specific states in descending order by the date they were uploaded. @@ -90,8 +89,9 @@ public abstract class ContributionDao { * @return A DataSource factory for paginated contributions with the specified states. */ @Query("SELECT * from contribution WHERE state IN (:states) order by media_dateUploaded DESC") - public abstract DataSource.Factory getContributions( - List states); + abstract fun getContributions( + states: List + ): DataSource.Factory /** * Gets contributions with specific states in ascending order by the date the upload started. @@ -100,17 +100,19 @@ public abstract class ContributionDao { * @return A DataSource factory for paginated contributions with the specified states. */ @Query("SELECT * from contribution WHERE state IN (:states) order by dateUploadStarted ASC") - public abstract DataSource.Factory getContributionsSortedByDateUploadStarted( - List states); + abstract fun getContributionsSortedByDateUploadStarted( + states: List + ): DataSource.Factory @Query("SELECT COUNT(*) from contribution WHERE state in (:toUpdateStates)") - public abstract Single getPendingUploads(int[] toUpdateStates); + abstract fun getPendingUploads(toUpdateStates: IntArray): Single @Query("Delete FROM contribution") - public abstract void deleteAll() throws SQLiteException; + @Throws(SQLiteException::class) + abstract fun deleteAll() @Update - public abstract void updateSynchronous(Contribution contribution); + abstract fun updateSynchronous(contribution: Contribution) /** * Updates the state of contributions with specific states. @@ -119,16 +121,17 @@ public abstract class ContributionDao { * @param newState The new state to set. */ @Query("UPDATE contribution SET state = :newState WHERE state IN (:states)") - public abstract void updateContributionsState(List states, int newState); + abstract fun updateContributionsState(states: List, newState: Int) - public Completable update(final Contribution contribution) { - return Completable - .fromAction(() -> { - contribution.setDateModified(Calendar.getInstance().getTime()); - updateSynchronous(contribution); - }); + fun update(contribution: Contribution): Completable { + return Completable.fromAction { + contribution.dateModified = Calendar.getInstance().time + updateSynchronous(contribution) + } } + + /** * Updates the state of contributions with specific states asynchronously. * @@ -136,10 +139,10 @@ public abstract class ContributionDao { * @param newState The new state to set. * @return A Completable indicating the result of the operation. */ - public Completable updateContributionsWithStates(List states, int newState) { + fun updateContributionsWithStates(states: List, newState: Int): Completable { return Completable - .fromAction(() -> { - updateContributionsState(states, newState); - }); + .fromAction { + updateContributionsState(states, newState) + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt index 782b15c0c..75db6ffc0 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/worker/UploadWorker.kt @@ -149,7 +149,7 @@ class UploadWorker( currentNotification.build(), ) contribution!!.transferred = transferred - contributionDao.update(contribution).blockingAwait() + contributionDao.update(contribution!!).blockingAwait() } open fun onChunkUploaded( From ac9356835a4a94b26fcd75332d13c6fe518ce5cd Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Fri, 31 Jan 2025 22:00:38 +0530 Subject: [PATCH 12/14] Rename .java to .kt --- .../{ContributionsContract.java => ContributionsContract.kt} | 0 .../{ContributionsFragment.java => ContributionsFragment.kt} | 0 ...{ContributionsListAdapter.java => ContributionsListAdapter.kt} | 0 ...ontributionsListContract.java => ContributionsListContract.kt} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsContract.java => ContributionsContract.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsFragment.java => ContributionsFragment.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsListAdapter.java => ContributionsListAdapter.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsListContract.java => ContributionsListContract.kt} (100%) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsFragment.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt From daca7b31c1af133df5c0cd7b4d90a0973809a5da Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Fri, 31 Jan 2025 22:00:39 +0530 Subject: [PATCH 13/14] Migrated ContributionsContract,ContributionFragment,ContributionListAdapter,ContributionsListContract from java to Kotlin --- .../contributions/ContributionsContract.kt | 22 +- .../contributions/ContributionsFragment.kt | 1176 +++++++++-------- .../contributions/ContributionsListAdapter.kt | 105 +- .../ContributionsListContract.kt | 24 +- .../contributions/ContributionsModule.java | 17 +- .../di/CommonsDaggerSupportFragment.kt | 9 +- .../explore/map/ExploreMapFragment.java | 2 +- .../nrw/commons/media/MediaDetailFragment.kt | 2 +- .../fragments/NearbyParentFragment.java | 20 +- .../categories/UploadCategoriesFragment.kt | 5 +- 10 files changed, 723 insertions(+), 659 deletions(-) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt index 439780332..269536428 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsContract.kt @@ -1,23 +1,19 @@ -package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import android.content.Context; -import fr.free.nrw.commons.BasePresenter; +import android.content.Context +import fr.free.nrw.commons.BasePresenter /** * The contract for Contributions View & Presenter */ -public class ContributionsContract { +interface ContributionsContract { - public interface View { - - void showMessage(String localizedMessage); - - Context getContext(); + interface View { + fun showMessage(localizedMessage: String) + fun getContext(): Context } - public interface UserActionListener extends BasePresenter { - - Contribution getContributionsWithTitle(String uri); - + interface UserActionListener : BasePresenter { + fun getContributionsWithTitle(uri: String): Contribution } } 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 ca9677691..d22bba6c6 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 @@ -1,305 +1,337 @@ -package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import static android.content.Context.SENSOR_SERVICE; -import static fr.free.nrw.commons.contributions.Contribution.STATE_FAILED; -import static fr.free.nrw.commons.contributions.Contribution.STATE_PAUSED; -import static fr.free.nrw.commons.nearby.fragments.NearbyParentFragment.WLM_URL; -import static fr.free.nrw.commons.profile.ProfileActivity.KEY_USERNAME; -import static fr.free.nrw.commons.utils.LengthUtils.computeBearing; -import static fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween; +import android.Manifest.permission +import android.annotation.SuppressLint +import android.content.Context +import android.content.Intent +import android.hardware.Sensor +import android.hardware.SensorEvent +import android.hardware.SensorEventListener +import android.hardware.SensorManager +import android.os.Bundle +import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.View +import android.view.ViewGroup +import android.widget.CheckBox +import android.widget.CompoundButton +import android.widget.ImageView +import android.widget.TextView +import android.widget.Toast +import androidx.activity.result.ActivityResultCallback +import androidx.activity.result.contract.ActivityResultContracts +import androidx.fragment.app.Fragment +import androidx.fragment.app.FragmentManager +import androidx.lifecycle.Observer +import androidx.paging.PagedList +import androidx.work.WorkInfo +import androidx.work.WorkManager +import fr.free.nrw.commons.MapController.NearbyPlacesInfo +import fr.free.nrw.commons.Media +import fr.free.nrw.commons.R +import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.auth.SessionManager +import fr.free.nrw.commons.campaigns.CampaignView +import fr.free.nrw.commons.campaigns.CampaignsPresenter +import fr.free.nrw.commons.campaigns.ICampaignsView +import fr.free.nrw.commons.campaigns.models.Campaign +import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment +import fr.free.nrw.commons.databinding.FragmentContributionsBinding +import fr.free.nrw.commons.di.CommonsDaggerSupportFragment +import fr.free.nrw.commons.kvstore.JsonKvStore +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.location.LocationServiceManager +import fr.free.nrw.commons.location.LocationUpdateListener +import fr.free.nrw.commons.media.MediaDetailPagerFragment +import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider +import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient +import fr.free.nrw.commons.nearby.NearbyController +import fr.free.nrw.commons.nearby.NearbyNotificationCardView +import fr.free.nrw.commons.nearby.Place +import fr.free.nrw.commons.nearby.fragments.NearbyParentFragment +import fr.free.nrw.commons.notification.NotificationActivity.Companion.startYourself +import fr.free.nrw.commons.notification.NotificationController +import fr.free.nrw.commons.notification.models.Notification +import fr.free.nrw.commons.profile.ProfileActivity +import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.upload.UploadProgressActivity +import fr.free.nrw.commons.upload.worker.UploadWorker +import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour +import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.LengthUtils.computeBearing +import fr.free.nrw.commons.utils.LengthUtils.formatDistanceBetween +import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished +import fr.free.nrw.commons.utils.PermissionUtils.hasPermission +import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import io.reactivex.Observable +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.CompositeDisposable +import io.reactivex.schedulers.Schedulers +import timber.log.Timber +import java.util.Calendar +import java.util.Date +import javax.inject.Inject +import javax.inject.Named -import android.Manifest; -import android.Manifest.permission; -import android.annotation.SuppressLint; -import android.content.Context; -import android.content.Intent; -import android.hardware.Sensor; -import android.hardware.SensorEvent; -import android.hardware.SensorEventListener; -import android.hardware.SensorManager; -import android.os.Bundle; -import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.view.ViewGroup; -import android.widget.CheckBox; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; -import androidx.activity.result.ActivityResultCallback; -import androidx.activity.result.ActivityResultLauncher; -import androidx.activity.result.contract.ActivityResultContracts; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager.OnBackStackChangedListener; -import androidx.fragment.app.FragmentTransaction; -import fr.free.nrw.commons.Utils; -import fr.free.nrw.commons.auth.SessionManager; -import fr.free.nrw.commons.databinding.FragmentContributionsBinding; -import fr.free.nrw.commons.notification.models.Notification; -import fr.free.nrw.commons.notification.NotificationController; -import fr.free.nrw.commons.profile.ProfileActivity; -import fr.free.nrw.commons.theme.BaseActivity; -import fr.free.nrw.commons.upload.UploadProgressActivity; -import java.util.Calendar; -import java.util.Date; -import java.util.List; -import java.util.Map; -import javax.inject.Inject; -import javax.inject.Named; -import androidx.work.WorkManager; -import fr.free.nrw.commons.Media; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.campaigns.models.Campaign; -import fr.free.nrw.commons.campaigns.CampaignView; -import fr.free.nrw.commons.campaigns.CampaignsPresenter; -import fr.free.nrw.commons.campaigns.ICampaignsView; -import fr.free.nrw.commons.contributions.ContributionsListFragment.Callback; -import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment; -import fr.free.nrw.commons.di.CommonsDaggerSupportFragment; -import fr.free.nrw.commons.kvstore.JsonKvStore; -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.location.LocationServiceManager; -import fr.free.nrw.commons.location.LocationUpdateListener; -import fr.free.nrw.commons.media.MediaDetailPagerFragment; -import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider; -import fr.free.nrw.commons.mwapi.OkHttpJsonApiClient; -import fr.free.nrw.commons.nearby.NearbyController; -import fr.free.nrw.commons.nearby.NearbyNotificationCardView; -import fr.free.nrw.commons.nearby.Place; -import fr.free.nrw.commons.notification.NotificationActivity; -import fr.free.nrw.commons.upload.worker.UploadWorker; -import fr.free.nrw.commons.utils.ConfigUtils; -import fr.free.nrw.commons.utils.DialogUtil; -import fr.free.nrw.commons.utils.NetworkUtils; -import fr.free.nrw.commons.utils.PermissionUtils; -import fr.free.nrw.commons.utils.ViewUtil; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; -import timber.log.Timber; - -public class ContributionsFragment - extends CommonsDaggerSupportFragment - implements - OnBackStackChangedListener, - LocationUpdateListener, - MediaDetailProvider, - SensorEventListener, - ICampaignsView, ContributionsContract.View, Callback { +class ContributionsFragment + : CommonsDaggerSupportFragment(), FragmentManager.OnBackStackChangedListener, + LocationUpdateListener, MediaDetailProvider, SensorEventListener, ICampaignsView, + ContributionsContract.View, + ContributionsListFragment.Callback { + @JvmField @Inject @Named("default_preferences") - JsonKvStore store; + var store: JsonKvStore? = null + + @JvmField @Inject - NearbyController nearbyController; + var nearbyController: NearbyController? = null + + @JvmField @Inject - OkHttpJsonApiClient okHttpJsonApiClient; + var okHttpJsonApiClient: OkHttpJsonApiClient? = null + + @JvmField @Inject - CampaignsPresenter presenter; + var presenter: CampaignsPresenter? = null + + @JvmField @Inject - LocationServiceManager locationManager; + var locationManager: LocationServiceManager? = null + + @JvmField @Inject - NotificationController notificationController; + var notificationController: NotificationController? = null + + @JvmField @Inject - ContributionController contributionController; + var contributionController: ContributionController? = null - private CompositeDisposable compositeDisposable = new CompositeDisposable(); + override var compositeDisposable: CompositeDisposable = CompositeDisposable() - private ContributionsListFragment contributionsListFragment; - private static final String CONTRIBUTION_LIST_FRAGMENT_TAG = "ContributionListFragmentTag"; - private MediaDetailPagerFragment mediaDetailPagerFragment; - static final String MEDIA_DETAIL_PAGER_FRAGMENT_TAG = "MediaDetailFragmentTag"; - private static final int MAX_RETRIES = 10; + private var contributionsListFragment: ContributionsListFragment? = null - public FragmentContributionsBinding binding; + // Getter for mediaDetailPagerFragment + var mediaDetailPagerFragment: MediaDetailPagerFragment? = null + private set + var binding: FragmentContributionsBinding? = null + @JvmField @Inject - ContributionsPresenter contributionsPresenter; + var contributionsPresenter: ContributionsPresenter? = null + @JvmField @Inject - SessionManager sessionManager; + var sessionManager: SessionManager? = null - private LatLng currentLatLng; + private var currentLatLng: LatLng? = null - private boolean isFragmentAttachedBefore = false; - private View checkBoxView; - private CheckBox checkBox; + private var isFragmentAttachedBefore = false + private var checkBoxView: View? = null + private var checkBox: CheckBox? = null - public TextView notificationCount; + var notificationCount: TextView? = null - public TextView pendingUploadsCountTextView; + var pendingUploadsCountTextView: TextView? = null - public TextView uploadsErrorTextView; + var uploadsErrorTextView: TextView? = null - public ImageView pendingUploadsImageView; + var pendingUploadsImageView: ImageView? = null - private Campaign wlmCampaign; + private var wlmCampaign: Campaign? = null - String userName; - private boolean isUserProfile; + var userName: String? = null + private var isUserProfile = false - private SensorManager mSensorManager; - private Sensor mLight; - private float direction; - private ActivityResultLauncher nearbyLocationPermissionLauncher = registerForActivityResult( - new ActivityResultContracts.RequestMultiplePermissions(), - new ActivityResultCallback>() { - @Override - public void onActivityResult(Map result) { - boolean areAllGranted = true; - for (final boolean b : result.values()) { - areAllGranted = areAllGranted && b; - } + private var mSensorManager: SensorManager? = null + private var mLight: Sensor? = null + private var direction = 0f + private val nearbyLocationPermissionLauncher = + registerForActivityResult, Map>( + ActivityResultContracts.RequestMultiplePermissions(), + object : ActivityResultCallback> { + override fun onActivityResult(result: Map) { + var areAllGranted = true + for (b in result.values) { + areAllGranted = areAllGranted && b + } - if (areAllGranted) { - onLocationPermissionGranted(); - } else { - if (shouldShowRequestPermissionRationale( - Manifest.permission.ACCESS_FINE_LOCATION) - && store.getBoolean("displayLocationPermissionForCardView", true) - && !store.getBoolean("doNotAskForLocationPermission", false) - && (((MainActivity) getActivity()).activeFragment - == ActiveFragment.CONTRIBUTIONS)) { - binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; + if (areAllGranted) { + onLocationPermissionGranted() } else { - displayYouWontSeeNearbyMessage(); + if (shouldShowRequestPermissionRationale( + permission.ACCESS_FINE_LOCATION + ) + && store!!.getBoolean("displayLocationPermissionForCardView", true) + && !store!!.getBoolean("doNotAskForLocationPermission", false) + && ((activity as MainActivity).activeFragment + == ActiveFragment.CONTRIBUTIONS) + ) { + binding!!.cardViewNearby.permissionType = + NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION + } else { + displayYouWontSeeNearbyMessage() + } } } - } - }); + }) - @NonNull - public static ContributionsFragment newInstance() { - ContributionsFragment fragment = new ContributionsFragment(); - fragment.setRetainInstance(true); - return fragment; - } + private var shouldShowMediaDetailsFragment = false - private boolean shouldShowMediaDetailsFragment; - - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null && getArguments().getString(KEY_USERNAME) != null) { - userName = getArguments().getString(KEY_USERNAME); - isUserProfile = true; + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + if (arguments != null && requireArguments().getString(ProfileActivity.KEY_USERNAME) != null) { + userName = requireArguments().getString(ProfileActivity.KEY_USERNAME) + isUserProfile = true } - mSensorManager = (SensorManager) getActivity().getSystemService(SENSOR_SERVICE); - mLight = mSensorManager.getDefaultSensor(Sensor.TYPE_ORIENTATION); + mSensorManager = requireActivity().getSystemService(Context.SENSOR_SERVICE) as SensorManager + mLight = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ORIENTATION) } - @Nullable - @Override - public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = FragmentContributionsBinding.inflate(inflater, container, false) - binding = FragmentContributionsBinding.inflate(inflater, container, false); - - initWLMCampaign(); - presenter.onAttachView(this); - contributionsPresenter.onAttachView(this); - binding.campaignsView.setVisibility(View.GONE); - checkBoxView = View.inflate(getActivity(), R.layout.nearby_permission_dialog, null); - checkBox = (CheckBox) checkBoxView.findViewById(R.id.never_ask_again); - checkBox.setOnCheckedChangeListener((buttonView, isChecked) -> { + initWLMCampaign() + presenter!!.onAttachView(this) + contributionsPresenter!!.onAttachView(this) + binding!!.campaignsView.visibility = View.GONE + checkBoxView = View.inflate(activity, R.layout.nearby_permission_dialog, null) + checkBox = checkBoxView?.findViewById(R.id.never_ask_again) as CheckBox + checkBox!!.setOnCheckedChangeListener { buttonView: CompoundButton?, isChecked: Boolean -> if (isChecked) { // Do not ask for permission on activity start again - store.putBoolean("displayLocationPermissionForCardView", false); + store!!.putBoolean("displayLocationPermissionForCardView", false) } - }); + } if (savedInstanceState != null) { - mediaDetailPagerFragment = (MediaDetailPagerFragment) getChildFragmentManager() - .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG); - contributionsListFragment = (ContributionsListFragment) getChildFragmentManager() - .findFragmentByTag(CONTRIBUTION_LIST_FRAGMENT_TAG); - shouldShowMediaDetailsFragment = savedInstanceState.getBoolean("mediaDetailsVisible"); + mediaDetailPagerFragment = childFragmentManager + .findFragmentByTag(MEDIA_DETAIL_PAGER_FRAGMENT_TAG) as MediaDetailPagerFragment? + contributionsListFragment = childFragmentManager + .findFragmentByTag(CONTRIBUTION_LIST_FRAGMENT_TAG) as ContributionsListFragment? + shouldShowMediaDetailsFragment = savedInstanceState.getBoolean("mediaDetailsVisible") } - initFragments(); + initFragments() if (!isUserProfile) { - upDateUploadCount(); + upDateUploadCount() } if (shouldShowMediaDetailsFragment) { - showMediaDetailPagerFragment(); + showMediaDetailPagerFragment() } else { if (mediaDetailPagerFragment != null) { - removeFragment(mediaDetailPagerFragment); + removeFragment(mediaDetailPagerFragment!!) } - showContributionsListFragment(); + showContributionsListFragment() } - if (!ConfigUtils.isBetaFlavour() && sessionManager.isUserLoggedIn() - && sessionManager.getCurrentAccount() != null && !isUserProfile) { - setUploadCount(); + if (!isBetaFlavour && sessionManager!!.isUserLoggedIn + && sessionManager!!.currentAccount != null && !isUserProfile + ) { + setUploadCount() } - setHasOptionsMenu(true); - return binding.getRoot(); + setHasOptionsMenu(true) + return binding!!.root } /** * Initialise the campaign object for WML */ - private void initWLMCampaign() { - wlmCampaign = new Campaign(getString(R.string.wlm_campaign_title), + private fun initWLMCampaign() { + wlmCampaign = Campaign( + getString(R.string.wlm_campaign_title), getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(), - Utils.getWLMEndDate().toString(), WLM_URL, true); + Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true + ) } - @Override - public void onCreateOptionsMenu(@NonNull final Menu menu, - @NonNull final MenuInflater inflater) { - + override fun onCreateOptionsMenu( + menu: Menu, + inflater: MenuInflater + ) { // Removing contributions menu items for ProfileActivity - if (getActivity() instanceof ProfileActivity) { - return; + + if (activity is ProfileActivity) { + return } - inflater.inflate(R.menu.contribution_activity_notification_menu, menu); + inflater.inflate(R.menu.contribution_activity_notification_menu, menu) - MenuItem notificationsMenuItem = menu.findItem(R.id.notifications); - final View notification = notificationsMenuItem.getActionView(); - notificationCount = notification.findViewById(R.id.notification_count_badge); - MenuItem uploadMenuItem = menu.findItem(R.id.upload_tab); - final View uploadMenuItemActionView = uploadMenuItem.getActionView(); - pendingUploadsCountTextView = uploadMenuItemActionView.findViewById( - R.id.pending_uploads_count_badge); + val notificationsMenuItem = menu.findItem(R.id.notifications) + val notification = notificationsMenuItem.actionView + notificationCount = notification!!.findViewById(R.id.notification_count_badge) + val uploadMenuItem = menu.findItem(R.id.upload_tab) + val uploadMenuItemActionView = uploadMenuItem.actionView + pendingUploadsCountTextView = uploadMenuItemActionView!!.findViewById( + R.id.pending_uploads_count_badge + ) uploadsErrorTextView = uploadMenuItemActionView.findViewById( - R.id.uploads_error_count_badge); + R.id.uploads_error_count_badge + ) pendingUploadsImageView = uploadMenuItemActionView.findViewById( - R.id.pending_uploads_image_view); + R.id.pending_uploads_image_view + ) if (pendingUploadsImageView != null) { - pendingUploadsImageView.setOnClickListener(view -> { - startActivity(new Intent(getContext(), UploadProgressActivity.class)); - }); + pendingUploadsImageView!!.setOnClickListener { view: View? -> + startActivity( + Intent( + context, + UploadProgressActivity::class.java + ) + ) + } } if (pendingUploadsCountTextView != null) { - pendingUploadsCountTextView.setOnClickListener(view -> { - startActivity(new Intent(getContext(), UploadProgressActivity.class)); - }); + pendingUploadsCountTextView!!.setOnClickListener { view: View? -> + startActivity( + Intent( + context, + UploadProgressActivity::class.java + ) + ) + } } if (uploadsErrorTextView != null) { - uploadsErrorTextView.setOnClickListener(view -> { - startActivity(new Intent(getContext(), UploadProgressActivity.class)); - }); + uploadsErrorTextView!!.setOnClickListener { view: View? -> + startActivity( + Intent( + context, + UploadProgressActivity::class.java + ) + ) + } + } + notification.setOnClickListener { view: View? -> + startYourself( + context, "unread" + ) } - notification.setOnClickListener(view -> { - NotificationActivity.Companion.startYourself(getContext(), "unread"); - }); } @SuppressLint("CheckResult") - public void setNotificationCount() { - compositeDisposable.add(notificationController.getNotifications(false) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::initNotificationViews, - throwable -> Timber.e(throwable, "Error occurred while loading notifications"))); + fun setNotificationCount() { + compositeDisposable.add( + notificationController!!.getNotifications(false) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { notificationList: List -> + this.initNotificationViews( + notificationList + ) + }, + { throwable: Throwable? -> + Timber.e( + throwable, + "Error occurred while loading notifications" + ) + }) + ) } /** @@ -307,48 +339,49 @@ public class ContributionsFragment * Sets the visibility of the upload icon based on the number of failed and pending * contributions. */ -// public void setUploadIconVisibility() { -// contributionController.getFailedAndPendingContributions(); -// contributionController.failedAndPendingContributionList.observe(getViewLifecycleOwner(), -// list -> { -// updateUploadIcon(list.size()); -// }); -// } - + // public void setUploadIconVisibility() { + // contributionController.getFailedAndPendingContributions(); + // contributionController.failedAndPendingContributionList.observe(getViewLifecycleOwner(), + // list -> { + // updateUploadIcon(list.size()); + // }); + // } /** * Sets the count for the upload icon based on the number of pending and failed contributions. */ - public void setUploadIconCount() { - contributionController.getPendingContributions(); - contributionController.pendingContributionList.observe(getViewLifecycleOwner(), - list -> { - updatePendingIcon(list.size()); - }); - contributionController.getFailedContributions(); - contributionController.failedContributionList.observe(getViewLifecycleOwner(), list -> { - updateErrorIcon(list.size()); - }); + fun setUploadIconCount() { + contributionController!!.pendingContributions + contributionController!!.pendingContributionList!!.observe( + viewLifecycleOwner, + Observer> { list: PagedList -> + updatePendingIcon(list.size) + }) + contributionController!!.failedContributions + contributionController!!.failedContributionList!!.observe( + viewLifecycleOwner, + Observer> { list: PagedList -> + updateErrorIcon(list.size) + }) } - public void scrollToTop() { + fun scrollToTop() { if (contributionsListFragment != null) { - contributionsListFragment.scrollToTop(); + contributionsListFragment!!.scrollToTop() } } - private void initNotificationViews(List notificationList) { - Timber.d("Number of notifications is %d", notificationList.size()); + private fun initNotificationViews(notificationList: List) { + Timber.d("Number of notifications is %d", notificationList.size) if (notificationList.isEmpty()) { - notificationCount.setVisibility(View.GONE); + notificationCount!!.visibility = View.GONE } else { - notificationCount.setVisibility(View.VISIBLE); - notificationCount.setText(String.valueOf(notificationList.size())); + notificationCount!!.visibility = View.VISIBLE + notificationCount!!.text = notificationList.size.toString() } } - @Override - public void onAttach(Context context) { - super.onAttach(context); + override fun onAttach(context: Context) { + super.onAttach(context) /* - There are some operations we need auth, so we need to make sure isAuthCookieAcquired. - And since we use same retained fragment doesn't want to make all network operations @@ -356,8 +389,8 @@ public class ContributionsFragment operations on first time fragment attached to an activity. Then they will be retained until fragment life time ends. */ - if (!isFragmentAttachedBefore && getActivity() != null) { - isFragmentAttachedBefore = true; + if (!isFragmentAttachedBefore && activity != null) { + isFragmentAttachedBefore = true } } @@ -365,56 +398,62 @@ public class ContributionsFragment * Replace FrameLayout with ContributionsListFragment, user will see contributions list. Creates * new one if null. */ - private void showContributionsListFragment() { + private fun showContributionsListFragment() { // show nearby card view on contributions list is visible - if (binding.cardViewNearby != null && !isUserProfile) { - if (store.getBoolean("displayNearbyCardView", true)) { - if (binding.cardViewNearby.cardViewVisibilityState - == NearbyNotificationCardView.CardViewVisibilityState.READY) { - binding.cardViewNearby.setVisibility(View.VISIBLE); + if (binding!!.cardViewNearby != null && !isUserProfile) { + if (store!!.getBoolean("displayNearbyCardView", true)) { + if (binding!!.cardViewNearby.cardViewVisibilityState + == NearbyNotificationCardView.CardViewVisibilityState.READY + ) { + binding!!.cardViewNearby.visibility = View.VISIBLE } } else { - binding.cardViewNearby.setVisibility(View.GONE); + binding!!.cardViewNearby.visibility = View.GONE } } - showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, - mediaDetailPagerFragment); + showFragment( + contributionsListFragment!!, CONTRIBUTION_LIST_FRAGMENT_TAG, + mediaDetailPagerFragment + ) } - private void showMediaDetailPagerFragment() { + private fun showMediaDetailPagerFragment() { // hide nearby card view on media detail is visible - setupViewForMediaDetails(); - showFragment(mediaDetailPagerFragment, MEDIA_DETAIL_PAGER_FRAGMENT_TAG, - contributionsListFragment); + setupViewForMediaDetails() + showFragment( + mediaDetailPagerFragment!!, MEDIA_DETAIL_PAGER_FRAGMENT_TAG, + contributionsListFragment + ) } - private void setupViewForMediaDetails() { + private fun setupViewForMediaDetails() { if (binding != null) { - binding.campaignsView.setVisibility(View.GONE); + binding!!.campaignsView.visibility = View.GONE } } - @Override - public void onBackStackChanged() { - fetchCampaigns(); + override fun onBackStackChanged() { + fetchCampaigns() } - private void initFragments() { + private fun initFragments() { if (null == contributionsListFragment) { - contributionsListFragment = new ContributionsListFragment(); - Bundle contributionsListBundle = new Bundle(); - contributionsListBundle.putString(KEY_USERNAME, userName); - contributionsListFragment.setArguments(contributionsListBundle); + contributionsListFragment = ContributionsListFragment() + val contributionsListBundle = Bundle() + contributionsListBundle.putString(ProfileActivity.KEY_USERNAME, userName) + contributionsListFragment!!.arguments = contributionsListBundle } if (shouldShowMediaDetailsFragment) { - showMediaDetailPagerFragment(); + showMediaDetailPagerFragment() } else { - showContributionsListFragment(); + showContributionsListFragment() } - showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, - mediaDetailPagerFragment); + showFragment( + contributionsListFragment!!, CONTRIBUTION_LIST_FRAGMENT_TAG, + mediaDetailPagerFragment + ) } /** @@ -424,248 +463,265 @@ public class ContributionsFragment * @param tag * @param otherFragment */ - private void showFragment(Fragment fragment, String tag, Fragment otherFragment) { - FragmentTransaction transaction = getChildFragmentManager().beginTransaction(); - if (fragment.isAdded() && otherFragment != null) { - transaction.hide(otherFragment); - transaction.show(fragment); - transaction.addToBackStack(tag); - transaction.commit(); - getChildFragmentManager().executePendingTransactions(); - } else if (fragment.isAdded() && otherFragment == null) { - transaction.show(fragment); - transaction.addToBackStack(tag); - transaction.commit(); - getChildFragmentManager().executePendingTransactions(); - } else if (!fragment.isAdded() && otherFragment != null) { - transaction.hide(otherFragment); - transaction.add(R.id.root_frame, fragment, tag); - transaction.addToBackStack(tag); - transaction.commit(); - getChildFragmentManager().executePendingTransactions(); - } else if (!fragment.isAdded()) { - transaction.replace(R.id.root_frame, fragment, tag); - transaction.addToBackStack(tag); - transaction.commit(); - getChildFragmentManager().executePendingTransactions(); + private fun showFragment(fragment: Fragment, tag: String, otherFragment: Fragment?) { + val transaction = childFragmentManager.beginTransaction() + if (fragment.isAdded && otherFragment != null) { + transaction.hide(otherFragment) + transaction.show(fragment) + transaction.addToBackStack(tag) + transaction.commit() + childFragmentManager.executePendingTransactions() + } else if (fragment.isAdded && otherFragment == null) { + transaction.show(fragment) + transaction.addToBackStack(tag) + transaction.commit() + childFragmentManager.executePendingTransactions() + } else if (!fragment.isAdded && otherFragment != null) { + transaction.hide(otherFragment) + transaction.add(R.id.root_frame, fragment, tag) + transaction.addToBackStack(tag) + transaction.commit() + childFragmentManager.executePendingTransactions() + } else if (!fragment.isAdded) { + transaction.replace(R.id.root_frame, fragment, tag) + transaction.addToBackStack(tag) + transaction.commit() + childFragmentManager.executePendingTransactions() } } - public void removeFragment(Fragment fragment) { - getChildFragmentManager() + fun removeFragment(fragment: Fragment) { + childFragmentManager .beginTransaction() .remove(fragment) - .commit(); - getChildFragmentManager().executePendingTransactions(); + .commit() + childFragmentManager.executePendingTransactions() } - @SuppressWarnings("ConstantConditions") - private void setUploadCount() { - compositeDisposable.add(okHttpJsonApiClient - .getUploadCount(((MainActivity) getActivity()).sessionManager.getCurrentAccount().name) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::displayUploadCount, - t -> Timber.e(t, "Fetching upload count failed") - )); + private fun setUploadCount() { + okHttpJsonApiClient + ?.getUploadCount((activity as MainActivity).sessionManager.currentAccount!!.name) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread())?.let { + compositeDisposable.add( + it + .subscribe( + { uploadCount: Int -> this.displayUploadCount(uploadCount) }, + { t: Throwable? -> Timber.e(t, "Fetching upload count failed") } + )) + } } - private void displayUploadCount(Integer uploadCount) { - if (getActivity().isFinishing() - || getResources() == null) { - return; + private fun displayUploadCount(uploadCount: Int) { + if (requireActivity().isFinishing + || resources == null + ) { + return } - ((MainActivity) getActivity()).setNumOfUploads(uploadCount); - + (activity as MainActivity).setNumOfUploads(uploadCount) } - @Override - public void onPause() { - super.onPause(); - locationManager.removeLocationListener(this); - locationManager.unregisterLocationManager(); - mSensorManager.unregisterListener(this); + override fun onPause() { + super.onPause() + locationManager!!.removeLocationListener(this) + locationManager!!.unregisterLocationManager() + mSensorManager!!.unregisterListener(this) } - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); + override fun onSaveInstanceState(outState: Bundle) { + super.onSaveInstanceState(outState) } - @Override - public void onResume() { - super.onResume(); - contributionsPresenter.onAttachView(this); - locationManager.addLocationListener(this); + override fun onResume() { + super.onResume() + contributionsPresenter!!.onAttachView(this) + locationManager!!.addLocationListener(this) if (binding == null) { - return; + return } - binding.cardViewNearby.permissionRequestButton.setOnClickListener(v -> { - showNearbyCardPermissionRationale(); - }); + binding!!.cardViewNearby.permissionRequestButton.setOnClickListener { v: View? -> + showNearbyCardPermissionRationale() + } // Notification cards should only be seen on contributions list, not in media details if (mediaDetailPagerFragment == null && !isUserProfile) { - if (store.getBoolean("displayNearbyCardView", true)) { - checkPermissionsAndShowNearbyCardView(); + if (store!!.getBoolean("displayNearbyCardView", true)) { + checkPermissionsAndShowNearbyCardView() // Calling nearby card to keep showing it even when user clicks on it and comes back try { - updateClosestNearbyCardViewInfo(); - } catch (Exception e) { - Timber.e(e); + updateClosestNearbyCardViewInfo() + } catch (e: Exception) { + Timber.e(e) } - if (binding.cardViewNearby.cardViewVisibilityState - == NearbyNotificationCardView.CardViewVisibilityState.READY) { - binding.cardViewNearby.setVisibility(View.VISIBLE); + if (binding!!.cardViewNearby.cardViewVisibilityState + == NearbyNotificationCardView.CardViewVisibilityState.READY + ) { + binding!!.cardViewNearby.visibility = View.VISIBLE } - } else { // Hide nearby notification card view if related shared preferences is false - binding.cardViewNearby.setVisibility(View.GONE); + binding!!.cardViewNearby.visibility = View.GONE } // Notification Count and Campaigns should not be set, if it is used in User Profile if (!isUserProfile) { - setNotificationCount(); - fetchCampaigns(); + setNotificationCount() + fetchCampaigns() // Temporarily disabled, see issue [https://github.com/commons-app/apps-android-commons/issues/5847] // setUploadIconVisibility(); - setUploadIconCount(); + setUploadIconCount() } } - mSensorManager.registerListener(this, mLight, SensorManager.SENSOR_DELAY_UI); + mSensorManager!!.registerListener(this, mLight, SensorManager.SENSOR_DELAY_UI) } - private void checkPermissionsAndShowNearbyCardView() { - if (PermissionUtils.hasPermission(getActivity(), - new String[]{Manifest.permission.ACCESS_FINE_LOCATION})) { - onLocationPermissionGranted(); - } else if (shouldShowRequestPermissionRationale(Manifest.permission.ACCESS_FINE_LOCATION) - && store.getBoolean("displayLocationPermissionForCardView", true) - && !store.getBoolean("doNotAskForLocationPermission", false) - && (((MainActivity) getActivity()).activeFragment == ActiveFragment.CONTRIBUTIONS)) { - binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION; - showNearbyCardPermissionRationale(); + private fun checkPermissionsAndShowNearbyCardView() { + if (hasPermission( + requireActivity(), + arrayOf(permission.ACCESS_FINE_LOCATION) + ) + ) { + onLocationPermissionGranted() + } else if (shouldShowRequestPermissionRationale(permission.ACCESS_FINE_LOCATION) + && store!!.getBoolean("displayLocationPermissionForCardView", true) + && !store!!.getBoolean("doNotAskForLocationPermission", false) + && ((activity as MainActivity).activeFragment == ActiveFragment.CONTRIBUTIONS) + ) { + binding!!.cardViewNearby.permissionType = + NearbyNotificationCardView.PermissionType.ENABLE_LOCATION_PERMISSION + showNearbyCardPermissionRationale() } } - private void requestLocationPermission() { - nearbyLocationPermissionLauncher.launch(new String[]{permission.ACCESS_FINE_LOCATION}); + private fun requestLocationPermission() { + nearbyLocationPermissionLauncher.launch(arrayOf(permission.ACCESS_FINE_LOCATION)) } - private void onLocationPermissionGranted() { - binding.cardViewNearby.permissionType = NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED; - locationManager.registerLocationManager(); + private fun onLocationPermissionGranted() { + binding!!.cardViewNearby.permissionType = + NearbyNotificationCardView.PermissionType.NO_PERMISSION_NEEDED + locationManager!!.registerLocationManager() } - private void showNearbyCardPermissionRationale() { - DialogUtil.showAlertDialog(getActivity(), + private fun showNearbyCardPermissionRationale() { + showAlertDialog( + requireActivity(), getString(R.string.nearby_card_permission_title), getString(R.string.nearby_card_permission_explanation), - this::requestLocationPermission, - this::displayYouWontSeeNearbyMessage, + { this.requestLocationPermission() }, + { this.displayYouWontSeeNearbyMessage() }, checkBoxView - ); + ) } - private void displayYouWontSeeNearbyMessage() { - ViewUtil.showLongToast(getActivity(), - getResources().getString(R.string.unable_to_display_nearest_place)); + private fun displayYouWontSeeNearbyMessage() { + showLongToast( + requireActivity(), + resources.getString(R.string.unable_to_display_nearest_place) + ) // Set to true as the user doesn't want the app to ask for location permission anymore - store.putBoolean("doNotAskForLocationPermission", true); + store!!.putBoolean("doNotAskForLocationPermission", true) } - private void updateClosestNearbyCardViewInfo() { - currentLatLng = locationManager.getLastLocation(); - compositeDisposable.add(Observable.fromCallable(() -> nearbyController - .loadAttractionsFromLocation(currentLatLng, currentLatLng, true, - false)) // thanks to boolean, it will only return closest result + private fun updateClosestNearbyCardViewInfo() { + currentLatLng = locationManager!!.getLastLocation() + compositeDisposable.add(Observable.fromCallable { + nearbyController?.loadAttractionsFromLocation( + currentLatLng, currentLatLng, true, + false + ) + } // thanks to boolean, it will only return closest result .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(this::updateNearbyNotification, - throwable -> { - Timber.d(throwable); - updateNearbyNotification(null); - })); + .subscribe( + { nearbyPlacesInfo: NearbyPlacesInfo? -> + this.updateNearbyNotification( + nearbyPlacesInfo + ) + }, + { throwable: Throwable? -> + Timber.d(throwable) + updateNearbyNotification(null) + }) + ) } - private void updateNearbyNotification( - @Nullable NearbyController.NearbyPlacesInfo nearbyPlacesInfo) { - if (nearbyPlacesInfo != null && nearbyPlacesInfo.placeList != null - && nearbyPlacesInfo.placeList.size() > 0) { - Place closestNearbyPlace = null; + private fun updateNearbyNotification( + nearbyPlacesInfo: NearbyPlacesInfo? + ) { + if (nearbyPlacesInfo?.placeList != null && nearbyPlacesInfo.placeList.size > 0) { + var closestNearbyPlace: Place? = null // Find the first nearby place that has no image and exists - for (Place place : nearbyPlacesInfo.placeList) { - if (place.pic.equals("") && place.exists) { - closestNearbyPlace = place; - break; + for (place in nearbyPlacesInfo.placeList) { + if (place.pic == "" && place.exists) { + closestNearbyPlace = place + break } } if (closestNearbyPlace == null) { - binding.cardViewNearby.setVisibility(View.GONE); + binding!!.cardViewNearby.visibility = View.GONE } else { - String distance = formatDistanceBetween(currentLatLng, closestNearbyPlace.location); - closestNearbyPlace.setDistance(distance); - direction = (float) computeBearing(currentLatLng, closestNearbyPlace.location); - binding.cardViewNearby.updateContent(closestNearbyPlace); + val distance = formatDistanceBetween(currentLatLng, closestNearbyPlace.location) + closestNearbyPlace.setDistance(distance) + direction = computeBearing(currentLatLng!!, closestNearbyPlace.location).toFloat() + binding!!.cardViewNearby.updateContent(closestNearbyPlace) } } else { // Means that no close nearby place is found - binding.cardViewNearby.setVisibility(View.GONE); + binding!!.cardViewNearby.visibility = View.GONE } // Prevent Nearby banner from appearing in Media Details, fixing bug https://github.com/commons-app/apps-android-commons/issues/4731 - if (mediaDetailPagerFragment != null && !contributionsListFragment.isVisible()) { - binding.cardViewNearby.setVisibility(View.GONE); + if (mediaDetailPagerFragment != null && !contributionsListFragment!!.isVisible) { + binding!!.cardViewNearby.visibility = View.GONE } } - @Override - public void onDestroy() { + override fun onDestroy() { try { - compositeDisposable.clear(); - getChildFragmentManager().removeOnBackStackChangedListener(this); - locationManager.unregisterLocationManager(); - locationManager.removeLocationListener(this); - super.onDestroy(); - } catch (IllegalArgumentException | IllegalStateException exception) { - Timber.e(exception); + compositeDisposable.clear() + childFragmentManager.removeOnBackStackChangedListener(this) + locationManager!!.unregisterLocationManager() + locationManager!!.removeLocationListener(this) + super.onDestroy() + } catch (exception: IllegalArgumentException) { + Timber.e(exception) + } catch (exception: IllegalStateException) { + Timber.e(exception) } } - @Override - public void onLocationChangedSignificantly(LatLng latLng) { + override fun onLocationChangedSignificantly(latLng: LatLng) { // Will be called if location changed more than 1000 meter - updateClosestNearbyCardViewInfo(); + updateClosestNearbyCardViewInfo() } - @Override - public void onLocationChangedSlightly(LatLng latLng) { + override fun onLocationChangedSlightly(latLng: LatLng) { /* Update closest nearby notification card onLocationChangedSlightly */ try { - updateClosestNearbyCardViewInfo(); - } catch (Exception e) { - Timber.e(e); + updateClosestNearbyCardViewInfo() + } catch (e: Exception) { + Timber.e(e) } } - @Override - public void onLocationChangedMedium(LatLng latLng) { + override fun onLocationChangedMedium(latLng: LatLng) { // Update closest nearby card view if location changed more than 500 meters - updateClosestNearbyCardViewInfo(); + updateClosestNearbyCardViewInfo() } - @Override - public void onViewCreated(@NonNull View view, - @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); + override fun onViewCreated( + view: View, + savedInstanceState: Bundle? + ) { + super.onViewCreated(view, savedInstanceState) } /** @@ -673,55 +729,50 @@ public class ContributionsFragment * The WLM Card gets the priority over monuments, so if the WLM is going on we show that instead * of campaigns on the campaigns card */ - private void fetchCampaigns() { - if (Utils.isMonumentsEnabled(new Date())) { + private fun fetchCampaigns() { + if (Utils.isMonumentsEnabled(Date())) { if (binding != null) { - binding.campaignsView.setCampaign(wlmCampaign); - binding.campaignsView.setVisibility(View.VISIBLE); + binding!!.campaignsView.setCampaign(wlmCampaign) + binding!!.campaignsView.visibility = View.VISIBLE } - } else if (store.getBoolean(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE, true)) { - presenter.getCampaigns(); + } else if (store!!.getBoolean(CampaignView.CAMPAIGNS_DEFAULT_PREFERENCE, true)) { + presenter!!.getCampaigns() } else { if (binding != null) { - binding.campaignsView.setVisibility(View.GONE); + binding!!.campaignsView.visibility = View.GONE } } } - @Override - public void showMessage(String message) { - Toast.makeText(getContext(), message, Toast.LENGTH_SHORT).show(); + override fun showMessage(message: String) { + Toast.makeText(context, message, Toast.LENGTH_SHORT).show() } - @Override - public void showCampaigns(Campaign campaign) { + override fun showCampaigns(campaign: Campaign?) { if (campaign != null && !isUserProfile) { if (binding != null) { - binding.campaignsView.setCampaign(campaign); + binding!!.campaignsView.setCampaign(campaign) } } } - @Override - public void onDestroyView() { - super.onDestroyView(); - presenter.onDetachView(); + override fun onDestroyView() { + super.onDestroyView() + presenter!!.onDetachView() } - @Override - public void notifyDataSetChanged() { + override fun notifyDataSetChanged() { if (mediaDetailPagerFragment != null) { - mediaDetailPagerFragment.notifyDataSetChanged(); + mediaDetailPagerFragment!!.notifyDataSetChanged() } } /** * Notify the viewpager that number of items have changed. */ - @Override - public void viewPagerNotifyDataSetChanged() { + override fun viewPagerNotifyDataSetChanged() { if (mediaDetailPagerFragment != null) { - mediaDetailPagerFragment.notifyDataSetChanged(); + mediaDetailPagerFragment!!.notifyDataSetChanged() } } @@ -731,13 +782,13 @@ public class ContributionsFragment * * @param pendingCount The number of pending uploads. */ - public void updatePendingIcon(int pendingCount) { + fun updatePendingIcon(pendingCount: Int) { if (pendingUploadsCountTextView != null) { if (pendingCount != 0) { - pendingUploadsCountTextView.setVisibility(View.VISIBLE); - pendingUploadsCountTextView.setText(String.valueOf(pendingCount)); + pendingUploadsCountTextView!!.visibility = View.VISIBLE + pendingUploadsCountTextView!!.text = pendingCount.toString() } else { - pendingUploadsCountTextView.setVisibility(View.INVISIBLE); + pendingUploadsCountTextView!!.visibility = View.INVISIBLE } } } @@ -747,13 +798,13 @@ public class ContributionsFragment * * @param errorCount The number of error uploads. */ - public void updateErrorIcon(int errorCount) { + fun updateErrorIcon(errorCount: Int) { if (uploadsErrorTextView != null) { if (errorCount != 0) { - uploadsErrorTextView.setVisibility(View.VISIBLE); - uploadsErrorTextView.setText(String.valueOf(errorCount)); + uploadsErrorTextView!!.visibility = View.VISIBLE + uploadsErrorTextView!!.text = errorCount.toString() } else { - uploadsErrorTextView.setVisibility(View.GONE); + uploadsErrorTextView!!.visibility = View.GONE } } } @@ -762,96 +813,90 @@ public class ContributionsFragment * Temporarily disabled, see issue [https://github.com/commons-app/apps-android-commons/issues/5847] * @param count The number of pending uploads. */ -// public void updateUploadIcon(int count) { -// if (pendingUploadsImageView != null) { -// if (count != 0) { -// pendingUploadsImageView.setVisibility(View.VISIBLE); -// } else { -// pendingUploadsImageView.setVisibility(View.GONE); -// } -// } -// } - + // public void updateUploadIcon(int count) { + // if (pendingUploadsImageView != null) { + // if (count != 0) { + // pendingUploadsImageView.setVisibility(View.VISIBLE); + // } else { + // pendingUploadsImageView.setVisibility(View.GONE); + // } + // } + // } /** * Replace whatever is in the current contributionsFragmentContainer view with * mediaDetailPagerFragment, and preserve previous state in back stack. Called when user selects * a contribution. */ - @Override - public void showDetail(int position, boolean isWikipediaButtonDisplayed) { - if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment.isVisible()) { - mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true); + override fun showDetail(position: Int, isWikipediaButtonDisplayed: Boolean) { + if (mediaDetailPagerFragment == null || !mediaDetailPagerFragment!!.isVisible) { + mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true) if (isUserProfile) { - ((ProfileActivity) getActivity()).setScroll(false); + (activity as ProfileActivity).setScroll(false) } - showMediaDetailPagerFragment(); + showMediaDetailPagerFragment() } - mediaDetailPagerFragment.showImage(position, isWikipediaButtonDisplayed); + mediaDetailPagerFragment!!.showImage(position, isWikipediaButtonDisplayed) } - @Override - public Media getMediaAtPosition(int i) { - return contributionsListFragment.getMediaAtPosition(i); + override fun getMediaAtPosition(i: Int): Media { + return contributionsListFragment!!.getMediaAtPosition(i) } - @Override - public int getTotalMediaCount() { - return contributionsListFragment.getTotalMediaCount(); + override fun getTotalMediaCount(): Int { + return contributionsListFragment!!.totalMediaCount } - @Override - public Integer getContributionStateAt(int position) { - return contributionsListFragment.getContributionStateAt(position); + override fun getContributionStateAt(position: Int): Int { + return contributionsListFragment!!.getContributionStateAt(position) } - public boolean backButtonClicked() { - if (mediaDetailPagerFragment != null && mediaDetailPagerFragment.isVisible()) { - if (store.getBoolean("displayNearbyCardView", true) && !isUserProfile) { - if (binding.cardViewNearby.cardViewVisibilityState - == NearbyNotificationCardView.CardViewVisibilityState.READY) { - binding.cardViewNearby.setVisibility(View.VISIBLE); + fun backButtonClicked(): Boolean { + if (mediaDetailPagerFragment != null && mediaDetailPagerFragment!!.isVisible) { + if (store!!.getBoolean("displayNearbyCardView", true) && !isUserProfile) { + if (binding!!.cardViewNearby.cardViewVisibilityState + == NearbyNotificationCardView.CardViewVisibilityState.READY + ) { + binding!!.cardViewNearby.visibility = View.VISIBLE } } else { - binding.cardViewNearby.setVisibility(View.GONE); + binding!!.cardViewNearby.visibility = View.GONE } - removeFragment(mediaDetailPagerFragment); - showFragment(contributionsListFragment, CONTRIBUTION_LIST_FRAGMENT_TAG, - mediaDetailPagerFragment); + removeFragment(mediaDetailPagerFragment!!) + showFragment( + contributionsListFragment!!, CONTRIBUTION_LIST_FRAGMENT_TAG, + mediaDetailPagerFragment + ) if (isUserProfile) { // Fragment is associated with ProfileActivity // Enable ParentViewPager Scroll - ((ProfileActivity) getActivity()).setScroll(true); + (activity as ProfileActivity).setScroll(true) } else { - fetchCampaigns(); + fetchCampaigns() } - if (getActivity() instanceof MainActivity) { + if (activity is MainActivity) { // Fragment is associated with MainActivity - ((BaseActivity) getActivity()).getSupportActionBar() - .setDisplayHomeAsUpEnabled(false); - ((MainActivity) getActivity()).showTabs(); + (activity as BaseActivity).supportActionBar + ?.setDisplayHomeAsUpEnabled(false) + (activity as MainActivity).showTabs() } - return true; + return true } - return false; - } - - // Getter for mediaDetailPagerFragment - public MediaDetailPagerFragment getMediaDetailPagerFragment() { - return mediaDetailPagerFragment; + return false } /** * this function updates the number of contributions */ - void upDateUploadCount() { - WorkManager.getInstance(getContext()) - .getWorkInfosForUniqueWorkLiveData(UploadWorker.class.getSimpleName()).observe( - getViewLifecycleOwner(), workInfos -> { - if (workInfos.size() > 0) { - setUploadCount(); - } - }); + fun upDateUploadCount() { + WorkManager.getInstance(context) + .getWorkInfosForUniqueWorkLiveData(UploadWorker::class.java.simpleName).observe( + viewLifecycleOwner + ) { workInfos: List -> + if (workInfos.size > 0) { + setUploadCount() + } + } } @@ -860,18 +905,18 @@ public class ContributionsFragment * * @param contribution */ - public void restartUpload(Contribution contribution) { - contribution.setDateUploadStarted(Calendar.getInstance().getTime()); - if (contribution.getState() == Contribution.STATE_FAILED) { - if (contribution.getErrorInfo() == null) { - contribution.setChunkInfo(null); - contribution.setTransferred(0); + fun restartUpload(contribution: Contribution) { + contribution.dateUploadStarted = Calendar.getInstance().time + if (contribution.state == Contribution.STATE_FAILED) { + if (contribution.errorInfo == null) { + contribution.chunkInfo = null + contribution.transferred = 0 } - contributionsPresenter.checkDuplicateImageAndRestartContribution(contribution); + contributionsPresenter!!.checkDuplicateImageAndRestartContribution(contribution) } else { - contribution.setState(Contribution.STATE_QUEUED); - contributionsPresenter.saveContribution(contribution); - Timber.d("Restarting for %s", contribution.toString()); + contribution.state = Contribution.STATE_QUEUED + contributionsPresenter!!.saveContribution(contribution) + Timber.d("Restarting for %s", contribution.toString()) } } @@ -880,33 +925,36 @@ public class ContributionsFragment * * @param contribution contribution to be retried */ - public void retryUpload(Contribution contribution) { - if (NetworkUtils.isInternetConnectionEstablished(getContext())) { - if (contribution.getState() == STATE_PAUSED) { - restartUpload(contribution); - } else if (contribution.getState() == STATE_FAILED) { - int retries = contribution.getRetries(); + fun retryUpload(contribution: Contribution) { + if (isInternetConnectionEstablished(context)) { + if (contribution.state == Contribution.STATE_PAUSED) { + restartUpload(contribution) + } else if (contribution.state == Contribution.STATE_FAILED) { + val retries = contribution.retries // TODO: Improve UX. Additional details: https://github.com/commons-app/apps-android-commons/pull/5257#discussion_r1304662562 /* Limit the number of retries for a failed upload to handle cases like invalid filename as such uploads will never be successful */ if (retries < MAX_RETRIES) { - contribution.setRetries(retries + 1); - Timber.d("Retried uploading %s %d times", contribution.getMedia().getFilename(), - retries + 1); - restartUpload(contribution); + contribution.retries = retries + 1 + Timber.d( + "Retried uploading %s %d times", contribution.media.filename, + retries + 1 + ) + restartUpload(contribution) } else { // TODO: Show the exact reason for failure - Toast.makeText(getContext(), - R.string.retry_limit_reached, Toast.LENGTH_SHORT).show(); + Toast.makeText( + context, + R.string.retry_limit_reached, Toast.LENGTH_SHORT + ).show() } } else { - Timber.d("Skipping re-upload for non-failed %s", contribution.toString()); + Timber.d("Skipping re-upload for non-failed %s", contribution.toString()) } } else { - ViewUtil.showLongToast(getContext(), R.string.this_function_needs_network_connection); + showLongToast(context, R.string.this_function_needs_network_connection) } - } /** @@ -914,27 +962,37 @@ public class ContributionsFragment * * @param index item position that has been nominated */ - @Override - public void refreshNominatedMedia(int index) { - if (mediaDetailPagerFragment != null && !contributionsListFragment.isVisible()) { - removeFragment(mediaDetailPagerFragment); - mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true); - mediaDetailPagerFragment.showImage(index); - showMediaDetailPagerFragment(); + override fun refreshNominatedMedia(index: Int) { + if (mediaDetailPagerFragment != null && !contributionsListFragment!!.isVisible) { + removeFragment(mediaDetailPagerFragment!!) + mediaDetailPagerFragment = MediaDetailPagerFragment.newInstance(false, true) + mediaDetailPagerFragment?.showImage(index) + showMediaDetailPagerFragment() } } /** * When the device rotates, rotate the Nearby banner's compass arrow in tandem. */ - @Override - public void onSensorChanged(SensorEvent event) { - float rotateDegree = Math.round(event.values[0]); - binding.cardViewNearby.rotateCompass(rotateDegree, direction); + override fun onSensorChanged(event: SensorEvent) { + val rotateDegree = Math.round(event.values[0]).toFloat() + binding!!.cardViewNearby.rotateCompass(rotateDegree, direction) } - @Override - public void onAccuracyChanged(Sensor sensor, int accuracy) { + override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) { // Nothing to do. } + + companion object { + private const val CONTRIBUTION_LIST_FRAGMENT_TAG = "ContributionListFragmentTag" + const val MEDIA_DETAIL_PAGER_FRAGMENT_TAG: String = "MediaDetailFragmentTag" + private const val MAX_RETRIES = 10 + + @JvmStatic + fun newInstance(): ContributionsFragment { + val fragment = ContributionsFragment() + fragment.retainInstance = true + return fragment + } + } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt index 3f9e8d541..b41de1c6e 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListAdapter.kt @@ -1,77 +1,72 @@ - package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import android.view.LayoutInflater; -import android.view.ViewGroup; -import androidx.annotation.NonNull; -import androidx.paging.PagedListAdapter; -import androidx.recyclerview.widget.DiffUtil; -import fr.free.nrw.commons.R; -import fr.free.nrw.commons.media.MediaClient; +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.paging.PagedListAdapter +import androidx.recyclerview.widget.DiffUtil +import fr.free.nrw.commons.R +import fr.free.nrw.commons.media.MediaClient - /** +/** * Represents The View Adapter for the List of Contributions */ -public class ContributionsListAdapter extends - PagedListAdapter { - - private final Callback callback; - private final MediaClient mediaClient; - - ContributionsListAdapter(final Callback callback, - final MediaClient mediaClient) { - super(DIFF_CALLBACK); - this.callback = callback; - this.mediaClient = mediaClient; - } - - /** - * Uses DiffUtil to calculate the changes in the list - * It has methods that check ID and the content of the items to determine if its a new item - */ - private static final DiffUtil.ItemCallback DIFF_CALLBACK = - new DiffUtil.ItemCallback() { - @Override - public boolean areItemsTheSame(final Contribution oldContribution, final Contribution newContribution) { - return oldContribution.getPageId().equals(newContribution.getPageId()); - } - - @Override - public boolean areContentsTheSame(final Contribution oldContribution, final Contribution newContribution) { - return oldContribution.equals(newContribution); - } - }; - +class ContributionsListAdapter internal constructor( + private val callback: Callback, + private val mediaClient: MediaClient +) : PagedListAdapter(DIFF_CALLBACK) { /** * Initializes the view holder with contribution data */ - @Override - public void onBindViewHolder(@NonNull ContributionViewHolder holder, int position) { - holder.init(position, getItem(position)); + override fun onBindViewHolder(holder: ContributionViewHolder, position: Int) { + holder.init(position, getItem(position)) } - Contribution getContributionForPosition(final int position) { - return getItem(position); + fun getContributionForPosition(position: Int): Contribution? { + return getItem(position) } /** * Creates the new View Holder which will be used to display items(contributions) using the * onBindViewHolder(viewHolder,position) */ - @NonNull - @Override - public ContributionViewHolder onCreateViewHolder(@NonNull final ViewGroup parent, - final int viewType) { - final ContributionViewHolder viewHolder = new ContributionViewHolder( - LayoutInflater.from(parent.getContext()) + override fun onCreateViewHolder( + parent: ViewGroup, + viewType: Int + ): ContributionViewHolder { + val viewHolder = ContributionViewHolder( + LayoutInflater.from(parent.context) .inflate(R.layout.layout_contribution, parent, false), - callback, mediaClient); - return viewHolder; + callback, mediaClient + ) + return viewHolder } - public interface Callback { + interface Callback { + fun openMediaDetail(contribution: Int, isWikipediaPageExists: Boolean) - void openMediaDetail(int contribution, boolean isWikipediaPageExists); + fun addImageToWikipedia(contribution: Contribution?) + } - void addImageToWikipedia(Contribution contribution); + companion object { + /** + * Uses DiffUtil to calculate the changes in the list + * It has methods that check ID and the content of the items to determine if its a new item + */ + private val DIFF_CALLBACK: DiffUtil.ItemCallback = + object : DiffUtil.ItemCallback() { + override fun areItemsTheSame( + oldContribution: Contribution, + newContribution: Contribution + ): Boolean { + return oldContribution.pageId == newContribution.pageId + } + + override fun areContentsTheSame( + oldContribution: Contribution, + newContribution: Contribution + ): Boolean { + return oldContribution == newContribution + } + } } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt index 0d0a19436..c6b8dd8a8 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListContract.kt @@ -1,25 +1,21 @@ -package fr.free.nrw.commons.contributions; +package fr.free.nrw.commons.contributions -import androidx.swiperefreshlayout.widget.SwipeRefreshLayout; -import fr.free.nrw.commons.BasePresenter; +import androidx.swiperefreshlayout.widget.SwipeRefreshLayout +import fr.free.nrw.commons.BasePresenter /** * The contract for Contributions list View & Presenter */ -public class ContributionsListContract { +class ContributionsListContract { + interface View { + fun showWelcomeTip(numberOfUploads: Boolean) - public interface View { + fun showProgress(shouldShow: Boolean) - void showWelcomeTip(boolean numberOfUploads); - - void showProgress(boolean shouldShow); - - void showNoContributionsUI(boolean shouldShow); + fun showNoContributionsUI(shouldShow: Boolean) } - public interface UserActionListener extends BasePresenter { - - void refreshList(SwipeRefreshLayout swipeRefreshLayout); - + interface UserActionListener : BasePresenter { + fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java index 798b161eb..da4fb58f5 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java @@ -1,15 +1,26 @@ package fr.free.nrw.commons.contributions; +import javax.inject.Named; import dagger.Binds; import dagger.Module; +import dagger.Provides; +import fr.free.nrw.commons.kvstore.JsonKvStore; /** - * The Dagger Module for contributions related presenters and (some other objects maybe in future) + * The Dagger Module for contributions-related presenters and other dependencies */ @Module public abstract class ContributionsModule { @Binds - public abstract ContributionsContract.UserActionListener bindsContibutionsPresenter( - ContributionsPresenter presenter); + public abstract ContributionsContract.UserActionListener bindsContributionsPresenter( + ContributionsPresenter presenter + ); + + @Provides + static JsonKvStore providesApplicationKvStore( + @Named("default_preferences") JsonKvStore kvStore + ) { + return kvStore; + } } diff --git a/app/src/main/java/fr/free/nrw/commons/di/CommonsDaggerSupportFragment.kt b/app/src/main/java/fr/free/nrw/commons/di/CommonsDaggerSupportFragment.kt index 8204d4415..5468cfa10 100644 --- a/app/src/main/java/fr/free/nrw/commons/di/CommonsDaggerSupportFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/di/CommonsDaggerSupportFragment.kt @@ -15,8 +15,8 @@ abstract class CommonsDaggerSupportFragment : Fragment(), HasSupportFragmentInje @Inject @JvmField var childFragmentInjector: DispatchingAndroidInjector? = null - @JvmField - protected var compositeDisposable: CompositeDisposable = CompositeDisposable() + // Removed @JvmField to allow overriding + protected open var compositeDisposable: CompositeDisposable = CompositeDisposable() override fun onAttach(context: Context) { inject() @@ -63,4 +63,9 @@ abstract class CommonsDaggerSupportFragment : Fragment(), HasSupportFragmentInje return getInstance(activity.applicationContext) } + + // Ensure getContext() returns a non-null Context + override fun getContext(): Context { + return super.getContext() ?: throw IllegalStateException("Context is null") + } } 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.java index e64b96190..1b1659182 100644 --- 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.java @@ -467,7 +467,7 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment nearbyPlacesInfoObservable = presenter.loadAttractionsFromLocation(getLastMapFocus(), currentLatLng, false); } - compositeDisposable.add(nearbyPlacesInfoObservable + getCompositeDisposable().add(nearbyPlacesInfoObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(explorePlacesInfo -> { diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt index d714b094a..40c9785db 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailFragment.kt @@ -493,7 +493,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C val contributionsFragment: ContributionsFragment? = this.getContributionsFragmentParent() if (contributionsFragment?.binding != null) { - contributionsFragment.binding.cardViewNearby.visibility = View.GONE + contributionsFragment.binding!!.cardViewNearby.visibility = View.GONE } // detail provider is null when fragment is shown in review activity 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 95f19f699..2b64f0e37 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 @@ -742,7 +742,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment public void onPause() { super.onPause(); binding.map.onPause(); - compositeDisposable.clear(); + getCompositeDisposable().clear(); presenter.detachView(); registerUnregisterLocationListener(true); try { @@ -857,7 +857,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment 0.75); binding.nearbyFilterList.searchListView.setAdapter(nearbyFilterSearchRecyclerViewAdapter); LayoutUtils.setLayoutHeightAlignedToWidth(1.25, binding.nearbyFilterList.getRoot()); - compositeDisposable.add( + getCompositeDisposable().add( RxSearchView.queryTextChanges(binding.nearbyFilter.searchViewLayout.searchView) .takeUntil(RxView.detaches(binding.nearbyFilter.searchViewLayout.searchView)) .debounce(500, TimeUnit.MILLISECONDS) @@ -1234,7 +1234,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment */ private void emptyCache() { // reload the map once the cache is cleared - compositeDisposable.add( + getCompositeDisposable().add( placesRepository.clearCache() .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) @@ -1269,7 +1269,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment final Observable savePlacesObservable = Observable .fromCallable(() -> nearbyController .getPlacesAsKML(getMapFocus())); - compositeDisposable.add(savePlacesObservable + getCompositeDisposable().add(savePlacesObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(kmlString -> { @@ -1303,7 +1303,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment final Observable savePlacesObservable = Observable .fromCallable(() -> nearbyController .getPlacesAsGPX(getMapFocus())); - compositeDisposable.add(savePlacesObservable + getCompositeDisposable().add(savePlacesObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(gpxString -> { @@ -1405,7 +1405,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment final Observable> getPlaceObservable = Observable .fromCallable(() -> nearbyController .getPlaces(List.of(place))); - compositeDisposable.add(getPlaceObservable + getCompositeDisposable().add(getPlaceObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(placeList -> { @@ -1449,7 +1449,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment searchLatLng, false, true, Utils.isMonumentsEnabled(new Date()), customQuery)); - compositeDisposable.add(nearbyPlacesInfoObservable + getCompositeDisposable().add(nearbyPlacesInfoObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(nearbyPlacesInfo -> { @@ -1486,7 +1486,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment searchLatLng, false, true, Utils.isMonumentsEnabled(new Date()), customQuery)); - compositeDisposable.add(nearbyPlacesInfoObservable + getCompositeDisposable().add(nearbyPlacesInfoObservable .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(nearbyPlacesInfo -> { @@ -1518,7 +1518,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment } public void savePlaceToDatabase(Place place) { - compositeDisposable.add(placesRepository + getCompositeDisposable().add(placesRepository .save(place) .subscribeOn(Schedulers.io()) .subscribe()); @@ -1531,7 +1531,7 @@ public class NearbyParentFragment extends CommonsDaggerSupportFragment @Override public void stopQuery() { stopQuery = true; - compositeDisposable.clear(); + getCompositeDisposable().clear(); } /** diff --git a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt index 80037a028..7a92cf6c5 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/categories/UploadCategoriesFragment.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons.upload.categories +import android.annotation.SuppressLint import android.app.Activity import android.app.ProgressDialog import android.content.Context @@ -89,6 +90,7 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View { } } + @SuppressLint("StringFormatMatches") private fun init() { if (binding == null) { return @@ -372,8 +374,9 @@ class UploadCategoriesFragment : UploadBaseFragment(), CategoriesContract.View { (requireActivity() as AppCompatActivity).supportActionBar?.hide() + if (parentFragment?.parentFragment?.parentFragment is ContributionsFragment) { - ((parentFragment?.parentFragment?.parentFragment) as ContributionsFragment).binding.cardViewNearby.visibility = View.GONE + ((parentFragment?.parentFragment?.parentFragment) as ContributionsFragment).binding?.cardViewNearby?.visibility = View.GONE } } } From c07203b7fad0dd51fd01ea66bf3c9df6755f5dfd Mon Sep 17 00:00:00 2001 From: Sujal-Gupta-SG Date: Mon, 3 Feb 2025 16:46:27 +0530 Subject: [PATCH 14/14] Rename .java to .kt --- .../{ContributionViewHolder.java => ContributionViewHolder.kt} | 0 ...ontributionsListFragment.java => ContributionsListFragment.kt} | 0 ...tributionsListPresenter.java => ContributionsListPresenter.kt} | 0 ...utionsLocalDataSource.java => ContributionsLocalDataSource.kt} | 0 .../{ContributionsModule.java => ContributionsModule.kt} | 0 .../{ContributionsPresenter.java => ContributionsPresenter.kt} | 0 .../{ContributionsRepository.java => ContributionsRepository.kt} | 0 .../commons/contributions/{MainActivity.java => MainActivity.kt} | 0 .../{SetWallpaperWorker.java => SetWallpaperWorker.kt} | 0 .../{UnswipableViewPager.java => UnswipableViewPager.kt} | 0 10 files changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionViewHolder.java => ContributionViewHolder.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsListFragment.java => ContributionsListFragment.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsListPresenter.java => ContributionsListPresenter.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsLocalDataSource.java => ContributionsLocalDataSource.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsModule.java => ContributionsModule.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsPresenter.java => ContributionsPresenter.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{ContributionsRepository.java => ContributionsRepository.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{MainActivity.java => MainActivity.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{SetWallpaperWorker.java => SetWallpaperWorker.kt} (100%) rename app/src/main/java/fr/free/nrw/commons/contributions/{UnswipableViewPager.java => UnswipableViewPager.kt} (100%) diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionViewHolder.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListPresenter.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsLocalDataSource.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsModule.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsPresenter.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.java rename to app/src/main/java/fr/free/nrw/commons/contributions/ContributionsRepository.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java b/app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.java rename to app/src/main/java/fr/free/nrw/commons/contributions/MainActivity.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.java b/app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.java rename to app/src/main/java/fr/free/nrw/commons/contributions/SetWallpaperWorker.kt diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.java b/app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt similarity index 100% rename from app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.java rename to app/src/main/java/fr/free/nrw/commons/contributions/UnswipableViewPager.kt