Implement IDELogService

This commit is contained in:
Akash Yadav
2022-01-03 10:52:40 +05:30
parent 214a0fe5f3
commit bcd3c1de79
7 changed files with 202 additions and 110 deletions
@@ -205,6 +205,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mPagerAdapter = new EditorPagerAdapter (getSupportFragmentManager (), this.mProject);
mFileTreeFragment = FileTreeFragment.newInstance (this.mProject).setFileActionListener (this);
mDaemonStatusFragment = new TextSheetFragment ().setTextSelectable (true);
ActionBarDrawerToggle toggle = new ActionBarDrawerToggle (this, mBinding.editorDrawerLayout, mBinding.editorToolbar, R.string.app_name, R.string.app_name);
mBinding.editorDrawerLayout.addDrawerListener (toggle);
mBinding.startNav.setNavigationItemSelectedListener (this);
@@ -20,6 +20,7 @@ package com.itsaky.androidide.app;
import com.google.firebase.messaging.FirebaseMessaging;
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
import com.itsaky.androidide.project.ProjectResourceFinder;
import com.itsaky.androidide.services.IDELogService;
import com.itsaky.androidide.services.MessagingService;
import com.itsaky.inflater.ILayoutInflater;
import com.itsaky.inflater.IResourceFinder;
@@ -41,7 +42,8 @@ public class StudioApp extends BaseApplication {
private static ApiInfo mApiInfo;
private static AttrInfo mAttrInfo;
private static WidgetInfo mWidgetInfo;
private IDELogService ideLogService;
private IResourceFinder mResFinder;
private XMLCompletionService mXmlCompletionService;
@@ -53,29 +55,15 @@ public class StudioApp extends BaseApplication {
@Override
public void onCreate() {
this.instance = this;
instance = this;
super.onCreate();
this.ideLogService = new IDELogService ();
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
initializeApiInformation();
}
private void handleLog(CharSequence seq) {
if(seq == null)
return;
String line = seq.toString();
line = line.endsWith("\n") ? line : line + "\n";
File log = new File(FileUtil.getExternalStorageDir(), "ide_xlog/idelog.txt");
try {
FileOutputStream os = new FileOutputStream(log, true);
os.write(line.getBytes());
os.close();
} catch (IOException e) {
// ignored
}
initializeApiInformation();
}
public void createInflater (LayoutInflaterConfiguration config) {
@@ -100,6 +88,10 @@ public class StudioApp extends BaseApplication {
return this.mResFinder;
}
public IDELogService getIdeLogService () {
return ideLogService;
}
/**
* Reads API version information from api-versions.xml
*/
@@ -126,15 +118,15 @@ public class StudioApp extends BaseApplication {
}
public ApiInfo apiInfo() {
return this.mApiInfo;
return mApiInfo;
}
public AttrInfo attrInfo () {
return this.mAttrInfo;
return mAttrInfo;
}
public WidgetInfo widgetInfo () {
return this.mWidgetInfo;
return mWidgetInfo;
}
public boolean isXmlServiceStarted() {
@@ -144,12 +136,8 @@ public class StudioApp extends BaseApplication {
public void setStopGradleDaemon(boolean startGradleDaemon) {
this.stopGradleDaemon = startGradleDaemon;
}
public boolean isStopGradleDaemon() {
return stopGradleDaemon;
}
public static StudioApp getInstance() {
public static StudioApp getInstance() {
return instance;
}
}
@@ -17,8 +17,15 @@
package com.itsaky.androidide.fragments;
import com.itsaky.androidide.app.StudioApp;
public class IDELogFragment extends LogViewFragment {
public IDELogFragment () {
final var logService = StudioApp.getInstance ().getIdeLogService ();
if (!logService.isStarted ()) {
logService.start ();
}
logService.addLogListener (this::appendLog);
}
}
@@ -18,6 +18,8 @@
package com.itsaky.androidide.models;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.itsaky.androidide.R;
import java.util.Locale;
@@ -25,6 +27,8 @@ import io.github.rosemoe.editor.widget.EditorColorScheme;
public class LogLine {
public String unformatted;
public String date;
public String time;
public String pid;
@@ -35,21 +39,28 @@ public class LogLine {
public int priority;
public boolean formatted;
// It makes things easier in LogLanguageImpl
public static final int INFO = EditorColorScheme.LOG_INFO;
public static final int DEBUG = EditorColorScheme.LOG_DEBUG;
public static final int ERROR = EditorColorScheme.LOG_ERROR;
public static final int WARNING = EditorColorScheme.LOG_WARNING;
public LogLine(LogLine src ) {
public LogLine(@NonNull LogLine src ) {
this(src.date, src.time, src.pid, src.tid, src.priorityChar, src.tag, src.message);
}
public LogLine(String date, String time, String pid, String tid, String priorityChar, String tag) {
this(date, time, pid, tid, priorityChar, tag, "");
public LogLine (String date, String time, String pid, String tid, String priorityChar, String tag, String message) {
this (date, time, pid, tid, priorityChar, tag, message, true);
}
public LogLine(String date, String time, String pid, String tid, String priorityChar, String tag, String message) {
public LogLine (String unformatted) {
this.unformatted = unformatted;
this.formatted = false;
}
public LogLine(String date, String time, String pid, String tid, String priorityChar, @NonNull String tag, String message, boolean formatted) {
this.date = date;
this.time = time;
this.pid = pid;
@@ -58,12 +69,31 @@ public class LogLine {
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));
}
}
@NonNull
public static LogLine forLogString (@NonNull final String log) {
try {
final var split = log.split ("\\s");
return new LogLine(
split[0], // date
split[1], // time
split[2], // process id
split[3], // thread id
split[4], // priority
split[5], // tag
split[6] // message
);
} catch (Throwable th) {
return new LogLine (log);
}
}
private int parsePriority(String s) {
if(s == null || s.trim().length() > 1) {
return DEBUG;
@@ -94,6 +124,6 @@ public class LogLine {
@Override
public String toString() {
return String.format("%-6s %-13s %-6s %-6s %-2s %-40s %s", date, time, pid, tid, priorityChar, tag, message);
return this.formatted ? String.format("%-6s %-13s %-6s %-6s %-2s %-40s %s", date, time, pid, tid, priorityChar, tag, message) : this.unformatted;
}
}
@@ -0,0 +1,69 @@
/*
* 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.services;
import androidx.annotation.NonNull;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.shell.ShellServer;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
public class IDELogService {
private final ShellServer shell;
private final List<LogReceiver.LogListener> logListeners;
private boolean started;
public IDELogService () {
this.shell = StudioApp.getInstance ().newShell (this::output);
this.logListeners = new ArrayList<> ();
}
public void start () {
if (started) {
return;
}
this.shell.bgAppend ("logcat -v threadtime");
started = true;
}
public boolean isStarted () {
return started;
}
public void addLogListener (LogReceiver.LogListener listener) {
this.logListeners.add (Objects.requireNonNull (listener));
}
public void removeLogListener (LogReceiver.LogListener listener) {
this.logListeners.remove (Objects.requireNonNull (listener));
}
private void output (@NonNull String log) {
final var line = LogLine.forLogString (log);
for (final var listener : this.logListeners) {
listener.appendLogLine (line);
}
}
}
@@ -122,7 +122,11 @@ public abstract class BaseApplication extends MultiDexApplication {
}
public ShellServer newShell(ShellServer.Callback callback, boolean redirectErrors) {
ShellServer shellServer = new ShellServer(callback, "sh", Environment.mkdirIfNotExits(getRootDir()).getAbsolutePath(), Environment.getEnvironment(true), redirectErrors);
ShellServer shellServer = new ShellServer(callback,
"sh",
Environment.mkdirIfNotExits(getRootDir()).getAbsolutePath(),
Environment.getEnvironment(true),
redirectErrors);
shellServer.start();
return shellServer;
}
@@ -1,24 +1,28 @@
/************************************************************************************
* 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.shell;
import androidx.annotation.NonNull;
import com.itsaky.androidide.utils.Logger;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
@@ -28,14 +32,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ShellServer extends Thread {
@@ -43,119 +40,115 @@ public class ShellServer extends Thread {
private BufferedReader output;
private Process process;
public ShellServer(Callback callback, String command, String dirPath, Map<String, String> env, boolean redirectErrors) {
private static final Logger LOG = Logger.instance ("ShellServer");
public ShellServer (Callback callback, String command, String dirPath, Map<String, String> env, boolean redirectErrors) {
this.callback = callback;
ProcessBuilder processBuilder = new ProcessBuilder(new String[]{command});
processBuilder.directory(new File(dirPath));
processBuilder.redirectErrorStream(redirectErrors);
processBuilder.environment().putAll(env);
ProcessBuilder processBuilder = new ProcessBuilder (command);
processBuilder.directory (new File (dirPath));
processBuilder.redirectErrorStream (redirectErrors);
processBuilder.environment ().putAll (env);
try {
this.process = processBuilder.start();
this.output = new BufferedReader(new InputStreamReader(process.getInputStream()));
this.process = processBuilder.start ();
this.output = new BufferedReader (new InputStreamReader (process.getInputStream ()));
} catch (Throwable th) {
if (callback != null) {
String out = getFullStackTrace(th).concat("\n");
callback.output(out);
String out = getFullStackTrace (th).concat ("\n");
callback.output (out);
}
}
}
public InputStream getProcessInputStream() {
return process.getInputStream();
public InputStream getProcessInputStream () {
return process.getInputStream ();
}
public OutputStream getProcessOutputStream() {
return process.getOutputStream();
public OutputStream getProcessOutputStream () {
return process.getOutputStream ();
}
public void append(String str) {
append(str, true);
public void append (String str) {
append (str, true);
}
public ShellServer bgAppend(String str) {
append(str, false);
return this;
}
public ShellServer bgAppend(String... arr) {
for (String s : arr)
bgAppend(s);
return this;
}
public void append(String str, boolean z) {
public void bgAppend (String str) {
append (str, false);
}
public void append (String str, boolean z) {
if (z) {
try {
if (str.endsWith("\n")) {
output(str);
if (str.endsWith ("\n")) {
output (str);
} else {
output(str.concat("\n"));
output (str.concat ("\n"));
}
} catch (Throwable th) {
output(th.toString().concat("\n"));
output (th.toString ().concat ("\n"));
return;
}
}
try {
this.process.getOutputStream().write(str.concat("\n").getBytes());
this.process.getOutputStream().flush();
} catch (Throwable e) {
// Ignored
}
this.process.getOutputStream ().write (str.concat ("\n").getBytes ());
this.process.getOutputStream ().flush ();
} catch (Throwable e) {
LOG.error ("Unable to write to shell server", e);
}
}
public void append(String... strArr) {
public void append (@NonNull String... strArr) {
for (String append : strArr) {
append(append);
append (append);
}
}
public void exit() {
append("exit");
public void exit () {
append ("exit");
}
public void output(String str) {
public void output (String str) {
if (this.callback != null) {
this.callback.output(str);
this.callback.output (str);
}
}
@Override
public void run() {
public void run () {
while (true) {
try {
String readLine = this.output.readLine();
String readLine = this.output.readLine ();
if (readLine == null) {
break;
}
output(readLine.concat("\n"));
output (readLine.concat ("\n"));
} catch (Throwable th) {
output(th.toString().concat("\n"));
output (th.toString ().concat ("\n"));
}
}
closeIOQuietly(new Closeable[]{this.output, this.process.getInputStream(), this.process.getErrorStream(), this.process.getOutputStream()});
this.process.destroy();
closeIOQuietly (this.output, this.process.getInputStream (), this.process.getErrorStream (), this.process.getOutputStream ());
this.process.destroy ();
}
private String getFullStackTrace (Throwable th) {
@NonNull
private String getFullStackTrace (@NonNull Throwable th) {
StringWriter sw = new StringWriter ();
th.printStackTrace( new PrintWriter (sw));
return sw.toString();
th.printStackTrace (new PrintWriter (sw));
return sw.toString ();
}
private void closeIOQuietly (Closeable ... toClose) {
private void closeIOQuietly (@NonNull Closeable... toClose) {
for (Closeable c : toClose) {
try {
c.close();
c.close ();
} catch (IOException e) {
// ignored
}
}
}
public interface Callback {
public void output(CharSequence charSequence);
}
public interface Callback {
void output (String charSequence);
}
}