mirror of
https://github.com/signalapp/Signal-Android.git
synced 2026-04-25 11:20:47 +01:00
View debug log screen through WebView through a module.
This commit is contained in:
5
debuglogs-viewer/lib/src/main/AndroidManifest.xml
Normal file
5
debuglogs-viewer/lib/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
||||
42
debuglogs-viewer/lib/src/main/assets/debuglogs-viewer.html
Normal file
42
debuglogs-viewer/lib/src/main/assets/debuglogs-viewer.html
Normal file
@@ -0,0 +1,42 @@
|
||||
<!--
|
||||
~ Copyright 2025 Signal Messenger, LLC
|
||||
~ SPDX-License-Identifier: AGPL-3.0-only
|
||||
-->
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<style>
|
||||
#container { position: absolute; top: 0; bottom: 0; left: 0; right: 0; height: 100%; width: 100%; }
|
||||
|
||||
/* Scrollbar Setup */
|
||||
.ace_scrollbar::-webkit-scrollbar { width: 0; height: 0; }
|
||||
.show-scrollbar .ace_scrollbar::-webkit-scrollbar { width: 5px; height: 5px; }
|
||||
.ace_scrollbar::-webkit-scrollbar-thumb { background-color: #999999; }
|
||||
|
||||
/* Light Mode: Line color based on log level */
|
||||
.ace_editor { background-color: #FBFCFF; }
|
||||
.ace_none { color: #000000; }
|
||||
.ace_verbose { color: #515151; }
|
||||
.ace_debug { color: #089314; }
|
||||
.ace_info { color: #0a7087; }
|
||||
.ace_warning { color: #b58c12; }
|
||||
.ace_error { color: #af0d0a; }
|
||||
|
||||
/* Dark Mode: Line color based on log level */
|
||||
body.dark .ace_editor { background-color: #1B1C1F; }
|
||||
body.dark .ace_none { color: #ffffff; }
|
||||
body.dark .ace_verbose { color: #8a8a8a; }
|
||||
body.dark .ace_debug { color: #5ca72b; }
|
||||
body.dark .ace_info { color: #46bbb9; }
|
||||
body.dark .ace_warning { color: #cdd637; }
|
||||
body.dark .ace_error { color: #ff6b68; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container"></div>
|
||||
<script src="https://www.unpkg.com/ace-builds@latest/src-noconflict/ace.js"></script>
|
||||
<script src="debuglogs-viewer.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
49
debuglogs-viewer/lib/src/main/assets/debuglogs-viewer.js
Normal file
49
debuglogs-viewer/lib/src/main/assets/debuglogs-viewer.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Create custom text mode to color different levels of debug log lines
|
||||
const TextMode = ace.require("ace/mode/text").Mode;
|
||||
const TextHighlightRules = ace.require("ace/mode/text_highlight_rules").TextHighlightRules;
|
||||
|
||||
function CustomHighlightRules() {
|
||||
this.$rules = {
|
||||
start: [
|
||||
{ token: "verbose", regex: "^.*\\sV\\s.*$" },
|
||||
{ token: "debug", regex: "^.*\\sD\\s.*$" },
|
||||
{ token: "info", regex: "^.*\\sI\\s.*$" },
|
||||
{ token: "warning", regex: "^.*\\sW\\s.*$" },
|
||||
{ token: "error", regex: "^.*\\sE\\s.*$" },
|
||||
{ token: "none", regex: ".*" },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
CustomHighlightRules.prototype = new TextHighlightRules();
|
||||
|
||||
function CustomMode() {
|
||||
TextMode.call(this);
|
||||
this.HighlightRules = CustomHighlightRules;
|
||||
}
|
||||
|
||||
CustomMode.prototype = Object.create(TextMode.prototype);
|
||||
CustomMode.prototype.constructor = CustomMode;
|
||||
|
||||
// Create Ace Editor using the custom mode
|
||||
let editor = ace.edit("container", {
|
||||
mode: new CustomMode(),
|
||||
theme: "ace/theme/textmate",
|
||||
wrap: false, // Allow for horizontal scrolling
|
||||
readOnly: true,
|
||||
showGutter: false,
|
||||
highlightActiveLine: false,
|
||||
highlightSelectedWord: false, // Prevent Ace Editor from automatically highlighting all instances of a selected word (really laggy!)
|
||||
showPrintMargin: false,
|
||||
});
|
||||
|
||||
// Show scrollbar that fades after a second since last scroll
|
||||
let timeout;
|
||||
function showScrollBar() {
|
||||
editor.container.classList.add("show-scrollbar");
|
||||
clearTimeout(timeout);
|
||||
timeout = setTimeout(() => editor.container.classList.remove("show-scrollbar"), 1000);
|
||||
}
|
||||
|
||||
editor.session.on("changeScrollTop", showScrollBar);
|
||||
editor.session.on("changeScrollLeft", showScrollBar);
|
||||
@@ -0,0 +1,92 @@
|
||||
/*
|
||||
* Copyright 2025 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.signal.debuglogsviewer
|
||||
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.res.Configuration
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import kotlinx.coroutines.Runnable
|
||||
import org.json.JSONObject
|
||||
|
||||
var readOnly = true
|
||||
|
||||
object DebugLogsViewer {
|
||||
@JvmStatic
|
||||
fun initWebView(webview: WebView, context: Context, onFinished: Runnable) {
|
||||
webview.settings.apply {
|
||||
javaScriptEnabled = true
|
||||
builtInZoomControls = true
|
||||
displayZoomControls = false
|
||||
}
|
||||
webview.isVerticalScrollBarEnabled = false
|
||||
webview.isHorizontalScrollBarEnabled = false
|
||||
|
||||
webview.loadUrl("file:///android_asset/debuglogs-viewer.html")
|
||||
|
||||
webview.webViewClient = object : WebViewClient() {
|
||||
override fun onPageFinished(view: WebView?, url: String?) {
|
||||
// Set dark mode colors if in dark mode
|
||||
val isDarkMode = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
|
||||
if (isDarkMode) {
|
||||
webview.evaluateJavascript("document.body.classList.add('dark');", null)
|
||||
}
|
||||
onFinished.run()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun presentLines(webview: WebView, lines: String) {
|
||||
// Set the debug log lines
|
||||
val escaped = JSONObject.quote(lines)
|
||||
webview.evaluateJavascript("editor.insert($escaped);", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun scrollToTop(webview: WebView) {
|
||||
webview.evaluateJavascript("editor.scrollToRow(0);", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun scrollToBottom(webview: WebView) {
|
||||
webview.evaluateJavascript("editor.scrollToRow(editor.session.getLength() - 1);", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onFind(webview: WebView) {
|
||||
webview.evaluateJavascript("document.getElementById('searchBar').style.display = 'block';", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onFilter(webview: WebView) {
|
||||
webview.evaluateJavascript("document.getElementById('filterLevel').style.display = 'block';", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onEdit(webview: WebView) {
|
||||
readOnly = !readOnly
|
||||
webview.evaluateJavascript("editor.setReadOnly($readOnly);", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onCancelEdit(webview: WebView, lines: String) {
|
||||
readOnly = !readOnly
|
||||
webview.evaluateJavascript("editor.setReadOnly($readOnly);", null)
|
||||
webview.evaluateJavascript("editor.setValue($lines, -1);", null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun onCopy(webview: WebView, context: Context, appName: String) {
|
||||
webview.evaluateJavascript ("editor.getValue();") { value ->
|
||||
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = ClipData.newPlainText(appName, value)
|
||||
clipboard.setPrimaryClip(clip)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user