making java kotlin since 2023(tm)

Signed-off-by: androidacy-user <opensource@androidacy.com>
pull/27/head
androidacy-user 2 years ago
parent 906a5cbab9
commit 0708c2f7a9

@ -1,254 +1,255 @@
package com.fox2code.mmm.markdown; package com.fox2code.mmm.markdown
import android.content.Intent; import android.content.DialogInterface
import android.content.pm.PackageManager; import android.content.pm.PackageManager
import android.content.res.Configuration; import android.content.res.Configuration
import android.graphics.Color; import android.graphics.Color
import android.os.Build; import android.os.Build
import android.os.Bundle; import android.os.Bundle
import android.view.View; import android.view.MenuItem
import android.view.ViewGroup; import android.view.View
import android.view.WindowManager; import android.view.ViewGroup
import android.widget.TextView; import android.view.WindowManager
import android.widget.Toast; import android.widget.TextView
import android.widget.Toast
import androidx.annotation.Nullable; import com.fox2code.foxcompat.app.FoxActivity
import com.fox2code.mmm.Constants
import com.fox2code.foxcompat.app.FoxActivity; import com.fox2code.mmm.MainApplication
import com.fox2code.mmm.Constants; import com.fox2code.mmm.R
import com.fox2code.mmm.MainApplication; import com.fox2code.mmm.XHooks
import com.fox2code.mmm.R; import com.fox2code.mmm.utils.IntentHelper
import com.fox2code.mmm.XHooks; import com.fox2code.mmm.utils.io.net.Http
import com.fox2code.mmm.utils.IntentHelper; import com.google.android.material.bottomnavigation.BottomNavigationView
import com.fox2code.mmm.utils.io.net.Http; import com.google.android.material.chip.Chip
import com.google.android.material.bottomnavigation.BottomNavigationView; import com.google.android.material.chip.ChipGroup
import com.google.android.material.chip.Chip; import com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.google.android.material.chip.ChipGroup; import com.topjohnwu.superuser.internal.UiThreadHandler
import com.google.android.material.dialog.MaterialAlertDialogBuilder; import org.matomo.sdk.extra.TrackHelper
import com.topjohnwu.superuser.internal.UiThreadHandler; import timber.log.Timber
import java.io.IOException
import org.matomo.sdk.extra.TrackHelper; import java.nio.charset.StandardCharsets
import java.io.IOException; class MarkdownActivity : FoxActivity() {
import java.nio.charset.StandardCharsets; private var header: TextView? = null
import java.util.HashMap; private var footer: TextView? = null
import java.util.Objects; override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
import timber.log.Timber; TrackHelper.track().screen(this).with(MainApplication.getINSTANCE().tracker)
setDisplayHomeAsUpEnabled(true)
public class MarkdownActivity extends FoxActivity { val intent = this.intent
private static final HashMap<String, String> redirects = new HashMap<>(4);
private static final String[] variants = new String[]{"readme.md", "README.MD", ".github/README.md"};
private TextView header;
private TextView footer;
private static byte[] getRawMarkdown(String url) throws IOException {
String newUrl = redirects.get(url);
if (newUrl != null && !newUrl.equals(url)) {
return Http.doHttpGet(newUrl, true);
}
try {
return Http.doHttpGet(url, true);
} catch (IOException e) {
// Workaround GitHub README.md case sensitivity issue
if (url.startsWith("https://raw.githubusercontent.com/") && url.endsWith("/README.md")) {
String prefix = url.substring(0, url.length() - 9);
for (String suffix : variants) {
newUrl = prefix + suffix;
try { // Try with lowercase version
byte[] rawMarkdown = Http.doHttpGet(prefix + suffix, true);
redirects.put(url, newUrl); // Avoid retries
return rawMarkdown;
} catch (IOException ignored) {
}
}
}
throw e;
}
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
TrackHelper.track().screen(this).with(MainApplication.getINSTANCE().getTracker());
this.setDisplayHomeAsUpEnabled(true);
Intent intent = this.getIntent();
if (!MainApplication.checkSecret(intent)) { if (!MainApplication.checkSecret(intent)) {
Timber.e("Impersonation detected!"); Timber.e("Impersonation detected!")
this.forceBackPressed(); forceBackPressed()
return; return
} }
String url = Objects.requireNonNull(intent.getExtras()).getString(Constants.EXTRA_MARKDOWN_URL); val url = intent.extras?.getString(Constants.EXTRA_MARKDOWN_URL)
String title = intent.getExtras().getString(Constants.EXTRA_MARKDOWN_TITLE); var title = intent.extras!!.getString(Constants.EXTRA_MARKDOWN_TITLE)
String config = intent.getExtras().getString(Constants.EXTRA_MARKDOWN_CONFIG); val config = intent.extras!!.getString(Constants.EXTRA_MARKDOWN_CONFIG)
boolean change_boot = intent.getExtras().getBoolean(Constants.EXTRA_MARKDOWN_CHANGE_BOOT); val changeBoot = intent.extras!!.getBoolean(Constants.EXTRA_MARKDOWN_CHANGE_BOOT)
boolean needs_ramdisk = intent.getExtras().getBoolean(Constants.EXTRA_MARKDOWN_NEEDS_RAMDISK); val needsRamdisk = intent.extras!!.getBoolean(Constants.EXTRA_MARKDOWN_NEEDS_RAMDISK)
int min_magisk = intent.getExtras().getInt(Constants.EXTRA_MARKDOWN_MIN_MAGISK); val minMagisk = intent.extras!!.getInt(Constants.EXTRA_MARKDOWN_MIN_MAGISK)
int min_api = intent.getExtras().getInt(Constants.EXTRA_MARKDOWN_MIN_API); val minApi = intent.extras!!.getInt(Constants.EXTRA_MARKDOWN_MIN_API)
int max_api = intent.getExtras().getInt(Constants.EXTRA_MARKDOWN_MAX_API); val maxApi = intent.extras!!.getInt(Constants.EXTRA_MARKDOWN_MAX_API)
if (title != null && !title.isEmpty()) { if (!title.isNullOrEmpty()) {
this.setTitle(title); this.title = title
} } else {
setActionBarBackground(null); @Suppress("UNUSED_VALUE")
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0); title = url
if (config != null && !config.isEmpty()) { }
String configPkg = IntentHelper.getPackageOfConfig(config); setActionBarBackground(null)
@Suppress("DEPRECATION")
this.window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION, 0)
if (!config.isNullOrEmpty()) {
val configPkg = IntentHelper.getPackageOfConfig(config)
try { try {
XHooks.checkConfigTargetExists(this, configPkg, config); XHooks.checkConfigTargetExists(this, configPkg, config)
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24, menu -> { this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24) { _: MenuItem? ->
IntentHelper.openConfig(this, config); IntentHelper.openConfig(this, config)
return true; true
}); }
} catch (PackageManager.NameNotFoundException e) { } catch (e: PackageManager.NameNotFoundException) {
Timber.w("Config package \"" + configPkg + "\" missing for markdown view"); Timber.w("Config package \"$configPkg\" missing for markdown view")
} }
} }
// validate the url won't crash the app // validate the url won't crash the app
if (url == null || url.isEmpty() || url.contains("..")) { if (url.isNullOrEmpty() || url.contains("..")) {
Timber.e("Invalid url %s", String.valueOf(url)); Timber.e("Invalid url %s", url.toString())
this.forceBackPressed(); forceBackPressed()
return; return
} }
//noinspection UnnecessaryCallToStringValueOf Timber.i("Url for markdown %s", url.toString())
Timber.i("Url for markdown %s", String.valueOf(url)); setContentView(R.layout.markdown_view)
setContentView(R.layout.markdown_view); val markdownBackground = findViewById<ViewGroup>(R.id.markdownBackground)
final ViewGroup markdownBackground = findViewById(R.id.markdownBackground); val textView = findViewById<TextView>(R.id.markdownView)
final TextView textView = findViewById(R.id.markdownView); header = findViewById(R.id.markdownHeader)
this.header = findViewById(R.id.markdownHeader); footer = findViewById(R.id.markdownFooter)
this.footer = findViewById(R.id.markdownFooter); updateBlurState()
this.updateBlurState(); UiThreadHandler.handler.post { // Fix header/footer height
UiThreadHandler.handler.post(() -> // Fix header/footer height this.updateScreenInsets(this.resources.configuration)
this.updateScreenInsets(this.getResources().getConfiguration())); }
// Really bad created (MSG by Der_Googler) // Really bad created (MSG by Der_Googler)
// set "message" to null to disable dialog // set "message" to null to disable dialog
if (change_boot) this.addChip(MarkdownChip.CHANGE_BOOT); if (changeBoot) this.addChip(MarkdownChip.CHANGE_BOOT)
if (needs_ramdisk) this.addChip(MarkdownChip.NEED_RAMDISK); if (needsRamdisk) this.addChip(MarkdownChip.NEED_RAMDISK)
if (min_magisk != 0) this.addChip(MarkdownChip.MIN_MAGISK, String.valueOf(min_magisk)); if (minMagisk != 0) this.addChip(MarkdownChip.MIN_MAGISK, minMagisk.toString())
if (min_api != 0) this.addChip(MarkdownChip.MIN_SDK, parseAndroidVersion(min_api)); if (minApi != 0) this.addChip(MarkdownChip.MIN_SDK, parseAndroidVersion(minApi))
if (max_api != 0) this.addChip(MarkdownChip.MAX_SDK, parseAndroidVersion(max_api)); if (maxApi != 0) this.addChip(MarkdownChip.MAX_SDK, parseAndroidVersion(maxApi))
Thread({
new Thread(() -> {
try { try {
Timber.i("Downloading"); Timber.i("Downloading")
byte[] rawMarkdown = getRawMarkdown(url); val rawMarkdown = getRawMarkdown(url)
Timber.i("Encoding"); Timber.i("Encoding")
String markdown = new String(rawMarkdown, StandardCharsets.UTF_8); val markdown = String(rawMarkdown, StandardCharsets.UTF_8)
Timber.i("Done!"); Timber.i("Done!")
runOnUiThread(() -> { runOnUiThread {
findViewById(R.id.markdownFooter).setMinimumHeight(this.getNavigationBarHeight()); footer?.minimumHeight = this.navigationBarHeight
MainApplication.getINSTANCE().getMarkwon().setMarkdown(textView, MarkdownUrlLinker.urlLinkify(markdown)); MainApplication.getINSTANCE().markwon.setMarkdown(
textView,
MarkdownUrlLinker.urlLinkify(markdown)
)
if (markdownBackground != null) { if (markdownBackground != null) {
markdownBackground.setClickable(true); markdownBackground.isClickable = true
}
} }
}); } catch (e: Exception) {
} catch (Exception e) { Timber.e(e)
Timber.e(e); runOnUiThread {
runOnUiThread(() -> Toast.makeText(this, R.string.failed_download, Toast.LENGTH_SHORT).show()); Toast.makeText(this, R.string.failed_download, Toast.LENGTH_SHORT).show()
} }
}, "Markdown load thread").start(); }
}, "Markdown load thread").start()
} }
private void updateBlurState() { private fun updateBlurState() {
if (MainApplication.isBlurEnabled()) { if (MainApplication.isBlurEnabled()) {
// set bottom navigation bar color to transparent blur // set bottom navigation bar color to transparent blur
BottomNavigationView bottomNavigationView = findViewById(R.id.bottom_navigation); val bottomNavigationView = findViewById<BottomNavigationView>(R.id.bottom_navigation)
if (bottomNavigationView != null) { if (bottomNavigationView != null) {
bottomNavigationView.setBackgroundColor(Color.TRANSPARENT); bottomNavigationView.setBackgroundColor(Color.TRANSPARENT)
bottomNavigationView.setAlpha(0.8F); bottomNavigationView.alpha = 0.8f
} else { } else {
Timber.w("Bottom navigation view not found"); Timber.w("Bottom navigation view not found")
} }
// set dialogs to have transparent blur // set dialogs to have transparent blur
getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND); window.addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND)
} }
} }
private void updateScreenInsets() { private fun updateScreenInsets() {
this.runOnUiThread(() -> this.updateScreenInsets(this.getResources().getConfiguration())); runOnUiThread { this.updateScreenInsets(this.resources.configuration) }
} }
private void updateScreenInsets(Configuration configuration) { private fun updateScreenInsets(configuration: Configuration) {
boolean landscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE; val landscape = configuration.orientation == Configuration.ORIENTATION_LANDSCAPE
int bottomInset = (landscape ? 0 : this.getNavigationBarHeight()); val bottomInset = if (landscape) 0 else this.navigationBarHeight
int statusBarHeight = getStatusBarHeight(); val statusBarHeight = statusBarHeight
int actionBarHeight = getActionBarHeight(); val actionBarHeight = actionBarHeight
int combinedBarsHeight = statusBarHeight + actionBarHeight; val combinedBarsHeight = statusBarHeight + actionBarHeight
this.header.setMinHeight(combinedBarsHeight); header!!.minHeight = combinedBarsHeight
this.footer.setMinHeight(bottomInset); footer!!.minHeight = bottomInset
//this.actionBarBlur.invalidate(); //this.actionBarBlur.invalidate();
} }
@Override override fun refreshUI() {
public void refreshUI() { super.refreshUI()
super.refreshUI(); this.updateScreenInsets()
this.updateScreenInsets(); updateBlurState()
this.updateBlurState();
} }
@Override override fun onWindowUpdated() {
protected void onWindowUpdated() { this.updateScreenInsets()
this.updateScreenInsets();
} }
private void addChip(MarkdownChip markdownChip) { private fun addChip(markdownChip: MarkdownChip) {
this.makeChip(this.getString(markdownChip.title), markdownChip.desc == 0 ? null : this.getString(markdownChip.desc)); makeChip(
this.getString(markdownChip.title),
if (markdownChip.desc == 0) null else this.getString(markdownChip.desc)
)
} }
private void addChip(MarkdownChip markdownChip, String extra) { private fun addChip(markdownChip: MarkdownChip, extra: String) {
String title = this.getString(markdownChip.title); var title = this.getString(markdownChip.title)
if (title.contains("%s")) { title = if (title.contains("%s")) {
title = title.replace("%s", extra); title.replace("%s", extra)
} else { } else {
title = title + " " + extra; "$title $extra"
} }
this.makeChip(title, markdownChip.desc == 0 ? null : this.getString(markdownChip.desc)); makeChip(title, if (markdownChip.desc == 0) null else this.getString(markdownChip.desc))
} }
private void makeChip(String title, String message) { private fun makeChip(title: String, message: String?) {
final ChipGroup chip_group_holder = findViewById(R.id.chip_group_holder); val chipGroupHolder = findViewById<ChipGroup>(R.id.chip_group_holder)
Chip chip = new Chip(this); val chip = Chip(this)
chip.setText(title); chip.text = title
chip.setVisibility(View.VISIBLE); chip.visibility = View.VISIBLE
if (message != null) { if (message != null) {
chip.setOnClickListener(_view -> { chip.setOnClickListener { _: View? ->
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this); val builder = MaterialAlertDialogBuilder(this)
builder.setTitle(title).setMessage(message).setCancelable(true)
builder.setTitle(title).setMessage(message).setCancelable(true).setPositiveButton(R.string.ok, (x, y) -> x.dismiss()).show(); .setPositiveButton(R.string.ok) { x: DialogInterface, _: Int -> x.dismiss() }
.show()
}); }
}
chipGroupHolder.addView(chip)
}
private fun parseAndroidVersion(version: Int): String {
return when (version) {
Build.VERSION_CODES.JELLY_BEAN -> "4.1 JellyBean"
Build.VERSION_CODES.JELLY_BEAN_MR1 -> "4.2 JellyBean"
Build.VERSION_CODES.JELLY_BEAN_MR2 -> "4.3 JellyBean"
Build.VERSION_CODES.KITKAT -> "4.4 KitKat"
Build.VERSION_CODES.KITKAT_WATCH -> "4.4 KitKat Watch"
Build.VERSION_CODES.LOLLIPOP -> "5.0 Lollipop"
Build.VERSION_CODES.LOLLIPOP_MR1 -> "5.1 Lollipop"
Build.VERSION_CODES.M -> "6.0 Marshmallow"
Build.VERSION_CODES.N -> "7.0 Nougat"
Build.VERSION_CODES.N_MR1 -> "7.1 Nougat"
Build.VERSION_CODES.O -> "8.0 Oreo"
Build.VERSION_CODES.O_MR1 -> "8.1 Oreo"
Build.VERSION_CODES.P -> "9.0 Pie"
Build.VERSION_CODES.Q -> "10 (Q)"
Build.VERSION_CODES.R -> "11 (R)"
Build.VERSION_CODES.S -> "12 (S)"
Build.VERSION_CODES.S_V2 -> "12L"
Build.VERSION_CODES.TIRAMISU -> "13 Tiramisu"
else -> "Sdk: $version"
}
}
override fun onResume() {
super.onResume()
val footer = findViewById<View>(R.id.markdownFooter)
if (footer != null) footer.minimumHeight = this.navigationBarHeight
}
companion object {
private val redirects = HashMap<String, String>(4)
private val variants = arrayOf("readme.md", "README.MD", ".github/README.md")
@Throws(IOException::class)
private fun getRawMarkdown(url: String): ByteArray {
var newUrl = redirects[url]
return if (newUrl != null && newUrl != url) {
Http.doHttpGet(newUrl, true)
} else try {
Http.doHttpGet(url, true)
} catch (e: IOException) {
// Workaround GitHub README.md case sensitivity issue
if (url.startsWith("https://raw.githubusercontent.com/") && url.endsWith("/README.md")) {
val prefix = url.substring(0, url.length - 9)
for (suffix in variants) {
newUrl = prefix + suffix
try { // Try with lowercase version
val rawMarkdown = Http.doHttpGet(prefix + suffix, true)
redirects[url] = newUrl // Avoid retries
return rawMarkdown
} catch (ignored: IOException) {
} }
chip_group_holder.addView(chip);
} }
private String parseAndroidVersion(int version) {
return switch (version) {
case Build.VERSION_CODES.JELLY_BEAN -> "4.1 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR1 -> "4.2 JellyBean";
case Build.VERSION_CODES.JELLY_BEAN_MR2 -> "4.3 JellyBean";
case Build.VERSION_CODES.KITKAT -> "4.4 KitKat";
case Build.VERSION_CODES.KITKAT_WATCH -> "4.4 KitKat Watch";
case Build.VERSION_CODES.LOLLIPOP -> "5.0 Lollipop";
case Build.VERSION_CODES.LOLLIPOP_MR1 -> "5.1 Lollipop";
case Build.VERSION_CODES.M -> "6.0 Marshmallow";
case Build.VERSION_CODES.N -> "7.0 Nougat";
case Build.VERSION_CODES.N_MR1 -> "7.1 Nougat";
case Build.VERSION_CODES.O -> "8.0 Oreo";
case Build.VERSION_CODES.O_MR1 -> "8.1 Oreo";
case Build.VERSION_CODES.P -> "9.0 Pie";
case Build.VERSION_CODES.Q -> "10 (Q)";
case Build.VERSION_CODES.R -> "11 (R)";
case Build.VERSION_CODES.S -> "12 (S)";
case Build.VERSION_CODES.S_V2 -> "12L";
case Build.VERSION_CODES.TIRAMISU -> "13 Tiramisu";
default -> "Sdk: " + version;
};
} }
throw e
@Override }
protected void onResume() { }
super.onResume();
View footer = findViewById(R.id.markdownFooter);
if (footer != null) footer.setMinimumHeight(this.getNavigationBarHeight());
} }
} }

