mirror of
https://github.com/topjohnwu/Magisk.git
synced 2025-10-17 08:52:57 +00:00
Render Markdown natively
Stop using problematic WebView
This commit is contained in:
@@ -52,8 +52,8 @@ public class AboutActivity extends BaseActivity {
|
||||
BuildConfig.VERSION_NAME, BuildConfig.VERSION_CODE, getPackageName()));
|
||||
|
||||
appChangelog.setOnClickListener(v -> {
|
||||
new MarkDownWindow(this, getString(R.string.app_changelog),
|
||||
getResources().openRawResource(R.raw.changelog)).exec();
|
||||
MarkDownWindow.show(this, getString(R.string.app_changelog),
|
||||
getResources().openRawResource(R.raw.changelog));
|
||||
});
|
||||
|
||||
String translators = getString(R.string.translators);
|
||||
|
@@ -101,7 +101,7 @@ public class ReposAdapter extends SectionedAdapter<ReposAdapter.SectionHolder, R
|
||||
holder.updateTime.setText(context.getString(R.string.updated_on, repo.getLastUpdateString()));
|
||||
|
||||
holder.infoLayout.setOnClickListener(v ->
|
||||
new MarkDownWindow((BaseActivity) context, null, repo.getDetailUrl()).exec());
|
||||
MarkDownWindow.show((BaseActivity) context, null, repo.getDetailUrl()));
|
||||
|
||||
holder.downloadImage.setOnClickListener(v -> {
|
||||
new CustomAlertDialog((BaseActivity) context)
|
||||
|
@@ -41,7 +41,7 @@ public class MagiskInstallDialog extends CustomAlertDialog {
|
||||
// Open forum links in browser
|
||||
AppUtils.openLink(a, Uri.parse(Data.magiskNoteLink));
|
||||
} else {
|
||||
new MarkDownWindow(a, null, Data.magiskNoteLink).exec();
|
||||
MarkDownWindow.show(a, null, Data.magiskNoteLink);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@@ -21,8 +21,7 @@ public class ManagerInstallDialog extends CustomAlertDialog {
|
||||
setPositiveButton(R.string.install, (d, i) -> DownloadApp.upgrade(name));
|
||||
setNegativeButton(R.string.no_thanks, null);
|
||||
if (!TextUtils.isEmpty(Data.managerNoteLink)) {
|
||||
setNeutralButton(R.string.app_changelog, (d, i) ->
|
||||
new MarkDownWindow(a, null, Data.managerNoteLink).exec());
|
||||
setNeutralButton(R.string.app_changelog, (d, i) -> MarkDownWindow.show(a, null, Data.managerNoteLink));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -1,86 +1,107 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.webkit.WebView;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.AsyncTask;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
||||
import com.caverock.androidsvg.SVG;
|
||||
import com.caverock.androidsvg.SVGParseException;
|
||||
import com.topjohnwu.core.App;
|
||||
import com.topjohnwu.core.Data;
|
||||
import com.topjohnwu.core.tasks.ParallelTask;
|
||||
import com.topjohnwu.core.utils.Utils;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.net.Networking;
|
||||
import com.topjohnwu.net.ResponseListener;
|
||||
import com.topjohnwu.superuser.ShellUtils;
|
||||
|
||||
import org.commonmark.node.Node;
|
||||
import org.commonmark.parser.Parser;
|
||||
import org.commonmark.renderer.html.HtmlRenderer;
|
||||
import com.topjohnwu.utils.ByteArrayStream;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import ru.noties.markwon.Markwon;
|
||||
import ru.noties.markwon.SpannableConfiguration;
|
||||
import ru.noties.markwon.spans.AsyncDrawable;
|
||||
|
||||
public class MarkDownWindow extends ParallelTask<Void, Void, String> {
|
||||
public class MarkDownWindow {
|
||||
|
||||
private String mTitle;
|
||||
private String mUrl;
|
||||
private InputStream is;
|
||||
private static final SpannableConfiguration config = SpannableConfiguration.builder(App.self)
|
||||
.asyncDrawableLoader(new Loader()).build();
|
||||
|
||||
|
||||
public MarkDownWindow(Activity context, String title, String url) {
|
||||
super(context);
|
||||
mTitle = title;
|
||||
mUrl = url;
|
||||
public static void show(Activity activity, String title, String url) {
|
||||
Networking.get(url).getAsString(new Listener(activity, title));
|
||||
}
|
||||
|
||||
public MarkDownWindow(Activity context, String title, InputStream in) {
|
||||
super(context);
|
||||
mTitle = title;
|
||||
is = in;
|
||||
public static void show (Activity activity, String title, InputStream is) {
|
||||
try {
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
ShellUtils.pump(is, baos);
|
||||
new Listener(activity, title).onResponse(baos.toString());
|
||||
} catch (IOException ignored) {}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String doInBackground(Void... voids) {
|
||||
App app = App.self;
|
||||
String md;
|
||||
if (mUrl != null) {
|
||||
md = Utils.dlString(mUrl);
|
||||
} else {
|
||||
try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
ShellUtils.pump(is, out);
|
||||
md = out.toString();
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
static class Listener implements ResponseListener<String> {
|
||||
|
||||
Activity activity;
|
||||
String title;
|
||||
|
||||
Listener(Activity a, String t) {
|
||||
activity = a;
|
||||
title = t;
|
||||
}
|
||||
String css;
|
||||
try (InputStream in = app.getResources()
|
||||
.openRawResource(Data.isDarkTheme ? R.raw.dark : R.raw.light);
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream()) {
|
||||
ShellUtils.pump(in, out);
|
||||
css = out.toString();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
|
||||
@Override
|
||||
public void onResponse(String md) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(activity);
|
||||
alert.setTitle(title);
|
||||
View mv = LayoutInflater.from(activity).inflate(R.layout.markdown_window, null);
|
||||
Markwon.setMarkdown(mv.findViewById(R.id.md_txt), config, md);
|
||||
alert.setView(mv);
|
||||
alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
||||
alert.show();
|
||||
}
|
||||
Parser parser = Parser.builder().build();
|
||||
HtmlRenderer renderer = HtmlRenderer.builder().build();
|
||||
Node doc = parser.parse(md);
|
||||
return String.format("<style>%s</style>%s", css, renderer.render(doc));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(String html) {
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(getActivity());
|
||||
alert.setTitle(mTitle);
|
||||
static class Loader implements AsyncDrawable.Loader {
|
||||
|
||||
WebView wv = new WebView(getActivity());
|
||||
wv.loadDataWithBaseURL("fake://", html, "text/html", "UTF-8", null);
|
||||
@Override
|
||||
public void load(@NonNull String url, @NonNull AsyncDrawable asyncDrawable) {
|
||||
AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
|
||||
InputStream is = Networking.get(url).execForInputStream().getResult();
|
||||
if (is == null)
|
||||
return;
|
||||
ByteArrayStream buf = new ByteArrayStream();
|
||||
buf.readFrom(is);
|
||||
// First try default drawables
|
||||
Drawable drawable = Drawable.createFromStream(buf.getInputStream(), "");
|
||||
if (drawable == null) {
|
||||
// SVG
|
||||
try {
|
||||
SVG svg = SVG.getFromInputStream(buf.getInputStream());
|
||||
int width = Utils.dpInPx((int) svg.getDocumentWidth());
|
||||
int height = Utils.dpInPx((int) svg.getDocumentHeight());
|
||||
final Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_4444);
|
||||
final Canvas canvas = new Canvas(bitmap);
|
||||
float density = App.self.getResources().getDisplayMetrics().density;
|
||||
canvas.scale(density, density);
|
||||
svg.renderToCanvas(canvas);
|
||||
drawable = new BitmapDrawable(App.self.getResources(), bitmap);
|
||||
} catch (SVGParseException ignored) {}
|
||||
}
|
||||
if (drawable != null) {
|
||||
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
|
||||
asyncDrawable.setResult(drawable);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
alert.setView(wv);
|
||||
alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
||||
alert.show();
|
||||
@Override
|
||||
public void cancel(@NonNull String url) {}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user