Use RecyclerView to show application and IDE logs

This commit is contained in:
Akash Yadav
2022-06-20 14:53:59 +05:30
parent bda6d50e5f
commit dc55c7c609
12 changed files with 360 additions and 305 deletions
+2 -54
View File
@@ -140,65 +140,13 @@
<entry key="..\:/Users/Akash Yadav/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_create_project_content.xml" value="0.33" />
<entry key="..\:/Users/Akash Yadav/AndroidStudioProjects/android-ide/app/src/main/res/layout/layout_widget_item.xml" value="0.264" />
<entry key="..\:/Users/Akash Yadav/AndroidStudioProjects/android-ide/app/src/main/res/menu/menu_designer_widgets.xml" value="0.20854700854700856" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/activity_about.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/activity_crash_handler.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/activity_main.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/activity_preferences.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/activity_splash.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/fragment_log_view.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/fragment_main.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/fragment_non_editable_editor.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/fragment_wizard.xml" value="0.26046511627906976" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_about_items.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_add_attr_sheet.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_attr_editor_sheet.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_attr_editor_sheet_item.xml" value="0.25" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_attr_value_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_banner.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_boolean_attr_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_code_action_item.xml" value="0.5" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_code_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_color_attr_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_completion_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_crash_report.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_create_file_java.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_diagnostic_group.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_diagnostic_info.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_diagnostic_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_dialog_text_input.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_dimension_attr_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_editor_actions.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_editor_bottom_sheet.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_editor_file_tree.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_editor_find_replace.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_filetree_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_loading_wizard.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_nav_header.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_optionssheet_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_popup_menu_item.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_progress_sheet.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_projects.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_projects_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_reference_attr_editor.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_search_project.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_search_result_group.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_search_result_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_sheet.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_simple_icon_text.xml" value="0.5" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_string_attr_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_symbol_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_text_action_item.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_text_attr_editor.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/layout_text_size_slider.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/setup_footer.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/wizard_details.xml" value="0.33" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/wizard_template_item.xml" value="0.20416666666666666" />
<entry key="..\:/Users/Svetl/Documents/GitHub/AndroidIDE/app/src/main/res/layout/wizard_templates.xml" value="0.20416666666666666" />
<entry key="..\:/itsaky/.gradle/caches/transforms-3/e4862cae80f8754ca1a451ad3e00b14c/transformed/jetified-editor-0.10.11/res/drawable/round_select_all_20.xml" value="0.1305" />
<entry key="app/src/main/res/drawable/ic_gradle.xml" value="0.3265" />
<entry key="app/src/main/res/drawable/ic_language_gradle.xml" value="0.3265" />
<entry key="app/src/main/res/layout/activity_download.xml" value="0.35572916666666665" />
<entry key="app/src/main/res/layout/fragment_log.xml" value="0.35572916666666665" />
<entry key="app/src/main/res/layout/layout_dialog_text_input.xml" value="0.35572916666666665" />
<entry key="app/src/main/res/layout/layout_log_line.xml" value="0.35572916666666665" />
</map>
</option>
</component>
Binary file not shown.
@@ -84,7 +84,6 @@ import com.itsaky.androidide.databinding.ActivityEditorBinding;
import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding;
import com.itsaky.androidide.databinding.LayoutSearchProjectBinding;
import com.itsaky.androidide.fragments.FileTreeFragment;
import com.itsaky.androidide.fragments.IDELogFragment;
import com.itsaky.androidide.fragments.LogViewFragment;
import com.itsaky.androidide.fragments.NonEditableEditorFragment;
import com.itsaky.androidide.fragments.SearchResultFragment;
@@ -1378,9 +1377,15 @@ public class EditorActivity extends StudioActivity
editor.getEditor().setText("");
}
} else if (fragment instanceof LogViewFragment) {
final var editor = (LogViewFragment) fragment;
if (editor.getEditor() != null) {
editor.getEditor().setText("");
final LogViewFragment logFrag = (LogViewFragment) fragment;
final var adapter = logFrag.getAdapter();
if (adapter != null) {
adapter.clear();
} else {
LOG.error(
"Cannot clear contents. Adapter in LogViewFragment("
+ logFrag.getLogType()
+ ") is null.");
}
}
});
@@ -1390,22 +1395,40 @@ public class EditorActivity extends StudioActivity
final var fragment =
bottomSheetTabAdapter.getFragmentAtIndex(
mBinding.bottomSheet.tabs.getSelectedTabPosition());
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";
if (fragment instanceof LogViewFragment) {
final var logFrag = (LogViewFragment) fragment;
final var type = logFrag.getLogType();
final var adapter = logFrag.getAdapter();
if (adapter != null) {
//noinspection deprecation
final var progress =
ProgressDialog.show(EditorActivity.this, null, getString(R.string.please_wait));
CompletableFuture.runAsync(
() -> {
final var text = adapter.getAsString();
ThreadUtils.runOnUiThread(
() -> {
progress.dismiss();
shareText(text, type);
});
});
} else {
LOG.error("Adapter in LogViewFragment(" + type + ") is null");
}
return;
}
shareText(text, type);
if (fragment instanceof SimpleOutputFragment) {
final var editor = (SimpleOutputFragment) fragment;
final var text = Objects.requireNonNull(editor.getEditor()).getText();
final var type = "build_output";
shareText(text, type);
return;
}
LOG.error("Unknown fragment:", fragment);
});
if (!getApp().getPrefManager().getBoolean(KEY_BOTTOM_SHEET_SHOWN)
@@ -25,9 +25,9 @@ import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import com.itsaky.androidide.R;
import com.itsaky.androidide.fragments.AppLogFragment;
import com.itsaky.androidide.fragments.DiagnosticsListFragment;
import com.itsaky.androidide.fragments.IDELogFragment;
import com.itsaky.androidide.fragments.LogViewFragment;
import com.itsaky.androidide.fragments.SearchResultFragment;
import com.itsaky.androidide.fragments.SimpleOutputFragment;
import com.itsaky.androidide.utils.ILogger;
@@ -51,7 +51,7 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
SimpleOutputFragment.class,
++index));
this.fragments.add(
new Tab(fragmentActivity.getString(R.string.app_logs), LogViewFragment.class, ++index));
new Tab(fragmentActivity.getString(R.string.app_logs), AppLogFragment.class, ++index));
this.fragments.add(
new Tab(fragmentActivity.getString(R.string.ide_logs), IDELogFragment.class, ++index));
this.fragments.add(
@@ -99,8 +99,8 @@ public class EditorBottomSheetTabAdapter extends FragmentStateAdapter {
}
@Nullable
public LogViewFragment getLogFragment() {
return findFragmentByClass(LogViewFragment.class);
public AppLogFragment getLogFragment() {
return findFragmentByClass(AppLogFragment.class);
}
@Nullable
@@ -0,0 +1,136 @@
/*
* 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.adapters
import android.annotation.SuppressLint
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.util.TypedValue
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.blankj.utilcode.util.SizeUtils
import com.itsaky.androidide.adapters.LogLinesAdapter.VH
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.utils.TypefaceUtils
/**
* Adapter for the log view.
* @author Akash Yadav
*/
class LogLinesAdapter(private val lines: MutableList<LogLine> = mutableListOf()) :
RecyclerView.Adapter<VH>() {
var simpleFormatting = true
companion object {
const val MAX_LINES = 10 * 1000
}
class VH(val text: TextView) : RecyclerView.ViewHolder(text)
@SuppressLint("NotifyDataSetChanged")
fun clear() {
this.lines.clear()
this.notifyDataSetChanged()
}
fun add(line: LogLine) {
this.lines.add(line)
this.notifyItemInserted(this.lines.lastIndex)
if (this.lines.size > MAX_LINES) {
this.lines.removeAt(0)
this.notifyItemRemoved(0)
}
}
fun getItem(index: Int) = this.lines[index]
fun getAsString() =
StringBuilder()
.apply {
for (line in lines) {
append(
if (simpleFormatting) {
line.toSimpleString()
} else {
line.toString()
})
append("\n")
}
}
.toString()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
val dp1 = SizeUtils.dp2px(1f)
val text = TextView(parent.context)
text.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11f)
text.setPaddingRelative(dp1, dp1 / 2, dp1, dp1 / 2)
text.setTextIsSelectable(true)
text.typeface = TypefaceUtils.jetbrainsMono()
return VH(text)
}
override fun onBindViewHolder(holder: VH, position: Int) {
val line = getItem(position)
val spanned =
if (line is LogWrapper) {
line.spanned!!
} else {
val spannable =
SpannableString(
if (simpleFormatting) line.toSimpleString() else line.toString())
val color =
when (line.priority) {
LogLine.ERROR -> -0xbbcca
LogLine.WARNING -> -0x14c5
LogLine.INFO -> -0xb350b0
else -> -0xa0a0b
}
spannable.setSpan(
ForegroundColorSpan(color),
0,
spannable.length,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
lines[position] = LogWrapper(line).apply { spanned = spannable }
spannable
}
holder.text.text = spanned
}
override fun getItemCount() = this.lines.size
class LogWrapper(val log: LogLine) : LogLine() {
var spanned: Spannable? = null
override fun equals(other: Any?): Boolean {
return this.log == other
}
override fun hashCode(): Int {
return this.log.hashCode()
}
override fun toString() = this.log.toString()
override fun toSimpleString(): String = this.log.toSimpleString()
override fun formattedTagAndMessage(): String = this.log.formattedTagAndMessage()
}
}
@@ -1,53 +0,0 @@
/*
* 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.fragments;
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);
}
}
@@ -0,0 +1,60 @@
/*
* 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.fragments
import com.itsaky.androidide.utils.ILogger
/**
* Fragment to show IDE logs.
* @author Akash Yadav
*/
class IDELogFragment : LogViewFragment() {
private var logListener: ILogger.LogListener? =
ILogger.LogListener { priority, tag, message ->
if (message.contains("\n")) {
val split = message.split("\n").toTypedArray()
for (line in split) {
logLine(priority, tag, line)
}
} else {
logLine(priority, tag, message)
}
}
init {
ILogger.addLogListener(this.logListener)
}
override fun isSimpleFormattingEnabled() = true
override fun getLogType() = "ide_logs"
override fun onDestroy() {
super.onDestroy()
ILogger.removeLogListener(logListener)
logListener = null
}
}
/**
* Fragment to show application logs.
* @author Akash Yadav
*/
class AppLogFragment : LogViewFragment() {
override fun isSimpleFormattingEnabled() = false
override fun getLogType() = "app_logs"
}
@@ -1,174 +0,0 @@
/*
* 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.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.stream.Collectors;
public class LogViewFragment extends Fragment {
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);
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) {
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() {
if (binding == null) {
return null;
}
return binding.editor;
}
@Override
public void onDestroyView() {
super.onDestroyView();
binding = null;
}
public void appendLog(LogLine line) {
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);
}
}
protected String onCreateLogString(@NonNull LogLine line) {
return line.toString();
}
protected Language getLanguage() {
return new LogLanguageImpl();
}
}
@@ -0,0 +1,84 @@
/*
* 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.fragments
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.itsaky.androidide.adapters.LogLinesAdapter
import com.itsaky.androidide.databinding.FragmentLogBinding
import com.itsaky.androidide.models.LogLine
import com.itsaky.androidide.utils.ILogger
/**
* Fragment to show logs in a [androidx.recyclerview.widget.RecyclerView].
* @author Akash Yadav
*/
abstract class LogViewFragment : Fragment() {
private val log = ILogger.newInstance(javaClass.simpleName)
var binding: FragmentLogBinding? = null
var adapter: LogLinesAdapter? = null
fun appendLog(line: LogLine) {
if (this.binding == null || this.adapter == null) {
return
}
this.binding!!.lines.post {
this.adapter!!.simpleFormatting = isSimpleFormattingEnabled()
this.adapter!!.add(line)
}
}
abstract fun getLogType(): String
abstract fun isSimpleFormattingEnabled(): Boolean
protected open fun logLine(priority: Int, tag: String, message: String) {
val line = LogLine(ILogger.priorityChar(priority), tag, message)
appendLog(line)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
binding = FragmentLogBinding.inflate(inflater, container, false)
return binding!!.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val manager = LinearLayoutManager(requireContext())
this.binding!!.lines.layoutManager = manager
this.adapter = LogLinesAdapter()
this.binding!!.lines.adapter = this.adapter
}
override fun onDestroy() {
super.onDestroy()
this.binding = null
this.adapter = null
}
}
+31
View File
@@ -0,0 +1,31 @@
<?xml version="1.0" encoding="utf-8"?><!--
~ 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/>.
-->
<HorizontalScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/lines"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingStart="8dp"
android:paddingEnd="8dp" />
</HorizontalScrollView>
@@ -53,7 +53,7 @@ public class LogLine {
// For JSONRpc
@SuppressWarnings("unused")
private LogLine() {}
protected LogLine() {}
public LogLine(char priorityChar, String tag, String message) {
this("", "", "", "", priorityChar, tag, message);