You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
FoxMagiskModuleManager/app/src/main/java/com/fox2code/mmm/markdown/MarkdownActivity.java

326 lines
13 KiB
Java

package com.fox2code.mmm.markdown;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.widget.HorizontalScrollView;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.core.graphics.ColorUtils;
import com.fox2code.mmm.Constants;
import com.fox2code.mmm.MainApplication;
import com.fox2code.mmm.R;
import com.fox2code.mmm.XHooks;
import com.fox2code.mmm.compat.CompatActivity;
import com.fox2code.mmm.compat.CompatDisplay;
import com.fox2code.mmm.utils.Http;
import com.fox2code.mmm.utils.IntentHelper;
import com.google.android.material.chip.Chip;
import com.google.android.material.chip.ChipGroup;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.topjohnwu.superuser.Shell;
import com.topjohnwu.superuser.internal.UiThreadHandler;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import eightbitlab.com.blurview.BlurView;
import eightbitlab.com.blurview.RenderScriptBlur;
public class MarkdownActivity extends CompatActivity {
private static final String TAG = "MarkdownActivity";
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 BlurView chipHolder;
private TextView actionBarPadding;
private BlurView actionBarBlur;
private ColorDrawable anyBarBackground;
private ScrollView scrollView;
private LinearLayout md_layout;
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);
this.setDisplayHomeAsUpEnabled(true);
Intent intent = this.getIntent();
if (!MainApplication.checkSecret(intent)) {
Log.e(TAG, "Impersonation detected!");
this.forceBackPressed();
return;
}
String url = intent.getExtras()
.getString(Constants.EXTRA_MARKDOWN_URL);
String title = intent.getExtras()
.getString(Constants.EXTRA_MARKDOWN_TITLE);
String config = intent.getExtras()
.getString(Constants.EXTRA_MARKDOWN_CONFIG);
boolean change_boot = intent.getExtras()
.getBoolean(Constants.EXTRA_MARKDOWN_CHANGE_BOOT);
boolean needs_ramdisk = intent.getExtras()
.getBoolean(Constants.EXTRA_MARKDOWN_NEEDS_RAMDISK);
int min_magisk = intent.getExtras()
.getInt(Constants.EXTRA_MARKDOWN_MIN_MAGISK);
int min_api = intent.getExtras()
.getInt(Constants.EXTRA_MARKDOWN_MIN_API);
int max_api = intent.getExtras()
.getInt(Constants.EXTRA_MARKDOWN_MAX_API);
if (title != null && !title.isEmpty()) {
this.setTitle(title);
}
setActionBarBackground(null);
this.getWindow().setFlags(
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION,
WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
if (config != null && !config.isEmpty()) {
String configPkg = IntentHelper.getPackageOfConfig(config);
try {
XHooks.checkConfigTargetExists(this, configPkg, config);
this.setActionBarExtraMenuButton(R.drawable.ic_baseline_app_settings_alt_24, menu -> {
IntentHelper.openConfig(this, config);
return true;
});
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Config package \"" +
configPkg + "\" missing for markdown view");
}
}
Log.i(TAG, "Url for markdown " + url);
setContentView(R.layout.markdown_view);
final ViewGroup markdownBackground = findViewById(R.id.markdownBackground);
final TextView textView = findViewById(R.id.markdownView);
this.chipHolder = findViewById(R.id.chip_holder);
this.anyBarBackground = new ColorDrawable(Color.TRANSPARENT);
this.actionBarPadding = findViewById(R.id.markdown_action_bar_padding);
this.actionBarBlur = findViewById(R.id.markdown_action_bar_blur);
this.scrollView = findViewById(R.id.scrollView2);
this.md_layout = findViewById(R.id.md_layout);
final TextView footer = findViewById(R.id.markdownFooter);
UiThreadHandler.handler.postDelayed(() -> // Fix footer height
footer.setMinHeight(this.getNavigationBarHeight()), 1L);
this.actionBarBlur.setBackground(this.anyBarBackground);
this.setupBlurView(this.chipHolder, markdownBackground, this.anyBarBackground);
this.setupBlurView(this.actionBarBlur, markdownBackground, this.anyBarBackground);
this.updateScreenInsets();
this.updateUI();
// Really bad created (MSG by Der_Googler)
if (MainApplication.isChipsDisabled()) {
this.chipHolder.setVisibility(View.GONE);
} else {
this.chipHolder.setPadding(0,0,0,this.getNavigationBarHeight());
// set "message" to null to disable dialog
this.setChip(change_boot,
getString(R.string.module_can_change_boot),
"This module may change the boot image");
this.setChip(needs_ramdisk,
getString(R.string.module_needs_ramdisk),
"This module need boot ramdisk to be installed");
this.setChip(min_magisk, "Min. Magisk \"" + min_magisk + "\"",
null);
this.setChip(min_api, "Min. Android " + min_api,
null);
this.setChip(max_api, "Max. Android " + max_api,
null);
}
new Thread(() -> {
try {
Log.d(TAG, "Downloading");
byte[] rawMarkdown = getRawMarkdown(url);
Log.d(TAG, "Encoding");
String markdown = new String(rawMarkdown, StandardCharsets.UTF_8);
Log.d(TAG, "Done!");
runOnUiThread(() -> {
findViewById(R.id.markdownFooter)
.setMinimumHeight(this.getNavigationBarHeight());
MainApplication.getINSTANCE().getMarkwon().setMarkdown(
textView, MarkdownUrlLinker.urlLinkify(markdown));
if (markdownBackground != null) {
markdownBackground.setClickable(true);
markdownBackground.setOnClickListener(v -> this.onBackPressed());
}
});
} catch (Exception e) {
Log.e(TAG, "Failed download", e);
runOnUiThread(() -> {
Toast.makeText(this, R.string.failed_download,
Toast.LENGTH_SHORT).show();
this.onBackPressed();
});
}
}, "Markdown load thread").start();
}
private void setupBlurView(BlurView view, ViewGroup setupWith, ColorDrawable background) {
view.setBackground(background);
view.setupWith(setupWith).setFrameClearDrawable(
this.getWindow().getDecorView().getBackground())
.setBlurAlgorithm(new RenderScriptBlur(this))
.setBlurRadius(4F).setBlurAutoUpdate(true)
.setHasFixedTransformationMatrix(true);
}
private void updateScreenInsets() {
this.runOnUiThread(() -> this.updateScreenInsets(
this.getResources().getConfiguration()));
}
private void updateScreenInsets(Configuration configuration) {
boolean landscape = configuration.orientation ==
Configuration.ORIENTATION_LANDSCAPE;
int bottomInset = (landscape ? 0 : this.getNavigationBarHeight());
int statusBarHeight = getStatusBarHeight();
int actionBarHeight = getActionBarHeight();
int combinedBarsHeight = statusBarHeight + actionBarHeight;
this.actionBarPadding.setMinHeight(combinedBarsHeight);
//this.actionBarBlur.invalidate();
}
private void updateUI() {
boolean isLightMode = this.isLightTheme();
int colorBackground;
try {
colorBackground = this.getColorCompat(
android.R.attr.windowBackground);
} catch (Resources.NotFoundException e) {
colorBackground = this.getColorCompat(isLightMode ?
R.color.white : R.color.black);
}
this.md_layout.setPadding(0,this.getActionBarHeight(this) + this.getStatusBarHeight(),0,this.getNavigationBarHeight() + 56);
if (MainApplication.isBlurEnabled()) {
this.actionBarBlur.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, this.getActionBarHeight(this) + this.getStatusBarHeight()));
this.chipHolder.setBlurEnabled(true);
this.anyBarBackground.setColor(ColorUtils
.setAlphaComponent(colorBackground, 0x02));
this.anyBarBackground.setColor(Color.TRANSPARENT);
this.actionBarBlur.setBlurEnabled(true);
} else {
this.chipHolder.setBlurEnabled(false);
this.chipHolder.setOverlayColor(Color.TRANSPARENT);
this.anyBarBackground.setColor(colorBackground);
this.actionBarBlur.setBlurEnabled(false);
this.actionBarBlur.setOverlayColor(Color.TRANSPARENT);
}
}
private void setChip(boolean bool, String title, String message) {
if (bool) {
this.makeChip(title, message);
}
}
private void setChip(int i, String title, String message) {
if (i != 0) {
this.makeChip(title, message);
}
}
private void makeChip(String title, String message) {
final ChipGroup chip_group_holder = findViewById(R.id.chip_group_holder);
Chip chip = new Chip(this);
chip.setText(title);
chip.setVisibility(View.VISIBLE);
if (message != null) {
chip.setOnClickListener(_view -> {
MaterialAlertDialogBuilder builder =
new MaterialAlertDialogBuilder(this);
builder
.setTitle(title)
.setMessage(message)
.setCancelable(true)
.setPositiveButton(R.string.ok, (x, y) -> x.dismiss()).show();
});
}
chip_group_holder.addView(chip);
}
private String parseAndroidVersion(int version) {
switch (version) {
case Build.VERSION_CODES.LOLLIPOP:
return "5.0";
case Build.VERSION_CODES.LOLLIPOP_MR1:
return "5.1";
case Build.VERSION_CODES.M:
return "6.0";
case Build.VERSION_CODES.N:
return "7.0";
case Build.VERSION_CODES.N_MR1:
return "7.1";
case Build.VERSION_CODES.O:
return "8.0";
case Build.VERSION_CODES.O_MR1:
return "8.1";
case Build.VERSION_CODES.P:
return "9.0 (P)";
case Build.VERSION_CODES.Q:
return "10 (Q)";
case Build.VERSION_CODES.R:
return "11 (R)";
case Build.VERSION_CODES.S:
return "12 (S)";
case Build.VERSION_CODES.S_V2:
return "12L";
default:
return "false";
}
}
@Override
protected void onResume() {
super.onResume();
View footer = findViewById(R.id.markdownFooter);
if (footer != null) footer.setMinimumHeight(this.getNavigationBarHeight());
}
}