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

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