diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 05dd8ce5..2a1107f8 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -206,11 +206,12 @@ dependencies { implementation("androidx.browser:browser:1.3.0") implementation("androidx.constraintlayout:constraintlayout:2.0.4") implementation("androidx.core:core:1.5.0") - implementation("androidx.fragment:fragment:1.3.4") + implementation("androidx.fragment:fragment:1.3.5") implementation("androidx.navigation:navigation-fragment:$navVersion") implementation("androidx.navigation:navigation-ui:$navVersion") implementation("androidx.recyclerview:recyclerview:1.2.1") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") + implementation("androidx.webkit:webkit:1.4.0") implementation("com.caverock:androidsvg-aar:1.4") implementation("com.github.bumptech.glide:glide:$glideVersion") implementation("com.github.bumptech.glide:okhttp3-integration:$glideVersion") @@ -230,13 +231,6 @@ dependencies { implementation("dev.rikka.rikkax.widget:borderview:1.0.1") implementation("dev.rikka.rikkax.widget:switchbar:1.0.2") implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.0.1") - implementation("io.noties.markwon:core:$markwonVersion") - implementation("io.noties.markwon:ext-strikethrough:$markwonVersion") - implementation("io.noties.markwon:ext-tables:$markwonVersion") - implementation("io.noties.markwon:ext-tasklist:$markwonVersion") - implementation("io.noties.markwon:html:$markwonVersion") - implementation("io.noties.markwon:image-glide:$markwonVersion") - implementation("io.noties.markwon:linkify:$markwonVersion") implementation("me.zhanghai.android.appiconloader:appiconloader-glide:1.3.1") implementation("me.zhanghai.android.fastscroll:library:1.1.6") implementation("org.lsposed.hiddenapibypass:hiddenapibypass:2.0") diff --git a/app/src/main/java/org/lsposed/manager/App.java b/app/src/main/java/org/lsposed/manager/App.java index 4ada2d4e..78e59603 100644 --- a/app/src/main/java/org/lsposed/manager/App.java +++ b/app/src/main/java/org/lsposed/manager/App.java @@ -42,6 +42,7 @@ import java.io.StringWriter; import java.util.Locale; import okhttp3.Cache; +import okhttp3.MediaType; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; import rikka.material.app.DayNightDelegate; @@ -62,6 +63,10 @@ public class App extends Application { private static Cache okHttpCache; private SharedPreferences pref; + public static final MediaType JSON + = MediaType.get("application/json; charset=utf-8"); + + public static App getInstance() { return instance; } diff --git a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java index ac259934..d1e1e22b 100644 --- a/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java +++ b/app/src/main/java/org/lsposed/manager/ui/fragment/RepoItemFragment.java @@ -19,16 +19,25 @@ package org.lsposed.manager.ui.fragment; +import android.content.Intent; +import android.content.res.Configuration; +import android.graphics.Color; import android.os.Bundle; import android.text.Spannable; import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.style.ClickableSpan; -import android.text.util.Linkify; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.webkit.RenderProcessGoneDetail; +import android.webkit.WebResourceRequest; +import android.webkit.WebResourceResponse; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.webkit.WebViewClient; +import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; @@ -37,12 +46,16 @@ import androidx.appcompat.app.AlertDialog; import androidx.lifecycle.Lifecycle; import androidx.recyclerview.widget.RecyclerView; import androidx.viewpager2.widget.ViewPager2; +import androidx.webkit.WebSettingsCompat; +import androidx.webkit.WebViewFeature; import com.google.android.material.button.MaterialButton; import com.google.android.material.progressindicator.CircularProgressIndicator; import com.google.android.material.snackbar.Snackbar; import com.google.android.material.tabs.TabLayoutMediator; +import org.json.JSONObject; +import org.lsposed.manager.App; import org.lsposed.manager.R; import org.lsposed.manager.databinding.FragmentPagerBinding; import org.lsposed.manager.databinding.ItemRepoLoadmoreBinding; @@ -56,25 +69,15 @@ import org.lsposed.manager.repo.model.OnlineModule; import org.lsposed.manager.repo.model.Release; import org.lsposed.manager.repo.model.ReleaseAsset; import org.lsposed.manager.ui.widget.LinkifyTextView; -import org.lsposed.manager.util.GlideApp; import org.lsposed.manager.util.LinearLayoutManagerFix; import org.lsposed.manager.util.NavUtil; import org.lsposed.manager.util.chrome.CustomTabsURLSpan; -import org.lsposed.manager.util.chrome.LinkTransformationMethod; +import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; -import io.noties.markwon.Markwon; -import io.noties.markwon.SoftBreakAddsNewLinePlugin; -import io.noties.markwon.ext.strikethrough.StrikethroughPlugin; -import io.noties.markwon.ext.tables.TablePlugin; -import io.noties.markwon.ext.tasklist.TaskListPlugin; -import io.noties.markwon.html.HtmlPlugin; -import io.noties.markwon.image.glide.GlideImagesPlugin; -import io.noties.markwon.linkify.LinkifyPlugin; -import io.noties.markwon.utils.NoCopySpannableFactory; import rikka.recyclerview.RecyclerViewKt; import rikka.widget.borderview.BorderNestedScrollView; import rikka.widget.borderview.BorderRecyclerView; @@ -82,7 +85,6 @@ import rikka.widget.borderview.BorderView; public class RepoItemFragment extends BaseFragment implements RepoLoader.Listener { FragmentPagerBinding binding; - private Markwon markwon; private OnlineModule module; private ReleaseAdapter releaseAdapter; @@ -121,20 +123,69 @@ public class RepoItemFragment extends BaseFragment implements RepoLoader.Listene RepoLoader.getInstance().addListener(this); super.onCreate(savedInstanceState); - markwon = Markwon.builder(requireActivity()) - .usePlugin(StrikethroughPlugin.create()) - .usePlugin(TablePlugin.create(requireActivity())) - .usePlugin(TaskListPlugin.create(requireActivity())) - .usePlugin(HtmlPlugin.create()) - .usePlugin(GlideImagesPlugin.create(GlideApp.with(this))) - .usePlugin(LinkifyPlugin.create(Linkify.WEB_URLS, true)) - .usePlugin(SoftBreakAddsNewLinePlugin.create()) - .build(); String modulePackageName = getArguments().getString("modulePackageName"); module = RepoLoader.getInstance().getOnlineModule(modulePackageName); } + private void renderGithubMarkdown(WebView view, String text, ProgressBar progressBar) { + try { + var json = new JSONObject(); + json.put("text", text); + view.setBackgroundColor(Color.TRANSPARENT); + var setting = view.getSettings(); + setting.setDomStorageEnabled(true); + setting.setAppCacheEnabled(true); + setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); + setting.setAllowContentAccess(false); + setting.setAllowFileAccess(false); + setting.setGeolocationEnabled(false); + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK)) { + int nightModeFlags = getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK; + if (nightModeFlags == Configuration.UI_MODE_NIGHT_YES) { + WebSettingsCompat.setForceDark(setting, WebSettingsCompat.FORCE_DARK_ON); + } + } + if (WebViewFeature.isFeatureSupported(WebViewFeature.FORCE_DARK_STRATEGY)) { + WebSettingsCompat.setForceDarkStrategy(setting, WebSettingsCompat.DARK_STRATEGY_PREFER_WEB_THEME_OVER_USER_AGENT_DARKENING); + } + setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + view.setWebViewClient(new WebViewClient() { + @Override + public void onPageFinished(WebView view, String url) { + if (progressBar != null) + progressBar.setVisibility(ProgressBar.GONE); + super.onPageFinished(view, url); + } + + @Override + public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) { + if (request.getUrl().toString().equals("https://api.github.com/markdown")) return false; + Intent i = new Intent(Intent.ACTION_VIEW, request.getUrl()); + startActivity(i); + return true; + } + }); + + // wrapper for https://api.github.com/markdown + /* + async function handleRequest(request) { + let response = await fetch("https://api.github.com/markdown", request); + if (!response.ok) return response; + response = new Response(`
${await response.text()}`, response); + response.headers.set( + "Content-Security-Policy", + "img-src *;" + ) + return response; + } + */ + view.postUrl("https://render.lsposed.workers.dev/", json.toString().getBytes(StandardCharsets.UTF_8)); + } catch (Throwable e) { + android.util.Log.e(App.TAG, "render readme", e); + } + } + @Override public boolean onOptionsItemSelected(@NonNull MenuItem item) { int id = item.getItemId(); @@ -306,10 +357,8 @@ public class RepoItemFragment extends BaseFragment implements RepoLoader.Listene } else { Release release = items.get(position); holder.title.setText(release.getName()); - holder.description.setTransformationMethod(new LinkTransformationMethod(requireActivity())); - holder.description.setSpannableFactory(NoCopySpannableFactory.getInstance()); - markwon.setMarkdown(holder.description, release.getDescription()); - holder.description.setMovementMethod(null); + holder.progress.show(); + renderGithubMarkdown(holder.description, release.getDescription(), holder.progress); holder.openInBrowser.setOnClickListener(v -> NavUtil.startURL(requireActivity(), release.getUrl())); List