From 3bd0ec446610ab9df1ee17360a3840522382dd60 Mon Sep 17 00:00:00 2001 From: Paul Hawke Date: Fri, 4 Jul 2025 06:18:52 -0500 Subject: [PATCH] Convert top level "Utils" class to kotlin (#6364) * Unused class removed * Convert BasePresenter to kotlin * Removed redundent class * Move the Utils class into the utils package * Inline the creation of a page title object * Move license utilities into their own file * Inline app rating since its only ever used in 1 place * Moved GeoCoordinates utilities into their own class * Moved Monuments related utils into their own class * Moved screen capture into its own util class * Moved handleWebUrl to its own utility class * Moved fixExtension to its own class * Moved clipboard copy into its own utility class * Renames class to match remaining utility method * Convert UnderlineUtils to kotlin * Converted the copy-to-clipboard utility to kotlin * Converted license name and url lookup to kotlin * Converted fixExtension to kotlin * Convert handleGeoCoordinates to kotlin * Monument utils converted to kotlin * Convert then inline screeen capture in kotlin * Convert handleWebUrl to kotlin --- .../java/fr/free/nrw/commons/AboutActivity.kt | 66 ++--- .../fr/free/nrw/commons/BasePresenter.java | 18 -- .../java/fr/free/nrw/commons/BasePresenter.kt | 10 + .../java/fr/free/nrw/commons/License.java | 79 ------ .../main/java/fr/free/nrw/commons/Media.kt | 5 +- .../java/fr/free/nrw/commons/MvpView.java | 8 - .../main/java/fr/free/nrw/commons/Utils.java | 264 ------------------ .../free/nrw/commons/WelcomePagerAdapter.java | 7 +- .../fr/free/nrw/commons/auth/LoginActivity.kt | 6 +- .../nrw/commons/campaigns/CampaignView.kt | 4 +- .../commons/campaigns/CampaignsPresenter.kt | 2 +- .../nrw/commons/campaigns/ICampaignsView.kt | 3 +- .../category/CategoryDetailsActivity.kt | 10 +- .../contributions/ContributionsContract.kt | 1 - .../contributions/ContributionsFragment.kt | 14 +- .../ContributionsListContract.kt | 2 +- .../ContributionsListFragment.kt | 8 +- .../WikidataItemDetailsActivity.java | 9 +- .../explore/map/ExploreMapFragment.java | 7 +- .../locationpicker/LocationPickerActivity.kt | 6 +- .../nrw/commons/media/MediaDetailFragment.kt | 19 +- .../media/MediaDetailPagerFragment.java | 6 +- .../fragments/CommonPlaceClickActions.kt | 7 +- .../nearby/fragments/NearbyParentFragment.kt | 31 +- .../notification/NotificationActivity.kt | 4 +- .../nrw/commons/profile/ProfileActivity.kt | 28 +- .../achievements/AchievementsFragment.kt | 4 +- .../nrw/commons/settings/SettingsFragment.kt | 10 +- .../nrw/commons/upload/PageContentsCreator.kt | 4 +- .../fr/free/nrw/commons/upload/UploadItem.kt | 4 +- .../upload/license/MediaLicenseFragment.kt | 16 +- .../upload/license/MediaLicensePresenter.kt | 10 +- .../UploadMediaDetailsContract.kt | 2 +- .../free/nrw/commons/utils/ClipboardUtils.kt | 20 ++ .../fr/free/nrw/commons/utils/FixExtension.kt | 38 +++ .../free/nrw/commons/utils/GeoCoordinates.kt | 27 ++ .../fr/free/nrw/commons/utils/Licenses.kt | 31 ++ .../fr/free/nrw/commons/utils/Monuments.kt | 39 +++ .../free/nrw/commons/utils/UnderlineUtils.kt | 19 ++ .../fr/free/nrw/commons/utils/UrlUtils.kt | 33 +++ .../kotlin/fr/free/nrw/commons/UtilsTest.kt | 7 +- .../ContributionsFragmentUnitTests.kt | 7 - .../upload/MediaLicensePresenterTest.kt | 10 +- .../commons/utils/UtilsFixExtensionTest.kt | 1 - 44 files changed, 387 insertions(+), 519 deletions(-) delete mode 100644 app/src/main/java/fr/free/nrw/commons/BasePresenter.java create mode 100644 app/src/main/java/fr/free/nrw/commons/BasePresenter.kt delete mode 100644 app/src/main/java/fr/free/nrw/commons/License.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/MvpView.java delete mode 100644 app/src/main/java/fr/free/nrw/commons/Utils.java create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt create mode 100644 app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt diff --git a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt index 143f5e569..ebbb4097a 100644 --- a/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/AboutActivity.kt @@ -1,7 +1,9 @@ package fr.free.nrw.commons import android.annotation.SuppressLint +import android.content.ActivityNotFoundException import android.content.Intent +import android.content.Intent.ACTION_VIEW import android.net.Uri import android.os.Bundle import android.view.Menu @@ -16,6 +18,9 @@ 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 +import androidx.core.net.toUri +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.setUnderlinedText /** * Represents about screen of this app @@ -59,30 +64,12 @@ class AboutActivity : BaseActivity() { 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 - ) + binding!!.aboutFaq.setUnderlinedText(R.string.about_faq) + binding!!.aboutRateUs.setUnderlinedText(R.string.about_rate_us) + binding!!.aboutUserGuide.setUnderlinedText(R.string.user_guide) + binding!!.aboutPrivacyPolicy.setUnderlinedText(R.string.about_privacy_policy) + binding!!.aboutTranslate.setUnderlinedText(R.string.about_translate) + binding!!.aboutCredits.setUnderlinedText(R.string.about_credits) /* To set listeners, we can create a separate method and use lambda syntax. @@ -106,47 +93,56 @@ class AboutActivity : BaseActivity() { fun launchFacebook(view: View?) { val intent: Intent try { - intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.FACEBOOK_APP_URL)) + intent = Intent(ACTION_VIEW, Urls.FACEBOOK_APP_URL.toUri()) intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME) startActivity(intent) } catch (e: Exception) { - Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL)) + handleWebUrl(this, Urls.FACEBOOK_WEB_URL.toUri()) } } fun launchGithub(view: View?) { val intent: Intent try { - intent = Intent(Intent.ACTION_VIEW, Uri.parse(Urls.GITHUB_REPO_URL)) + intent = Intent(ACTION_VIEW, Urls.GITHUB_REPO_URL.toUri()) intent.setPackage(Urls.GITHUB_PACKAGE_NAME) startActivity(intent) } catch (e: Exception) { - Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL)) + handleWebUrl(this, Urls.GITHUB_REPO_URL.toUri()) } } fun launchWebsite(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL)) + handleWebUrl(this, Urls.WEBSITE_URL.toUri()) } fun launchRatings(view: View?) { - Utils.rateApp(this) + try { + startActivity( + Intent( + ACTION_VIEW, + (Urls.PLAY_STORE_PREFIX + packageName).toUri() + ) + ) + } catch (_: ActivityNotFoundException) { + handleWebUrl(this, (Urls.PLAY_STORE_URL_PREFIX + packageName).toUri()) + } } fun launchCredits(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL)) + handleWebUrl(this, Urls.CREDITS_URL.toUri()) } fun launchUserGuide(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL)) + handleWebUrl(this, Urls.USER_GUIDE_URL.toUri()) } fun launchPrivacyPolicy(view: View?) { - Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) + handleWebUrl(this, BuildConfig.PRIVACY_POLICY_URL.toUri()) } fun launchFrequentlyAskedQuesions(view: View?) { - Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL)) + handleWebUrl(this, Urls.FAQ_URL.toUri()) } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -193,7 +189,7 @@ class AboutActivity : BaseActivity() { val positiveButtonRunnable = Runnable { val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition] - Utils.handleWebUrl(this@AboutActivity, Uri.parse(Urls.TRANSLATE_WIKI_URL + langCode)) + handleWebUrl(this@AboutActivity, (Urls.TRANSLATE_WIKI_URL + langCode).toUri()) } showAlertDialog( this, diff --git a/app/src/main/java/fr/free/nrw/commons/BasePresenter.java b/app/src/main/java/fr/free/nrw/commons/BasePresenter.java deleted file mode 100644 index 2653b3711..000000000 --- a/app/src/main/java/fr/free/nrw/commons/BasePresenter.java +++ /dev/null @@ -1,18 +0,0 @@ -package fr.free.nrw.commons; - -import androidx.annotation.NonNull; - -/** - * Base presenter, enforcing contracts to atach and detach view - */ -public interface BasePresenter { - /** - * Until a view is attached, it is open to listen events from the presenter - */ - void onAttachView(@NonNull T view); - - /** - * Detaching a view makes sure that the view no more receives events from the presenter - */ - void onDetachView(); -} diff --git a/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt b/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt new file mode 100644 index 000000000..085307c3e --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/BasePresenter.kt @@ -0,0 +1,10 @@ +package fr.free.nrw.commons + +/** + * Base presenter, enforcing contracts to attach and detach view + */ +interface BasePresenter { + fun onAttachView(view: T) + + fun onDetachView() +} diff --git a/app/src/main/java/fr/free/nrw/commons/License.java b/app/src/main/java/fr/free/nrw/commons/License.java deleted file mode 100644 index 1fea236ee..000000000 --- a/app/src/main/java/fr/free/nrw/commons/License.java +++ /dev/null @@ -1,79 +0,0 @@ -package fr.free.nrw.commons; - -import androidx.annotation.Nullable; - -/** - * represents Licence object - */ -public class License { - private String key; - private String template; - private String url; - private String name; - - /** - * Constructs a new instance of License. - * - * @param key license key - * @param template license template - * @param url license URL - * @param name licence name - * - * @throws RuntimeException if License.key or Licence.template is null - */ - public License(String key, String template, String url, String name) { - if (key == null) { - throw new RuntimeException("License.key must not be null"); - } - if (template == null) { - throw new RuntimeException("License.template must not be null"); - } - this.key = key; - this.template = template; - this.url = url; - this.name = name; - } - - /** - * Gets the license key. - * @return license key as a String. - */ - public String getKey() { - return key; - } - - /** - * Gets the license template. - * @return license template as a String. - */ - public String getTemplate() { - return template; - } - - /** - * Gets the license name. If name is null, return license key. - * @return license name as string. if name null, license key as String - */ - public String getName() { - if (name == null) { - // hack - return getKey(); - } else { - return name; - } - } - - /** - * Gets the license URL - * - * @param language license language - * @return URL - */ - public @Nullable String getUrl(String language) { - if (url == null) { - return null; - } else { - return url.replace("$lang", language); - } - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/Media.kt b/app/src/main/java/fr/free/nrw/commons/Media.kt index 7bd8e95fd..dbe722e91 100644 --- a/app/src/main/java/fr/free/nrw/commons/Media.kt +++ b/app/src/main/java/fr/free/nrw/commons/Media.kt @@ -1,7 +1,9 @@ package fr.free.nrw.commons import android.os.Parcelable +import fr.free.nrw.commons.BuildConfig.COMMONS_URL import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.wikidata.model.WikiSite import fr.free.nrw.commons.wikidata.model.page.PageTitle import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.Parcelize @@ -173,7 +175,8 @@ class Media constructor( * Gets file page title * @return New media page title */ - val pageTitle: PageTitle get() = Utils.getPageTitle(filename!!) + val pageTitle: PageTitle + get() = PageTitle(filename!!, WikiSite(COMMONS_URL)) /** * Returns wikicode to use the media file on a MediaWiki site diff --git a/app/src/main/java/fr/free/nrw/commons/MvpView.java b/app/src/main/java/fr/free/nrw/commons/MvpView.java deleted file mode 100644 index 7485b2aaf..000000000 --- a/app/src/main/java/fr/free/nrw/commons/MvpView.java +++ /dev/null @@ -1,8 +0,0 @@ -package fr.free.nrw.commons; - -/** - * Base interface for all the views - */ -public interface MvpView { - void showMessage(String message); -} diff --git a/app/src/main/java/fr/free/nrw/commons/Utils.java b/app/src/main/java/fr/free/nrw/commons/Utils.java deleted file mode 100644 index 8d0f8b530..000000000 --- a/app/src/main/java/fr/free/nrw/commons/Utils.java +++ /dev/null @@ -1,264 +0,0 @@ -package fr.free.nrw.commons; - -import android.content.ClipData; -import android.content.ClipboardManager; -import android.content.Context; -import android.content.Intent; -import android.graphics.Bitmap; -import android.net.Uri; -import android.text.SpannableString; -import android.text.style.UnderlineSpan; -import android.view.View; -import android.widget.TextView; - -import androidx.annotation.NonNull; -import androidx.browser.customtabs.CustomTabColorSchemeParams; -import androidx.browser.customtabs.CustomTabsIntent; -import androidx.core.content.ContextCompat; - -import java.util.Calendar; -import java.util.Date; -import fr.free.nrw.commons.wikidata.model.WikiSite; -import fr.free.nrw.commons.wikidata.model.page.PageTitle; - -import java.util.Locale; -import java.util.regex.Pattern; - -import fr.free.nrw.commons.location.LatLng; -import fr.free.nrw.commons.settings.Prefs; -import fr.free.nrw.commons.utils.ViewUtil; -import timber.log.Timber; - -public class Utils { - - public static PageTitle getPageTitle(@NonNull String title) { - return new PageTitle(title, new WikiSite(BuildConfig.COMMONS_URL)); - } - - /** - * Generates licence name with given ID - * @param license License ID - * @return Name of license - */ - public static int licenseNameFor(String license) { - switch (license) { - case Prefs.Licenses.CC_BY_3: - return R.string.license_name_cc_by; - case Prefs.Licenses.CC_BY_4: - return R.string.license_name_cc_by_four; - case Prefs.Licenses.CC_BY_SA_3: - return R.string.license_name_cc_by_sa; - case Prefs.Licenses.CC_BY_SA_4: - return R.string.license_name_cc_by_sa_four; - case Prefs.Licenses.CC0: - return R.string.license_name_cc0; - } - throw new IllegalStateException("Unrecognized license value: " + license); - } - - /** - * Generates license url with given ID - * @param license License ID - * @return Url of license - */ - - - @NonNull - public static String licenseUrlFor(String license) { - switch (license) { - case Prefs.Licenses.CC_BY_3: - return "https://creativecommons.org/licenses/by/3.0/"; - case Prefs.Licenses.CC_BY_4: - return "https://creativecommons.org/licenses/by/4.0/"; - case Prefs.Licenses.CC_BY_SA_3: - return "https://creativecommons.org/licenses/by-sa/3.0/"; - case Prefs.Licenses.CC_BY_SA_4: - return "https://creativecommons.org/licenses/by-sa/4.0/"; - case Prefs.Licenses.CC0: - return "https://creativecommons.org/publicdomain/zero/1.0/"; - default: - throw new IllegalStateException("Unrecognized license value: " + license); - } - } - - /** - * Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected - * @param title File name - * @param extension Correct extension - * @return File with correct extension - */ - public static String fixExtension(String title, String extension) { - Pattern jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE); - - // People are used to ".jpg" more than ".jpeg" which the system gives us. - if (extension != null && extension.toLowerCase(Locale.ENGLISH).equals("jpeg")) { - extension = "jpg"; - } - title = jpegPattern.matcher(title).replaceFirst(".jpg"); - if (extension != null && !title.toLowerCase(Locale.getDefault()) - .endsWith("." + extension.toLowerCase(Locale.ENGLISH))) { - title += "." + extension; - } - - // If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228) - // If title has an extension in it, if won't be true - if (extension == null && title.lastIndexOf(".")<=0) { - extension = "jpg"; - title += "." + extension; - } - - return title; - } - - /** - * Launches intent to rate app - * @param context - */ - public static void rateApp(Context context) { - final String appPackageName = context.getPackageName(); - try { - context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Urls.PLAY_STORE_PREFIX + appPackageName))); - } - catch (android.content.ActivityNotFoundException anfe) { - handleWebUrl(context, Uri.parse(Urls.PLAY_STORE_URL_PREFIX + appPackageName)); - } - } - - /** - * Opens Custom Tab Activity with in-app browser for the specified URL. - * Launches intent for web URL - * @param context - * @param url - */ - public static void handleWebUrl(Context context, Uri url) { - Timber.d("Launching web url %s", url.toString()); - - final CustomTabColorSchemeParams color = new CustomTabColorSchemeParams.Builder() - .setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor)) - .setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor)) - .build(); - - CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(); - builder.setDefaultColorSchemeParams(color); - builder.setExitAnimations(context, android.R.anim.slide_in_left, android.R.anim.slide_out_right); - CustomTabsIntent customTabsIntent = builder.build(); - // Clear previous browser tasks, so that back/exit buttons work as intended. - customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); - customTabsIntent.launchUrl(context, url); - } - - /** - * Util function to handle geo coordinates. It no longer depends on google maps and any app - * capable of handling the map intent can handle it - * - * @param context The context for launching intent - * @param latLng The latitude and longitude of the location - */ - public static void handleGeoCoordinates(final Context context, final LatLng latLng) { - handleGeoCoordinates(context, latLng, 16); - } - - /** - * Util function to handle geo coordinates with specified zoom level. It no longer depends on - * google maps and any app capable of handling the map intent can handle it - * - * @param context The context for launching intent - * @param latLng The latitude and longitude of the location - * @param zoomLevel The zoom level - */ - public static void handleGeoCoordinates(final Context context, final LatLng latLng, - final double zoomLevel) { - final Intent mapIntent = new Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel)); - if (mapIntent.resolveActivity(context.getPackageManager()) != null) { - context.startActivity(mapIntent); - } else { - ViewUtil.showShortToast(context, context.getString(R.string.map_application_missing)); - } - } - - /** - * To take screenshot of the screen and return it in Bitmap format - * - * @param view - * @return - */ - public static Bitmap getScreenShot(View view) { - View screenView = view.getRootView(); - screenView.setDrawingCacheEnabled(true); - Bitmap drawingCache = screenView.getDrawingCache(); - if (drawingCache != null) { - Bitmap bitmap = Bitmap.createBitmap(drawingCache); - screenView.setDrawingCacheEnabled(false); - return bitmap; - } - return null; - } - - /* - *Copies the content to the clipboard - * - */ - public static void copy(String label,String text, Context context){ - ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = ClipData.newPlainText(label, text); - clipboard.setPrimaryClip(clip); - } - - /** - * This method sets underlined string text to a TextView - * - * @param textView TextView associated with string resource - * @param stringResourceName string resource name - * @param context - */ - public static void setUnderlinedText(TextView textView, int stringResourceName, Context context) { - SpannableString content = new SpannableString(context.getString(stringResourceName)); - content.setSpan(new UnderlineSpan(), 0, content.length(), 0); - textView.setText(content); - } - - /** - * For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt - * @param date - * @return - */ - public static boolean isMonumentsEnabled(final Date date) { - if (date.getMonth() == 8) { - return true; - } - return false; - } - - /** - * Util function to get the start date of wlm monument - * For this release we are hardcoding it to be 1st September - * @return - */ - public static String getWLMStartDate() { - return "1 Sep"; - } - - /*** - * Util function to get the end date of wlm monument - * For this release we are hardcoding it to be 31st October - * @return - */ - public static String getWLMEndDate() { - return "30 Sep"; - } - - /*** - * Function to get the current WLM year - * It increments at the start of September in line with the other WLM functions - * (No consideration of locales for now) - * @param calendar - * @return - */ - public static int getWikiLovesMonumentsYear(Calendar calendar) { - int year = calendar.get(Calendar.YEAR); - if (calendar.get(Calendar.MONTH) < Calendar.SEPTEMBER) { - year -= 1; - } - return year; - } -} diff --git a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java b/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java index 8fd3fc704..a9b7381df 100644 --- a/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java +++ b/app/src/main/java/fr/free/nrw/commons/WelcomePagerAdapter.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; + import android.net.Uri; import android.view.LayoutInflater; import android.view.View; @@ -7,6 +9,7 @@ import android.view.ViewGroup; import android.widget.TextView; import androidx.viewpager.widget.PagerAdapter; +import fr.free.nrw.commons.utils.UnderlineUtils; public class WelcomePagerAdapter extends PagerAdapter { private static final int[] PAGE_LAYOUTS = new int[]{ @@ -46,8 +49,8 @@ public class WelcomePagerAdapter extends PagerAdapter { if (position == PAGE_LAYOUTS.length - 1) { // Add link to more information TextView moreInfo = layout.findViewById(R.id.welcomeInfo); - Utils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text, container.getContext()); - moreInfo.setOnClickListener(view -> Utils.handleWebUrl( + UnderlineUtils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text); + moreInfo.setOnClickListener(view -> handleWebUrl( container.getContext(), Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents") )); diff --git a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt index 840bc7ca3..a606d639f 100644 --- a/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/auth/LoginActivity.kt @@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.login.LoginCallback import fr.free.nrw.commons.auth.login.LoginClient import fr.free.nrw.commons.auth.login.LoginResult @@ -38,6 +37,7 @@ import fr.free.nrw.commons.utils.ActivityUtils.startActivityWithFlags import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard +import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.disposables.CompositeDisposable import timber.log.Timber import java.util.Locale @@ -254,10 +254,10 @@ class LoginActivity : AccountAuthenticatorActivity() { } private fun forgotPassword() = - Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)) + handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)) private fun onPrivacyPolicyClicked() = - Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) + handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) private fun signUp() = startActivity(Intent(this, SignupActivity::class.java)) diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt index 7a4720177..7a30ff5c4 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignView.kt @@ -7,7 +7,6 @@ import android.view.LayoutInflater import android.view.View import androidx.core.content.ContextCompat import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.campaigns.models.Campaign import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.databinding.LayoutCampaginBinding @@ -16,6 +15,7 @@ import fr.free.nrw.commons.utils.CommonsDateUtil.getIso8601DateFormatShort import fr.free.nrw.commons.utils.DateUtil.getExtraShortDateString import fr.free.nrw.commons.utils.SwipableCardView import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.handleWebUrl import timber.log.Timber import java.text.ParseException @@ -74,7 +74,7 @@ class CampaignView : SwipableCardView { if (it.isWLMCampaign) { ((context) as MainActivity).showNearby() } else { - Utils.handleWebUrl(context, Uri.parse(it.link)) + handleWebUrl(context, Uri.parse(it.link)) } } } diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt index 4743e0e54..53013c1ae 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/CampaignsPresenter.kt @@ -26,7 +26,7 @@ class CampaignsPresenter @Inject constructor( private val okHttpJsonApiClient: OkHttpJsonApiClient?, @param:Named(IO_THREAD) private val ioScheduler: Scheduler, @param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler -) : BasePresenter { +) : BasePresenter { private var view: ICampaignsView? = null private var disposable: Disposable? = null private var campaign: Campaign? = null diff --git a/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt b/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt index 62a19aaac..1cbf7da1f 100644 --- a/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt +++ b/app/src/main/java/fr/free/nrw/commons/campaigns/ICampaignsView.kt @@ -1,11 +1,10 @@ package fr.free.nrw.commons.campaigns -import fr.free.nrw.commons.MvpView import fr.free.nrw.commons.campaigns.models.Campaign /** * Interface which defines the view contracts of the campaign view */ -interface ICampaignsView : MvpView { +interface ICampaignsView { fun showCampaigns(campaign: Campaign?) } diff --git a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt index a42d26fd6..e15d9baf3 100644 --- a/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/category/CategoryDetailsActivity.kt @@ -13,9 +13,9 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle +import fr.free.nrw.commons.BuildConfig.COMMONS_URL import fr.free.nrw.commons.Media import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment @@ -23,6 +23,9 @@ import fr.free.nrw.commons.explore.categories.parent.ParentCategoriesFragment import fr.free.nrw.commons.explore.categories.sub.SubCategoriesFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.theme.BaseActivity +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.wikidata.model.WikiSite +import fr.free.nrw.commons.wikidata.model.page.PageTitle import kotlinx.coroutines.launch import javax.inject.Inject @@ -199,8 +202,9 @@ class CategoryDetailsActivity : BaseActivity(), override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_browser_current_category -> { - val title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName) - Utils.handleWebUrl(this, Uri.parse(title.canonicalUri)) + val title = PageTitle(CATEGORY_PREFIX + categoryName, WikiSite(COMMONS_URL)) + + handleWebUrl(this, Uri.parse(title.canonicalUri)) true } 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 7027950e3..0e039729f 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 @@ -9,7 +9,6 @@ import fr.free.nrw.commons.BasePresenter interface ContributionsContract { interface View { - fun showMessage(localizedMessage: String) fun getContext(): Context? } 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 3992d35dd..541cc6e56 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 @@ -30,7 +30,6 @@ 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 @@ -64,6 +63,9 @@ 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 fr.free.nrw.commons.utils.isMonumentsEnabled +import fr.free.nrw.commons.utils.wLMEndDate +import fr.free.nrw.commons.utils.wLMStartDate import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.CompositeDisposable @@ -242,8 +244,8 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On private fun initWLMCampaign() { wlmCampaign = Campaign( getString(R.string.wlm_campaign_title), - getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(), - Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true + getString(R.string.wlm_campaign_description), wLMStartDate, + wLMEndDate, NearbyParentFragment.WLM_URL, true ) } @@ -729,7 +731,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On * of campaigns on the campaigns card */ private fun fetchCampaigns() { - if (Utils.isMonumentsEnabled(Date())) { + if (isMonumentsEnabled) { if (binding != null) { binding!!.campaignsView.setCampaign(wlmCampaign) binding!!.campaignsView.visibility = View.VISIBLE @@ -743,10 +745,6 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On } } - override fun showMessage(message: String) { - Toast.makeText(context, message, Toast.LENGTH_SHORT).show() - } - override fun showCampaigns(campaign: Campaign?) { if (campaign != null && !isUserProfile) { if (binding != null) { 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 c6b8dd8a8..0c8c822ad 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 @@ -15,7 +15,7 @@ class ContributionsListContract { fun showNoContributionsUI(shouldShow: Boolean) } - interface UserActionListener : BasePresenter { + interface UserActionListener : BasePresenter { fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?) } } diff --git a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt index 9ecb35b24..b86cd6dc9 100644 --- a/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/contributions/ContributionsListFragment.kt @@ -29,7 +29,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator import fr.free.nrw.commons.Media import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance import fr.free.nrw.commons.databinding.FragmentContributionsListBinding @@ -41,6 +40,8 @@ import fr.free.nrw.commons.profile.ProfileActivity import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.showShortToast +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.model.WikiSite import org.apache.commons.lang3.StringUtils import javax.inject.Inject @@ -527,14 +528,13 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL */ override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) { if (copyWikicode) { - val wikicode = contribution!!.media.wikiCode - Utils.copy("wikicode", wikicode, context) + requireContext().copyToClipboard("wikicode", contribution!!.media.wikiCode) } val url = languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace ?.getWikipediaPageTitle()) - Utils.handleWebUrl(context, Uri.parse(url)) + handleWebUrl(requireContext(), Uri.parse(url)) } fun getContributionStateAt(position: Int): Int { diff --git a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java index cf7269123..89593d07e 100644 --- a/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java +++ b/app/src/main/java/fr/free/nrw/commons/explore/depictions/WikidataItemDetailsActivity.java @@ -1,5 +1,7 @@ package fr.free.nrw.commons.explore.depictions; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; + import android.content.Context; import android.content.Intent; import android.net.Uri; @@ -8,16 +10,11 @@ import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.FrameLayout; -import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; -import androidx.viewpager.widget.ViewPager; import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.tabs.TabLayout; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao; import fr.free.nrw.commons.category.CategoryImagesCallback; @@ -249,7 +246,7 @@ public class WikidataItemDetailsActivity extends BaseActivity implements MediaDe case R.id.browser_actions_menu_items: String entityId=getIntent().getStringExtra("entityId"); Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId); - Utils.handleWebUrl(this, uri); + handleWebUrl(this, uri); return true; case R.id.menu_bookmark_current_item: 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 2b3aa9f3c..364f4d53a 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 @@ -2,7 +2,9 @@ package fr.free.nrw.commons.explore.map; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SIGNIFICANTLY_CHANGED; import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_CHANGED; +import static fr.free.nrw.commons.utils.GeoCoordinatesKt.handleGeoCoordinates; import static fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; import android.Manifest.permission; import android.annotation.SuppressLint; @@ -36,7 +38,6 @@ import fr.free.nrw.commons.BaseMarker; import fr.free.nrw.commons.MapController; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; import fr.free.nrw.commons.bookmarks.locations.BookmarkLocationsDao; import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.databinding.FragmentExploreMapBinding; @@ -639,13 +640,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment */ private void passInfoToSheet(final Place place) { binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener( - view -> Utils.handleGeoCoordinates(getActivity(), + view -> handleGeoCoordinates(requireActivity(), place.getLocation(), binding.mapView.getZoomLevelDouble())); binding.bottomSheetDetailsBinding.commonsButton.setVisibility( place.hasCommonsLink() ? View.VISIBLE : View.GONE); binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( - view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); + view -> handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); int index = 0; for (Media media : mediaList) { diff --git a/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt b/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt index 2a7b7713b..a8b6ddf26 100644 --- a/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/locationpicker/LocationPickerActivity.kt @@ -30,7 +30,6 @@ import fr.free.nrw.commons.CameraPosition import fr.free.nrw.commons.CommonsApplication 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.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.coordinates.CoordinateEditHelper @@ -45,6 +44,7 @@ import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Compani import fr.free.nrw.commons.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL +import fr.free.nrw.commons.utils.handleGeoCoordinates import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.osmdroid.tileprovider.tilesource.TileSourceFactory @@ -432,8 +432,8 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback { position?.let { mapView?.zoomLevelDouble?.let { zoomLevel -> - Utils.handleGeoCoordinates(this, it, zoomLevel) - } ?: Utils.handleGeoCoordinates(this, it) + handleGeoCoordinates(this, it, zoomLevel) + } ?: handleGeoCoordinates(this, it) } } 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 f371b733f..5980e1fb5 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 @@ -77,7 +77,7 @@ import fr.free.nrw.commons.CommonsApplication.Companion.instance import fr.free.nrw.commons.Media import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.utils.UnderlineUtils import fr.free.nrw.commons.actions.ThanksClient import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException @@ -119,6 +119,10 @@ import fr.free.nrw.commons.utils.PermissionUtils.hasPermission import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.ViewUtilWrapper +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.setUnderlinedText import fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision import io.reactivex.Observable import io.reactivex.Single @@ -316,8 +320,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C _binding = FragmentMediaDetailBinding.inflate(inflater, container, false) val view: View = binding.root - - Utils.setUnderlinedText(binding.seeMore, R.string.nominated_see_more, requireContext()) + binding.seeMore.setUnderlinedText(R.string.nominated_see_more) if (isCategoryImage) { binding.authorLinearLayout.visibility = View.VISIBLE @@ -909,7 +912,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onMediaDetailLicenceClicked() { val url: String? = media!!.licenseUrl if (!StringUtils.isBlank(url) && activity != null) { - Utils.handleWebUrl(activity, Uri.parse(url)) + handleWebUrl(requireContext(), Uri.parse(url)) } else { viewUtil.showShortToast(requireActivity(), getString(R.string.null_url)) } @@ -917,17 +920,17 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onMediaDetailCoordinatesClicked() { if (media!!.coordinates != null && activity != null) { - Utils.handleGeoCoordinates(activity, media!!.coordinates) + handleGeoCoordinates(requireContext(), media!!.coordinates!!) } } private fun onCopyWikicodeClicked() { val data: String = "[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]" - Utils.copy("wikiCode", data, context) + requireContext().copyToClipboard("wikiCode", data) Timber.d("Generated wikidata copy code: %s", data) - Toast.makeText(context, getString(R.string.wikicode_copied), Toast.LENGTH_SHORT) + Toast.makeText(requireContext(), getString(R.string.wikicode_copied), Toast.LENGTH_SHORT) .show() } @@ -1765,7 +1768,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C private fun onSeeMoreClicked() { if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) { - Utils.handleWebUrl(activity, Uri.parse(media!!.pageTitle.mobileUri)) + handleWebUrl(requireContext(), Uri.parse(media!!.pageTitle.mobileUri)) } } diff --git a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java index cba582a35..324d5867b 100644 --- a/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java +++ b/app/src/main/java/fr/free/nrw/commons/media/MediaDetailPagerFragment.java @@ -1,6 +1,6 @@ package fr.free.nrw.commons.media; -import static fr.free.nrw.commons.Utils.handleWebUrl; +import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl; import android.os.Handler; import android.os.Looper; @@ -31,7 +31,7 @@ import com.google.android.material.snackbar.Snackbar; import fr.free.nrw.commons.CommonsApplication; import fr.free.nrw.commons.Media; import fr.free.nrw.commons.R; -import fr.free.nrw.commons.Utils; +import fr.free.nrw.commons.utils.ClipboardUtils; import fr.free.nrw.commons.auth.SessionManager; import fr.free.nrw.commons.bookmarks.models.Bookmark; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider; @@ -216,7 +216,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple return true; case R.id.menu_copy_link: String uri = m.getPageTitle().getCanonicalUri(); - Utils.copy("shareLink", uri, requireContext()); + ClipboardUtils.copy("shareLink", uri, requireContext()); Timber.d("Copied share link to clipboard: %s", uri); Toast.makeText(requireContext(), getString(R.string.menu_link_copied), Toast.LENGTH_SHORT).show(); diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt index 202f2c305..5f4d0ab13 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/CommonPlaceClickActions.kt @@ -10,12 +10,13 @@ import androidx.activity.result.ActivityResultLauncher import androidx.appcompat.app.AlertDialog import androidx.appcompat.widget.PopupMenu import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.LoginActivity import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.utils.ActivityUtils +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl import fr.free.nrw.commons.wikidata.WikidataConstants import timber.log.Timber import javax.inject.Inject @@ -104,7 +105,7 @@ class CommonPlaceClickActions fun onDirectionsClicked(): (Place) -> Unit = { - Utils.handleGeoCoordinates(activity, it.getLocation()) + handleGeoCoordinates(activity, it.getLocation()) } private fun storeSharedPrefs(selectedPlace: Place) { @@ -113,7 +114,7 @@ class CommonPlaceClickActions } private fun openWebView(link: Uri): Boolean { - Utils.handleWebUrl(activity, link) + handleWebUrl(activity, link) return true } diff --git a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt index 26875927e..a0dcead07 100644 --- a/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/nearby/fragments/NearbyParentFragment.kt @@ -58,12 +58,10 @@ import fr.free.nrw.commons.CommonsApplication 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.bookmarks.locations.BookmarkLocationsDao import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment -import fr.free.nrw.commons.customselector.ui.selector.ImageLoader import fr.free.nrw.commons.databinding.FragmentNearbyParentBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.filepicker.FilePicker @@ -76,7 +74,6 @@ import fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType import fr.free.nrw.commons.location.LocationUpdateListener import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaDetailPagerFragment -import fr.free.nrw.commons.media.MediaDetailPagerFragment.MediaDetailProvider import fr.free.nrw.commons.navtab.NavTab import fr.free.nrw.commons.nearby.BottomSheetAdapter import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener @@ -105,6 +102,10 @@ import fr.free.nrw.commons.utils.NearbyFABUtils.removeAnchorFromFAB import fr.free.nrw.commons.utils.NetworkUtils.isInternetConnectionEstablished import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.copyToClipboard +import fr.free.nrw.commons.utils.handleGeoCoordinates +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.isMonumentsEnabled import fr.free.nrw.commons.wikidata.WikidataConstants import fr.free.nrw.commons.wikidata.WikidataEditListener import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener @@ -140,7 +141,6 @@ import java.util.UUID import java.util.concurrent.TimeUnit import javax.inject.Inject import javax.inject.Named -import javax.sql.DataSource import kotlin.concurrent.Volatile @@ -467,7 +467,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), } } _isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true - if (Utils.isMonumentsEnabled(Date())) { + if (isMonumentsEnabled) { binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE } else { binding?.rlContainerWlmMonthMessage?.visibility = View.GONE @@ -836,7 +836,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), loadAnimations() setBottomSheetCallbacks() addActionToTitle() - if (!Utils.isMonumentsEnabled(Date())) { + if (!isMonumentsEnabled) { NearbyFilterState.setWlmSelected(false) } } @@ -1017,11 +1017,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), */ private fun addActionToTitle() { binding!!.bottomSheetDetails.title.setOnLongClickListener { view -> - Utils.copy( - "place", binding!!.bottomSheetDetails.title.text.toString(), - context + requireContext().copyToClipboard( + "place", binding!!.bottomSheetDetails.title.text.toString() ) - Toast.makeText(context, fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT) + Toast.makeText(requireContext(), fr.free.nrw.commons.R.string.text_copy, Toast.LENGTH_SHORT) .show() true } @@ -1580,7 +1579,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), searchLatLng, false, true, - Utils.isMonumentsEnabled(Date()), + isMonumentsEnabled, customQuery ) } @@ -1633,7 +1632,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), searchLatLng, false, true, - Utils.isMonumentsEnabled(Date()), + isMonumentsEnabled, customQuery ) } @@ -2854,14 +2853,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), R.drawable.ic_directions_black_24dp -> { selectedPlace?.let { - Utils.handleGeoCoordinates(this.context, it.getLocation()) + handleGeoCoordinates(requireContext(), it.getLocation()) binding?.map?.zoomLevelDouble ?: 0.0 } } R.drawable.ic_wikidata_logo_24dp -> { selectedPlace?.siteLinks?.wikidataLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } @@ -2879,13 +2878,13 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(), R.drawable.ic_wikipedia_logo_24dp -> { selectedPlace?.siteLinks?.wikipediaLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } R.drawable.ic_commons_icon_vector -> { selectedPlace?.siteLinks?.commonsLink?.let { - Utils.handleWebUrl(this.context, it) + handleWebUrl(requireContext(), it) } } diff --git a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt index 1547f89ad..76975964b 100644 --- a/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/notification/NotificationActivity.kt @@ -13,7 +13,6 @@ import androidx.recyclerview.widget.LinearLayoutManager import com.google.android.material.snackbar.Snackbar import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.databinding.ActivityNotificationBinding @@ -22,6 +21,7 @@ import fr.free.nrw.commons.notification.models.NotificationType import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.utils.NetworkUtils import fr.free.nrw.commons.utils.ViewUtil +import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.Observable import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers @@ -197,7 +197,7 @@ class NotificationActivity : BaseActivity() { private fun handleUrl(url: String?) { if (url.isNullOrEmpty()) return - Utils.handleWebUrl(this, Uri.parse(url)) + handleWebUrl(this, Uri.parse(url)) } private fun setItems(notificationList: List?) { diff --git a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt index 164842c9a..105cf1860 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/ProfileActivity.kt @@ -3,16 +3,16 @@ package fr.free.nrw.commons.profile import android.content.Context import android.content.Intent import android.graphics.Bitmap -import android.net.Uri import android.os.Bundle import android.util.Log -import android.view.* +import android.view.Menu +import android.view.MenuItem +import android.view.View import android.widget.ImageView import android.widget.TextView import androidx.core.content.FileProvider import androidx.fragment.app.Fragment import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.contributions.ContributionsFragment @@ -23,7 +23,7 @@ import fr.free.nrw.commons.theme.BaseActivity import fr.free.nrw.commons.utils.DialogUtil import java.io.File import java.io.FileOutputStream -import java.util.* +import java.util.Locale import javax.inject.Inject /** @@ -133,7 +133,7 @@ class ProfileActivity : BaseActivity() { return when (item.itemId) { R.id.share_app_icon -> { val rootView = window.decorView.findViewById(android.R.id.content) - val screenShot = Utils.getScreenShot(rootView) + val screenShot = getScreenShot(rootView) if (screenShot == null) { Log.e("ERROR", "ScreenShot is null") return false @@ -212,6 +212,24 @@ class ProfileActivity : BaseActivity() { binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE } + /** + * To take screenshot of the screen and return it in Bitmap format + * + * @param view + * @return + */ + fun getScreenShot(view: View): Bitmap? { + val screenView = view.rootView + screenView.isDrawingCacheEnabled = true + val drawingCache = screenView.drawingCache + if (drawingCache != null) { + val bitmap = Bitmap.createBitmap(drawingCache) + screenView.isDrawingCacheEnabled = false + return bitmap + } + return null + } + companion object { const val KEY_USERNAME = "username" const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions" diff --git a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt index f967b8619..8f23674ca 100644 --- a/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/profile/achievements/AchievementsFragment.kt @@ -15,7 +15,6 @@ import com.google.android.material.badge.BadgeDrawable import com.google.android.material.badge.BadgeUtils import com.google.android.material.badge.ExperimentalBadgeUtils import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.databinding.FragmentAchievementsBinding import fr.free.nrw.commons.di.CommonsDaggerSupportFragment @@ -27,6 +26,7 @@ import fr.free.nrw.commons.utils.ConfigUtils.isBetaFlavour import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar import fr.free.nrw.commons.utils.ViewUtil.showLongToast +import fr.free.nrw.commons.utils.handleWebUrl import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.schedulers.Schedulers import org.apache.commons.lang3.StringUtils @@ -524,7 +524,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){ getString(R.string.ok), getString(R.string.read_help_link), {}, - { Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) }, + { handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) }, null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt index 092f057e9..387dd4672 100644 --- a/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/settings/SettingsFragment.kt @@ -33,9 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport import com.karumi.dexter.PermissionToken import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.multi.MultiplePermissionsListener -import fr.free.nrw.commons.BuildConfig.MOBILE_META_URL import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.activity.SingleWebViewActivity import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.contributions.ContributionController @@ -53,6 +51,7 @@ import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.PermissionUtils import fr.free.nrw.commons.utils.StringUtil import fr.free.nrw.commons.utils.ViewUtil +import fr.free.nrw.commons.utils.handleWebUrl import java.util.Locale import javax.inject.Inject import javax.inject.Named @@ -239,7 +238,10 @@ class SettingsFragment : PreferenceFragmentCompat() { val betaTesterPreference: Preference? = findPreference("becomeBetaTester") betaTesterPreference?.setOnPreferenceClickListener { - Utils.handleWebUrl(requireActivity(), Uri.parse(getString(R.string.beta_opt_in_link))) + handleWebUrl( + requireActivity(), + Uri.parse(getString(R.string.beta_opt_in_link)) + ) true } @@ -296,7 +298,7 @@ class SettingsFragment : PreferenceFragmentCompat() { getString(R.string.ok), getString(R.string.read_help_link), { }, - { Utils.handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) }, + { handleWebUrl(requireContext(), Uri.parse(GET_CONTENT_PICKER_HELP_URL)) }, null ) } diff --git a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt index 0c4ded8b2..2de17f849 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/PageContentsCreator.kt @@ -1,11 +1,11 @@ package fr.free.nrw.commons.upload import android.content.Context -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource import fr.free.nrw.commons.settings.Prefs.Licenses import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha +import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear import org.apache.commons.lang3.StringUtils import java.text.SimpleDateFormat import java.util.Calendar @@ -49,7 +49,7 @@ class PageContentsCreator @Inject constructor(private val context: Context) { String.format( Locale.ENGLISH, "{{Wiki Loves Monuments %d|1= %s}}\n", - Utils.getWikiLovesMonumentsYear(Calendar.getInstance()), + getWikiLovesMonumentsYear(Calendar.getInstance()), contribution.countryCode ) ) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt index f357cd112..6d2321def 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/UploadItem.kt @@ -1,10 +1,10 @@ package fr.free.nrw.commons.upload import android.net.Uri -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.utils.ImageUtils +import fr.free.nrw.commons.utils.fixExtension class UploadItem( var mediaUri: Uri?, @@ -32,7 +32,7 @@ class UploadItem( * languages have been entered, the first language is used. */ val filename: String - get() = Utils.fixExtension( + get() = fixExtension( uploadMediaDetails[0].captionText, getExtensionFromMimeType(mimeType) ) diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt index 0415d3270..65826a505 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicenseFragment.kt @@ -16,11 +16,13 @@ import android.widget.AdapterView import android.widget.ArrayAdapter import android.widget.TextView import fr.free.nrw.commons.R -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadBaseFragment import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog +import fr.free.nrw.commons.utils.handleWebUrl +import fr.free.nrw.commons.utils.toLicenseName +import fr.free.nrw.commons.utils.toLicenseUrl import timber.log.Timber import javax.inject.Inject @@ -126,20 +128,20 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View { } override fun setSelectedLicense(license: String?) { - var position = licenses!!.indexOf(getString(Utils.licenseNameFor(license))) + var position = license?.let { licenses!!.indexOf(getString(it.toLicenseName())) } ?: -1 // Check if position is valid if (position < 0) { Timber.d("Invalid position: %d. Using default licenses", position) position = licenses!!.size - 1 - } else { - Timber.d("Position: %d %s", position, getString(Utils.licenseNameFor(license))) } binding.spinnerLicenseList.setSelection(position) } override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) { - val licenseHyperLink = "" + - getString(Utils.licenseNameFor(selectedLicense)) + "
" + if (selectedLicense == null) return + + val licenseHyperLink = "" + + getString(selectedLicense.toLicenseName()) + "
" setTextViewHTML( binding.tvShareLicenseSummary, resources @@ -184,7 +186,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View { } private fun launchBrowser(hyperLink: String) = - Utils.handleWebUrl(context, Uri.parse(hyperLink)) + handleWebUrl(requireContext(), Uri.parse(hyperLink)) override fun onDestroyView() { presenter.onDetachView() diff --git a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt index 25d1a2324..df75019b2 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/license/MediaLicensePresenter.kt @@ -1,9 +1,9 @@ package fr.free.nrw.commons.upload.license -import fr.free.nrw.commons.Utils import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.settings.Prefs +import fr.free.nrw.commons.utils.toLicenseName import timber.log.Timber import java.lang.reflect.Method import java.lang.reflect.Proxy @@ -34,12 +34,14 @@ class MediaLicensePresenter @Inject constructor( val licenses = repository.getLicenses() view.setLicenses(licenses) - var selectedLicense = defaultKVStore.getString( + //CC_BY_SA_4 is the default one used by the commons web app + var selectedLicense: String = defaultKVStore.getString( Prefs.DEFAULT_LICENSE, Prefs.Licenses.CC_BY_SA_4 - ) //CC_BY_SA_4 is the default one used by the commons web app + ) ?: Prefs.Licenses.CC_BY_SA_4 + try { //I have to make sure that the stored default license was not one of the deprecated one's - Utils.licenseNameFor(selectedLicense) + selectedLicense.toLicenseName() } catch (exception: IllegalStateException) { Timber.e(exception) selectedLicense = Prefs.Licenses.CC_BY_SA_4 diff --git a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt index c368b96ac..d6d774208 100644 --- a/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt +++ b/app/src/main/java/fr/free/nrw/commons/upload/mediaDetails/UploadMediaDetailsContract.kt @@ -54,7 +54,7 @@ interface UploadMediaDetailsContract { fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem) } - interface UserActionListener : BasePresenter { + interface UserActionListener : BasePresenter { fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore) fun receiveImage( diff --git a/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt new file mode 100644 index 000000000..64d3636f0 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/ClipboardUtils.kt @@ -0,0 +1,20 @@ +package fr.free.nrw.commons.utils + +import android.content.ClipData +import android.content.ClipboardManager +import android.content.Context +import android.content.Context.CLIPBOARD_SERVICE + +object ClipboardUtils { + // Convenience for Java usages - remove when they are converted. + @JvmStatic + fun copy(label: String?, text: String?, context: Context) { + context.copyToClipboard(label, text) + } +} + +fun Context.copyToClipboard(label: String?, text: String?) { + with(getSystemService(CLIPBOARD_SERVICE) as ClipboardManager) { + setPrimaryClip(ClipData.newPlainText(label, text)) + } +} \ No newline at end of file diff --git a/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt b/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt new file mode 100644 index 000000000..b9e3988a3 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/FixExtension.kt @@ -0,0 +1,38 @@ +package fr.free.nrw.commons.utils + +import java.util.Locale +import java.util.regex.Pattern + +private val jpegPattern = Pattern.compile("\\.jpeg$", Pattern.CASE_INSENSITIVE) + +/** + * Adds extension to filename. Converts to .jpg if system provides .jpeg, adds .jpg if no extension detected + * @param theTitle File name + * @param ext Correct extension + * @return File with correct extension + */ +fun fixExtension(theTitle: String, ext: String?): String { + var result = theTitle + var extension = ext + + // People are used to ".jpg" more than ".jpeg" which the system gives us. + if (extension != null && extension.lowercase() == "jpeg") { + extension = "jpg" + } + + result = jpegPattern.matcher(result).replaceFirst(".jpg") + if (extension != null && + !result.lowercase(Locale.getDefault()).endsWith("." + extension.lowercase()) + ) { + result += ".$extension" + } + + // If extension is still null, make it jpg. (Hotfix for https://github.com/commons-app/apps-android-commons/issues/228) + // If title has an extension in it, if won't be true + if (extension == null && result.lastIndexOf(".") <= 0) { + extension = "jpg" + result += ".$extension" + } + + return result +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt b/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt new file mode 100644 index 000000000..513e36f10 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/GeoCoordinates.kt @@ -0,0 +1,27 @@ +package fr.free.nrw.commons.utils + +import android.content.Context +import android.content.Intent +import fr.free.nrw.commons.R +import fr.free.nrw.commons.location.LatLng +import fr.free.nrw.commons.utils.ViewUtil.showShortToast + +/** + * Util function to handle geo coordinates with specified zoom level. It no longer depends on + * google maps and any app capable of handling the map intent can handle it + * + * @param context The context for launching intent + * @param latLng The latitude and longitude of the location + * @param zoomLevel The zoom level + */ +fun handleGeoCoordinates( + context: Context, latLng: LatLng, + zoomLevel: Double = 16.0 +) { + val mapIntent = Intent(Intent.ACTION_VIEW, latLng.getGmmIntentUri(zoomLevel)) + if (mapIntent.resolveActivity(context.packageManager) != null) { + context.startActivity(mapIntent) + } else { + showShortToast(context, context.getString(R.string.map_application_missing)) + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt b/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt new file mode 100644 index 000000000..065a14718 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/Licenses.kt @@ -0,0 +1,31 @@ +package fr.free.nrw.commons.utils + +import fr.free.nrw.commons.R +import fr.free.nrw.commons.settings.Prefs + +/** + * Generates licence name with given ID + * @return Name of license + */ +fun String.toLicenseName(): Int = when (this) { + Prefs.Licenses.CC_BY_3 -> R.string.license_name_cc_by + Prefs.Licenses.CC_BY_4 -> R.string.license_name_cc_by_four + Prefs.Licenses.CC_BY_SA_3 -> R.string.license_name_cc_by_sa + Prefs.Licenses.CC_BY_SA_4 -> R.string.license_name_cc_by_sa_four + Prefs.Licenses.CC0 -> R.string.license_name_cc0 + else -> throw IllegalStateException("Unrecognized license value: $this") +} + +/** + * Generates license url with given ID + * @return Url of license + */ +fun String.toLicenseUrl(): String = when (this) { + Prefs.Licenses.CC_BY_3 -> "https://creativecommons.org/licenses/by/3.0/" + Prefs.Licenses.CC_BY_4 -> "https://creativecommons.org/licenses/by/4.0/" + Prefs.Licenses.CC_BY_SA_3 -> "https://creativecommons.org/licenses/by-sa/3.0/" + Prefs.Licenses.CC_BY_SA_4 -> "https://creativecommons.org/licenses/by-sa/4.0/" + Prefs.Licenses.CC0 -> "https://creativecommons.org/publicdomain/zero/1.0/" + else -> throw IllegalStateException("Unrecognized license value: $this") +} + diff --git a/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt b/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt new file mode 100644 index 000000000..d5f5736f5 --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/Monuments.kt @@ -0,0 +1,39 @@ +package fr.free.nrw.commons.utils + +import java.util.Calendar +import java.util.Date + +/** + * Get the start date of wlm monument + * For this release we are hardcoding it to be 1st September + * @return + */ +const val wLMStartDate: String = "1 Sep" + +/*** + * Get the end date of wlm monument + * For this release we are hardcoding it to be 31st October + * @return + */ +const val wLMEndDate: String = "30 Sep" + +/** + * For now we are enabling the monuments only when the date lies between 1 Sept & 31 OCt + */ +val isMonumentsEnabled: Boolean + get() = Date().month == 8 + +/*** + * Function to get the current WLM year + * It increments at the start of September in line with the other WLM functions + * (No consideration of locales for now) + * @param calendar + * @return + */ +fun getWikiLovesMonumentsYear(calendar: Calendar): Int { + var year = calendar[Calendar.YEAR] + if (calendar[Calendar.MONTH] < Calendar.SEPTEMBER) { + year -= 1 + } + return year +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt new file mode 100644 index 000000000..75760d4ab --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/UnderlineUtils.kt @@ -0,0 +1,19 @@ +package fr.free.nrw.commons.utils + +import android.widget.TextView +import androidx.core.text.buildSpannedString +import androidx.core.text.underline + +object UnderlineUtils { + // Convenience method for Java usages - remove when those classes are converted + @JvmStatic + fun setUnderlinedText(textView: TextView, stringResourceName: Int) { + textView.setUnderlinedText(stringResourceName) + } +} + +fun TextView.setUnderlinedText(stringResourceName: Int) { + text = buildSpannedString { + underline { append(context.getString(stringResourceName)) } + } +} diff --git a/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt b/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt new file mode 100644 index 000000000..4843cf0aa --- /dev/null +++ b/app/src/main/java/fr/free/nrw/commons/utils/UrlUtils.kt @@ -0,0 +1,33 @@ +package fr.free.nrw.commons.utils + +import android.content.Context +import android.content.Intent +import android.net.Uri +import androidx.browser.customtabs.CustomTabColorSchemeParams +import androidx.browser.customtabs.CustomTabsIntent +import androidx.core.content.ContextCompat +import fr.free.nrw.commons.R +import timber.log.Timber + +/** + * Opens Custom Tab Activity with in-app browser for the specified URL. + * Launches intent for web URL + */ +fun handleWebUrl(context: Context, url: Uri) { + Timber.d("Launching web url %s", url.toString()) + + val color = CustomTabColorSchemeParams.Builder() + .setToolbarColor(ContextCompat.getColor(context, R.color.primaryColor)) + .setSecondaryToolbarColor(ContextCompat.getColor(context, R.color.primaryDarkColor)) + .build() + + val customTabsIntent = CustomTabsIntent.Builder() + .setDefaultColorSchemeParams(color) + .setExitAnimations( + context, android.R.anim.slide_in_left, android.R.anim.slide_out_right + ).build() + + // Clear previous browser tasks, so that back/exit buttons work as intended. + customTabsIntent.intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + customTabsIntent.launchUrl(context, url) +} diff --git a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt index e9e68a3ad..d9151335a 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/UtilsTest.kt @@ -1,5 +1,6 @@ package fr.free.nrw.commons +import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear import org.junit.Test import org.junit.jupiter.api.Assertions import java.util.Calendar @@ -9,20 +10,20 @@ class UtilsTest { fun wikiLovesMonumentsYearBeforeSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.FEBRUARY, 1) - Assertions.assertEquals(2021, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2021, getWikiLovesMonumentsYear(cal)) } @Test fun wikiLovesMonumentsYearInSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.SEPTEMBER, 1) - Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal)) } @Test fun wikiLovesMonumentsYearAfterSeptember() { val cal = Calendar.getInstance() cal.set(2022, Calendar.DECEMBER, 1) - Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) + Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal)) } } diff --git a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt index 4fd5689da..e3f1c86cc 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/contributions/ContributionsFragmentUnitTests.kt @@ -248,13 +248,6 @@ class ContributionsFragmentUnitTests { fragment.onDestroyView() } - @Test - @Throws(Exception::class) - fun testShowMessage() { - Shadows.shadowOf(Looper.getMainLooper()).idle() - fragment.showMessage("") - } - @Test @Throws(Exception::class) fun testShowCampaigns() { diff --git a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt index 68b1c95cb..b448df6d2 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/upload/MediaLicensePresenterTest.kt @@ -1,11 +1,12 @@ package fr.free.nrw.commons.upload import com.nhaarman.mockitokotlin2.verify -import fr.free.nrw.commons.Utils +import fr.free.nrw.commons.utils.UnderlineUtils import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.upload.license.MediaLicenseContract import fr.free.nrw.commons.upload.license.MediaLicensePresenter +import fr.free.nrw.commons.utils.toLicenseName import org.junit.After import org.junit.Before import org.junit.Test @@ -25,7 +26,7 @@ import org.robolectric.RobolectricTestRunner */ @RunWith(RobolectricTestRunner::class) -@PrepareForTest(Utils::class) +@PrepareForTest(UnderlineUtils::class) class MediaLicensePresenterTest { @Mock internal lateinit var repository: UploadRepository @@ -39,7 +40,7 @@ class MediaLicensePresenterTest { @InjectMocks lateinit var mediaLicensePresenter: MediaLicensePresenter - private lateinit var mockedUtil: MockedStatic + private lateinit var mockedUtil: MockedStatic /** * initial setup test environemnt @@ -49,8 +50,7 @@ class MediaLicensePresenterTest { fun setUp() { MockitoAnnotations.openMocks(this) mediaLicensePresenter.onAttachView(view) - mockedUtil = Mockito.mockStatic(Utils::class.java) - `when`(Utils.licenseNameFor(ArgumentMatchers.anyString())).thenReturn(1) + mockedUtil = Mockito.mockStatic(UnderlineUtils::class.java) } @After diff --git a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt index 24e9b233a..f4e6a771b 100644 --- a/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt +++ b/app/src/test/kotlin/fr/free/nrw/commons/utils/UtilsFixExtensionTest.kt @@ -1,6 +1,5 @@ package fr.free.nrw.commons.utils -import fr.free.nrw.commons.Utils.fixExtension import org.junit.Assert.assertEquals import org.junit.Test