From dc5c3a94d240108ddeaa391d18894cafeddeae0a Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 04:35:17 +0300 Subject: [PATCH 01/12] added gradle files + fixed build errors --- .gitignore | 1 + AndroidManifest.xml | 2 +- build.gradle | 31 +++++++++++++++++++++++++++++++ gradle.properties | 22 ++++++++++++++++++++++ res/layout/main.xml | 16 +++++++--------- 5 files changed, 62 insertions(+), 10 deletions(-) create mode 100644 build.gradle create mode 100644 gradle.properties diff --git a/.gitignore b/.gitignore index fab398e..2e35763 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ gen/ +build/ local.properties .DS_STORE *.swp diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 9c395e1..4be2d9c 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -6,7 +6,7 @@ - + diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..a7376d7 --- /dev/null +++ b/build.gradle @@ -0,0 +1,31 @@ +apply plugin: 'com.android.application' +//apply plugin: 'android-library' + +group = GROUP +version = VERSION + +description = "Android Logcat/Dmesg viewer for devices" + +sourceCompatibility = 1.7 +targetCompatibility = 1.7 + +android { + compileSdkVersion 22 + buildToolsVersion '22.0.1' + defaultConfig { + applicationId "com.michaelrnovak.util.logger" + minSdkVersion 3 + targetSdkVersion 3 + versionCode 7 + versionName "1.4" + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + aidl.srcDirs = ['src'] + res.srcDirs = ['res'] + } + } +} diff --git a/gradle.properties b/gradle.properties new file mode 100644 index 0000000..690844f --- /dev/null +++ b/gradle.properties @@ -0,0 +1,22 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Settings specified in this file will override any Gradle settings +# configured through the IDE. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx10248m -XX:MaxPermSize=256m +# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +GROUP=com.github.androidnerds +VERSION=1.4 +ARTIFACT_ID=logger diff --git a/res/layout/main.xml b/res/layout/main.xml index 25d65d2..1e18d39 100644 --- a/res/layout/main.xml +++ b/res/layout/main.xml @@ -24,15 +24,13 @@ android:layout_height="fill_parent" > - + - + From 81fc9d24cd872879c3ce9e4073a51f1e372a1871 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 04:52:07 +0300 Subject: [PATCH 02/12] added some translatable strings --- res/values/strings.xml | 8 +++++++- src/com/michaelrnovak/util/logger/Logger.java | 18 +++++++----------- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 6a6c198..8d2830f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -1,5 +1,11 @@ - Hello World, Logger! Logger + + Filter Log + Filter Tag + Select Buffer + Email Log + Save Log + Select Log diff --git a/src/com/michaelrnovak/util/logger/Logger.java b/src/com/michaelrnovak/util/logger/Logger.java index db40044..2021a54 100644 --- a/src/com/michaelrnovak/util/logger/Logger.java +++ b/src/com/michaelrnovak/util/logger/Logger.java @@ -136,17 +136,13 @@ public boolean onPrepareOptionsMenu(Menu menu) { @Override public boolean onCreateOptionsMenu(Menu menu) { - if (mBuffer == 0) { - menu.add(Menu.NONE, FILTER_OPTION, 1, "Filter Log").setIcon(R.drawable.ic_menu_filter); - } else { - menu.add(Menu.NONE, FILTER_OPTION, 1, "Filter Log").setIcon(R.drawable.ic_menu_filter).setEnabled(false); - } - - menu.add(Menu.NONE, TAG_OPTION, 2, "Filter Tag").setIcon(R.drawable.ic_menu_tag); - menu.add(Menu.NONE, BUFFER_OPTION, 3, "Select Buffer").setIcon(android.R.drawable.ic_menu_manage); - menu.add(Menu.NONE, EMAIL_OPTION, 4, "Email Log").setIcon(android.R.drawable.ic_menu_send); - menu.add(Menu.NONE, SAVE_OPTION, 5, "Save Log").setIcon(android.R.drawable.ic_menu_save); - menu.add(Menu.NONE, TYPE_OPTION, 6, "Select Log").setIcon(R.drawable.ic_menu_monitor); + menu.add(Menu.NONE, FILTER_OPTION, 1, R.string.menu_filter).setIcon(R.drawable.ic_menu_filter) + .setEnabled(mBuffer == 0); + menu.add(Menu.NONE, TAG_OPTION, 2, R.string.menu_tag).setIcon(R.drawable.ic_menu_tag); + menu.add(Menu.NONE, BUFFER_OPTION, 3, R.string.menu_buffer).setIcon(android.R.drawable.ic_menu_manage); + menu.add(Menu.NONE, EMAIL_OPTION, 4, R.string.menu_email).setIcon(android.R.drawable.ic_menu_send); + menu.add(Menu.NONE, SAVE_OPTION, 5, R.string.menu_save).setIcon(android.R.drawable.ic_menu_save); + menu.add(Menu.NONE, TYPE_OPTION, 6, R.string.menu_select).setIcon(R.drawable.ic_menu_monitor); return super.onCreateOptionsMenu(menu); } From ae497df38dd50c5c535eecaef517c97dc4cf87ee Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 05:04:02 +0300 Subject: [PATCH 03/12] added Fatal log-level, reordered the log-levels, filtering shows chosen and higher levels --- src/com/michaelrnovak/util/logger/Logger.java | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/src/com/michaelrnovak/util/logger/Logger.java b/src/com/michaelrnovak/util/logger/Logger.java index 2021a54..9e584a7 100644 --- a/src/com/michaelrnovak/util/logger/Logger.java +++ b/src/com/michaelrnovak/util/logger/Logger.java @@ -70,7 +70,7 @@ public class Logger extends ListActivity { private int mLogType = 0; private String mFilterTag = ""; private boolean mServiceRunning = false; - public int MAX_LINES = 250; + public static int MAX_LINES = 250; public static final int DIALOG_FILTER_ID = 1; public static final int DIALOG_SAVE_ID = 2; public static final int DIALOG_SAVE_PROGRESS_ID = 3; @@ -84,8 +84,8 @@ public class Logger extends ListActivity { public static final int BUFFER_OPTION = Menu.FIRST + 3; public static final int TYPE_OPTION = Menu.FIRST + 4; public static final int TAG_OPTION = Menu.FIRST + 5; - final CharSequence[] items = {"Debug", "Error", "Info", "Verbose", "Warn", "All"}; - final char[] mFilters = {'D', 'E', 'I', 'V', 'W'}; + final CharSequence[] items = {"Verbose", "Debug", "Info", "Warn", "Error", "Fatal", "Silent"}; + final char[] mFilters = {'V', 'D', 'I', 'W', 'E', 'F', 'S'}; final CharSequence[] buffers = {"Main", "Radio", "Events"}; final CharSequence[] types = {"Logcat", "Dmesg"}; @@ -230,7 +230,7 @@ protected Dialog onCreateDialog(int id) { DialogInterface.OnClickListener mClickListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - if (which == 5) { + if (which >= mFilters.length) { mFilter = -1; } else { mFilter = which; @@ -478,8 +478,16 @@ public View getView(int pos, View convertView, ViewGroup parent) { return convertView; } + private int indexOfLogLevel(final char c) { + int filters = mFilters.length; + int index = 0; + for (; index < filters && mFilters[index] != c; index++) { + } + return index < filters ? index : -1; + } + public void addLine(String line) { - if (mFilter != -1 && line.charAt(0) != mFilters[mFilter]) { + if (null == line || line.length() == 0 || indexOfLogLevel(line.charAt(0)) < mFilter) { return; } @@ -523,7 +531,7 @@ public LogFormattedString(String line) { Integer labelColor = LABEL_COLOR_MAP.get(line.charAt(0)); if (labelColor == null) { - labelColor = LABEL_COLOR_MAP.get('E'); + labelColor = LABEL_COLOR_MAP.get('F'); } setSpan(new ForegroundColorSpan(labelColor), 0, 1, 0); @@ -542,11 +550,12 @@ public LogFormattedString(String line) { static { LABEL_COLOR_MAP = new HashMap(); - LABEL_COLOR_MAP.put('D', 0xff9999ff); LABEL_COLOR_MAP.put('V', 0xffcccccc); + LABEL_COLOR_MAP.put('D', 0xff9999ff); LABEL_COLOR_MAP.put('I', 0xffeeeeee); - LABEL_COLOR_MAP.put('E', 0xffff9999); LABEL_COLOR_MAP.put('W', 0xffffff99); + LABEL_COLOR_MAP.put('E', 0xffff9999); + LABEL_COLOR_MAP.put('F', 0xffff0000); } } } From 6776b80d189bcaea801a8afa874282302bd305c0 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 05:13:33 +0300 Subject: [PATCH 04/12] eliminated duplicate code --- src/com/michaelrnovak/util/logger/Logger.java | 68 +++++++++---------- 1 file changed, 31 insertions(+), 37 deletions(-) diff --git a/src/com/michaelrnovak/util/logger/Logger.java b/src/com/michaelrnovak/util/logger/Logger.java index 9e584a7..fcb5ba1 100644 --- a/src/com/michaelrnovak/util/logger/Logger.java +++ b/src/com/michaelrnovak/util/logger/Logger.java @@ -60,6 +60,8 @@ import java.util.HashMap; public class Logger extends ListActivity { + public static final String TAG = "Logger"; + private ILogProcessor mService; private AlertDialog mDialog; private ProgressDialog mProgressDialog; @@ -69,6 +71,7 @@ public class Logger extends ListActivity { private int mBuffer = 0; private int mLogType = 0; private String mFilterTag = ""; + private String mFilterTagLowerCase = ""; private boolean mServiceRunning = false; public static int MAX_LINES = 250; public static final int DIALOG_FILTER_ID = 1; @@ -88,7 +91,7 @@ public class Logger extends ListActivity { final char[] mFilters = {'V', 'D', 'I', 'W', 'E', 'F', 'S'}; final CharSequence[] buffers = {"Main", "Radio", "Events"}; final CharSequence[] types = {"Logcat", "Dmesg"}; - + @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -101,7 +104,7 @@ public void onCreate(Bundle savedInstanceState) { mAdapter = new LoggerListAdapter(this); setListAdapter(mAdapter); } - + @Override public void onResume() { super.onResume(); @@ -259,12 +262,12 @@ public void onClick(DialogInterface dialog, int which) { if (which == -1) { EditText et = (EditText) mDialog.findViewById(R.id.filename); onCreateDialog(DIALOG_SAVE_PROGRESS_ID); - Log.d("Logger", "Filename: " + et.getText().toString()); + Log.d(TAG, "Filename: " + et.getText().toString()); try { mService.write(et.getText().toString(), mFilterTag); } catch (RemoteException e) { - Log.e("Logger", "Trouble writing the log to a file"); + Log.e(TAG, "Trouble writing the log to a file"); } } } @@ -275,11 +278,13 @@ public void onClick(DialogInterface dialog, int which) { if (which == -1) { EditText et = (EditText) mDialog.findViewById(R.id.filename); mFilterTag = et.getText().toString().trim(); + mFilterTagLowerCase = mFilterTag.toLowerCase(); updateFilterTag(); } else { EditText et = (EditText) mDialog.findViewById(R.id.filename); et.setText(""); mFilterTag = ""; + mFilterTagLowerCase = ""; updateFilterTag(); } } @@ -290,7 +295,7 @@ public void stopLogging() { mServiceRunning = false; if (mServiceRunning) { - Log.d("Logger", "mServiceRunning is still TRUE"); + Log.d(TAG, "mServiceRunning is still TRUE"); } } @@ -301,32 +306,29 @@ public void startLogging() { mService.run(mLogType); mServiceRunning = true; } catch (RemoteException e) { - Log.e("Logger", "Could not start logging"); + Log.e(TAG, "Could not start logging"); } } - private void updateFilter() { + private void reset() { mAdapter.resetLines(); try { mService.reset(buffers[mBuffer].toString()); } catch (RemoteException e) { - Log.e("Logger", "Service is gone..."); + Log.e(TAG, "Service is gone..."); } mDialog.dismiss(); + + } + + private void updateFilter() { + reset(); } private void updateBuffer() { - mAdapter.resetLines(); - - try { - mService.reset(buffers[mBuffer].toString()); - } catch (RemoteException e) { - Log.e("Logger", "Service is gone..."); - } - - mDialog.dismiss(); + reset(); } private void updateLog() { @@ -335,24 +337,16 @@ private void updateLog() { try { mService.restart(mLogType); } catch (RemoteException e) { - Log.e("Logger", "Service is gone..."); + Log.e(TAG, "Service is gone..."); } mDialog.dismiss(); } private void updateFilterTag() { - mAdapter.resetLines(); - - try { - mService.reset(buffers[mBuffer].toString()); - } catch (RemoteException e) { - Log.e("Logger", "Service is gone..."); - } - - mDialog.dismiss(); + reset(); } - + private void saveResult(String msg) { mProgressDialog.dismiss(); @@ -376,7 +370,7 @@ private void generateEmailMessage() { try { mService.write("tmp.log", mFilterTag); } catch (RemoteException e) { - Log.e("Logger", "Error generating email attachment."); + Log.e(TAG, "Error generating email attachment."); } } @@ -384,10 +378,10 @@ private void generateEmailMessage() { public void handleMessage(Message msg) { switch (msg.what) { case LogProcessor.MSG_READ_FAIL: - Log.d("Logger", "MSG_READ_FAIL"); + Log.d(TAG, "MSG_READ_FAIL"); break; case LogProcessor.MSG_LOG_FAIL: - Log.d("Logger", "MSG_LOG_FAIL"); + Log.d(TAG, "MSG_LOG_FAIL"); break; case LogProcessor.MSG_NEW_LINE: mAdapter.addLine((String) msg.obj); @@ -410,12 +404,12 @@ public void onServiceConnected(ComponentName className, IBinder service) { mService.run(mLogType); mServiceRunning = true; } catch (RemoteException e) { - Log.e("Logger", "Could not start logging"); + Log.e(TAG, "Could not start logging"); } } public void onServiceDisconnected(ComponentName className) { - Log.i("Logger", "onServiceDisconnected has been called"); + Log.i(TAG, "onServiceDisconnected has been called"); mService = null; } }; @@ -468,7 +462,7 @@ public View getView(int pos, View convertView, ViewGroup parent) { holder.setText(line); } - final boolean autoscroll = + final boolean autoscroll = (getListView().getScrollY() + getListView().getHeight() >= getListView().getBottom()) ? true : false; if (autoscroll) { @@ -494,7 +488,7 @@ public void addLine(String line) { if (!mFilterTag.equals("")) { String tag = line.substring(2, line.indexOf("(")); - if (!mFilterTag.toLowerCase().equals(tag.toLowerCase().trim())) { + if (!mFilterTagLowerCase.equals(tag.toLowerCase().trim())) { return; } } @@ -538,7 +532,7 @@ public LogFormattedString(String line) { setSpan(new StyleSpan(Typeface.BOLD), 0, 1, 0); int leftIdx; - + if ((leftIdx = line.indexOf(':', 2)) >= 0) { setSpan(new ForegroundColorSpan(labelColor), 2, leftIdx, 0); setSpan(new StyleSpan(Typeface.ITALIC), 2, leftIdx, 0); @@ -547,7 +541,7 @@ public LogFormattedString(String line) { setSpan(new ForegroundColorSpan(0xffddaacc), 0, length(), 0); } } - + static { LABEL_COLOR_MAP = new HashMap(); LABEL_COLOR_MAP.put('V', 0xffcccccc); From 33232448a71682d58027e02e3ef9aeb4899320ef Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 05:33:42 +0300 Subject: [PATCH 05/12] fixed NullPointerException, removed duplicate code --- .../util/logger/service/LogProcessor.java | 59 ++++++++----------- 1 file changed, 23 insertions(+), 36 deletions(-) diff --git a/src/com/michaelrnovak/util/logger/service/LogProcessor.java b/src/com/michaelrnovak/util/logger/service/LogProcessor.java index 792c843..6a3793d 100644 --- a/src/com/michaelrnovak/util/logger/service/LogProcessor.java +++ b/src/com/michaelrnovak/util/logger/service/LogProcessor.java @@ -32,17 +32,17 @@ import java.util.Vector; public class LogProcessor extends Service { + public static final String TAG = "LogProcessor"; private static Handler mHandler; private String mFile; private String mBuffer = "main"; private Vector mScrollback; - private int mLines; private int mType; private String mFilterTag; private volatile boolean threadKill = false; private volatile boolean mStatus = false; - public int MAX_LINES = 250; + public static int MAX_LINES = 250; public static final int MSG_READ_FAIL = 1; public static final int MSG_LOG_FAIL = 2; public static final int MSG_NEW_LINE = 3; @@ -52,20 +52,20 @@ public class LogProcessor extends Service { @Override public void onCreate() { super.onCreate(); + mScrollback = new Vector(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); - Log.i("Logger", "Logger Service has hit the onStart method."); + Log.v(TAG, "Logger Service has hit the onStart method."); } Runnable worker = new Runnable() { public void run() { runLog(); mStatus = true; - Log.d("Logger", "status... " + mStatus); - return; + Log.d(TAG, "status... " + mStatus); } }; @@ -101,23 +101,19 @@ private void runLog() { } mScrollback.add(line); - mLines++; } - Log.i("Logger", "Prepping thread for termination"); + Log.i(TAG, "Prepping thread for termination"); reader.close(); process.destroy(); process = null; reader = null; mScrollback.removeAllElements(); - mScrollback = null; - mLines = 0; } catch (IOException e) { communicate(MSG_READ_FAIL); } - Log.d("Logger", "Exiting thread..."); - return; + Log.d(TAG, "Exiting thread..."); } private synchronized void requestKill() { @@ -153,50 +149,42 @@ public boolean onUnbind(Intent intent) { } private final ILogProcessor.Stub mBinder = new ILogProcessor.Stub() { - public void reset(String buffer) { + private void kill() { requestKill(); while (!mStatus) { try { - Log.d("Logger", "waiting..."); + Log.v(TAG, "waiting..."); } catch (Exception e) { - Log.d("Logger", "Woot! obj has been interrupted!"); + Log.d(TAG, "Woot! obj has been interrupted!"); } } - + threadKill = false; + } + + public void reset(String buffer) { + kill(); + mBuffer = buffer.toLowerCase(); - mLines = 0; - mScrollback = new Vector(); - Thread thr = new Thread(worker); - thr.start(); + run(mType); } public void run(int type) { mType = type; - mLines = 0; - mScrollback = new Vector(); + mScrollback.removeAllElements(); Thread thr = new Thread(worker); thr.start(); } public void restart(int type) { - requestKill(); - - while(!mStatus) { - try { - Log.d("Logger", "waiting..."); - } catch (Exception e) { - Log.d("Logger", "Woot! we have an exception"); - } - } + kill(); - threadKill = false; run(type); } public void stop() { - Log.i("Logger", "stop() method called in service."); + Log.i(TAG, "stop() method called in service."); requestKill(); stopSelf(); } @@ -212,7 +200,6 @@ public void write(String file, String tag) { Runnable writer = new Runnable() { public void run() { writeLog(); - return; } }; @@ -221,6 +208,7 @@ private void writeLog() { try { File f = new File("/sdcard/" + mFile); FileWriter w = new FileWriter(f); + final String lowerCaseFilterTag = mFilterTag.toLowerCase(); for (int i = 0; i < mScrollback.size(); i++) { String line = mScrollback.elementAt(i); @@ -228,7 +216,7 @@ private void writeLog() { if (!mFilterTag.equals("")) { String tag = line.substring(2, line.indexOf("(")); - if (mFilterTag.toLowerCase().equals(tag.toLowerCase().trim())) { + if (lowerCaseFilterTag.equals(tag.toLowerCase().trim())) { w.write(line + "\n"); } } else { @@ -247,11 +235,10 @@ private void writeLog() { w.close(); f = null; } catch (Exception e) { - Log.e("Logger", "Error writing the log to a file. Exception: " + e.toString()); + Log.e(TAG, "Error writing the log to a file.", e); Message.obtain(mHandler, MSG_LOG_SAVE, "error").sendToTarget(); } - return; } } From 9655a30f24f543c6e554a5b0032865e04daf7b43 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 05:43:28 +0300 Subject: [PATCH 06/12] fixed NullPointerException --- src/com/michaelrnovak/util/logger/service/LogProcessor.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/com/michaelrnovak/util/logger/service/LogProcessor.java b/src/com/michaelrnovak/util/logger/service/LogProcessor.java index 6a3793d..70d45c8 100644 --- a/src/com/michaelrnovak/util/logger/service/LogProcessor.java +++ b/src/com/michaelrnovak/util/logger/service/LogProcessor.java @@ -96,7 +96,7 @@ private void runLog() { logLine(line); - if (mLines == MAX_LINES) { + if (mScrollback.size() == MAX_LINES) { mScrollback.removeElementAt(0); } From c24bb82bde5fdd586ab1ed154f16c67ea1dc8655 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 06:03:45 +0300 Subject: [PATCH 07/12] added "time" log format --- .../michaelrnovak/util/logger/LogLine.java | 154 ++++++++++++++++++ src/com/michaelrnovak/util/logger/Logger.java | 52 ++---- .../util/logger/service/LogProcessor.java | 72 ++++---- 3 files changed, 216 insertions(+), 62 deletions(-) create mode 100644 src/com/michaelrnovak/util/logger/LogLine.java diff --git a/src/com/michaelrnovak/util/logger/LogLine.java b/src/com/michaelrnovak/util/logger/LogLine.java new file mode 100644 index 0000000..12e7150 --- /dev/null +++ b/src/com/michaelrnovak/util/logger/LogLine.java @@ -0,0 +1,154 @@ +package com.michaelrnovak.util.logger; + +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Created by Gavriel Fleischer on 2015-07-06. + */ +public class LogLine { + static final String DATE_FORMAT = "MM-dd HH:mm:ss.SSS"; + static final DateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat(DATE_FORMAT); + + protected final int pid; + protected final char level; + protected final String tag; + protected final String text; + + public static class Brief extends LogLine { + // V/Logger( 4973): log message + public static Brief fromString(final String line) throws ParseException { + char level = line.charAt(0); + String tag = line.substring(2, line.indexOf('(')); + int pid = getInt(line, tag.length() + 4, ' ', ')'); + String text = line.substring(line.indexOf(':', tag.length() + 5) + 2); + return new Brief(level, tag, pid, text); + } + public Brief(char level, String tag, int pid, String text) { + super(level, tag, pid, text); + } + } + + public static class Time extends Brief { + protected final Date date; + // V/Logger( 4973): log message + // 07-06 16:45:25.447 W/Logger( 5015): log message + public static Time fromString(final String line) throws ParseException { + Date date = SIMPLE_DATE_FORMAT.parse(line.substring(0, 18)); + char level = line.charAt(0 + 19); + String tag = line.substring(2 + 19, line.indexOf('(')); + int pid = getInt(line, tag.length() + 4 + 19, ' ', ')'); + String text = line.substring(line.indexOf(':', tag.length() + 5+19) + 2); + return new Time(date, level, tag, pid, text); + } + public Time(Date date, char level, String tag, int pid, String text) { + super(level, tag, pid, text); + this.date = date; + } + public Date getDate() { + return date; + } + @Override + public String getHeader() { + return date.toString() + ' ' + super.getHeader(); + } + } + + public static class Long extends Time { + protected final StringBuilder sb; + + // [ 07-06 13:21:53.668 19517:19581 W/Logger ]\nlog message + public static Long fromString(final String line) throws ParseException { + Date date = SIMPLE_DATE_FORMAT.parse(line.substring(2, 20)); + int pid = getInt(line, 21, ' ', ':'); + char level = line.charAt(33); + String tag = line.substring(35, line.length() - 2); + return new Long(date, pid, -1, level, tag, line); + } + public Long(Date date, int pid, int tid, char level, String tag) { + super(date, level, tag, pid, null); + sb = new StringBuilder(); + } + public Long(Date date, int pid, int tid, char level, String tag, String text) { + this(date, pid, tid, level, tag); + sb.append(text); + } + public boolean add(final String line) { + if (null != line) { + sb.append('\n'); + sb.append(line); + return true; + } else { + return false; + } + } + @Override + public String toString() { + return sb.toString(); + } + @Override + public String getHeader() { + final String str = toString(); + return str.substring(0, str.indexOf(']') + 1); + } + } + + public static LogLine fromString(final String line, final String format) throws ParseException { + if ("brief".equals(format)) { + return Brief.fromString(line); + } + else if ("time".equals(format)) { + return Time.fromString(line); + } + else if ("long".equals(format)) { + return Long.fromString(line); + } else { + return null; + } + } + + protected static int getInt(final String line, int offset, char before, char after) { + while (line.charAt(offset) == before) { offset++; } + final String intString = line.substring(offset, line.indexOf(after, offset)); + int integer = Integer.parseInt(intString); + return integer; + } + + protected LogLine(char level, String tag, int pid, String text) { + this.level = level; + this.tag = tag; + this.pid = pid; + this.text = text; + } + + @Override + public String toString() { + return getHeader() + ' ' + text; + } + + public int getPid() { + return pid; + } + + public char getLevel() { + return level; + } + + public String getTag() { + return tag; + } + + public String getHeader() { + final StringBuilder sb = new StringBuilder(); + sb.append(getLevel()); + sb.append('/'); + sb.append(getTag()); + sb.append('('); + sb.append(getPid()); + sb.append(')'); + sb.append(':'); + return sb.toString(); + } +} diff --git a/src/com/michaelrnovak/util/logger/Logger.java b/src/com/michaelrnovak/util/logger/Logger.java index fcb5ba1..90b4628 100644 --- a/src/com/michaelrnovak/util/logger/Logger.java +++ b/src/com/michaelrnovak/util/logger/Logger.java @@ -28,7 +28,6 @@ import android.content.ServiceConnection; import android.graphics.Typeface; import android.net.Uri; -import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -43,12 +42,10 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.ViewGroup.LayoutParams; import android.widget.BaseAdapter; import android.widget.EditText; -import android.widget.LinearLayout; +import android.widget.ListAdapter; import android.widget.ListView; -import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; @@ -73,7 +70,6 @@ public class Logger extends ListActivity { private String mFilterTag = ""; private String mFilterTagLowerCase = ""; private boolean mServiceRunning = false; - public static int MAX_LINES = 250; public static final int DIALOG_FILTER_ID = 1; public static final int DIALOG_SAVE_ID = 2; public static final int DIALOG_SAVE_PROGRESS_ID = 3; @@ -384,7 +380,7 @@ public void handleMessage(Message msg) { Log.d(TAG, "MSG_LOG_FAIL"); break; case LogProcessor.MSG_NEW_LINE: - mAdapter.addLine((String) msg.obj); + mAdapter.addLine((LogLine) msg.obj); break; case LogProcessor.MSG_LOG_SAVE: saveResult((String) msg.obj); @@ -420,11 +416,11 @@ public void onServiceDisconnected(ComponentName className) { */ public class LoggerListAdapter extends BaseAdapter { private Context mContext; - private ArrayList mLines; + private ArrayList mLines; public LoggerListAdapter(Context c) { mContext = c; - mLines = new ArrayList(); + mLines = new ArrayList(); mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } @@ -442,7 +438,7 @@ public Object getItem(int pos) { public View getView(int pos, View convertView, ViewGroup parent) { TextView holder; - String line = mLines.get(pos); + LogLine line = mLines.get(pos); if (convertView == null) { //inflate the view here because there's no existing view object. @@ -459,11 +455,11 @@ public View getView(int pos, View convertView, ViewGroup parent) { if (mLogType == 0) { holder.setText(new LogFormattedString(line)); } else { - holder.setText(line); + holder.setText(line.toString()); } final boolean autoscroll = - (getListView().getScrollY() + getListView().getHeight() >= getListView().getBottom()) ? true : false; + (getListView().getScrollY() + getListView().getHeight() >= getListView().getBottom()); if (autoscroll) { getListView().setSelection(mLines.size() - 1); @@ -480,15 +476,13 @@ private int indexOfLogLevel(final char c) { return index < filters ? index : -1; } - public void addLine(String line) { - if (null == line || line.length() == 0 || indexOfLogLevel(line.charAt(0)) < mFilter) { + public void addLine(LogLine line) { + if (null == line || indexOfLogLevel(line.getLevel()) < mFilter) { return; } - if (!mFilterTag.equals("")) { - String tag = line.substring(2, line.indexOf("(")); - - if (!mFilterTagLowerCase.equals(tag.toLowerCase().trim())) { + if (!mFilterTagLowerCase.equals("")) { + if (!mFilterTagLowerCase.equals(line.getTag().toLowerCase().trim())) { return; } } @@ -510,19 +504,11 @@ public void updateView() { private static class LogFormattedString extends SpannableString { public static final HashMap LABEL_COLOR_MAP; - public LogFormattedString(String line) { - super(line); + public LogFormattedString(LogLine line) { + super(line.toString() + '\n'); try { - if (line.length() < 4) { - throw new RuntimeException(); - } - - if (line.charAt(1) != '/') { - throw new RuntimeException(); - } - - Integer labelColor = LABEL_COLOR_MAP.get(line.charAt(0)); + Integer labelColor = LABEL_COLOR_MAP.get(line.getLevel()); if (labelColor == null) { labelColor = LABEL_COLOR_MAP.get('F'); @@ -531,12 +517,10 @@ public LogFormattedString(String line) { setSpan(new ForegroundColorSpan(labelColor), 0, 1, 0); setSpan(new StyleSpan(Typeface.BOLD), 0, 1, 0); - int leftIdx; - - if ((leftIdx = line.indexOf(':', 2)) >= 0) { - setSpan(new ForegroundColorSpan(labelColor), 2, leftIdx, 0); - setSpan(new StyleSpan(Typeface.ITALIC), 2, leftIdx, 0); - } + String header = line.getHeader(); + int headerLen = header.length(); + setSpan(new ForegroundColorSpan(labelColor), 0, headerLen, 0); + setSpan(new StyleSpan(Typeface.ITALIC), 0, headerLen, 0); } catch (Exception e) { setSpan(new ForegroundColorSpan(0xffddaacc), 0, length(), 0); } diff --git a/src/com/michaelrnovak/util/logger/service/LogProcessor.java b/src/com/michaelrnovak/util/logger/service/LogProcessor.java index 70d45c8..a195d07 100644 --- a/src/com/michaelrnovak/util/logger/service/LogProcessor.java +++ b/src/com/michaelrnovak/util/logger/service/LogProcessor.java @@ -31,18 +31,21 @@ import java.io.InputStreamReader; import java.util.Vector; +import com.michaelrnovak.util.logger.LogLine; + public class LogProcessor extends Service { public static final String TAG = "LogProcessor"; - + private static Handler mHandler; private String mFile; private String mBuffer = "main"; - private Vector mScrollback; + private String mLogFormat = "time"; // brief | time | long + private Vector mScrollback; private int mType; private String mFilterTag; private volatile boolean threadKill = false; private volatile boolean mStatus = false; - public static int MAX_LINES = 250; + public static final int MAX_LINES = 250; public static final int MSG_READ_FAIL = 1; public static final int MSG_LOG_FAIL = 2; public static final int MSG_NEW_LINE = 3; @@ -52,7 +55,7 @@ public class LogProcessor extends Service { @Override public void onCreate() { super.onCreate(); - mScrollback = new Vector(); + mScrollback = new Vector(); } @Override @@ -68,14 +71,14 @@ public void run() { Log.d(TAG, "status... " + mStatus); } }; - + private void runLog() { Process process = null; try { if (mType == 0) { - process = Runtime.getRuntime().exec("/system/bin/logcat -b " + mBuffer); + process = Runtime.getRuntime().exec("/system/bin/logcat -v " + mLogFormat + " -b " + mBuffer); } else if (mType == 1) { process = Runtime.getRuntime().exec("dmesg -s 1000000"); } @@ -88,21 +91,37 @@ private void runLog() { try { reader = new BufferedReader(new InputStreamReader(process.getInputStream())); - - String line; - + while (!killRequested()) { - line = reader.readLine(); - - logLine(line); - - if (mScrollback.size() == MAX_LINES) { - mScrollback.removeElementAt(0); + String line = reader.readLine(); + try { + LogLine logLine = LogLine.fromString(line, mLogFormat); + if (logLine instanceof LogLine.Long) { + boolean emptyLine = false; + boolean nextLine = false; + do { + reader.mark(1024); + line = reader.readLine(); + if (emptyLine && null != line && line.length() > 0 && line.charAt(0) == '[') { + reader.reset(); + nextLine = true; + } + emptyLine = (null == line || line.length() == 0); + } while (!nextLine && ((LogLine.Long) logLine).add(line)); + } +// if (logLine.getPid() == android.os.Process.myPid()) { + logLine(logLine); + + if (mScrollback.size() >= MAX_LINES) { + mScrollback.removeElementAt(0); + } + mScrollback.add(logLine); +// } + } catch (Exception e) { + Log.e(TAG, "runLog: " + line + ";", e); } - - mScrollback.add(line); } - + Log.i(TAG, "Prepping thread for termination"); reader.close(); process.destroy(); @@ -112,7 +131,7 @@ private void runLog() { } catch (IOException e) { communicate(MSG_READ_FAIL); } - + Log.d(TAG, "Exiting thread..."); } @@ -128,7 +147,7 @@ private void communicate(int msg) { Message.obtain(mHandler, msg, "error").sendToTarget(); } - private void logLine(String line) { + private void logLine(LogLine line) { Message.obtain(mHandler, MSG_NEW_LINE, line).sendToTarget(); } @@ -179,7 +198,7 @@ public void run(int type) { public void restart(int type) { kill(); - + run(type); } @@ -209,14 +228,12 @@ private void writeLog() { File f = new File("/sdcard/" + mFile); FileWriter w = new FileWriter(f); final String lowerCaseFilterTag = mFilterTag.toLowerCase(); - + for (int i = 0; i < mScrollback.size(); i++) { - String line = mScrollback.elementAt(i); + final LogLine line = mScrollback.elementAt(i); - if (!mFilterTag.equals("")) { - String tag = line.substring(2, line.indexOf("(")); - - if (lowerCaseFilterTag.equals(tag.toLowerCase().trim())) { + if (!lowerCaseFilterTag.equals("")) { + if (lowerCaseFilterTag.equals(line.getTag().toLowerCase().trim())) { w.write(line + "\n"); } } else { @@ -238,7 +255,6 @@ private void writeLog() { Log.e(TAG, "Error writing the log to a file.", e); Message.obtain(mHandler, MSG_LOG_SAVE, "error").sendToTarget(); } - } } From 4f2f5f9477546448c6c7f90f557d9101e15dc68c Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Tue, 7 Jul 2015 17:30:42 +0300 Subject: [PATCH 08/12] converted to Fragment + FragmentActivity --- AndroidManifest.xml | 37 ++-- build.gradle | 12 +- gradle.properties | 2 +- res/layout/activity_logger.xml | 7 + res/layout/fragment_logger.xml | 20 ++ res/layout/main.xml | 36 ---- res/menu/menu_logger.xml | 36 ++++ res/values-w820dp/dimens.xml | 6 + res/values/dimens.xml | 5 + res/values/strings.xml | 2 + .../michaelrnovak/util/logger/LogLine.java | 17 ++ .../util/logger/LoggerActivity.java | 14 ++ .../{Logger.java => LoggerFragment.java} | 173 +++++++++--------- .../util/logger/service/LogProcessor.java | 3 +- 14 files changed, 232 insertions(+), 138 deletions(-) create mode 100644 res/layout/activity_logger.xml create mode 100644 res/layout/fragment_logger.xml delete mode 100644 res/layout/main.xml create mode 100644 res/menu/menu_logger.xml create mode 100644 res/values-w820dp/dimens.xml create mode 100644 res/values/dimens.xml create mode 100644 src/com/michaelrnovak/util/logger/LoggerActivity.java rename src/com/michaelrnovak/util/logger/{Logger.java => LoggerFragment.java} (77%) diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 4be2d9c..dd48ff9 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -1,23 +1,34 @@ - + package="com.michaelrnovak.util.logger" + android:versionCode="8" + android:versionName="1.5" > + + + - - - + + + + + + + - - + + - - + diff --git a/build.gradle b/build.gradle index a7376d7..c9f8e3c 100644 --- a/build.gradle +++ b/build.gradle @@ -9,15 +9,19 @@ description = "Android Logcat/Dmesg viewer for devices" sourceCompatibility = 1.7 targetCompatibility = 1.7 +dependencies { + compile 'com.android.support:appcompat-v7:22.2.0' +} + android { compileSdkVersion 22 buildToolsVersion '22.0.1' defaultConfig { applicationId "com.michaelrnovak.util.logger" - minSdkVersion 3 - targetSdkVersion 3 - versionCode 7 - versionName "1.4" + minSdkVersion 7 + targetSdkVersion 7 + versionCode 8 + versionName "1.5" } sourceSets { diff --git a/gradle.properties b/gradle.properties index 690844f..cf7e0aa 100644 --- a/gradle.properties +++ b/gradle.properties @@ -18,5 +18,5 @@ # org.gradle.parallel=true GROUP=com.github.androidnerds -VERSION=1.4 +VERSION=1.5 ARTIFACT_ID=logger diff --git a/res/layout/activity_logger.xml b/res/layout/activity_logger.xml new file mode 100644 index 0000000..4d42c52 --- /dev/null +++ b/res/layout/activity_logger.xml @@ -0,0 +1,7 @@ + diff --git a/res/layout/fragment_logger.xml b/res/layout/fragment_logger.xml new file mode 100644 index 0000000..1f0ebe7 --- /dev/null +++ b/res/layout/fragment_logger.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/res/layout/main.xml b/res/layout/main.xml deleted file mode 100644 index 1e18d39..0000000 --- a/res/layout/main.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - diff --git a/res/menu/menu_logger.xml b/res/menu/menu_logger.xml new file mode 100644 index 0000000..3e65b1a --- /dev/null +++ b/res/menu/menu_logger.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + diff --git a/res/values-w820dp/dimens.xml b/res/values-w820dp/dimens.xml new file mode 100644 index 0000000..63fc816 --- /dev/null +++ b/res/values-w820dp/dimens.xml @@ -0,0 +1,6 @@ + + + 64dp + diff --git a/res/values/dimens.xml b/res/values/dimens.xml new file mode 100644 index 0000000..47c8224 --- /dev/null +++ b/res/values/dimens.xml @@ -0,0 +1,5 @@ + + + 16dp + 16dp + diff --git a/res/values/strings.xml b/res/values/strings.xml index 8d2830f..4da34f6 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -2,6 +2,8 @@ Logger + Logs + Filter Log Filter Tag Select Buffer diff --git a/src/com/michaelrnovak/util/logger/LogLine.java b/src/com/michaelrnovak/util/logger/LogLine.java index 12e7150..d2bd5dd 100644 --- a/src/com/michaelrnovak/util/logger/LogLine.java +++ b/src/com/michaelrnovak/util/logger/LogLine.java @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2015 Gavriel Fleischer + * + * This program 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 2 + * of the License, or (at your option) any later version. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ package com.michaelrnovak.util.logger; import java.text.DateFormat; diff --git a/src/com/michaelrnovak/util/logger/LoggerActivity.java b/src/com/michaelrnovak/util/logger/LoggerActivity.java new file mode 100644 index 0000000..17d7243 --- /dev/null +++ b/src/com/michaelrnovak/util/logger/LoggerActivity.java @@ -0,0 +1,14 @@ +package com.michaelrnovak.util.logger; + +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; + + +public class LoggerActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_logger); + } +} diff --git a/src/com/michaelrnovak/util/logger/Logger.java b/src/com/michaelrnovak/util/logger/LoggerFragment.java similarity index 77% rename from src/com/michaelrnovak/util/logger/Logger.java rename to src/com/michaelrnovak/util/logger/LoggerFragment.java index 90b4628..ff9fe2d 100644 --- a/src/com/michaelrnovak/util/logger/Logger.java +++ b/src/com/michaelrnovak/util/logger/LoggerFragment.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2009, 2010 Michael Novak - * + * 2015 Gavriel Fleischer + * * This program 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 2 @@ -17,7 +18,6 @@ */ package com.michaelrnovak.util.logger; -import android.app.ListActivity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; @@ -33,12 +33,16 @@ import android.os.IBinder; import android.os.Message; import android.os.RemoteException; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; import android.text.SpannableString; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -56,8 +60,19 @@ import java.util.ArrayList; import java.util.HashMap; -public class Logger extends ListActivity { +public class LoggerFragment extends Fragment { public static final String TAG = "Logger"; + public static final int DIALOG_FILTER_ID = 1; + public static final int DIALOG_SAVE_ID = 2; + public static final int DIALOG_SAVE_PROGRESS_ID = 3; + public static final int DIALOG_EMAIL_ID = 4; + public static final int DIALOG_BUFFER_ID = 5; + public static final int DIALOG_TYPE_ID = 6; + public static final int DIALOG_TAG_ID = 7; + static final CharSequence[] LOG_LEVEL_NAMES = {"Verbose", "Debug", "Info", "Warn", "Error", "Fatal", "Silent"}; + static final char[] LOG_LEVEL_CHARS = {'V', 'D', 'I', 'W', 'E', 'F', 'S'}; + static final CharSequence[] BUFFERS = {"Main", "Radio", "Events"}; + static final CharSequence[] TYPES = {"Logcat", "Dmesg"}; private ILogProcessor mService; private AlertDialog mDialog; @@ -70,41 +85,36 @@ public class Logger extends ListActivity { private String mFilterTag = ""; private String mFilterTagLowerCase = ""; private boolean mServiceRunning = false; - public static final int DIALOG_FILTER_ID = 1; - public static final int DIALOG_SAVE_ID = 2; - public static final int DIALOG_SAVE_PROGRESS_ID = 3; - public static final int DIALOG_EMAIL_ID = 4; - public static final int DIALOG_BUFFER_ID = 5; - public static final int DIALOG_TYPE_ID = 6; - public static final int DIALOG_TAG_ID = 7; - public static final int FILTER_OPTION = Menu.FIRST; - public static final int EMAIL_OPTION = Menu.FIRST + 1; - public static final int SAVE_OPTION = Menu.FIRST + 2; - public static final int BUFFER_OPTION = Menu.FIRST + 3; - public static final int TYPE_OPTION = Menu.FIRST + 4; - public static final int TAG_OPTION = Menu.FIRST + 5; - final CharSequence[] items = {"Verbose", "Debug", "Info", "Warn", "Error", "Fatal", "Silent"}; - final char[] mFilters = {'V', 'D', 'I', 'W', 'E', 'F', 'S'}; - final CharSequence[] buffers = {"Main", "Radio", "Events"}; - final CharSequence[] types = {"Logcat", "Dmesg"}; + + private FragmentActivity fragmentActivity; + private ListView mListView; + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + + fragmentActivity = (FragmentActivity)super.getActivity(); + setHasOptionsMenu(true); + return inflater.inflate(R.layout.fragment_logger, container, false); + } @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.main); + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); - getListView().setStackFromBottom(true); - getListView().setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); - getListView().setDividerHeight(0); + ListView listView = getListView(); + listView.setStackFromBottom(true); + listView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); + listView.setDividerHeight(0); - mAdapter = new LoggerListAdapter(this); + mAdapter = new LoggerListAdapter(fragmentActivity); setListAdapter(mAdapter); } @Override public void onResume() { super.onResume(); - bindService(new Intent(this, LogProcessor.class), mConnection, Context.BIND_AUTO_CREATE); + fragmentActivity.bindService(new Intent(fragmentActivity, LogProcessor.class), mConnection, Context.BIND_AUTO_CREATE); //TODO: make sure this actually deletes and doesn't append. File f = new File("/sdcard/tmp.log"); @@ -117,101 +127,98 @@ public void onResume() { @Override public void onPause() { super.onPause(); - unbindService(mConnection); + fragmentActivity.unbindService(mConnection); } @Override - public boolean onPrepareOptionsMenu(Menu menu) { - MenuItem item = menu.getItem(0); + public void onPrepareOptionsMenu(Menu menu) { + menu.findItem(R.id.action_filter).setEnabled(mBuffer == 0); - if (mBuffer != 0) { - item.setEnabled(false); - } else { - item.setEnabled(true); - } - - return super.onPrepareOptionsMenu(menu); + super.onPrepareOptionsMenu(menu); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - menu.add(Menu.NONE, FILTER_OPTION, 1, R.string.menu_filter).setIcon(R.drawable.ic_menu_filter) - .setEnabled(mBuffer == 0); - menu.add(Menu.NONE, TAG_OPTION, 2, R.string.menu_tag).setIcon(R.drawable.ic_menu_tag); - menu.add(Menu.NONE, BUFFER_OPTION, 3, R.string.menu_buffer).setIcon(android.R.drawable.ic_menu_manage); - menu.add(Menu.NONE, EMAIL_OPTION, 4, R.string.menu_email).setIcon(android.R.drawable.ic_menu_send); - menu.add(Menu.NONE, SAVE_OPTION, 5, R.string.menu_save).setIcon(android.R.drawable.ic_menu_save); - menu.add(Menu.NONE, TYPE_OPTION, 6, R.string.menu_select).setIcon(R.drawable.ic_menu_monitor); - - return super.onCreateOptionsMenu(menu); + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + inflater.inflate(R.menu.menu_logger, menu); + super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case FILTER_OPTION: + int id = item.getItemId(); + if (id == R.id.action_filter) { onCreateDialog(DIALOG_FILTER_ID); - break; - case EMAIL_OPTION: + } else if (id == R.id.action_email) { generateEmailMessage(); - break; - case SAVE_OPTION: + } else if (id == R.id.action_save) { onCreateDialog(DIALOG_SAVE_ID); - break; - case BUFFER_OPTION: + } else if (id == R.id.action_buffer) { onCreateDialog(DIALOG_BUFFER_ID); - break; - case TYPE_OPTION: + } else if (id == R.id.action_select) { onCreateDialog(DIALOG_TYPE_ID); - break; - case TAG_OPTION: + } else if (id == R.id.action_tag) { onCreateDialog(DIALOG_TAG_ID); - break; - default: - break; + } else { } - return false; } - + + protected ListView getListView() { + if (mListView == null) { + mListView = (ListView)getView().findViewById(android.R.id.list); + } + return mListView; + } + protected void setListAdapter(ListAdapter adapter) { + getListView().setAdapter(adapter); + } +// protected ListAdapter getListAdapter() { +// ListAdapter adapter = getListView().getAdapter(); +// if (adapter instanceof HeaderViewListAdapter) { +// return ((HeaderViewListAdapter)adapter).getWrappedAdapter(); +// } else { +// return adapter; +// } +// } + protected Dialog onCreateDialog(int id) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); + AlertDialog.Builder builder = new AlertDialog.Builder(fragmentActivity); switch (id) { case DIALOG_FILTER_ID: builder.setTitle("Select a filter level"); - builder.setSingleChoiceItems(items, mFilter, mClickListener); + builder.setSingleChoiceItems(LOG_LEVEL_NAMES, mFilter, mClickListener); mDialog = builder.create(); break; case DIALOG_SAVE_ID: builder.setTitle("Enter filename:"); - LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View v = inflater.inflate(R.layout.file_save, (ViewGroup) findViewById(R.id.layout_root)); + LayoutInflater inflater = (LayoutInflater)fragmentActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View v = inflater.inflate(R.layout.file_save, (ViewGroup)getView().findViewById(R.id.layout_root)); builder.setView(v); builder.setNegativeButton("Cancel", mButtonListener); builder.setPositiveButton("Save", mButtonListener); mDialog = builder.create(); break; case DIALOG_SAVE_PROGRESS_ID: - mProgressDialog = ProgressDialog.show(this, "", "Saving...", true); + mProgressDialog = ProgressDialog.show(fragmentActivity, "", "Saving...", true); return mProgressDialog; case DIALOG_EMAIL_ID: - mProgressDialog = ProgressDialog.show(this, "", "Generating attachment...", true); + mProgressDialog = ProgressDialog.show(fragmentActivity, "", "Generating attachment...", true); return mProgressDialog; case DIALOG_BUFFER_ID: builder.setTitle("Select a buffer"); - builder.setSingleChoiceItems(buffers, mBuffer, mBufferListener); + builder.setSingleChoiceItems(BUFFERS, mBuffer, mBufferListener); mDialog = builder.create(); break; case DIALOG_TYPE_ID: builder.setTitle("Select a log"); - builder.setSingleChoiceItems(types, mLogType, mTypeListener); + builder.setSingleChoiceItems(TYPES, mLogType, mTypeListener); mDialog = builder.create(); break; case DIALOG_TAG_ID: builder.setTitle("Enter tag name"); - LayoutInflater inflate = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); - View t = inflate.inflate(R.layout.file_save, (ViewGroup) findViewById(R.id.layout_root)); + LayoutInflater inflate = (LayoutInflater)fragmentActivity.getSystemService(Context.LAYOUT_INFLATER_SERVICE); + View t = inflate.inflate(R.layout.file_save, (ViewGroup)getView().findViewById(R.id.layout_root)); EditText et = (EditText) t.findViewById(R.id.filename); et.setText(mFilterTag); builder.setView(t); @@ -229,7 +236,7 @@ protected Dialog onCreateDialog(int id) { DialogInterface.OnClickListener mClickListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { - if (which >= mFilters.length) { + if (which >= LOG_LEVEL_CHARS.length) { mFilter = -1; } else { mFilter = which; @@ -287,7 +294,7 @@ public void onClick(DialogInterface dialog, int which) { }; public void stopLogging() { - unbindService(mConnection); + fragmentActivity.unbindService(mConnection); mServiceRunning = false; if (mServiceRunning) { @@ -296,7 +303,7 @@ public void stopLogging() { } public void startLogging() { - bindService(new Intent(this, LogProcessor.class), mConnection, Context.BIND_AUTO_CREATE); + fragmentActivity.bindService(new Intent(fragmentActivity, LogProcessor.class), mConnection, Context.BIND_AUTO_CREATE); try { mService.run(mLogType); @@ -310,7 +317,7 @@ private void reset() { mAdapter.resetLines(); try { - mService.reset(buffers[mBuffer].toString()); + mService.reset(BUFFERS[mBuffer].toString()); } catch (RemoteException e) { Log.e(TAG, "Service is gone..."); } @@ -347,9 +354,9 @@ private void saveResult(String msg) { mProgressDialog.dismiss(); if (msg.equals("error")) { - Toast.makeText(this, "Error while saving the log to file!", Toast.LENGTH_LONG).show(); + Toast.makeText(fragmentActivity, "Error while saving the log to file!", Toast.LENGTH_LONG).show(); } else if (msg.equals("saved")) { - Toast.makeText(this, "Log has been saved to file.", Toast.LENGTH_LONG).show(); + Toast.makeText(fragmentActivity, "Log has been saved to file.", Toast.LENGTH_LONG).show(); } else if (msg.equals("attachment")) { Intent mail = new Intent(Intent.ACTION_SEND); mail.setType("text/plain"); @@ -469,9 +476,9 @@ public View getView(int pos, View convertView, ViewGroup parent) { } private int indexOfLogLevel(final char c) { - int filters = mFilters.length; + int filters = LOG_LEVEL_CHARS.length; int index = 0; - for (; index < filters && mFilters[index] != c; index++) { + for (; index < filters && LOG_LEVEL_CHARS[index] != c; index++) { } return index < filters ? index : -1; } diff --git a/src/com/michaelrnovak/util/logger/service/LogProcessor.java b/src/com/michaelrnovak/util/logger/service/LogProcessor.java index a195d07..de32bcd 100644 --- a/src/com/michaelrnovak/util/logger/service/LogProcessor.java +++ b/src/com/michaelrnovak/util/logger/service/LogProcessor.java @@ -1,6 +1,7 @@ /* * Copyright (C) 2009 Michael Novak - * + * 2015 Gavriel Fleischer + * * This program 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 2 From 5ff2c9c5a3a838ba65f0182c6fc52f94e9a1ce28 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Fri, 17 Jul 2015 01:45:48 +0300 Subject: [PATCH 09/12] added search-filter --- res/menu/menu_logger.xml | 38 +++- res/values/strings.xml | 7 +- .../michaelrnovak/util/logger/LogLine.java | 21 +- .../util/logger/LoggerFragment.java | 188 +++++++++++++++--- .../util/logger/service/LogProcessor.java | 176 ++++++++++------ 5 files changed, 327 insertions(+), 103 deletions(-) diff --git a/res/menu/menu_logger.xml b/res/menu/menu_logger.xml index 3e65b1a..5157162 100644 --- a/res/menu/menu_logger.xml +++ b/res/menu/menu_logger.xml @@ -3,34 +3,50 @@ xmlns:tools="http://schemas.android.com/tools" tools:context="com.michaelrnovak.util.logger.LoggerActivity"> - + + - + + android:orderInCategory="200" + app:showAsAction="ifRoom|collapseActionView" /> + + + + diff --git a/res/values/strings.xml b/res/values/strings.xml index 4da34f6..425c05f 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -4,8 +4,11 @@ Logs - Filter Log - Filter Tag + Search + Filter by loglevel + Filter by tag + Filter by app + Log format Select Buffer Email Log Save Log diff --git a/src/com/michaelrnovak/util/logger/LogLine.java b/src/com/michaelrnovak/util/logger/LogLine.java index d2bd5dd..29e6b19 100644 --- a/src/com/michaelrnovak/util/logger/LogLine.java +++ b/src/com/michaelrnovak/util/logger/LogLine.java @@ -38,7 +38,7 @@ public static class Brief extends LogLine { // V/Logger( 4973): log message public static Brief fromString(final String line) throws ParseException { char level = line.charAt(0); - String tag = line.substring(2, line.indexOf('(')); + String tag = line.substring(2, line.indexOf('(')).trim(); int pid = getInt(line, tag.length() + 4, ' ', ')'); String text = line.substring(line.indexOf(':', tag.length() + 5) + 2); return new Brief(level, tag, pid, text); @@ -53,9 +53,9 @@ public static class Time extends Brief { // V/Logger( 4973): log message // 07-06 16:45:25.447 W/Logger( 5015): log message public static Time fromString(final String line) throws ParseException { - Date date = SIMPLE_DATE_FORMAT.parse(line.substring(0, 18)); + Date date = parseDate(line.substring(0, 18)); char level = line.charAt(0 + 19); - String tag = line.substring(2 + 19, line.indexOf('(')); + String tag = line.substring(2 + 19, line.indexOf('(')).trim(); int pid = getInt(line, tag.length() + 4 + 19, ' ', ')'); String text = line.substring(line.indexOf(':', tag.length() + 5+19) + 2); return new Time(date, level, tag, pid, text); @@ -78,10 +78,10 @@ public static class Long extends Time { // [ 07-06 13:21:53.668 19517:19581 W/Logger ]\nlog message public static Long fromString(final String line) throws ParseException { - Date date = SIMPLE_DATE_FORMAT.parse(line.substring(2, 20)); + Date date = parseDate(line.substring(2, 20)); int pid = getInt(line, 21, ' ', ':'); char level = line.charAt(33); - String tag = line.substring(35, line.length() - 2); + String tag = line.substring(35, line.length() - 2).trim(); return new Long(date, pid, -1, level, tag, line); } public Long(Date date, int pid, int tid, char level, String tag) { @@ -133,6 +133,17 @@ protected static int getInt(final String line, int offset, char before, char aft return integer; } + private static Date parseDate(final String line) throws ParseException { + Date date = SIMPLE_DATE_FORMAT.parse(line); + Date now = new Date(); + int year = now.getYear(); + date.setYear(year); + if (now.getTime() < date.getTime()) { + date.setYear(year - 1); + } + return date; + } + protected LogLine(char level, String tag, int pid, String text) { this.level = level; this.tag = tag; diff --git a/src/com/michaelrnovak/util/logger/LoggerFragment.java b/src/com/michaelrnovak/util/logger/LoggerFragment.java index ff9fe2d..4a65383 100644 --- a/src/com/michaelrnovak/util/logger/LoggerFragment.java +++ b/src/com/michaelrnovak/util/logger/LoggerFragment.java @@ -18,9 +18,11 @@ */ package com.michaelrnovak.util.logger; +import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; +import android.app.SearchManager; import android.content.ComponentName; import android.content.Context; import android.content.DialogInterface; @@ -28,6 +30,7 @@ import android.content.ServiceConnection; import android.graphics.Typeface; import android.net.Uri; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; @@ -36,7 +39,10 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; +import android.support.v4.view.MenuItemCompat; +import android.support.v7.widget.SearchView; import android.text.SpannableString; +import android.text.style.BackgroundColorSpan; import android.text.style.ForegroundColorSpan; import android.text.style.StyleSpan; import android.util.Log; @@ -48,6 +54,8 @@ import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.EditText; +import android.widget.Filter; +import android.widget.Filterable; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; @@ -59,6 +67,7 @@ import java.io.File; import java.util.ArrayList; import java.util.HashMap; +import java.util.List; public class LoggerFragment extends Fragment { public static final String TAG = "Logger"; @@ -69,21 +78,26 @@ public class LoggerFragment extends Fragment { public static final int DIALOG_BUFFER_ID = 5; public static final int DIALOG_TYPE_ID = 6; public static final int DIALOG_TAG_ID = 7; + public static final int DIALOG_APP_ID = 8; static final CharSequence[] LOG_LEVEL_NAMES = {"Verbose", "Debug", "Info", "Warn", "Error", "Fatal", "Silent"}; static final char[] LOG_LEVEL_CHARS = {'V', 'D', 'I', 'W', 'E', 'F', 'S'}; - static final CharSequence[] BUFFERS = {"Main", "Radio", "Events"}; + public static final CharSequence[] LOG_FORMAT = {"brief", "time", "long"}; + static final CharSequence[] BUFFERS = {"Main", "Events" /*, "Radio", "System"*/}; static final CharSequence[] TYPES = {"Logcat", "Dmesg"}; + static final CharSequence[] NO_YES = {"No", "Yes"}; private ILogProcessor mService; private AlertDialog mDialog; private ProgressDialog mProgressDialog; private LoggerListAdapter mAdapter; private LayoutInflater mInflater; - private int mFilter = -1; - private int mBuffer = 0; - private int mLogType = 0; + private int mFilterLevel = 0; private String mFilterTag = ""; private String mFilterTagLowerCase = ""; + private int mFilterApp = 0; + private int mBuffer = 0; + private int mLogType = 0; + private String searchQuery = ""; private boolean mServiceRunning = false; private FragmentActivity fragmentActivity; @@ -132,22 +146,57 @@ public void onPause() { @Override public void onPrepareOptionsMenu(Menu menu) { - menu.findItem(R.id.action_filter).setEnabled(mBuffer == 0); + menu.findItem(R.id.action_filter_loglevel).setEnabled(mBuffer == 0); super.onPrepareOptionsMenu(menu); } + private void search(final String query) { + searchQuery = query; + mAdapter.getFilter().filter(query); + } + + @TargetApi(Build.VERSION_CODES.FROYO) @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { - inflater.inflate(R.menu.menu_logger, menu); super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.menu_logger, menu); + + MenuItem searchItem = menu.findItem(R.id.action_search); + SearchView search = (SearchView) MenuItemCompat.getActionView(searchItem); + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + SearchManager manager = (SearchManager) fragmentActivity.getSystemService(Context.SEARCH_SERVICE); + search.setSearchableInfo(manager.getSearchableInfo(fragmentActivity.getComponentName())); + } + search.setOnQueryTextListener(new SearchView.OnQueryTextListener() { + @Override + public boolean onQueryTextSubmit(final String query) { + if (BuildConfig.DEBUG) + Log.v(TAG, "onCreateOptionsMenu.onQueryTextSubmit: " + query); + search(query); + return true; + } + + @Override + public boolean onQueryTextChange(final String query) { + if (BuildConfig.DEBUG) + Log.v(TAG, "onCreateOptionsMenu.onQueryTextChange: " + query); + search(query); + return true; + } + }); } @Override public boolean onOptionsItemSelected(MenuItem item) { int id = item.getItemId(); - if (id == R.id.action_filter) { + if (id == R.id.action_filter_loglevel) { onCreateDialog(DIALOG_FILTER_ID); + } else if (id == R.id.action_filter_tag) { + onCreateDialog(DIALOG_TAG_ID); +// } else if (id == R.id.action_filter_app) { +// onCreateDialog(DIALOG_APP_ID); } else if (id == R.id.action_email) { generateEmailMessage(); } else if (id == R.id.action_save) { @@ -156,8 +205,6 @@ public boolean onOptionsItemSelected(MenuItem item) { onCreateDialog(DIALOG_BUFFER_ID); } else if (id == R.id.action_select) { onCreateDialog(DIALOG_TYPE_ID); - } else if (id == R.id.action_tag) { - onCreateDialog(DIALOG_TAG_ID); } else { } return false; @@ -187,7 +234,12 @@ protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_FILTER_ID: builder.setTitle("Select a filter level"); - builder.setSingleChoiceItems(LOG_LEVEL_NAMES, mFilter, mClickListener); + builder.setSingleChoiceItems(LOG_LEVEL_NAMES, mFilterLevel, mClickListener); + mDialog = builder.create(); + break; + case DIALOG_APP_ID: + builder.setTitle("Filter only this app?"); + builder.setSingleChoiceItems(NO_YES, mFilterApp, mAppListener); mDialog = builder.create(); break; case DIALOG_SAVE_ID: @@ -237,15 +289,22 @@ protected Dialog onCreateDialog(int id) { DialogInterface.OnClickListener mClickListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { if (which >= LOG_LEVEL_CHARS.length) { - mFilter = -1; + mFilterLevel = -1; } else { - mFilter = which; + mFilterLevel = which; } updateFilter(); } }; + DialogInterface.OnClickListener mAppListener = new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int which) { + mFilterApp = which; + updateLog(); + } + }; + DialogInterface.OnClickListener mBufferListener = new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int which) { mBuffer = which; @@ -421,31 +480,39 @@ public void onServiceDisconnected(ComponentName className) { * This is the list adapter for the Logger, it holds an array of strings and adds them * to the list view recycling views for obvious performance reasons. */ - public class LoggerListAdapter extends BaseAdapter { + public class LoggerListAdapter extends BaseAdapter implements Filterable { private Context mContext; - private ArrayList mLines; + private List mAllLines; + private List mFilteredLines; public LoggerListAdapter(Context c) { mContext = c; - mLines = new ArrayList(); + mAllLines = new ArrayList(); + synchronized (this) { + mFilteredLines = mAllLines; + } mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE); } - public int getCount() { - return mLines.size(); + @Override + synchronized public int getCount() { + return mFilteredLines.size(); } + @Override public long getItemId(int pos) { return pos; } - public Object getItem(int pos) { - return mLines.get(pos); + @Override + synchronized public Object getItem(int pos) { + return mFilteredLines.get(pos); } + @Override public View getView(int pos, View convertView, ViewGroup parent) { TextView holder; - LogLine line = mLines.get(pos); + LogLine line = (LogLine)getItem(pos); if (convertView == null) { //inflate the view here because there's no existing view object. @@ -460,7 +527,7 @@ public View getView(int pos, View convertView, ViewGroup parent) { } if (mLogType == 0) { - holder.setText(new LogFormattedString(line)); + holder.setText(new LogFormattedString(line, searchQuery)); } else { holder.setText(line.toString()); } @@ -469,12 +536,59 @@ public View getView(int pos, View convertView, ViewGroup parent) { (getListView().getScrollY() + getListView().getHeight() >= getListView().getBottom()); if (autoscroll) { - getListView().setSelection(mLines.size() - 1); + getListView().setSelection(getCount() - 1); } return convertView; } + Filter mFilter = new Filter() { + @Override + protected Filter.FilterResults performFiltering(CharSequence constraint) { + Filter.FilterResults results = new Filter.FilterResults(); + String filterSeq = constraint.toString().toLowerCase(); + if (filterSeq != null && filterSeq.length() > 0) { + List filteredResults = new ArrayList<>(); + for (LogLine line : mAllLines) { + if (line.toString().toLowerCase().contains(filterSeq)) { + filteredResults.add(line); + } + } + synchronized (mAdapter) { + results.values = filteredResults; + results.count = filteredResults.size(); + } + Log.v(TAG, "performFiltering1: results: " + results.count + ": " + (results.values == null ? "null" : "values")); + } else { + synchronized (mAdapter) { + results.values = mAllLines; + results.count = mAllLines.size(); + } + Log.v(TAG, "performFiltering2: results: " + results.count + ": " + (results.values == null ? "null" : "values")); + } + return results; + } + +// @SuppressWarnings("unchecked") + @Override + protected void publishResults(CharSequence constraint, Filter.FilterResults results) { + // NOTE: this function is *always* called from the UI thread. + synchronized (mAdapter) { + mFilteredLines = (List) results.values; + } + Log.v(TAG, "publishResults: results: " + results.count + ": " + (results.values == null ? "null" : "values")); + if (null == mFilteredLines) { + Log.e(TAG, "publishResults: mFilteredLines: null, results: " + results.count + ": " + (results.values == null ? "null" : "values")); + } + notifyDataSetChanged(); + } + }; + + @Override + public Filter getFilter() { + return mFilter; + } + private int indexOfLogLevel(final char c) { int filters = LOG_LEVEL_CHARS.length; int index = 0; @@ -484,7 +598,11 @@ private int indexOfLogLevel(final char c) { } public void addLine(LogLine line) { - if (null == line || indexOfLogLevel(line.getLevel()) < mFilter) { + if (null == line || indexOfLogLevel(line.getLevel()) < mFilterLevel) { + return; + } + + if (mFilterApp == 1 && line.getPid() != android.os.Process.myPid()) { return; } @@ -494,12 +612,12 @@ public void addLine(LogLine line) { } } - mLines.add(line); + mAllLines.add(line); notifyDataSetChanged(); } public void resetLines() { - mLines.clear(); + mAllLines.clear(); notifyDataSetChanged(); } @@ -511,8 +629,8 @@ public void updateView() { private static class LogFormattedString extends SpannableString { public static final HashMap LABEL_COLOR_MAP; - public LogFormattedString(LogLine line) { - super(line.toString() + '\n'); + public LogFormattedString(LogLine line, String searchQuery) { + super(line.toString()); try { Integer labelColor = LABEL_COLOR_MAP.get(line.getLevel()); @@ -528,8 +646,18 @@ public LogFormattedString(LogLine line) { int headerLen = header.length(); setSpan(new ForegroundColorSpan(labelColor), 0, headerLen, 0); setSpan(new StyleSpan(Typeface.ITALIC), 0, headerLen, 0); + + if (null != searchQuery && searchQuery.length() > 0) { + final String str = line.toString(); + int start = str.toLowerCase().indexOf(searchQuery.toLowerCase()); + if (start > -1) { + setSpan(new ForegroundColorSpan(LABEL_COLOR_MAP.get('q')), start, start + searchQuery.length(), 0); + setSpan(new BackgroundColorSpan(LABEL_COLOR_MAP.get('Q')), start, start + searchQuery.length(), 0); + setSpan(new StyleSpan(Typeface.BOLD), start, start + searchQuery.length(), 0); + } + } } catch (Exception e) { - setSpan(new ForegroundColorSpan(0xffddaacc), 0, length(), 0); + setSpan(new ForegroundColorSpan(LABEL_COLOR_MAP.get('e')), 0, length(), 0); } } @@ -541,6 +669,10 @@ public LogFormattedString(LogLine line) { LABEL_COLOR_MAP.put('W', 0xffffff99); LABEL_COLOR_MAP.put('E', 0xffff9999); LABEL_COLOR_MAP.put('F', 0xffff0000); + + LABEL_COLOR_MAP.put('e', 0xffddaacc); // for exception foreground + LABEL_COLOR_MAP.put('q', 0xffffffff); // for search query foreground + LABEL_COLOR_MAP.put('Q', 0xffff9999); // for search query background } } } diff --git a/src/com/michaelrnovak/util/logger/service/LogProcessor.java b/src/com/michaelrnovak/util/logger/service/LogProcessor.java index de32bcd..c578f5e 100644 --- a/src/com/michaelrnovak/util/logger/service/LogProcessor.java +++ b/src/com/michaelrnovak/util/logger/service/LogProcessor.java @@ -18,11 +18,15 @@ */ package com.michaelrnovak.util.logger.service; +import android.annotation.TargetApi; import android.app.Service; import android.content.Intent; +import android.os.Build; import android.os.IBinder; import android.os.Handler; import android.os.Message; +import android.support.v7.appcompat.BuildConfig; +import android.util.EventLog; import android.util.Log; import java.io.BufferedReader; @@ -31,32 +35,40 @@ import java.io.IOException; import java.io.InputStreamReader; import java.util.Vector; +import java.util.concurrent.atomic.AtomicBoolean; import com.michaelrnovak.util.logger.LogLine; +import com.michaelrnovak.util.logger.LoggerFragment; public class LogProcessor extends Service { - public static final String TAG = "LogProcessor"; + public static final String HIDDEN_TAG = "LogProcessor:HIDDEN"; + public static final String TAG = HIDDEN_TAG.substring(0, 12); + public static final int MAX_LINES = 250; + public static final int MSG_READ_FAIL = 1; + public static final int MSG_LOG_FAIL = 2; + public static final int MSG_NEW_LINE = 3; + public static final int MSG_RESET_LOG = 4; + public static final int MSG_LOG_SAVE = 5; private static Handler mHandler; + private String mFile; private String mBuffer = "main"; - private String mLogFormat = "time"; // brief | time | long + private int mLogFormat = 2; // 0:brief | 1:time | 2:long private Vector mScrollback; private int mType; private String mFilterTag; private volatile boolean threadKill = false; - private volatile boolean mStatus = false; - public static final int MAX_LINES = 250; - public static final int MSG_READ_FAIL = 1; - public static final int MSG_LOG_FAIL = 2; - public static final int MSG_NEW_LINE = 3; - public static final int MSG_RESET_LOG = 4; - public static final int MSG_LOG_SAVE = 5; - + private final AtomicBoolean mStatus; + + public LogProcessor() { + mStatus = new AtomicBoolean(false); + } + @Override public void onCreate() { super.onCreate(); - mScrollback = new Vector(); + mScrollback = new Vector(MAX_LINES); } @Override @@ -65,27 +77,20 @@ public void onStart(Intent intent, int startId) { Log.v(TAG, "Logger Service has hit the onStart method."); } - Runnable worker = new Runnable() { - public void run() { - runLog(); - mStatus = true; - Log.d(TAG, "status... " + mStatus); - } - }; - private void runLog() { + Log.d(TAG, "runLog thread started"); Process process = null; try { - if (mType == 0) { - process = Runtime.getRuntime().exec("/system/bin/logcat -v " + mLogFormat + " -b " + mBuffer); + process = Runtime.getRuntime().exec("/system/bin/logcat -v " + LoggerFragment.LOG_FORMAT[mLogFormat] + " -b " + mBuffer); } else if (mType == 1) { process = Runtime.getRuntime().exec("dmesg -s 1000000"); } - } catch (IOException e) { + Log.e(TAG, "runLog: can't create reader process", e); communicate(MSG_LOG_FAIL); + return; } BufferedReader reader = null; @@ -93,49 +98,82 @@ private void runLog() { try { reader = new BufferedReader(new InputStreamReader(process.getInputStream())); + int l = 0; while (!killRequested()) { String line = reader.readLine(); try { - LogLine logLine = LogLine.fromString(line, mLogFormat); - if (logLine instanceof LogLine.Long) { - boolean emptyLine = false; - boolean nextLine = false; - do { - reader.mark(1024); - line = reader.readLine(); - if (emptyLine && null != line && line.length() > 0 && line.charAt(0) == '[') { - reader.reset(); - nextLine = true; + if (null != line) { + LogLine logLine = LogLine.fromString(line, LoggerFragment.LOG_FORMAT[mLogFormat].toString()); + if (logLine instanceof LogLine.Long) { + boolean emptyLine = false; + boolean nextLine = false; + do { + reader.mark(1024); + line = null; + if (reader.ready()) { + line = reader.readLine(); + } else { + nextLine = true; + } + if (emptyLine && null != line && line.length() > 0 && line.charAt(0) == '[') { + reader.reset(); + nextLine = true; + } + emptyLine = (null == line || line.length() == 0); + } while (!nextLine && ((LogLine.Long) logLine).add(line)); + } + if (!HIDDEN_TAG.equals(logLine.getTag())) { + logLine(logLine); + if (mScrollback.size() >= MAX_LINES) { + mScrollback.removeElementAt(0); } - emptyLine = (null == line || line.length() == 0); - } while (!nextLine && ((LogLine.Long) logLine).add(line)); - } -// if (logLine.getPid() == android.os.Process.myPid()) { - logLine(logLine); - - if (mScrollback.size() >= MAX_LINES) { - mScrollback.removeElementAt(0); + mScrollback.add(logLine); } - mScrollback.add(logLine); -// } + l++; + } else { + Log.e(TAG, "runLog: reader closed"); + requestKill(); + } } catch (Exception e) { Log.e(TAG, "runLog: " + line + ";", e); } } - - Log.i(TAG, "Prepping thread for termination"); - reader.close(); - process.destroy(); - process = null; - reader = null; - mScrollback.removeAllElements(); } catch (IOException e) { communicate(MSG_READ_FAIL); + } finally { + Log.d(TAG, "Prepping thread for termination"); + if (null != reader) { + try { + reader.close(); + } catch (IOException e) { + } + } + if (null != process) { + process.destroy(); + } + mScrollback.removeAllElements(); } - Log.d(TAG, "Exiting thread..."); } - + + Runnable worker = new Runnable() { + public void run() { + Log.d(TAG, "before syncronized worker"); + synchronized (mStatus) { + mStatus.set(false); + } + long startedAt = System.currentTimeMillis(); + runLog(); + long finishedAt = System.currentTimeMillis(); + Log.d(TAG, "Worker worked for " + (finishedAt - startedAt) + "ms. status... " + mStatus + " => true"); + synchronized (mStatus) { + mStatus.set(true); + mStatus.notify(); + } + Log.d(TAG, "after syncronized worker"); + } + }; + private synchronized void requestKill() { threadKill = true; } @@ -169,16 +207,36 @@ public boolean onUnbind(Intent intent) { } private final ILogProcessor.Stub mBinder = new ILogProcessor.Stub() { + + @TargetApi(Build.VERSION_CODES.FROYO) private void kill() { requestKill(); - while (!mStatus) { - try { - Log.v(TAG, "waiting..."); - } catch (Exception e) { - Log.d(TAG, "Woot! obj has been interrupted!"); + Log.d(TAG, "before syncronized waiting... for " + mBuffer); + synchronized (mStatus/*LogProcessor.this*/) { + while (!mStatus.get()) { + try { + // we need to send a log, to wake up the reader (this is like reader.notify()) + if ("main".equals(mBuffer)) { + Log.v(BuildConfig.DEBUG ? TAG : HIDDEN_TAG, "main waiting..."); + } else if ("events".equals(mBuffer) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.FROYO) { + EventLog.writeEvent(0, (BuildConfig.DEBUG ? TAG : HIDDEN_TAG) + ": event waiting..."); + } +// Log.d(TAG, "syncronized waiting..."); + mStatus.wait(1000); + } catch (InterruptedException e) { + Log.d(TAG, "waiting interrupted by timeout"); + } } } + Log.d(TAG, "after syncronized waiting..."); +// while (!mStatus) { +// try { +// Log.v(TAG, "waiting..."); +// } catch (Exception e) { +// Log.d(TAG, "Woot! obj has been interrupted!"); +// } +// } threadKill = false; } @@ -232,7 +290,11 @@ private void writeLog() { for (int i = 0; i < mScrollback.size(); i++) { final LogLine line = mScrollback.elementAt(i); - + +// if (mFilterApp == 1 && line.getPid() != android.os.Process.myPid()) { +// return; +// } + if (!lowerCaseFilterTag.equals("")) { if (lowerCaseFilterTag.equals(line.getTag().toLowerCase().trim())) { w.write(line + "\n"); From 37a6ffb38461f48e0050e23ea016454d2a2a77d1 Mon Sep 17 00:00:00 2001 From: Gavriel Fleischer Date: Fri, 17 Jul 2015 01:53:11 +0300 Subject: [PATCH 10/12] added missing themes.xml --- res/values/themes.xml | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 res/values/themes.xml diff --git a/res/values/themes.xml b/res/values/themes.xml new file mode 100644 index 0000000..63cf638 --- /dev/null +++ b/res/values/themes.xml @@ -0,0 +1,4 @@ + + +