@ -1,19 +1,21 @@
package com.fox2code.mmm.markdown; package com.fox2code.mmm.markdown
import androidx.annotation.StringRes; import androidx.annotation.StringRes
import com.fox2code.mmm.R
import com.fox2code.mmm.R; enum class MarkdownChip(
@field:StringRes @param:StringRes val title: Int,
public enum MarkdownChip { @field:StringRes @param:StringRes val desc: Int
CHANGE_BOOT(R.string.module_can_change_boot, R.string.module_can_change_boot_desc), ) {
NEED_RAMDISK(R.string.module_needs_ramdisk, R.string.module_needs_ramdisk_desc), CHANGE_BOOT(
MIN_MAGISK(R.string.module_min_magisk_chip, 0), R.string.module_can_change_boot,
MIN_SDK(R.string.module_min_sdk_chip, 0), R.string.module_can_change_boot_desc
MAX_SDK(R.string.module_max_sdk_chip, 0); ),
@StringRes public final int title, desc; NEED_RAMDISK(
R.string.module_needs_ramdisk, R.string.module_needs_ramdisk_desc
MarkdownChip(@StringRes int title,@StringRes int desc) { ),
this.title = title; MIN_MAGISK(R.string.module_min_magisk_chip, 0), MIN_SDK(
this.desc = desc; R.string.module_min_sdk_chip, 0
} ),
MAX_SDK(R.string.module_max_sdk_chip, 0)
} }

