package com.eolwral.osmonitor.ui; import android.annotation.SuppressLint; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnMultiChoiceClickListener; import android.content.Intent; import android.content.res.Resources; import android.graphics.Color; import android.os.Bundle; import android.os.Environment; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ListFragment; import android.support.v4.content.LocalBroadcastManager; import android.support.v4.util.SimpleArrayMap; import android.support.v4.view.MenuItemCompat; import android.text.Editable; import android.text.Html; import android.text.TextWatcher; import android.text.format.DateFormat; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.BaseAdapter; import android.widget.EditText; import android.widget.Filter; import android.widget.ImageButton; import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.eolwral.osmonitor.R; import com.eolwral.osmonitor.core.dmesgInfo; import com.eolwral.osmonitor.core.dmesgInfoList; import com.eolwral.osmonitor.core.dmesgLevel; import com.eolwral.osmonitor.core.logPriority; import com.eolwral.osmonitor.core.logcatInfo; import com.eolwral.osmonitor.core.logcatInfoList; import com.eolwral.osmonitor.core.processInfo; import com.eolwral.osmonitor.core.processInfoList; import com.eolwral.osmonitor.ipc.IpcService; import com.eolwral.osmonitor.ipc.IpcService.ipcClientListener; import com.eolwral.osmonitor.ipc.ipcCategory; import com.eolwral.osmonitor.ipc.ipcData; import com.eolwral.osmonitor.ipc.ipcMessage; import com.eolwral.osmonitor.preference.OSMPreference; import com.eolwral.osmonitor.settings.Settings; import com.eolwral.osmonitor.util.ProcessUtil; import com.eolwral.osmonitor.util.UserInterfaceUtil; import java.io.File; import java.io.FileWriter; import java.nio.ByteBuffer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.Locale; public class MessageFragment extends ListFragment implements ipcClientListener { // print logcat format private enum PrintLogcatFormat { FORMAT_OFF, FORMAT_BRIEF, FORMAT_PROCESS, FORMAT_TAG, FORMAT_THREAD, FORMAT_RAW, FORMAT_TIME, FORMAT_THREADTIME, FORMAT_LONG; }; private PrintLogcatFormat printLogcatFMT = PrintLogcatFormat.FORMAT_OFF; // print dmesg format private enum PrintDmesgFormat { FORMAT_OFF, FORMAT_RAW }; private PrintDmesgFormat printDmesgFMT = PrintDmesgFormat.FORMAT_OFF; // ipc client private static IpcService ipc = IpcService.getInstance(); private static boolean ipcStop = false; private byte selectedType = ipcCategory.LOGCAT_MAIN; // data private ArrayList<logcatInfo> viewLogcatData = new ArrayList<logcatInfo>(); private ArrayList<dmesgInfo> viewDmesgData = new ArrayList<dmesgInfo>(); private byte logType = ipcCategory.LOGCAT_MAIN; private Settings settings = null; // selected log private SimpleArrayMap<Integer, Boolean> selectedData = new SimpleArrayMap<Integer, Boolean>(); private boolean selectedMode = false; // process mapping private ProcessUtil infoHelper = null; @SuppressLint("UseSparseArrays") private SimpleArrayMap<Integer, processInfo> map = new SimpleArrayMap<Integer, processInfo>(); // filter private final static int MAXLOGCAT = 30000; private List<ArrayList<logcatInfo>> sourceLogcatData = new ArrayList<ArrayList<logcatInfo>>(); private boolean[] filterLogcatArray = new boolean[logPriority.SILENT + 1]; private ArrayList<dmesgInfo> sourceDmesgData = new ArrayList<dmesgInfo>(); private boolean[] filterDmesgArray = new boolean[dmesgLevel.DEBUG + 1]; private MessageListAdapter messageList = null; private String filterString = ""; // stop or start private boolean stopUpdate = false; private ImageButton stopButton = null; private boolean autoScrollEnd = false; private TextView messageCount = null; public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // persist setRetainInstance(true); // enable fragment option menu setHasOptionsMenu(true); for (int index = 0; index < filterLogcatArray.length; index++) filterLogcatArray[index] = true; for (int index = 0; index < filterDmesgArray.length; index++) filterDmesgArray[index] = true; // settings settings = Settings.getInstance(getActivity().getApplicationContext()); // process utility infoHelper = ProcessUtil.getInstance(getActivity().getApplicationContext(), true); // reload format reloadFomrat(); // set list messageList = new MessageListAdapter(getActivity().getApplicationContext()); setListAdapter(messageList); // create array for (int count = 0; count < 4; count++) sourceLogcatData.add(new ArrayList<logcatInfo>()); } private void reloadFomrat() { loadDmesgFormat(); loadLogcatFormat(); } private void loadDmesgFormat() { switch (settings.getDmesgFormat()) { case 1: printDmesgFMT = PrintDmesgFormat.FORMAT_RAW; break; default: printDmesgFMT = PrintDmesgFormat.FORMAT_OFF; break; } } private void loadLogcatFormat() { switch (settings.getLogcatFormat()) { case 1: printLogcatFMT = PrintLogcatFormat.FORMAT_BRIEF; break; case 2: printLogcatFMT = PrintLogcatFormat.FORMAT_PROCESS; break; case 3: printLogcatFMT = PrintLogcatFormat.FORMAT_TAG; break; case 4: printLogcatFMT = PrintLogcatFormat.FORMAT_THREAD; break; case 5: printLogcatFMT = PrintLogcatFormat.FORMAT_THREADTIME; break; case 6: printLogcatFMT = PrintLogcatFormat.FORMAT_TIME; break; case 7: printLogcatFMT = PrintLogcatFormat.FORMAT_LONG; break; case 8: printLogcatFMT = PrintLogcatFormat.FORMAT_RAW; break; default: printLogcatFMT = PrintLogcatFormat.FORMAT_OFF; break; } } public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View v = inflater.inflate(R.layout.ui_message_fragment, container, false); messageCount = ((TextView) v.findViewById(R.id.id_message_count)); return v; } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.ui_message_menu, menu); // sort extend menu MenuItem expendMenu = menu.findItem(R.id.ui_message_type); Spinner expendItem = (Spinner) MenuItemCompat.getActionView(expendMenu); expendItem.setSelection(UserInterfaceUtil.convertTypeToLoc(selectedType)); // source menu expendItem.setOnItemSelectedListener(new OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { selectedType = UserInterfaceUtil.convertLocToType(position); // fix font color on Android 2.3.x if (parent.getChildAt(0) != null) ((TextView) parent.getChildAt(0)).setTextColor(Color.WHITE); // keep it going if (stopUpdate == true) stopButton.performClick(); // force refresh forceRefresh(); // restart if it has been stopped if (!stopUpdate) stopUpdate = false; } @Override public void onNothingSelected(AdapterView<?> arg0) { } }); // sort extend menu MenuItem searchMenu = menu.findItem(R.id.ui_message_search); View searchItem = (View) MenuItemCompat.getActionView(searchMenu); MenuItemCompat.setOnActionExpandListener(searchMenu, new HiddenTypeMenu( expendMenu)); // instant search TextView searchView = (TextView) searchItem .findViewById(R.id.id_action_search_text); searchView.setText(filterString); searchView.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence cs, int arg1, int arg2, int arg3) { // When user changed the Text messageList.getFilter().filter(cs); } @Override public void beforeTextChanged(CharSequence arg0, int arg1, int arg2, int arg3) { } @Override public void afterTextChanged(Editable arg0) { } }); // refresh button stopButton = (ImageButton) searchItem.findViewById(R.id.id_action_stop); if (stopUpdate) stopButton.setImageResource(R.drawable.ic_action_start); else stopButton.setImageResource(R.drawable.ic_action_stop); stopButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { stopUpdate = !stopUpdate; if (stopUpdate) stopButton.setImageResource(R.drawable.ic_action_start); else stopButton.setImageResource(R.drawable.ic_action_stop); } }); // filter spinner ImageButton filterButton = (ImageButton) searchItem .findViewById(R.id.id_action_filter); filterButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { showMultiChoiceItems(); } }); super.onCreateOptionsMenu(menu, inflater); } private class HiddenTypeMenu implements MenuItemCompat.OnActionExpandListener { private MenuItem expendMenu = null; public HiddenTypeMenu(MenuItem item) { this.expendMenu = item; } @Override public boolean onMenuItemActionExpand(MenuItem item) { if (expendMenu != null) { expendMenu.setVisible(false); expendMenu.setEnabled(false); } return true; } @Override public boolean onMenuItemActionCollapse(MenuItem item) { if (expendMenu != null) { expendMenu.setVisible(true); expendMenu.setEnabled(true); } return true; } } @Override public void onPrepareOptionsMenu(Menu menu) { MenuItem expendMenu = menu.findItem(R.id.ui_message_type); if (selectedMode == true) expendMenu.setVisible(false); else expendMenu.setVisible(true); super.onPrepareOptionsMenu(menu); } private void forceRefresh() { if (logType != selectedType) { ipc.removeRequest(this); byte newCommand[] = new byte[1]; newCommand[0] = selectedType; ipc.addRequest(newCommand, 0, this); } } private void showMultiChoiceItems() { Builder builder = new AlertDialog.Builder(getActivity()); if (isLogcat(logType)) { builder.setMultiChoiceItems(R.array.ui_message_logcat_level, filterLogcatArray, new OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { filterLogcatArray[which] = isChecked; } }); } else { builder.setMultiChoiceItems(R.array.ui_message_dmesg_level, filterDmesgArray, new OnMultiChoiceClickListener() { @Override public void onClick(DialogInterface dialog, int which, boolean isChecked) { filterDmesgArray[which] = isChecked; } }); } builder.setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { messageList.getFilter().doFilter(); } }); builder.show(); } private void exportLog(String fileName) { if (fileName.trim().equals("")) return; if (!fileName.contains(".csv")) fileName += ".csv"; try { File logFile = new File(Environment.getExternalStorageDirectory() .getPath() + "/" + fileName); if (logFile.exists()) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(R.string.ui_text_fileexist) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); return; } logFile.createNewFile(); int LogCount = 0; if (isLogcat(logType)) LogCount = viewLogcatData.size(); else LogCount = viewDmesgData.size(); FileWriter logWriter = new FileWriter(logFile); final Calendar calendar = Calendar.getInstance(); for (int index = 0; index < LogCount; index++) { StringBuilder logLine = new StringBuilder(); // filter specific entries if (selectedMode == true && !selectedData.containsKey(index)) continue; if (isLogcat(logType)) { calendar .setTimeInMillis(viewLogcatData.get(index).seconds() * 1000); logLine.append(DateFormat.format("yyyy-MM-dd hh:mm:ss", calendar.getTime()) + ","); logLine.append(UserInterfaceUtil.getLogpriority(viewLogcatData.get(index).priority()) + ","); logLine.append(viewLogcatData.get(index).tag() + ","); if (viewLogcatData.get(index).pid() == 0) logLine.append("System,"); else if (map.containsKey(viewLogcatData.get(index).pid())) logLine.append(infoHelper.getPackageName(map.get( viewLogcatData.get(index).pid()).name()) + ","); else logLine.append("Unknown,"); logLine.append(viewLogcatData.get(index).message() + "\n"); } else { if (viewDmesgData.get(index).seconds() != 0) { calendar .setTimeInMillis(viewDmesgData.get(index).seconds() * 1000); logLine.append(DateFormat.format("yyyy-MM-dd hh:mm:ss", calendar.getTime()) + ","); } logLine.append(UserInterfaceUtil.getDmesgLevel(viewDmesgData.get(index).level()) + ","); logLine.append(viewDmesgData.get(index).message().toString() + "\n"); } logWriter.write(logLine.toString()); } logWriter.close(); // refresh selectedMode = false; selectedData.clear(); ActivityCompat.invalidateOptionsMenu(getActivity()); messageList.notifyDataSetChanged(); } catch (Exception e) { new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(e.getMessage()) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); return; } new AlertDialog.Builder(getActivity()) .setTitle(R.string.ui_menu_logexport) .setMessage(R.string.ui_text_exportdone) .setPositiveButton(R.string.ui_text_okay, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }).create().show(); return; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.ui_menu_setting: onSettingClick(); break; case R.id.ui_message_export: onExportClick(); break; case R.id.ui_menu_exit: onExitClick(); break; } return super.onOptionsItemSelected(item); } private void onSettingClick() { Intent settings = new Intent(getActivity(), OSMPreference.class); settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); startActivity(settings); return; } private void onExitClick() { LocalBroadcastManager.getInstance(getActivity()).sendBroadcast( new Intent("Exit")); return; } private void onExportClick() { final Resources exportRes = getActivity().getResources(); final Calendar calendar = Calendar.getInstance(); final SimpleDateFormat formatter = new SimpleDateFormat( "yyyy-MM-dd-hh.mm.ss", Locale.getDefault()); Builder exportDialog = new AlertDialog.Builder(getActivity()); View exportView = LayoutInflater.from(getActivity()).inflate( R.layout.ui_message_export, null); TextView exportFile = (TextView) exportView .findViewById(R.id.id_export_filename); exportFile.setText("Log-" + formatter.format(calendar.getTime())); exportDialog.setView(exportView); exportDialog.setTitle(exportRes.getText(R.string.ui_menu_logexport)); exportDialog.setNegativeButton(exportRes.getText(R.string.ui_text_cancel),null); exportDialog.setPositiveButton(exportRes.getText(R.string.ui_text_okay), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { String FileName = ((EditText) ((AlertDialog) dialog) .findViewById(R.id.id_export_filename)).getText().toString(); exportLog(FileName); } }); exportDialog.create().show(); return; } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); ipc.removeRequest(this); ipcStop = !isVisibleToUser; if (isVisibleToUser == true) { byte newCommand[] = { logType, ipcCategory.PROCESS }; ipc.addRequest(newCommand, 0, this); } // reload format (re-enter) if (settings != null) reloadFomrat(); } @Override public void onRecvData(byte [] result) { // check if (ipcStop == true) return; // update if (stopUpdate == true || result == null) { byte newCommand[] = new byte[2]; newCommand[0] = selectedType; newCommand[1] = ipcCategory.PROCESS; ipc.addRequest(newCommand, settings.getInterval(), this); return; } // clean up sourceDmesgData.clear(); map.clear(); // convert data ipcMessage resultMessage = ipcMessage.getRootAsipcMessage(ByteBuffer.wrap(result)); for (int index = 0; index < resultMessage.dataLength(); index++) { try { ipcData rawData = resultMessage.data(index); // prepare mapping table if (rawData.category() == ipcCategory.PROCESS) { extractProcessInfo(rawData); continue; } if (isLogcat(rawData.category())) extractLogcatInfo(rawData); else if (rawData.category() == ipcCategory.DMESG) extractDmesgInfo(rawData); logType = rawData.category(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } // processing filter action messageList.getFilter().doFilter(); // send command again byte newCommand[] = new byte[2]; newCommand[0] = selectedType; newCommand[1] = ipcCategory.PROCESS; if (selectedType != logType) ipc.addRequest(newCommand, 0, this); else ipc.addRequest(newCommand, settings.getInterval(), this); } private void extractLogcatInfo(ipcData rawData) { logcatInfoList list = logcatInfoList.getRootAslogcatInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer()); for (int count = 0; count < list.listLength(); count++) { logcatInfo lgInfo = list.list(count); if (sourceLogcatData.get(UserInterfaceUtil.convertTypeToLoc(rawData.category())).size() > MAXLOGCAT) sourceLogcatData.get(UserInterfaceUtil.convertTypeToLoc(rawData.category())).remove(0); sourceLogcatData.get(UserInterfaceUtil.convertTypeToLoc(rawData.category())).add(lgInfo); } } private void extractDmesgInfo(ipcData rawData) { dmesgInfoList list = dmesgInfoList.getRootAsdmesgInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer()); for (int count = 0; count < list.listLength(); count++) { dmesgInfo dgInfo = list.list(count); sourceDmesgData.add(dgInfo); } } private void extractProcessInfo(ipcData rawData) { processInfoList list = processInfoList.getRootAsprocessInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer()); for (int count = 0; count < list.listLength(); count++) { processInfo psInfo = list.list(count); if (!infoHelper.checkPackageInformation(psInfo.name())) { infoHelper.doCacheInfo(psInfo.uid(), psInfo.owner(), psInfo.name()); } map.put(psInfo.pid(), psInfo); } } private boolean isLogcat(byte logType) { if (logType == ipcCategory.LOGCAT_MAIN || logType == ipcCategory.LOGCAT_EVENT || logType == ipcCategory.LOGCAT_SYSTEM || logType == ipcCategory.LOGCAT_RADIO) return true; return false; } /** * implement viewholder class for connection list */ private class ViewHolder { // main information TextView time; TextView tag; TextView level; TextView msg; // color int bkcolor; } private class MessageListAdapter extends BaseAdapter { private LayoutInflater itemInflater = null; private ViewHolder holder = null; private MessageFilter filter = null; public MessageListAdapter(Context mContext) { itemInflater = (LayoutInflater) mContext .getSystemService(Context.LAYOUT_INFLATER_SERVICE); } public MessageFilter getFilter() { if (filter == null) filter = new MessageFilter(); return filter; } @Override public int getCount() { if (isLogcat(logType)) return viewLogcatData.size(); return viewDmesgData.size(); } public Object getItem(int position) { return position; } public long getItemId(int position) { return position; } public View getView(int position, View convertView, ViewGroup parent) { View sv = null; // prepare view if (convertView == null) { sv = (View) itemInflater.inflate(R.layout.ui_message_item, parent, false); holder = new ViewHolder(); holder.time = ((TextView) sv.findViewById(R.id.id_message_time)); holder.level = ((TextView) sv.findViewById(R.id.id_message_level)); holder.tag = ((TextView) sv.findViewById(R.id.id_message_tag)); holder.msg = ((TextView) sv.findViewById(R.id.id_message_text)); sv.setTag(holder); } else { sv = (View) convertView; holder = (ViewHolder) sv.getTag(); } // draw current color for each item if (selectedData.containsKey(position) == true) holder.bkcolor = getResources().getColor(R.color.selected_osmonitor); else if (position % 2 == 0) holder.bkcolor = getResources().getColor(R.color.dkgrey_osmonitor); else holder.bkcolor = getResources().getColor(R.color.black_osmonitor); sv.setBackgroundColor(holder.bkcolor); // get data if (isLogcat(logType) && viewLogcatData.size() > position) { logcatInfo item = viewLogcatData.get(position); if (printLogcatFMT == PrintLogcatFormat.FORMAT_OFF) showLogcatDefaultFormat(item); else showLogcatFormat(item); } else if (viewDmesgData.size() > position) { dmesgInfo item = viewDmesgData.get(position); if (printDmesgFMT == PrintDmesgFormat.FORMAT_OFF) showDmesgDefaultFormat(item); else showDmesgFormat(item); } // long click sv.setOnLongClickListener(new MenuLongClickListener(position)); sv.setOnClickListener(new MenuShortClickListener(position)); // offer better indicator for interactive sv.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: v.setBackgroundColor(getResources().getColor( R.color.selected_osmonitor)); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: ViewHolder holder = (ViewHolder) v.getTag(); v.setBackgroundColor(holder.bkcolor); break; } return false; } }); return sv; } private class MenuShortClickListener implements OnClickListener { private int position; public MenuShortClickListener(int position) { this.position = position; } @Override public void onClick(View v) { if (selectedMode && !selectedData.containsKey(position)) selectedData.put(position, true); else selectedData.remove(position); if (selectedData.size() == 0) { selectedMode = false; ActivityCompat.invalidateOptionsMenu(getActivity()); } messageList.notifyDataSetChanged(); } } private class MenuLongClickListener implements OnLongClickListener { private int position; public MenuLongClickListener(int position) { this.position = position; } @Override public boolean onLongClick(View v) { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setItems(R.array.ui_message_menu_item, new MessageItemMenu( position)); builder.create().show(); return false; } } private class MessageItemMenu implements DialogInterface.OnClickListener { private int position; public MessageItemMenu(int position) { this.position = position; } public void onClick(DialogInterface dialog, int which) { switch (which) { case 0: showProcessInformation(position); break; case 1: selectedMode = true; selectedData.put(position, true); ActivityCompat.invalidateOptionsMenu(getActivity()); messageList.notifyDataSetChanged(); break; } } } private void showProcessInformation(int position) { if (!isLogcat(logType)) { Toast.makeText(getActivity(), getActivity().getResources().getText(R.string.ui_text_notfound), Toast.LENGTH_LONG).show(); return; } int pid = viewLogcatData.get(position).pid(); if (!map.containsKey(pid)) { Toast.makeText(getActivity(), getActivity().getResources().getText(R.string.ui_text_notfound), Toast.LENGTH_LONG).show(); return; } MessageProcessFragment procView = new MessageProcessFragment( getActivity()); procView.setTitle(infoHelper.getPackageName(map.get(pid).name())); procView.setProcessData(map.get(pid)); procView.show(); } private void showDmesgFormat(dmesgInfo item) { holder.msg.setVisibility(View.VISIBLE); holder.level.setVisibility(View.GONE); holder.time.setVisibility(View.GONE); holder.tag.setVisibility(View.GONE); String textColor = UserInterfaceUtil.convertToRGB(getDmesgColor(item.level())); holder.msg.setText(Html.fromHtml(highlightText(String.format("<%d>%s", item.seconds(), item.message().toString()), filterString, textColor))); } private void showDmesgDefaultFormat(dmesgInfo item) { holder.msg.setVisibility(View.VISIBLE); holder.level.setVisibility(View.VISIBLE); holder.time.setVisibility(View.VISIBLE); holder.tag.setVisibility(View.GONE); if (item.seconds() != 0) { final Calendar calendar = Calendar.getInstance(); final java.text.DateFormat convertTool = java.text.DateFormat .getDateTimeInstance(); calendar.setTimeInMillis(item.seconds() * 1000); holder.time.setText(convertTool.format(calendar.getTime())); } holder.msg.setText(Html.fromHtml(highlightText(item.message() .toString(), filterString, "#FFCCCCCC"))); holder.level.setTextColor(Color.BLACK); holder.level .setBackgroundColor(getDmesgColor(item.level())); holder.level.setText(getDmesgTag(item.level())); } private int getDmesgColor(int value) { switch (value) { case dmesgLevel.DEBUG: return settings.getDmesgDebugColor(); case dmesgLevel.INFORMATION: return settings.getDmesgInfoColor(); case dmesgLevel.NOTICE: return settings.getDmesgNoticeColor(); case dmesgLevel.WARNING: return settings.getDmesgWarningColor(); case dmesgLevel.EMERGENCY: return settings.getDmesgEmergencyColor(); case dmesgLevel.ERROR: return settings.getDmesgErrorColor(); case dmesgLevel.ALERT: return settings.getDmesgAlertColor(); case dmesgLevel.CRITICAL: return settings.getDmesgCriticalColor(); } return settings.getDmesgDebugColor(); } private String getDmesgTag(int value) { switch (value) { case dmesgLevel.DEBUG: return "D"; case dmesgLevel.INFORMATION: return "I"; case dmesgLevel.NOTICE: return "N"; case dmesgLevel.WARNING: return "W"; case dmesgLevel.EMERGENCY: return "E"; case dmesgLevel.ERROR: return "E"; case dmesgLevel.ALERT: return "A"; case dmesgLevel.CRITICAL: return "C"; } return "D"; } private void showLogcatFormat(logcatInfo item) { final Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(item.seconds() * 1000); holder.level.setVisibility(View.GONE); holder.time.setVisibility(View.GONE); holder.tag.setVisibility(View.GONE); String textColor = UserInterfaceUtil.convertToRGB(UserInterfaceUtil.getLogcatColor(item.priority())); switch (printLogcatFMT) { case FORMAT_PROCESS: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s(%5d) %s (%s)", UserInterfaceUtil.getLogcatTag(item.priority()), item.pid(), item.message().toString(), item.tag()), filterString, textColor))); break; case FORMAT_TAG: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s/%-8s: %s", UserInterfaceUtil.getLogcatTag(item.priority()), item.tag(), item.message().toString()), filterString, textColor))); break; case FORMAT_THREAD: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s(%5d:%5d) %s", UserInterfaceUtil.getLogcatTag(item.priority()), item.pid(), item.tid(), item.message().toString()), filterString, textColor))); break; case FORMAT_RAW: holder.msg.setText(Html.fromHtml(highlightText( String.format("%s", item.message().toString()), filterString, textColor))); break; case FORMAT_TIME: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s.%03d %s/%-8s(%5d): %s", DateFormat.format("MM-dd HH:mm:ss", calendar.getTime()), item.nanoSeconds() / 1000000, UserInterfaceUtil.getLogcatTag(item.priority()), item.tag(), item .pid(), item.message().toString()), filterString, textColor))); break; case FORMAT_THREADTIME: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s.%03d %5d %5d %s %-8s: %s", DateFormat.format("MM-dd HH:mm:ss", calendar.getTime()), item.nanoSeconds() / 1000000, item .pid(), item.tid(), UserInterfaceUtil.getLogcatTag(item.priority()), item.tag(), item.message().toString()), filterString, textColor))); break; case FORMAT_LONG: holder.msg.setText(Html.fromHtml(highlightText(String.format( "[ %s.%03d %5d:%5d %s/%-8s ]\n%s", DateFormat.format( "MM-dd HH:mm:ss", calendar.getTime()), item.nanoSeconds() / 1000000, item.pid(), item.tid(), UserInterfaceUtil.getLogcatTag(item.priority()), item.tag(), item .message().toString()), filterString, textColor))); break; case FORMAT_BRIEF: default: holder.msg.setText(Html.fromHtml(highlightText(String.format( "%s/%-8s(%5d): %s", UserInterfaceUtil.getLogcatTag(item.priority()), item.tag(), item.pid(), item.message().toString()), filterString, textColor))); break; } } private void showLogcatDefaultFormat(logcatInfo item) { final Calendar calendar = Calendar.getInstance(); final java.text.DateFormat convertTool = java.text.DateFormat .getDateTimeInstance(); calendar.setTimeInMillis(item.seconds() * 1000); holder.level.setVisibility(View.VISIBLE); holder.time.setVisibility(View.VISIBLE); holder.tag.setVisibility(View.VISIBLE); holder.time.setText(convertTool.format(calendar.getTime())); holder.tag.setText(Html.fromHtml(highlightText(item.tag(), filterString, "#FFCCCCCC"))); holder.msg.setText(Html.fromHtml(highlightText(item.message() .toString(), filterString, "#FFCCCCCC"))); holder.level.setText(UserInterfaceUtil.getLogcatTag(item.priority())); holder.level.setTextColor(Color.BLACK); holder.level.setBackgroundColor(UserInterfaceUtil.getLogcatColor(item.priority())); } private String highlightText(String Msg, String HLText, String Color) { if (HLText.length() == 0) return "<font color='" + Color + "'>" + Msg + "</font>"; return "<font color='" + Color + "'>" + Msg.replaceAll("(?i)(" + HLText + ")", "</font><font color='red'>$1</font><font color='" + Color + "'>") + "</font>"; } public void refresh() { if (isLogcat(logType)) messageCount.setText(String.format("%,d", viewLogcatData.size())); else messageCount.setText(String.format("%,d", viewDmesgData.size())); notifyDataSetChanged(); if (autoScrollEnd == true) { getListView().setSelection(getListView().getCount() - 1); getListView().clearFocus(); } } private class MessageFilter extends Filter { public void doFilter() { filter(filterString); } @Override protected FilterResults performFiltering(CharSequence constraint) { FilterResults result = new FilterResults(); // non-filter if (constraint != null) filterString = constraint.toString().toLowerCase(Locale.getDefault()); else filterString = ""; // filter if (isLogcat(logType)) { ArrayList<logcatInfo> filteredItems = new ArrayList<logcatInfo>(); for (int index = 0; index < sourceLogcatData.get( UserInterfaceUtil.convertTypeToLoc(logType)).size(); index++) { logcatInfo item = sourceLogcatData.get(UserInterfaceUtil.convertTypeToLoc(logType)) .get(index); if (filterLogcatArray[UserInterfaceUtil.convertLogcatType(item.priority())] == false) continue; if (filterString.length() != 0) if (!item.message().toLowerCase(Locale.getDefault()) .contains(filterString) && !item.tag().toLowerCase(Locale.getDefault()) .contains(filterString)) continue; filteredItems.add(item); } result.count = filteredItems.size(); result.values = filteredItems; } else { ArrayList<dmesgInfo> filteredItems = new ArrayList<dmesgInfo>(); for (int index = 0; index < sourceDmesgData.size(); index++) { dmesgInfo item = sourceDmesgData.get(index); if (filterDmesgArray[UserInterfaceUtil.convertDmesgType(item.level())] == false) continue; if (filterString.length() != 0) if (!item.message().toLowerCase(Locale.getDefault()) .contains(filterString)) continue; filteredItems.add(item); } result.count = filteredItems.size(); result.values = filteredItems; } return result; } @SuppressWarnings("unchecked") @Override protected void publishResults(CharSequence constraint, FilterResults results) { // detect user behavior if (getListView().getLastVisiblePosition() == (getListView().getCount() - 1)) autoScrollEnd = true; else autoScrollEnd = false; if (results.values == null) { viewDmesgData = sourceDmesgData; // avoid to access before sourceLogcatData is ready if (UserInterfaceUtil.convertTypeToLoc(selectedType) < sourceLogcatData.size()) viewLogcatData = sourceLogcatData .get(UserInterfaceUtil.convertTypeToLoc(selectedType)); } else { if (isLogcat(logType)) viewLogcatData = (ArrayList<logcatInfo>) results.values; else viewDmesgData = (ArrayList<dmesgInfo>) results.values; } refresh(); } } } }