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
This commit is contained in:
Paul Hawke 2025-07-04 06:18:52 -05:00 committed by GitHub
parent 4befff8f42
commit 3bd0ec4466
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 387 additions and 519 deletions

View file

@ -1,7 +1,9 @@
package fr.free.nrw.commons package fr.free.nrw.commons
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.content.ActivityNotFoundException
import android.content.Intent import android.content.Intent
import android.content.Intent.ACTION_VIEW
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.view.Menu 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.ConfigUtils.getVersionNameWithSha
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog
import java.util.Collections 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 * Represents about screen of this app
@ -59,30 +64,12 @@ class AboutActivity : BaseActivity() {
binding!!.aboutImprove.setHtmlText(improveText) binding!!.aboutImprove.setHtmlText(improveText)
binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha() binding!!.aboutVersion.text = applicationContext.getVersionNameWithSha()
Utils.setUnderlinedText( binding!!.aboutFaq.setUnderlinedText(R.string.about_faq)
binding!!.aboutFaq, R.string.about_faq, binding!!.aboutRateUs.setUnderlinedText(R.string.about_rate_us)
applicationContext binding!!.aboutUserGuide.setUnderlinedText(R.string.user_guide)
) binding!!.aboutPrivacyPolicy.setUnderlinedText(R.string.about_privacy_policy)
Utils.setUnderlinedText( binding!!.aboutTranslate.setUnderlinedText(R.string.about_translate)
binding!!.aboutRateUs, R.string.about_rate_us, binding!!.aboutCredits.setUnderlinedText(R.string.about_credits)
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. To set listeners, we can create a separate method and use lambda syntax.
@ -106,47 +93,56 @@ class AboutActivity : BaseActivity() {
fun launchFacebook(view: View?) { fun launchFacebook(view: View?) {
val intent: Intent val intent: Intent
try { 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) intent.setPackage(Urls.FACEBOOK_PACKAGE_NAME)
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
Utils.handleWebUrl(this, Uri.parse(Urls.FACEBOOK_WEB_URL)) handleWebUrl(this, Urls.FACEBOOK_WEB_URL.toUri())
} }
} }
fun launchGithub(view: View?) { fun launchGithub(view: View?) {
val intent: Intent val intent: Intent
try { 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) intent.setPackage(Urls.GITHUB_PACKAGE_NAME)
startActivity(intent) startActivity(intent)
} catch (e: Exception) { } catch (e: Exception) {
Utils.handleWebUrl(this, Uri.parse(Urls.GITHUB_REPO_URL)) handleWebUrl(this, Urls.GITHUB_REPO_URL.toUri())
} }
} }
fun launchWebsite(view: View?) { fun launchWebsite(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.WEBSITE_URL)) handleWebUrl(this, Urls.WEBSITE_URL.toUri())
} }
fun launchRatings(view: View?) { 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?) { fun launchCredits(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.CREDITS_URL)) handleWebUrl(this, Urls.CREDITS_URL.toUri())
} }
fun launchUserGuide(view: View?) { fun launchUserGuide(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.USER_GUIDE_URL)) handleWebUrl(this, Urls.USER_GUIDE_URL.toUri())
} }
fun launchPrivacyPolicy(view: View?) { fun launchPrivacyPolicy(view: View?) {
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) handleWebUrl(this, BuildConfig.PRIVACY_POLICY_URL.toUri())
} }
fun launchFrequentlyAskedQuesions(view: View?) { fun launchFrequentlyAskedQuesions(view: View?) {
Utils.handleWebUrl(this, Uri.parse(Urls.FAQ_URL)) handleWebUrl(this, Urls.FAQ_URL.toUri())
} }
override fun onCreateOptionsMenu(menu: Menu): Boolean { override fun onCreateOptionsMenu(menu: Menu): Boolean {
@ -193,7 +189,7 @@ class AboutActivity : BaseActivity() {
val positiveButtonRunnable = Runnable { val positiveButtonRunnable = Runnable {
val langCode = instance.languageLookUpTable!!.getCodes()[spinner.selectedItemPosition] 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( showAlertDialog(
this, this,

View file

@ -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<T> {
/**
* 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();
}

View file

@ -0,0 +1,10 @@
package fr.free.nrw.commons
/**
* Base presenter, enforcing contracts to attach and detach view
*/
interface BasePresenter<T> {
fun onAttachView(view: T)
fun onDetachView()
}

View file

@ -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);
}
}
}

View file

@ -1,7 +1,9 @@
package fr.free.nrw.commons package fr.free.nrw.commons
import android.os.Parcelable import android.os.Parcelable
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
import fr.free.nrw.commons.location.LatLng 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 fr.free.nrw.commons.wikidata.model.page.PageTitle
import kotlinx.parcelize.IgnoredOnParcel import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
@ -173,7 +175,8 @@ class Media constructor(
* Gets file page title * Gets file page title
* @return New media 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 * Returns wikicode to use the media file on a MediaWiki site

View file

@ -1,8 +0,0 @@
package fr.free.nrw.commons;
/**
* Base interface for all the views
*/
public interface MvpView {
void showMessage(String message);
}

View file

@ -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;
}
}

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons; package fr.free.nrw.commons;
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
import android.net.Uri; import android.net.Uri;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -7,6 +9,7 @@ import android.view.ViewGroup;
import android.widget.TextView; import android.widget.TextView;
import androidx.viewpager.widget.PagerAdapter; import androidx.viewpager.widget.PagerAdapter;
import fr.free.nrw.commons.utils.UnderlineUtils;
public class WelcomePagerAdapter extends PagerAdapter { public class WelcomePagerAdapter extends PagerAdapter {
private static final int[] PAGE_LAYOUTS = new int[]{ private static final int[] PAGE_LAYOUTS = new int[]{
@ -46,8 +49,8 @@ public class WelcomePagerAdapter extends PagerAdapter {
if (position == PAGE_LAYOUTS.length - 1) { if (position == PAGE_LAYOUTS.length - 1) {
// Add link to more information // Add link to more information
TextView moreInfo = layout.findViewById(R.id.welcomeInfo); TextView moreInfo = layout.findViewById(R.id.welcomeInfo);
Utils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text, container.getContext()); UnderlineUtils.setUnderlinedText(moreInfo, R.string.welcome_help_button_text);
moreInfo.setOnClickListener(view -> Utils.handleWebUrl( moreInfo.setOnClickListener(view -> handleWebUrl(
container.getContext(), container.getContext(),
Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents") Uri.parse("https://commons.wikimedia.org/wiki/Help:Contents")
)); ));

View file

@ -25,7 +25,6 @@ import androidx.core.content.ContextCompat
import fr.free.nrw.commons.BuildConfig import fr.free.nrw.commons.BuildConfig
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.R 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.LoginCallback
import fr.free.nrw.commons.auth.login.LoginClient import fr.free.nrw.commons.auth.login.LoginClient
import fr.free.nrw.commons.auth.login.LoginResult 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.ConfigUtils.isBetaFlavour
import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard import fr.free.nrw.commons.utils.ViewUtil.hideKeyboard
import fr.free.nrw.commons.utils.handleWebUrl
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
import timber.log.Timber import timber.log.Timber
import java.util.Locale import java.util.Locale
@ -254,10 +254,10 @@ class LoginActivity : AccountAuthenticatorActivity() {
} }
private fun forgotPassword() = private fun forgotPassword() =
Utils.handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL)) handleWebUrl(this, Uri.parse(BuildConfig.FORGOT_PASSWORD_URL))
private fun onPrivacyPolicyClicked() = private fun onPrivacyPolicyClicked() =
Utils.handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL)) handleWebUrl(this, Uri.parse(BuildConfig.PRIVACY_POLICY_URL))
private fun signUp() = private fun signUp() =
startActivity(Intent(this, SignupActivity::class.java)) startActivity(Intent(this, SignupActivity::class.java))

View file

@ -7,7 +7,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import fr.free.nrw.commons.R 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.campaigns.models.Campaign
import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.contributions.MainActivity
import fr.free.nrw.commons.databinding.LayoutCampaginBinding 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.DateUtil.getExtraShortDateString
import fr.free.nrw.commons.utils.SwipableCardView import fr.free.nrw.commons.utils.SwipableCardView
import fr.free.nrw.commons.utils.ViewUtil.showLongToast import fr.free.nrw.commons.utils.ViewUtil.showLongToast
import fr.free.nrw.commons.utils.handleWebUrl
import timber.log.Timber import timber.log.Timber
import java.text.ParseException import java.text.ParseException
@ -74,7 +74,7 @@ class CampaignView : SwipableCardView {
if (it.isWLMCampaign) { if (it.isWLMCampaign) {
((context) as MainActivity).showNearby() ((context) as MainActivity).showNearby()
} else { } else {
Utils.handleWebUrl(context, Uri.parse(it.link)) handleWebUrl(context, Uri.parse(it.link))
} }
} }
} }

View file

@ -26,7 +26,7 @@ class CampaignsPresenter @Inject constructor(
private val okHttpJsonApiClient: OkHttpJsonApiClient?, private val okHttpJsonApiClient: OkHttpJsonApiClient?,
@param:Named(IO_THREAD) private val ioScheduler: Scheduler, @param:Named(IO_THREAD) private val ioScheduler: Scheduler,
@param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler @param:Named(MAIN_THREAD) private val mainThreadScheduler: Scheduler
) : BasePresenter<ICampaignsView?> { ) : BasePresenter<ICampaignsView> {
private var view: ICampaignsView? = null private var view: ICampaignsView? = null
private var disposable: Disposable? = null private var disposable: Disposable? = null
private var campaign: Campaign? = null private var campaign: Campaign? = null

View file

@ -1,11 +1,10 @@
package fr.free.nrw.commons.campaigns package fr.free.nrw.commons.campaigns
import fr.free.nrw.commons.MvpView
import fr.free.nrw.commons.campaigns.models.Campaign import fr.free.nrw.commons.campaigns.models.Campaign
/** /**
* Interface which defines the view contracts of the campaign view * Interface which defines the view contracts of the campaign view
*/ */
interface ICampaignsView : MvpView { interface ICampaignsView {
fun showCampaigns(campaign: Campaign?) fun showCampaigns(campaign: Campaign?)
} }

View file

@ -13,9 +13,9 @@ import androidx.fragment.app.FragmentManager
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle import androidx.lifecycle.repeatOnLifecycle
import fr.free.nrw.commons.BuildConfig.COMMONS_URL
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.ViewPagerAdapter
import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding import fr.free.nrw.commons.databinding.ActivityCategoryDetailsBinding
import fr.free.nrw.commons.explore.categories.media.CategoriesMediaFragment 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.explore.categories.sub.SubCategoriesFragment
import fr.free.nrw.commons.media.MediaDetailPagerFragment import fr.free.nrw.commons.media.MediaDetailPagerFragment
import fr.free.nrw.commons.theme.BaseActivity 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 kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@ -199,8 +202,9 @@ class CategoryDetailsActivity : BaseActivity(),
override fun onOptionsItemSelected(item: MenuItem): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) { return when (item.itemId) {
R.id.menu_browser_current_category -> { R.id.menu_browser_current_category -> {
val title = Utils.getPageTitle(CATEGORY_PREFIX + categoryName) val title = PageTitle(CATEGORY_PREFIX + categoryName, WikiSite(COMMONS_URL))
Utils.handleWebUrl(this, Uri.parse(title.canonicalUri))
handleWebUrl(this, Uri.parse(title.canonicalUri))
true true
} }

View file

@ -9,7 +9,6 @@ import fr.free.nrw.commons.BasePresenter
interface ContributionsContract { interface ContributionsContract {
interface View { interface View {
fun showMessage(localizedMessage: String)
fun getContext(): Context? fun getContext(): Context?
} }

View file

@ -30,7 +30,6 @@ import androidx.work.WorkManager
import fr.free.nrw.commons.MapController.NearbyPlacesInfo import fr.free.nrw.commons.MapController.NearbyPlacesInfo
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R 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.SessionManager
import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.campaigns.CampaignView
import fr.free.nrw.commons.campaigns.CampaignsPresenter 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.NetworkUtils.isInternetConnectionEstablished
import fr.free.nrw.commons.utils.PermissionUtils.hasPermission import fr.free.nrw.commons.utils.PermissionUtils.hasPermission
import fr.free.nrw.commons.utils.ViewUtil.showLongToast 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.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable import io.reactivex.disposables.CompositeDisposable
@ -242,8 +244,8 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
private fun initWLMCampaign() { private fun initWLMCampaign() {
wlmCampaign = Campaign( wlmCampaign = Campaign(
getString(R.string.wlm_campaign_title), getString(R.string.wlm_campaign_title),
getString(R.string.wlm_campaign_description), Utils.getWLMStartDate().toString(), getString(R.string.wlm_campaign_description), wLMStartDate,
Utils.getWLMEndDate().toString(), NearbyParentFragment.WLM_URL, true wLMEndDate, NearbyParentFragment.WLM_URL, true
) )
} }
@ -729,7 +731,7 @@ class ContributionsFragment : CommonsDaggerSupportFragment(), FragmentManager.On
* of campaigns on the campaigns card * of campaigns on the campaigns card
*/ */
private fun fetchCampaigns() { private fun fetchCampaigns() {
if (Utils.isMonumentsEnabled(Date())) { if (isMonumentsEnabled) {
if (binding != null) { if (binding != null) {
binding!!.campaignsView.setCampaign(wlmCampaign) binding!!.campaignsView.setCampaign(wlmCampaign)
binding!!.campaignsView.visibility = View.VISIBLE 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?) { override fun showCampaigns(campaign: Campaign?) {
if (campaign != null && !isUserProfile) { if (campaign != null && !isUserProfile) {
if (binding != null) { if (binding != null) {

View file

@ -15,7 +15,7 @@ class ContributionsListContract {
fun showNoContributionsUI(shouldShow: Boolean) fun showNoContributionsUI(shouldShow: Boolean)
} }
interface UserActionListener : BasePresenter<View?> { interface UserActionListener : BasePresenter<View> {
fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?) fun refreshList(swipeRefreshLayout: SwipeRefreshLayout?)
} }
} }

View file

@ -29,7 +29,6 @@ import androidx.recyclerview.widget.SimpleItemAnimator
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.MediaDataExtractor
import fr.free.nrw.commons.R 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.SessionManager
import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance import fr.free.nrw.commons.contributions.WikipediaInstructionsDialogFragment.Companion.newInstance
import fr.free.nrw.commons.databinding.FragmentContributionsListBinding 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.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.showShortToast 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 fr.free.nrw.commons.wikidata.model.WikiSite
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import javax.inject.Inject import javax.inject.Inject
@ -527,14 +528,13 @@ class ContributionsListFragment : CommonsDaggerSupportFragment(), ContributionsL
*/ */
override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) { override fun onConfirmClicked(contribution: Contribution?, copyWikicode: Boolean) {
if (copyWikicode) { if (copyWikicode) {
val wikicode = contribution!!.media.wikiCode requireContext().copyToClipboard("wikicode", contribution!!.media.wikiCode)
Utils.copy("wikicode", wikicode, context)
} }
val url = val url =
languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace languageWikipediaSite!!.mobileUrl() + "/wiki/" + (contribution!!.wikidataPlace
?.getWikipediaPageTitle()) ?.getWikipediaPageTitle())
Utils.handleWebUrl(context, Uri.parse(url)) handleWebUrl(requireContext(), Uri.parse(url))
} }
fun getContributionStateAt(position: Int): Int { fun getContributionStateAt(position: Int): Int {

View file

@ -1,5 +1,7 @@
package fr.free.nrw.commons.explore.depictions; package fr.free.nrw.commons.explore.depictions;
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
@ -8,16 +10,11 @@ import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.widget.FrameLayout;
import androidx.appcompat.widget.Toolbar;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.snackbar.Snackbar; 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.Media;
import fr.free.nrw.commons.R; import fr.free.nrw.commons.R;
import fr.free.nrw.commons.Utils;
import fr.free.nrw.commons.ViewPagerAdapter; import fr.free.nrw.commons.ViewPagerAdapter;
import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao; import fr.free.nrw.commons.bookmarks.items.BookmarkItemsDao;
import fr.free.nrw.commons.category.CategoryImagesCallback; 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: case R.id.browser_actions_menu_items:
String entityId=getIntent().getStringExtra("entityId"); String entityId=getIntent().getStringExtra("entityId");
Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId); Uri uri = Uri.parse("https://www.wikidata.org/wiki/" + entityId);
Utils.handleWebUrl(this, uri); handleWebUrl(this, uri);
return true; return true;
case R.id.menu_bookmark_current_item: case R.id.menu_bookmark_current_item:

View file

@ -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_SIGNIFICANTLY_CHANGED;
import static fr.free.nrw.commons.location.LocationServiceManager.LocationChangeType.LOCATION_SLIGHTLY_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.MapUtils.ZOOM_LEVEL;
import static fr.free.nrw.commons.utils.UrlUtilsKt.handleWebUrl;
import android.Manifest.permission; import android.Manifest.permission;
import android.annotation.SuppressLint; 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.MapController;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; 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.bookmarks.locations.BookmarkLocationsDao;
import fr.free.nrw.commons.contributions.MainActivity; import fr.free.nrw.commons.contributions.MainActivity;
import fr.free.nrw.commons.databinding.FragmentExploreMapBinding; import fr.free.nrw.commons.databinding.FragmentExploreMapBinding;
@ -639,13 +640,13 @@ public class ExploreMapFragment extends CommonsDaggerSupportFragment
*/ */
private void passInfoToSheet(final Place place) { private void passInfoToSheet(final Place place) {
binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener( binding.bottomSheetDetailsBinding.directionsButton.setOnClickListener(
view -> Utils.handleGeoCoordinates(getActivity(), view -> handleGeoCoordinates(requireActivity(),
place.getLocation(), binding.mapView.getZoomLevelDouble())); place.getLocation(), binding.mapView.getZoomLevelDouble()));
binding.bottomSheetDetailsBinding.commonsButton.setVisibility( binding.bottomSheetDetailsBinding.commonsButton.setVisibility(
place.hasCommonsLink() ? View.VISIBLE : View.GONE); place.hasCommonsLink() ? View.VISIBLE : View.GONE);
binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener( binding.bottomSheetDetailsBinding.commonsButton.setOnClickListener(
view -> Utils.handleWebUrl(getContext(), place.siteLinks.getCommonsLink())); view -> handleWebUrl(getContext(), place.siteLinks.getCommonsLink()));
int index = 0; int index = 0;
for (Media media : mediaList) { for (Media media : mediaList) {

View file

@ -30,7 +30,6 @@ import fr.free.nrw.commons.CameraPosition
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R 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.SessionManager
import fr.free.nrw.commons.auth.csrf.CsrfTokenClient import fr.free.nrw.commons.auth.csrf.CsrfTokenClient
import fr.free.nrw.commons.coordinates.CoordinateEditHelper 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.upload.mediaDetails.UploadMediaDetailFragment.Companion.LAST_ZOOM
import fr.free.nrw.commons.utils.DialogUtil import fr.free.nrw.commons.utils.DialogUtil
import fr.free.nrw.commons.utils.MapUtils.ZOOM_LEVEL 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.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.osmdroid.tileprovider.tilesource.TileSourceFactory import org.osmdroid.tileprovider.tilesource.TileSourceFactory
@ -432,8 +432,8 @@ class LocationPickerActivity : BaseActivity(), LocationPermissionCallback {
position?.let { position?.let {
mapView?.zoomLevelDouble?.let { zoomLevel -> mapView?.zoomLevelDouble?.let { zoomLevel ->
Utils.handleGeoCoordinates(this, it, zoomLevel) handleGeoCoordinates(this, it, zoomLevel)
} ?: Utils.handleGeoCoordinates(this, it) } ?: handleGeoCoordinates(this, it)
} }
} }

View file

@ -77,7 +77,7 @@ import fr.free.nrw.commons.CommonsApplication.Companion.instance
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.MediaDataExtractor import fr.free.nrw.commons.MediaDataExtractor
import fr.free.nrw.commons.R 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.actions.ThanksClient
import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException 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
import fr.free.nrw.commons.utils.ViewUtil.showShortToast import fr.free.nrw.commons.utils.ViewUtil.showShortToast
import fr.free.nrw.commons.utils.ViewUtilWrapper 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 fr.free.nrw.commons.wikidata.mwapi.MwQueryPage.Revision
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
@ -316,8 +320,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
_binding = FragmentMediaDetailBinding.inflate(inflater, container, false) _binding = FragmentMediaDetailBinding.inflate(inflater, container, false)
val view: View = binding.root val view: View = binding.root
binding.seeMore.setUnderlinedText(R.string.nominated_see_more)
Utils.setUnderlinedText(binding.seeMore, R.string.nominated_see_more, requireContext())
if (isCategoryImage) { if (isCategoryImage) {
binding.authorLinearLayout.visibility = View.VISIBLE binding.authorLinearLayout.visibility = View.VISIBLE
@ -909,7 +912,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
private fun onMediaDetailLicenceClicked() { private fun onMediaDetailLicenceClicked() {
val url: String? = media!!.licenseUrl val url: String? = media!!.licenseUrl
if (!StringUtils.isBlank(url) && activity != null) { if (!StringUtils.isBlank(url) && activity != null) {
Utils.handleWebUrl(activity, Uri.parse(url)) handleWebUrl(requireContext(), Uri.parse(url))
} else { } else {
viewUtil.showShortToast(requireActivity(), getString(R.string.null_url)) viewUtil.showShortToast(requireActivity(), getString(R.string.null_url))
} }
@ -917,17 +920,17 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
private fun onMediaDetailCoordinatesClicked() { private fun onMediaDetailCoordinatesClicked() {
if (media!!.coordinates != null && activity != null) { if (media!!.coordinates != null && activity != null) {
Utils.handleGeoCoordinates(activity, media!!.coordinates) handleGeoCoordinates(requireContext(), media!!.coordinates!!)
} }
} }
private fun onCopyWikicodeClicked() { private fun onCopyWikicodeClicked() {
val data: String = val data: String =
"[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]" "[[" + media!!.filename + "|thumb|" + media!!.fallbackDescription + "]]"
Utils.copy("wikiCode", data, context) requireContext().copyToClipboard("wikiCode", data)
Timber.d("Generated wikidata copy code: %s", 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() .show()
} }
@ -1765,7 +1768,7 @@ class MediaDetailFragment : CommonsDaggerSupportFragment(), CategoryEditHelper.C
private fun onSeeMoreClicked() { private fun onSeeMoreClicked() {
if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) { if (binding.nominatedDeletionBanner.visibility == View.VISIBLE && activity != null) {
Utils.handleWebUrl(activity, Uri.parse(media!!.pageTitle.mobileUri)) handleWebUrl(requireContext(), Uri.parse(media!!.pageTitle.mobileUri))
} }
} }

View file

@ -1,6 +1,6 @@
package fr.free.nrw.commons.media; 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.Handler;
import android.os.Looper; 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.CommonsApplication;
import fr.free.nrw.commons.Media; import fr.free.nrw.commons.Media;
import fr.free.nrw.commons.R; 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.auth.SessionManager;
import fr.free.nrw.commons.bookmarks.models.Bookmark; import fr.free.nrw.commons.bookmarks.models.Bookmark;
import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider; import fr.free.nrw.commons.bookmarks.pictures.BookmarkPicturesContentProvider;
@ -216,7 +216,7 @@ public class MediaDetailPagerFragment extends CommonsDaggerSupportFragment imple
return true; return true;
case R.id.menu_copy_link: case R.id.menu_copy_link:
String uri = m.getPageTitle().getCanonicalUri(); String uri = m.getPageTitle().getCanonicalUri();
Utils.copy("shareLink", uri, requireContext()); ClipboardUtils.copy("shareLink", uri, requireContext());
Timber.d("Copied share link to clipboard: %s", uri); Timber.d("Copied share link to clipboard: %s", uri);
Toast.makeText(requireContext(), getString(R.string.menu_link_copied), Toast.makeText(requireContext(), getString(R.string.menu_link_copied),
Toast.LENGTH_SHORT).show(); Toast.LENGTH_SHORT).show();

View file

@ -10,12 +10,13 @@ import androidx.activity.result.ActivityResultLauncher
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.auth.LoginActivity import fr.free.nrw.commons.auth.LoginActivity
import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.contributions.ContributionController
import fr.free.nrw.commons.kvstore.JsonKvStore import fr.free.nrw.commons.kvstore.JsonKvStore
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.utils.ActivityUtils 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 fr.free.nrw.commons.wikidata.WikidataConstants
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -104,7 +105,7 @@ class CommonPlaceClickActions
fun onDirectionsClicked(): (Place) -> Unit = fun onDirectionsClicked(): (Place) -> Unit =
{ {
Utils.handleGeoCoordinates(activity, it.getLocation()) handleGeoCoordinates(activity, it.getLocation())
} }
private fun storeSharedPrefs(selectedPlace: Place) { private fun storeSharedPrefs(selectedPlace: Place) {
@ -113,7 +114,7 @@ class CommonPlaceClickActions
} }
private fun openWebView(link: Uri): Boolean { private fun openWebView(link: Uri): Boolean {
Utils.handleWebUrl(activity, link) handleWebUrl(activity, link)
return true return true
} }

View file

@ -58,12 +58,10 @@ import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.MapController.NearbyPlacesInfo import fr.free.nrw.commons.MapController.NearbyPlacesInfo
import fr.free.nrw.commons.Media import fr.free.nrw.commons.Media
import fr.free.nrw.commons.R 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.bookmarks.locations.BookmarkLocationsDao
import fr.free.nrw.commons.contributions.ContributionController import fr.free.nrw.commons.contributions.ContributionController
import fr.free.nrw.commons.contributions.MainActivity import fr.free.nrw.commons.contributions.MainActivity
import fr.free.nrw.commons.contributions.MainActivity.ActiveFragment 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.databinding.FragmentNearbyParentBinding
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment import fr.free.nrw.commons.di.CommonsDaggerSupportFragment
import fr.free.nrw.commons.filepicker.FilePicker 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.location.LocationUpdateListener
import fr.free.nrw.commons.media.MediaClient import fr.free.nrw.commons.media.MediaClient
import fr.free.nrw.commons.media.MediaDetailPagerFragment 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.navtab.NavTab
import fr.free.nrw.commons.nearby.BottomSheetAdapter import fr.free.nrw.commons.nearby.BottomSheetAdapter
import fr.free.nrw.commons.nearby.BottomSheetAdapter.ItemClickListener 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.NetworkUtils.isInternetConnectionEstablished
import fr.free.nrw.commons.utils.SystemThemeUtils import fr.free.nrw.commons.utils.SystemThemeUtils
import fr.free.nrw.commons.utils.ViewUtil.showLongToast 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.WikidataConstants
import fr.free.nrw.commons.wikidata.WikidataEditListener import fr.free.nrw.commons.wikidata.WikidataEditListener
import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener import fr.free.nrw.commons.wikidata.WikidataEditListener.WikidataP18EditListener
@ -140,7 +141,6 @@ import java.util.UUID
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
import javax.sql.DataSource
import kotlin.concurrent.Volatile import kotlin.concurrent.Volatile
@ -467,7 +467,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
} }
} }
_isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true _isDarkTheme = systemThemeUtils?.isDeviceInNightMode() == true
if (Utils.isMonumentsEnabled(Date())) { if (isMonumentsEnabled) {
binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE binding?.rlContainerWlmMonthMessage?.visibility = View.VISIBLE
} else { } else {
binding?.rlContainerWlmMonthMessage?.visibility = View.GONE binding?.rlContainerWlmMonthMessage?.visibility = View.GONE
@ -836,7 +836,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
loadAnimations() loadAnimations()
setBottomSheetCallbacks() setBottomSheetCallbacks()
addActionToTitle() addActionToTitle()
if (!Utils.isMonumentsEnabled(Date())) { if (!isMonumentsEnabled) {
NearbyFilterState.setWlmSelected(false) NearbyFilterState.setWlmSelected(false)
} }
} }
@ -1017,11 +1017,10 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
*/ */
private fun addActionToTitle() { private fun addActionToTitle() {
binding!!.bottomSheetDetails.title.setOnLongClickListener { view -> binding!!.bottomSheetDetails.title.setOnLongClickListener { view ->
Utils.copy( requireContext().copyToClipboard(
"place", binding!!.bottomSheetDetails.title.text.toString(), "place", binding!!.bottomSheetDetails.title.text.toString()
context
) )
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() .show()
true true
} }
@ -1580,7 +1579,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
searchLatLng, searchLatLng,
false, false,
true, true,
Utils.isMonumentsEnabled(Date()), isMonumentsEnabled,
customQuery customQuery
) )
} }
@ -1633,7 +1632,7 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
searchLatLng, searchLatLng,
false, false,
true, true,
Utils.isMonumentsEnabled(Date()), isMonumentsEnabled,
customQuery customQuery
) )
} }
@ -2854,14 +2853,14 @@ class NearbyParentFragment : CommonsDaggerSupportFragment(),
R.drawable.ic_directions_black_24dp -> { R.drawable.ic_directions_black_24dp -> {
selectedPlace?.let { selectedPlace?.let {
Utils.handleGeoCoordinates(this.context, it.getLocation()) handleGeoCoordinates(requireContext(), it.getLocation())
binding?.map?.zoomLevelDouble ?: 0.0 binding?.map?.zoomLevelDouble ?: 0.0
} }
} }
R.drawable.ic_wikidata_logo_24dp -> { R.drawable.ic_wikidata_logo_24dp -> {
selectedPlace?.siteLinks?.wikidataLink?.let { 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 -> { R.drawable.ic_wikipedia_logo_24dp -> {
selectedPlace?.siteLinks?.wikipediaLink?.let { selectedPlace?.siteLinks?.wikipediaLink?.let {
Utils.handleWebUrl(this.context, it) handleWebUrl(requireContext(), it)
} }
} }
R.drawable.ic_commons_icon_vector -> { R.drawable.ic_commons_icon_vector -> {
selectedPlace?.siteLinks?.commonsLink?.let { selectedPlace?.siteLinks?.commonsLink?.let {
Utils.handleWebUrl(this.context, it) handleWebUrl(requireContext(), it)
} }
} }

View file

@ -13,7 +13,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import fr.free.nrw.commons.CommonsApplication import fr.free.nrw.commons.CommonsApplication
import fr.free.nrw.commons.R 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.SessionManager
import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException import fr.free.nrw.commons.auth.csrf.InvalidLoginTokenException
import fr.free.nrw.commons.databinding.ActivityNotificationBinding 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.theme.BaseActivity
import fr.free.nrw.commons.utils.NetworkUtils import fr.free.nrw.commons.utils.NetworkUtils
import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil
import fr.free.nrw.commons.utils.handleWebUrl
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
@ -197,7 +197,7 @@ class NotificationActivity : BaseActivity() {
private fun handleUrl(url: String?) { private fun handleUrl(url: String?) {
if (url.isNullOrEmpty()) return if (url.isNullOrEmpty()) return
Utils.handleWebUrl(this, Uri.parse(url)) handleWebUrl(this, Uri.parse(url))
} }
private fun setItems(notificationList: List<Notification>?) { private fun setItems(notificationList: List<Notification>?) {

View file

@ -3,16 +3,16 @@ package fr.free.nrw.commons.profile
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.util.Log 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.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.ViewPagerAdapter import fr.free.nrw.commons.ViewPagerAdapter
import fr.free.nrw.commons.auth.SessionManager import fr.free.nrw.commons.auth.SessionManager
import fr.free.nrw.commons.contributions.ContributionsFragment 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 fr.free.nrw.commons.utils.DialogUtil
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.util.* import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
/** /**
@ -133,7 +133,7 @@ class ProfileActivity : BaseActivity() {
return when (item.itemId) { return when (item.itemId) {
R.id.share_app_icon -> { R.id.share_app_icon -> {
val rootView = window.decorView.findViewById<View>(android.R.id.content) val rootView = window.decorView.findViewById<View>(android.R.id.content)
val screenShot = Utils.getScreenShot(rootView) val screenShot = getScreenShot(rootView)
if (screenShot == null) { if (screenShot == null) {
Log.e("ERROR", "ScreenShot is null") Log.e("ERROR", "ScreenShot is null")
return false return false
@ -212,6 +212,24 @@ class ProfileActivity : BaseActivity() {
binding.tabLayout.visibility = if (isVisible) View.VISIBLE else View.GONE 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 { companion object {
const val KEY_USERNAME = "username" const val KEY_USERNAME = "username"
const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions" const val KEY_SHOULD_SHOW_CONTRIBUTIONS = "shouldShowContributions"

View file

@ -15,7 +15,6 @@ import com.google.android.material.badge.BadgeDrawable
import com.google.android.material.badge.BadgeUtils import com.google.android.material.badge.BadgeUtils
import com.google.android.material.badge.ExperimentalBadgeUtils import com.google.android.material.badge.ExperimentalBadgeUtils
import fr.free.nrw.commons.R 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.SessionManager
import fr.free.nrw.commons.databinding.FragmentAchievementsBinding import fr.free.nrw.commons.databinding.FragmentAchievementsBinding
import fr.free.nrw.commons.di.CommonsDaggerSupportFragment 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.DialogUtil.showAlertDialog
import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar import fr.free.nrw.commons.utils.ViewUtil.showDismissibleSnackBar
import fr.free.nrw.commons.utils.ViewUtil.showLongToast import fr.free.nrw.commons.utils.ViewUtil.showLongToast
import fr.free.nrw.commons.utils.handleWebUrl
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers import io.reactivex.schedulers.Schedulers
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
@ -524,7 +524,7 @@ class AchievementsFragment : CommonsDaggerSupportFragment(){
getString(R.string.ok), getString(R.string.ok),
getString(R.string.read_help_link), getString(R.string.read_help_link),
{}, {},
{ Utils.handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) }, { handleWebUrl(requireContext(), Uri.parse(helpLinkUrl)) },
null null
) )
} }

View file

@ -33,9 +33,7 @@ import com.karumi.dexter.MultiplePermissionsReport
import com.karumi.dexter.PermissionToken import com.karumi.dexter.PermissionToken
import com.karumi.dexter.listener.PermissionRequest import com.karumi.dexter.listener.PermissionRequest
import com.karumi.dexter.listener.multi.MultiplePermissionsListener 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.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.activity.SingleWebViewActivity import fr.free.nrw.commons.activity.SingleWebViewActivity
import fr.free.nrw.commons.campaigns.CampaignView import fr.free.nrw.commons.campaigns.CampaignView
import fr.free.nrw.commons.contributions.ContributionController 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.PermissionUtils
import fr.free.nrw.commons.utils.StringUtil import fr.free.nrw.commons.utils.StringUtil
import fr.free.nrw.commons.utils.ViewUtil import fr.free.nrw.commons.utils.ViewUtil
import fr.free.nrw.commons.utils.handleWebUrl
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Named import javax.inject.Named
@ -239,7 +238,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
val betaTesterPreference: Preference? = findPreference("becomeBetaTester") val betaTesterPreference: Preference? = findPreference("becomeBetaTester")
betaTesterPreference?.setOnPreferenceClickListener { 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 true
} }
@ -296,7 +298,7 @@ class SettingsFragment : PreferenceFragmentCompat() {
getString(R.string.ok), getString(R.string.ok),
getString(R.string.read_help_link), 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 null
) )
} }

View file

@ -1,11 +1,11 @@
package fr.free.nrw.commons.upload package fr.free.nrw.commons.upload
import android.content.Context import android.content.Context
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.contributions.Contribution import fr.free.nrw.commons.contributions.Contribution
import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource import fr.free.nrw.commons.filepicker.UploadableFile.DateTimeWithSource
import fr.free.nrw.commons.settings.Prefs.Licenses import fr.free.nrw.commons.settings.Prefs.Licenses
import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha import fr.free.nrw.commons.utils.ConfigUtils.getVersionNameWithSha
import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear
import org.apache.commons.lang3.StringUtils import org.apache.commons.lang3.StringUtils
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Calendar import java.util.Calendar
@ -49,7 +49,7 @@ class PageContentsCreator @Inject constructor(private val context: Context) {
String.format( String.format(
Locale.ENGLISH, Locale.ENGLISH,
"{{Wiki Loves Monuments %d|1= %s}}\n", "{{Wiki Loves Monuments %d|1= %s}}\n",
Utils.getWikiLovesMonumentsYear(Calendar.getInstance()), getWikiLovesMonumentsYear(Calendar.getInstance()),
contribution.countryCode contribution.countryCode
) )
) )

View file

@ -1,10 +1,10 @@
package fr.free.nrw.commons.upload package fr.free.nrw.commons.upload
import android.net.Uri import android.net.Uri
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType import fr.free.nrw.commons.filepicker.MimeTypeMapWrapper.Companion.getExtensionFromMimeType
import fr.free.nrw.commons.nearby.Place import fr.free.nrw.commons.nearby.Place
import fr.free.nrw.commons.utils.ImageUtils import fr.free.nrw.commons.utils.ImageUtils
import fr.free.nrw.commons.utils.fixExtension
class UploadItem( class UploadItem(
var mediaUri: Uri?, var mediaUri: Uri?,
@ -32,7 +32,7 @@ class UploadItem(
* languages have been entered, the first language is used. * languages have been entered, the first language is used.
*/ */
val filename: String val filename: String
get() = Utils.fixExtension( get() = fixExtension(
uploadMediaDetails[0].captionText, uploadMediaDetails[0].captionText,
getExtensionFromMimeType(mimeType) getExtensionFromMimeType(mimeType)
) )

View file

@ -16,11 +16,13 @@ import android.widget.AdapterView
import android.widget.ArrayAdapter import android.widget.ArrayAdapter
import android.widget.TextView import android.widget.TextView
import fr.free.nrw.commons.R import fr.free.nrw.commons.R
import fr.free.nrw.commons.Utils
import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding import fr.free.nrw.commons.databinding.FragmentMediaLicenseBinding
import fr.free.nrw.commons.upload.UploadActivity import fr.free.nrw.commons.upload.UploadActivity
import fr.free.nrw.commons.upload.UploadBaseFragment import fr.free.nrw.commons.upload.UploadBaseFragment
import fr.free.nrw.commons.utils.DialogUtil.showAlertDialog 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 timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -126,20 +128,20 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
} }
override fun setSelectedLicense(license: String?) { 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 // Check if position is valid
if (position < 0) { if (position < 0) {
Timber.d("Invalid position: %d. Using default licenses", position) Timber.d("Invalid position: %d. Using default licenses", position)
position = licenses!!.size - 1 position = licenses!!.size - 1
} else {
Timber.d("Position: %d %s", position, getString(Utils.licenseNameFor(license)))
} }
binding.spinnerLicenseList.setSelection(position) binding.spinnerLicenseList.setSelection(position)
} }
override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) { override fun updateLicenseSummary(selectedLicense: String?, numberOfItems: Int) {
val licenseHyperLink = "<a href='" + Utils.licenseUrlFor(selectedLicense) + "'>" + if (selectedLicense == null) return
getString(Utils.licenseNameFor(selectedLicense)) + "</a><br>"
val licenseHyperLink = "<a href='" + selectedLicense.toLicenseUrl() + "'>" +
getString(selectedLicense.toLicenseName()) + "</a><br>"
setTextViewHTML( setTextViewHTML(
binding.tvShareLicenseSummary, resources binding.tvShareLicenseSummary, resources
@ -184,7 +186,7 @@ class MediaLicenseFragment : UploadBaseFragment(), MediaLicenseContract.View {
} }
private fun launchBrowser(hyperLink: String) = private fun launchBrowser(hyperLink: String) =
Utils.handleWebUrl(context, Uri.parse(hyperLink)) handleWebUrl(requireContext(), Uri.parse(hyperLink))
override fun onDestroyView() { override fun onDestroyView() {
presenter.onDetachView() presenter.onDetachView()

View file

@ -1,9 +1,9 @@
package fr.free.nrw.commons.upload.license 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.kvstore.JsonKvStore
import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.settings.Prefs import fr.free.nrw.commons.settings.Prefs
import fr.free.nrw.commons.utils.toLicenseName
import timber.log.Timber import timber.log.Timber
import java.lang.reflect.Method import java.lang.reflect.Method
import java.lang.reflect.Proxy import java.lang.reflect.Proxy
@ -34,12 +34,14 @@ class MediaLicensePresenter @Inject constructor(
val licenses = repository.getLicenses() val licenses = repository.getLicenses()
view.setLicenses(licenses) 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.DEFAULT_LICENSE,
Prefs.Licenses.CC_BY_SA_4 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 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) { } catch (exception: IllegalStateException) {
Timber.e(exception) Timber.e(exception)
selectedLicense = Prefs.Licenses.CC_BY_SA_4 selectedLicense = Prefs.Licenses.CC_BY_SA_4

View file

@ -54,7 +54,7 @@ interface UploadMediaDetailsContract {
fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem) fun showBadImagePopup(errorCode: Int, index: Int, uploadItem: UploadItem)
} }
interface UserActionListener : BasePresenter<View?> { interface UserActionListener : BasePresenter<View> {
fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore) fun setupBasicKvStoreFactory(factory: (String) -> BasicKvStore)
fun receiveImage( fun receiveImage(

View file

@ -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))
}
}

View file

@ -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
}

View file

@ -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))
}
}

View file

@ -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")
}

View file

@ -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
}

View file

@ -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)) }
}
}

View file

@ -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)
}

