package com.fox2code.mmm.installer; import android.graphics.Typeface; import android.text.Spannable; import android.view.ViewGroup; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.fox2code.androidansi.AnsiContext; import java.util.ArrayList; public class InstallerTerminal extends RecyclerView.Adapter { private final RecyclerView recyclerView; private final ArrayList terminal; private final AnsiContext ansiContext; private final Object lock = new Object(); private final int foreground; private boolean ansiEnabled = false; public InstallerTerminal(RecyclerView recyclerView, boolean isLightTheme,int foreground) { recyclerView.setLayoutManager( new LinearLayoutManager(recyclerView.getContext())); this.recyclerView = recyclerView; this.foreground = foreground; this.terminal = new ArrayList<>(); this.ansiContext = (isLightTheme ? AnsiContext.LIGHT : AnsiContext.DARK).copy(); this.recyclerView.setAdapter(this); } @NonNull @Override public TextViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { return new TextViewHolder(new TextView(parent.getContext()), this.foreground); } @Override public void onBindViewHolder(@NonNull TextViewHolder holder, int position) { this.terminal.get(position).setText(holder.textView); } @Override public int getItemCount() { return this.terminal.size(); } public void addLine(String line) { synchronized (lock) { boolean bottom = !this.recyclerView.canScrollVertically(1); int index = this.terminal.size(); this.terminal.add(this.process(line)); this.notifyItemInserted(index); if (bottom) this.recyclerView.scrollToPosition(index); } } public void setLastLine(String line) { synchronized (lock) { int size = this.terminal.size(); if (size == 0) { this.terminal.add(this.process(line)); this.notifyItemInserted(0); } else { this.terminal.set(size - 1, this.process(line)); this.notifyItemChanged(size - 1); } } } public String getLastLine() { synchronized (lock) { int size = this.terminal.size(); return size == 0 ? "" : this.terminal.get(size - 1).line; } } public void removeLastLine() { synchronized (lock) { int size = this.terminal.size(); if (size != 0) { this.terminal.remove(size - 1); this.notifyItemRemoved(size - 1); } } } public void clearTerminal() { synchronized (lock) { int size = this.terminal.size(); if (size != 0) { this.terminal.clear(); this.notifyItemRangeRemoved(0, size); } } } public void scrollUp() { synchronized (lock) { this.recyclerView.scrollToPosition(0); } } public void scrollDown() { synchronized (lock) { this.recyclerView.scrollToPosition(this.terminal.size() - 1); } } public void enableAnsi() { this.ansiEnabled = true; } public void disableAnsi() { this.ansiEnabled = false; this.ansiContext.reset(); } public boolean isAnsiEnabled() { return this.ansiEnabled; } private ProcessedLine process(String line) { if (line.isEmpty()) return new ProcessedLine(" ", null); return new ProcessedLine(line, this.ansiEnabled ? this.ansiContext.parseAsSpannable(line) : null); } public static class TextViewHolder extends RecyclerView.ViewHolder { private final TextView textView; public TextViewHolder(@NonNull TextView itemView,int foreground) { super(itemView); this.textView = itemView; itemView.setTypeface(Typeface.MONOSPACE); itemView.setTextColor(foreground); itemView.setTextSize(12); itemView.setLines(1); itemView.setText(" "); } private void setText(String text) { this.textView.setText(text.isEmpty() ? " " : text); } } private static class ProcessedLine { public final String line; public final Spannable spannable; ProcessedLine(String line, Spannable spannable) { this.line = line; this.spannable = spannable; } public void setText(TextView textView) { textView.setText(this.spannable == null ? this.line: this.spannable); } } }