Added log filter feature (#74)

This commit is contained in:
MrIkso
2022-06-17 12:58:17 +03:00
parent da8723d2cc
commit 47fa1ae2b5
7 changed files with 389 additions and 252 deletions
@@ -933,7 +933,7 @@ public class EditorActivity extends StudioActivity
initialSetup();
setStatus(getString(R.string.msg_project_initialized));
mBinding.buildProgressIndicator.setVisibility(View.GONE);
if (mFindInProjectDialog != null && mFindInProjectDialog.isShowing()) {
mFindInProjectDialog.dismiss();
}
@@ -961,8 +961,8 @@ public class EditorActivity extends StudioActivity
@SuppressWarnings("UnusedReturnValue")
@NonNull
public CompletableFuture<TaskExecutionResult> execTasks(
Consumer<TaskExecutionResult> resultHandler, String... tasks) {
saveAll(false);
Consumer<TaskExecutionResult> resultHandler, String... tasks) {
saveAll(false);
runOnUiThread(() -> appendBuildOut("Executing tasks: " + TextUtils.join(", ", tasks)));
return mBuildService
.executeTasks(tasks)
@@ -1348,7 +1348,8 @@ public class EditorActivity extends StudioActivity
@Override
public void onTabSelected(TabLayout.Tab tab) {
final var fragment = bottomSheetTabAdapter.getFragmentAtIndex(tab.getPosition());
if (fragment instanceof NonEditableEditorFragment) {
if (fragment instanceof NonEditableEditorFragment
|| fragment instanceof LogViewFragment) {
mBinding.bottomSheet.clearFab.show();
mBinding.bottomSheet.shareOutputFab.show();
} else {
@@ -1376,6 +1377,11 @@ public class EditorActivity extends StudioActivity
if (editor.getEditor() != null) {
editor.getEditor().setText("");
}
} else if (fragment instanceof LogViewFragment) {
final var editor = (LogViewFragment) fragment;
if (editor.getEditor() != null) {
editor.getEditor().setText("");
}
}
});
@@ -1384,18 +1390,23 @@ public class EditorActivity extends StudioActivity
final var fragment =
bottomSheetTabAdapter.getFragmentAtIndex(
mBinding.bottomSheet.tabs.getSelectedTabPosition());
if (fragment instanceof NonEditableEditorFragment) {
final var editor = (NonEditableEditorFragment) fragment;
if (editor.getEditor() != null) {
final var text = editor.getEditor().getText();
final var type =
editor instanceof SimpleOutputFragment
? "build_output"
: editor instanceof IDELogFragment
? "ide_logs"
: editor instanceof LogViewFragment ? "app_logs" : "unknown_type";
shareText(text, type);
if (fragment instanceof Fragment) {
CharSequence text = "";
String type = "unknown_type";
if (fragment instanceof SimpleOutputFragment) {
var editor = (SimpleOutputFragment) fragment;
text = editor.getEditor().getText();
type = "build_output";
} else if (fragment instanceof IDELogFragment) {
var editor = (IDELogFragment) fragment;
text = editor.getEditor().getText();
type = "ide_logs";
} else if (fragment instanceof LogViewFragment) {
var editor = (IDELogFragment) fragment;
text = editor.getEditor().getText();
type = "app_logs";
}
shareText(text, type);
}
});
@@ -1520,18 +1531,14 @@ public class EditorActivity extends StudioActivity
try {
//noinspection ConstantConditions
final var rootProject = ProjectManager.INSTANCE.getRootProject();
var projectName = rootProject.getName().get();
if(projectName.isEmpty())
{
projectName = new File(ProjectManager.INSTANCE.getProjectDirPath()).getName();
getSupportActionBar()
.setSubtitle(projectName);
}
else{
getSupportActionBar()
.setSubtitle(projectName);
}
var projectName = rootProject.getName().get();
if (projectName.isEmpty()) {
projectName = new File(ProjectManager.INSTANCE.getProjectDirPath()).getName();
getSupportActionBar().setSubtitle(projectName);
} else {
getSupportActionBar().setSubtitle(projectName);
}
} catch (Throwable th) {
// ignored
}
@@ -1575,7 +1582,7 @@ public class EditorActivity extends StudioActivity
}
initializeLanguageServers();
// Actually, we don't need to start FileOptionsHandler
// Because it would work anyway
// But still we do...
@@ -22,32 +22,32 @@ import androidx.annotation.NonNull;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.utils.ILogger;
public class IDELogFragment extends LogViewFragment {
private final ILogger.LogListener listener = this::log;
public IDELogFragment() {
ILogger.addLogListener(listener);
}
@Override
protected String onCreateLogString(@NonNull LogLine line) {
return line.toSimpleString();
}
private void log(int priority, String tag, @NonNull String message) {
if (message.contains("\n")) {
final var split = message.split("\n");
for (var line : split) {
logLine(priority, tag, line);
}
} else {
logLine(priority, tag, message);
}
}
private void logLine(int priority, String tag, String message) {
final var line = new LogLine(ILogger.priorityChar(priority), tag, message);
appendLog(line);
}
}
public class IDELogFragment extends LogViewFragment {
private final ILogger.LogListener listener = this::log;
public IDELogFragment() {
ILogger.addLogListener(listener);
}
@Override
protected String onCreateLogString(@NonNull LogLine line) {
return line.toSimpleString();
}
private void log(int priority, String tag, @NonNull String message) {
if (message.contains("\n")) {
final var split = message.split("\n");
for (var line : split) {
logLine(priority, tag, line);
}
} else {
logLine(priority, tag, message);
}
}
private void logLine(int priority, String tag, String message) {
final var line = new LogLine(ILogger.priorityChar(priority), tag, message);
appendLog(line);
}
}
@@ -18,57 +18,147 @@
package com.itsaky.androidide.fragments;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import com.blankj.utilcode.util.StringUtils;
import com.blankj.utilcode.util.ThreadUtils;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.FragmentLogViewBinding;
import com.itsaky.androidide.language.logs.LogLanguageImpl;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.androidide.views.editor.IDEEditor;
import com.itsaky.androidide.utils.SingleTextWatcher;
import io.github.rosemoe.sora.lang.Language;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import io.github.rosemoe.sora.lang.Language;
public class LogViewFragment extends Fragment {
public class LogViewFragment extends NonEditableEditorFragment {
private FragmentLogViewBinding binding;
private final Language language = getLanguage();
private final List<LogLine> unsavedLines = new ArrayList<>();
private char priority;
private String tag;
@Nullable
@Override
public View onCreateView(
@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
binding = FragmentLogViewBinding.inflate(inflater, container, false);
return binding.getRoot();
}
@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Objects.requireNonNull(getEditor()).setEditorLanguage(language);
final var editor = getEditor();
editor.setEditable(false);
editor.setDividerWidth(0);
editor.setWordwrap(false);
editor.setUndoEnabled(false);
editor.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono());
editor.setTypefaceText(TypefaceUtils.jetbrainsMono());
editor.setTextSize(12);
editor.setColorScheme(new SchemeAndroidIDE());
editor.setEditorLanguage(language);
final var arr = requireContext().getResources().getStringArray(R.array.logcat_units);
binding.logPrioritySpinner.setAdapter(
new ArrayAdapter<>(
requireContext(),
androidx.appcompat.R.layout.support_simple_spinner_dropdown_item,
arr));
binding.logPrioritySpinner.setOnItemSelectedListener(
new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
priority = parent.getSelectedItem().toString().charAt(0);
getEditor().setText("");
}
@Override
public void onNothingSelected(AdapterView<?> parent) {}
});
binding
.tagFilterInput
.getEditText()
.addTextChangedListener(
new SingleTextWatcher() {
@Override
public void afterTextChanged(Editable editable) {
tag = editable.toString();
getEditor().setText("");
}
});
if (!unsavedLines.isEmpty()) {
for (var line : unsavedLines) {
applyToLanguage(line);
getEditor().append(line.toString().trim() + "\n");
applyToLanguage(line);
}
unsavedLines.clear();
}
}
private boolean filterLog(char priority, String tag, LogLine l) {
return 'V' == priority
|| (l.priorityChar == priority)
&& (TextUtils.isEmpty(tag)
|| (!TextUtils.isEmpty(tag) && (l.tag.toLowerCase().contains(tag.toLowerCase()))));
}
private void applyToLanguage(LogLine line) {
if (language instanceof LogLanguageImpl) {
((LogLanguageImpl) this.language).addLine(line);
}
}
@Nullable
public IDEEditor getEditor() {
return binding.editor;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
public void appendLog(LogLine line) {
if (getEditor() == null) {
unsavedLines.add(line);
return;
if (filterLog(priority, tag, line)) {
if (getEditor() == null) {
unsavedLines.add(line);
return;
}
final var lineString = onCreateLogString(line);
final var msg = lineString.endsWith("\n") ? lineString : lineString + "\n";
ThreadUtils.runOnUiThread(() -> getEditor().append(msg));
applyToLanguage(line);
}
applyToLanguage(line);
final var lineString = onCreateLogString(line);
final var msg = lineString.endsWith("\n") ? lineString : lineString + "\n";
ThreadUtils.runOnUiThread(() -> getEditor().append(msg));
}
protected String onCreateLogString(@NonNull LogLine line) {
@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp" >
<androidx.appcompat.widget.AppCompatSpinner
android:id="@+id/log_priority_spinner"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:entries="@array/logcat_units" />
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/tag_filter_input"
style="@style/AppTheme.TextInputLayout.Outlined"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_weight="1"
android:hint="@string/hint_log_tag_filter"
app:hintEnabled="true"
app:hintTextColor="@color/secondaryColor" >
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:singleLine="true" />
</com.google.android.material.textfield.TextInputLayout>
</LinearLayout>
<com.itsaky.androidide.views.editor.IDEEditor
android:id="@+id/editor"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
+14 -2
View File
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?><!--
<?xml version="1.0" encoding="utf-8"?>
<!--
~ This file is part of AndroidIDE.
~
~ AndroidIDE is free software: you can redistribute it and/or modify
@@ -13,8 +14,9 @@
~
~ You should have received a copy of the GNU General Public License
~ along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
-->
-->
<resources>
<string-array name="dimension_units">
<item>dp</item>
<item>sp</item>
@@ -23,4 +25,14 @@
<item>in</item>
<item>mm</item>
</string-array>
<string-array name="logcat_units">
<item>Verbose</item>
<item>Debug</item>
<item>Info</item>
<item>Warning</item>
<item>Error</item>
<item>Fatal</item>
</string-array>
</resources>
+1
View File
@@ -380,6 +380,7 @@
<string name="wizard_loading">Templates loading...</string>
<string name="next">Next</string>
<string name="exit">Exit</string>
<string name="hint_log_tag_filter">Log tags filter</string>
<string name="idepref_title_tpFix">JDK 11 tag pointers fix</string>
<string name="idepref_msg_tpFix">Set LD_PRELOAD to libhook.so to fix tag pointers issue for JDK 11 in Android 11 [DEPRECATED - Prefer using JDK 17].</string>
@@ -1,179 +1,161 @@
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*
*/
package com.itsaky.androidide.models;
public class LogLine {
// It makes things easier in LogLanguageImpl
public static int INFO;
public static int DEBUG;
public static int ERROR;
public static int WARNING;
static {
try {
final var klass = Class.forName("com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE");
INFO = klass.getField("LOG_INFO").getInt(null);
DEBUG = klass.getField("LOG_DEBUG").getInt(null);
ERROR = klass.getField("LOG_ERROR").getInt(null);
WARNING = klass.getField("LOG_WARNING").getInt(null);
} catch (Throwable err) {
INFO = 0;
DEBUG = 1;
ERROR = 2;
WARNING = 3;
}
}
public String unformatted;
public String date;
public String time;
public String pid;
public String tid;
public String tag;
public String message;
public char priorityChar;
public int priority;
public boolean formatted;
// For JSONRpc
@SuppressWarnings("unused")
private LogLine() {}
public LogLine(char priorityChar, String tag, String message) {
this("", "", "", "", priorityChar, tag, message);
}
public LogLine(
String date,
String time,
String pid,
String tid,
char priorityChar,
String tag,
String message) {
this(date, time, pid, tid, priorityChar, tag, message, true);
}
public LogLine(
String date,
String time,
String pid,
String tid,
char priorityChar,
String tag,
String message,
boolean formatted) {
this.date = date;
this.time = time;
this.pid = pid;
this.tid = tid;
this.priorityChar = priorityChar;
this.tag = tag;
this.message = message;
this.priority = parsePriority(priorityChar);
this.formatted = formatted;
if (tag.length() > 25) {
this.tag = "...".concat(tag.substring(tag.length() - 25));
}
}
private int parsePriority(char s) {
final var c = Character.toLowerCase(s);
if (c == 'w') {
return WARNING;
} else if (c == 'e') {
return ERROR;
} else if (c == 'i') {
return INFO;
} else {
return DEBUG;
}
}
public LogLine(String unformatted) {
this.unformatted = unformatted;
this.formatted = false;
}
public static LogLine forLogString(final String log) {
try {
final var split = log.split("\\s", 7);
return new LogLine(
split[0], // date
split[1], // time
split[2], // process id
split[3], // thread id
split[4].charAt(0), // priority
split[5], // tag
split[6] // message
);
} catch (Throwable th) {
return new LogLine(log);
}
}
public int getColor() {
int id = 0xffffffff;
if (priority == WARNING) {
id = 0xffff7043;
} else if (priority == ERROR) {
id = 0xffc50e29;
} else if (priority == INFO) {
id = 0xff4caf50;
}
return id;
}
@Override
public String toString() {
return this.formatted
? String.format(
"%-6s %-13s %-6s %-6s %-2s %-35s %s",
date, time, pid, tid, priorityChar, trimIfNeeded(tag, 35), message)
: this.unformatted;
}
private String trimIfNeeded(String tag, int maxLength) {
final var sb = new StringBuilder(tag);
final var length = tag.length();
if (length > maxLength) {
final var start = length - maxLength;
sb.delete(0, start);
// When the tag is long enough, prefix the tag with '..'
sb.setCharAt(0, '.');
sb.setCharAt(1, '.');
}
return sb.toString();
}
public String toSimpleString() {
return this.formatted
? String.format("%-25s %-2s %s", trimIfNeeded(tag, 25), priorityChar, message)
: this.unformatted;
}
public String formattedTagAndMessage() {
return this.formatted
? String.format("%-25s %s", trimIfNeeded(tag, 25), message)
: this.unformatted;
}
}
/*
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*
*/
package com.itsaky.androidide.models;
public class LogLine {
// It makes things easier in LogLanguageImpl
public static int INFO;
public static int DEBUG;
public static int ERROR;
public static int WARNING;
static {
try {
final var klass = Class.forName("com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE");
INFO = klass.getField("LOG_INFO").getInt(null);
DEBUG = klass.getField("LOG_DEBUG").getInt(null);
ERROR = klass.getField("LOG_ERROR").getInt(null);
WARNING = klass.getField("LOG_WARNING").getInt(null);
} catch (Throwable err) {
INFO = 0;
DEBUG = 1;
ERROR = 2;
WARNING = 3;
}
}
public String unformatted;
public String date;
public String time;
public String pid;
public String tid;
public String tag;
public String message;
public char priorityChar;
public int priority;
public boolean formatted;
// For JSONRpc
@SuppressWarnings("unused")
private LogLine() {}
public LogLine(char priorityChar, String tag, String message) {
this("", "", "", "", priorityChar, tag, message);
}
public LogLine(
String date,
String time,
String pid,
String tid,
char priorityChar,
String tag,
String message) {
this(date, time, pid, tid, priorityChar, tag, message, true);
}
public LogLine(
String date,
String time,
String pid,
String tid,
char priorityChar,
String tag,
String message,
boolean formatted) {
this.date = date;
this.time = time;
this.pid = pid;
this.tid = tid;
this.priorityChar = priorityChar;
this.tag = tag;
this.message = message;
this.priority = parsePriority(priorityChar);
this.formatted = formatted;
}
private int parsePriority(char s) {
final var c = Character.toLowerCase(s);
if (c == 'w') {
return WARNING;
} else if (c == 'e') {
return ERROR;
} else if (c == 'i') {
return INFO;
} else {
return DEBUG;
}
}
public LogLine(String unformatted) {
this.unformatted = unformatted;
this.formatted = false;
}
public static LogLine forLogString(final String log) {
try {
final var split = log.split("\\s", 7);
return new LogLine(
split[0], // date
split[1], // time
split[2], // process id
split[3], // thread id
split[4].charAt(0), // priority
split[5], // tag
split[6] // message
);
} catch (Throwable th) {
return new LogLine(log);
}
}
@Override
public String toString() {
return this.formatted
? String.format("%s %s %s/%s %s/%s %s", date, time, pid, tid, priorityChar, tag, message)
: this.unformatted;
}
private String trimIfNeeded(String tag, int maxLength) {
final var sb = new StringBuilder(tag);
final var length = tag.length();
if (length > maxLength) {
final var start = length - maxLength;
sb.delete(0, start);
// When the tag is long enough, prefix the tag with '..'
sb.setCharAt(0, '.');
sb.setCharAt(1, '.');
}
return sb.toString();
}
public String toSimpleString() {
return this.formatted
? String.format("%-25s %-2s %s", trimIfNeeded(tag, 25), priorityChar, message)
: this.unformatted;
}
public String formattedTagAndMessage() {
return this.formatted
? String.format("%-25s %s", trimIfNeeded(tag, 25), message)
: this.unformatted;
}
}