View file

@ -1,5 +1,6 @@
package fr.free.nrw.commons package fr.free.nrw.commons
import fr.free.nrw.commons.utils.getWikiLovesMonumentsYear
import org.junit.Test import org.junit.Test
import org.junit.jupiter.api.Assertions import org.junit.jupiter.api.Assertions
import java.util.Calendar import java.util.Calendar
@ -9,20 +10,20 @@ class UtilsTest {
fun wikiLovesMonumentsYearBeforeSeptember() { fun wikiLovesMonumentsYearBeforeSeptember() {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.set(2022, Calendar.FEBRUARY, 1) cal.set(2022, Calendar.FEBRUARY, 1)
Assertions.assertEquals(2021, Utils.getWikiLovesMonumentsYear(cal)) Assertions.assertEquals(2021, getWikiLovesMonumentsYear(cal))
} }
@Test @Test
fun wikiLovesMonumentsYearInSeptember() { fun wikiLovesMonumentsYearInSeptember() {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.set(2022, Calendar.SEPTEMBER, 1) cal.set(2022, Calendar.SEPTEMBER, 1)
Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal))
} }
@Test @Test
fun wikiLovesMonumentsYearAfterSeptember() { fun wikiLovesMonumentsYearAfterSeptember() {
val cal = Calendar.getInstance() val cal = Calendar.getInstance()
cal.set(2022, Calendar.DECEMBER, 1) cal.set(2022, Calendar.DECEMBER, 1)
Assertions.assertEquals(2022, Utils.getWikiLovesMonumentsYear(cal)) Assertions.assertEquals(2022, getWikiLovesMonumentsYear(cal))
} }
} }