@ -1,57 +1,51 @@
package com.fox2code.mmm.markdown; package com.fox2code.mmm.markdown
import java.util.ArrayList; import timber.log.Timber
import timber.log.Timber; enum class MarkdownUrlLinker {
public enum MarkdownUrlLinker {
; ;
public static String urlLinkify(String url) { private class LinkifyTask(val start: Int, val end: Int) {
int index = url.indexOf("https://"); companion object {
if (index == -1) val NULL = LinkifyTask(0, 0)
return url; }
ArrayList<LinkifyTask> linkifyTasks = new ArrayList<>(); }
int extra = 0;
companion object {
@JvmStatic
fun urlLinkify(url: String): String {
var index = url.indexOf("https://")
if (index == -1) return url
val linkifyTasks = ArrayList<LinkifyTask>()
var extra = 0
while (index != -1) { while (index != -1) {
int end = url.indexOf(' ', index); var end = url.indexOf(' ', index)
end = end == -1 ? url.indexOf('\n', index) : Math.min(url.indexOf('\n', index), end); end = if (end == -1) url.indexOf('\n', index) else url.indexOf('\n', index)
if (end == -1) .coerceAtMost(end)
end = url.length(); if (end == -1) end = url.length
if (index == 0 || '\n' == url.charAt(index - 1) || ' ' == url.charAt(index - 1)) { if (index == 0 || '\n' == url[index - 1] || ' ' == url[index - 1]) {
int endDomain = url.indexOf('/', index + 9); val endDomain = url.indexOf('/', index + 9)
char endCh = url.charAt(end - 1); val endCh = url[end - 1]
if (endDomain != -1 && endDomain < end && endCh != '>' && endCh != ')' && endCh != ']') { if (endDomain != -1 && endDomain < end && endCh != '>' && endCh != ')' && endCh != ']') {
linkifyTasks.add(new LinkifyTask(index, end)); linkifyTasks.add(LinkifyTask(index, end))
extra += (end - index) + 4; extra += end - index + 4
Timber.d("Linkify url: %s", url.substring(end)); Timber.d("Linkify url: %s", url.substring(end))
} }
} }
index = url.indexOf("https://", end); index = url.indexOf("https://", end)
} }
if (linkifyTasks.isEmpty()) if (linkifyTasks.isEmpty()) return url
return url; var prev = LinkifyTask.NULL
LinkifyTask prev = LinkifyTask.NULL; val stringBuilder = StringBuilder(url.length + extra)
StringBuilder stringBuilder = new StringBuilder(url.length() + extra); for (linkifyTask in linkifyTasks) {
for (LinkifyTask linkifyTask : linkifyTasks) { stringBuilder.append(url, prev.end, linkifyTask.start).append('[')
stringBuilder.append(url, prev.end, linkifyTask.start).append('[').append(url, linkifyTask.start, linkifyTask.end).append("](").append(url, linkifyTask.start, linkifyTask.end).append(')'); .append(url, linkifyTask.start, linkifyTask.end).append("](")
prev = linkifyTask; .append(url, linkifyTask.start, linkifyTask.end).append(')')
prev = linkifyTask
} }
if (prev.end != url.length()) if (prev.end != url.length) stringBuilder.append(url, prev.end, url.length)
stringBuilder.append(url, prev.end, url.length()); Timber.i("Added Markdown link to " + linkifyTasks.size + " urls")
Timber.i("Added Markdown link to " + linkifyTasks.size() + " urls"); return stringBuilder.toString()
return stringBuilder.toString();
}
private static class LinkifyTask {
static final LinkifyTask NULL = new LinkifyTask(0, 0);
private final int start;
private final int end;
private LinkifyTask(int start, int end) {
this.start = start;
this.end = end;
} }
} }
} }
Loading…
Cancel
Save