View file

@ -248,13 +248,6 @@ class ContributionsFragmentUnitTests {
fragment.onDestroyView() fragment.onDestroyView()
} }
@Test
@Throws(Exception::class)
fun testShowMessage() {
Shadows.shadowOf(Looper.getMainLooper()).idle()
fragment.showMessage("")
}
@Test @Test
@Throws(Exception::class) @Throws(Exception::class)
fun testShowCampaigns() { fun testShowCampaigns() {

View file

@ -1,11 +1,12 @@
package fr.free.nrw.commons.upload package fr.free.nrw.commons.upload
import com.nhaarman.mockitokotlin2.verify 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.kvstore.JsonKvStore
import fr.free.nrw.commons.repository.UploadRepository import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.upload.license.MediaLicenseContract import fr.free.nrw.commons.upload.license.MediaLicenseContract
import fr.free.nrw.commons.upload.license.MediaLicensePresenter import fr.free.nrw.commons.upload.license.MediaLicensePresenter
import fr.free.nrw.commons.utils.toLicenseName
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
@ -25,7 +26,7 @@ import org.robolectric.RobolectricTestRunner
*/ */
@RunWith(RobolectricTestRunner::class) @RunWith(RobolectricTestRunner::class)
@PrepareForTest(Utils::class) @PrepareForTest(UnderlineUtils::class)
class MediaLicensePresenterTest { class MediaLicensePresenterTest {
@Mock @Mock
internal lateinit var repository: UploadRepository internal lateinit var repository: UploadRepository
@ -39,7 +40,7 @@ class MediaLicensePresenterTest {
@InjectMocks @InjectMocks
lateinit var mediaLicensePresenter: MediaLicensePresenter lateinit var mediaLicensePresenter: MediaLicensePresenter
private lateinit var mockedUtil: MockedStatic<Utils> private lateinit var mockedUtil: MockedStatic<UnderlineUtils>
/** /**
* initial setup test environemnt * initial setup test environemnt
@ -49,8 +50,7 @@ class MediaLicensePresenterTest {
fun setUp() { fun setUp() {
MockitoAnnotations.openMocks(this) MockitoAnnotations.openMocks(this)
mediaLicensePresenter.onAttachView(view) mediaLicensePresenter.onAttachView(view)
mockedUtil = Mockito.mockStatic(Utils::class.java) mockedUtil = Mockito.mockStatic(UnderlineUtils::class.java)
`when`(Utils.licenseNameFor(ArgumentMatchers.anyString())).thenReturn(1)
} }
@After @After

View file

@ -1,6 +1,5 @@
package fr.free.nrw.commons.utils package fr.free.nrw.commons.utils
import fr.free.nrw.commons.Utils.fixExtension
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test