package com.eolwral.osmonitor.ui;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.graphics.drawable.BitmapDrawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Debug.MemoryInfo;
import android.support.v4.app.FragmentManager;
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.support.v4.view.MenuItemCompat.OnActionExpandListener;
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.view.ViewStub;
import android.view.WindowManager.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.PopupWindow;
import android.widget.PopupWindow.OnDismissListener;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.TextView;
import android.widget.Toast;
import com.eolwral.osmonitor.R;
import com.eolwral.osmonitor.core.osInfo;
import com.eolwral.osmonitor.core.processInfo;
import com.eolwral.osmonitor.core.processInfoList;
import com.eolwral.osmonitor.core.processStatus;
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.CoreUtil;
import com.eolwral.osmonitor.util.ProcessUtil;
import com.eolwral.osmonitor.util.UserInterfaceUtil;
import com.google.flatbuffers.FlatBufferBuilder;
import java.nio.ByteBuffer;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
public class ProcessFragment extends ListFragment implements ipcClientListener {
// ipc client
private IpcService ipcService = IpcService.getInstance();
private boolean ipcStop = false;
// screen
private TextView processCount = null;
private TextView cpuUsage = null;
private TextView memoryTotal = null;
private TextView memoryFree = null;
// data
private ProcessUtil infoHelper = null;
private FlatBufferBuilder sysProcess = null;
private ArrayList<processInfo> data = new ArrayList<processInfo>();
private osInfo info = null;
private Settings settings = null;
// status
private static int itemColor[] = null;
private static int oddItem = 0;
private static int evenItem = 1;
private static int selectedItem = 2;
private final SimpleArrayMap<String, Boolean> expandStatus = new SimpleArrayMap<String, Boolean>();
private final SimpleArrayMap<String, Integer> selectedStatus = new SimpleArrayMap<String, Integer>();
// tablet
private boolean tabletLayout = false;
private int selectedPID = -1;
private int selectedPriority = 0;
private String selectedProcess = "";
private ViewHolder selectedHolder = null;
// preference
private enum SortType {
SortbyUsage, SortbyMemory, SortbyPid, SortbyName, SortbyCPUTime, SortbyStatus
}
private SortType sortSetting = SortType.SortbyUsage;
// kill
private enum KillMode {
None, Select
}
private KillMode killSetting = KillMode.None;
ImageButton killButton = null;
// stop or start
private boolean stopUpdate = false;
private ImageButton stopButton = null;
private PopupWindow sortMenu = null;
private boolean openSortMenu = false;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// prepare draw color
itemColor = new int[3];
itemColor[oddItem] = getResources().getColor(R.color.dkgrey_osmonitor);
itemColor[evenItem] = getResources().getColor(R.color.black_osmonitor);
itemColor[selectedItem] = getResources().getColor(R.color.selected_osmonitor);
settings = Settings.getInstance(getActivity().getApplicationContext());
infoHelper = ProcessUtil.getInstance(getActivity().getApplicationContext(),
true);
setListAdapter(new ProcessListAdapter(getActivity().getApplicationContext()));
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.ui_process_fragment, container, false);
// detect layout
if (v.findViewById(R.id.ui_process_tablet_layout) != null) {
tabletLayout = true;
prepareSelectedHolder(v);
} else {
tabletLayout = false;
resetSelectedHolder();
}
// enable fragment option menu
setHasOptionsMenu(true);
// get UI item
processCount = ((TextView) v.findViewById(R.id.id_process_count));
cpuUsage = ((TextView) v.findViewById(R.id.id_process_cpuusage));
memoryTotal = ((TextView) v.findViewById(R.id.id_process_memorytotal));
memoryFree = ((TextView) v.findViewById(R.id.id_process_memoryfree));
// detect last sort mode
if (settings.getSortType().equals("Usage")) {
sortSetting = SortType.SortbyUsage;
} else if (settings.getSortType().equals("Pid")) {
sortSetting = SortType.SortbyPid;
} else if (settings.getSortType().equals("Memory")) {
sortSetting = SortType.SortbyMemory;
} else if (settings.getSortType().equals("Name")) {
sortSetting = SortType.SortbyName;
} else if (settings.getSortType().equals("CPUTime")) {
sortSetting = SortType.SortbyCPUTime;
}
return v;
}
private void prepareSelectedHolder(View v) {
selectedHolder = new ViewHolder();
selectedHolder.detailIcon = ((ImageView) v
.findViewById(R.id.id_process_detail_image));
selectedHolder.detailTitle = ((TextView) v
.findViewById(R.id.id_process_detail_title));
selectedHolder.detailName = ((TextView) v
.findViewById(R.id.id_process_detail_name));
selectedHolder.detailStatus = ((TextView) v
.findViewById(R.id.id_process_detail_status));
selectedHolder.detailStime = ((TextView) v
.findViewById(R.id.id_process_detail_stime));
selectedHolder.detailUtime = ((TextView) v
.findViewById(R.id.id_process_detail_utime));
selectedHolder.detailCPUtime = ((TextView) v
.findViewById(R.id.id_process_detail_cputime));
selectedHolder.detailMemory = ((TextView) v
.findViewById(R.id.id_process_detail_memory));
selectedHolder.detailPPID = ((TextView) v
.findViewById(R.id.id_process_detail_ppid));
selectedHolder.detailUser = ((TextView) v
.findViewById(R.id.id_process_detail_user));
selectedHolder.detailStarttime = ((TextView) v
.findViewById(R.id.id_process_detail_starttime));
selectedHolder.detailThread = ((TextView) v
.findViewById(R.id.id_process_detail_thread));
selectedHolder.detailNice = ((TextView) v
.findViewById(R.id.id_process_detail_nice));
String[] menuText = getResources().getStringArray(
R.array.ui_process_menu_item);
Button buttonAction = (Button) v.findViewById(R.id.id_process_button_kill);
buttonAction.setText(menuText[0]);
buttonAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (selectedPID != -1 && !selectedProcess.isEmpty())
killProcess(selectedPID, selectedProcess);
}
});
// custom added
buttonAction = (Button) v.findViewById(R.id.id_process_button_forceClose);
buttonAction.setText(menuText[5]);
buttonAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (selectedPID != -1 && !selectedProcess.isEmpty())
forceCloseProcess(selectedPID, selectedProcess);
}
});
buttonAction = (Button) v.findViewById(R.id.id_process_button_switch);
buttonAction.setText(menuText[1]);
buttonAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (selectedPID != -1 && !selectedProcess.isEmpty())
switchToProcess(selectedPID, selectedProcess);
}
});
// custom changed: (fixed incorrect text)
buttonAction = (Button) v.findViewById(R.id.id_process_button_watchlog);
buttonAction.setText(menuText[3]);
buttonAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (selectedPID != -1 && !selectedProcess.isEmpty())
watchLog(selectedPID, selectedProcess);
}
});
buttonAction = (Button) v.findViewById(R.id.id_process_button_setpriority);
buttonAction.setText(menuText[4]);
buttonAction.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (selectedPID != -1 && !selectedProcess.isEmpty())
setPriority(selectedPID, selectedProcess, selectedPriority);
}
});
}
private void resetSelectedHolder() {
selectedHolder = null;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.ui_process_menu, menu);
MenuItem toolsMenu = menu.findItem(R.id.ui_menu_tools);
MenuItemCompat.setOnActionExpandListener(toolsMenu,
new ToolActionExpandListener());
View toolsView = MenuItemCompat.getActionView(toolsMenu);
ImageButton sortButton = (ImageButton) toolsView
.findViewById(R.id.id_action_sort);
sortButton.setOnClickListener(new SortMenuClickListener());
killButton = (ImageButton) toolsView.findViewById(R.id.id_action_kill);
killButton.setOnClickListener(new KillButtonClickListener());
killSetting = KillMode.None;
// refresh button
stopButton = (ImageButton) toolsView.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);
}
});
return;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.ui_menu_help:
onHelpClick();
break;
case R.id.ui_menu_setting:
onSettingClick();
break;
case R.id.ui_menu_exit:
onExitClick();
break;
}
return super.onOptionsItemSelected(item);
}
private void onExitClick() {
LocalBroadcastManager.getInstance(getActivity()).sendBroadcast(
new Intent("Exit"));
return;
}
private void onSettingClick() {
Intent settings = new Intent(getActivity(), OSMPreference.class);
settings.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(settings);
return;
}
private void onHelpClick() {
ShowHelp();
return;
}
private class ToolActionExpandListener implements OnActionExpandListener {
@Override
public boolean onMenuItemActionExpand(MenuItem item) {
return true;
}
@Override
public boolean onMenuItemActionCollapse(MenuItem item) {
killSetting = KillMode.None;
selectedStatus.clear();
((ProcessListAdapter) getListAdapter()).refresh();
return true;
}
}
private class KillButtonClickListener implements OnClickListener {
@Override
public void onClick(View v) {
// enter select mode
if (killSetting == KillMode.None) {
killSetting = KillMode.Select;
killButton.setImageResource(R.drawable.ic_action_kill_done);
return;
}
// leave select mode
killButton.setImageResource(R.drawable.ic_action_kill);
killSetting = KillMode.None;
// selected items is empty
if (selectedStatus.size() == 0)
return;
// show message
String rawFormat = getResources().getString(R.string.ui_text_kill);
String strFormat = String.format(rawFormat, selectedStatus.size());
Toast.makeText(getActivity().getApplicationContext(), strFormat,
Toast.LENGTH_SHORT).show();
// kill all selected items
for (int i = 0; i < selectedStatus.size(); i++)
killProcess(selectedStatus.valueAt(i), selectedStatus.keyAt(i));
// clean up selected items
selectedStatus.clear();
}
}
private class SortMenuClickListener implements OnClickListener {
@Override
public void onClick(View v) {
openSortMenu = !openSortMenu;
if (!openSortMenu) {
sortMenu.dismiss();
return;
}
if (null == sortMenu) {
View layout = LayoutInflater.from(getActivity()).inflate(
R.layout.ui_process_menu_sort, null);
sortMenu = new PopupWindow(layout);
sortMenu.setBackgroundDrawable(new BitmapDrawable());
sortMenu.setFocusable(true);
sortMenu.setWidth(LayoutParams.WRAP_CONTENT);
sortMenu.setHeight(LayoutParams.WRAP_CONTENT);
sortMenu.setOnDismissListener(new OnDismissListener() {
@Override
public void onDismiss() {
openSortMenu = false;
}
});
}
RadioGroup sortGroup = (RadioGroup) sortMenu.getContentView()
.findViewById(R.id.id_process_sort_group);
switch (sortSetting) {
case SortbyUsage:
sortGroup.check(R.id.id_process_sort_usage);
break;
case SortbyMemory:
sortGroup.check(R.id.id_process_sort_memory);
break;
case SortbyPid:
sortGroup.check(R.id.id_process_sort_pid);
break;
case SortbyName:
sortGroup.check(R.id.id_process_sort_name);
break;
case SortbyCPUTime:
sortGroup.check(R.id.id_process_sort_cputime);
break;
case SortbyStatus:
sortGroup.check(R.id.id_process_sort_status);
break;
}
sortGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
switch (checkedId) {
case R.id.id_process_sort_usage:
sortSetting = SortType.SortbyUsage;
settings.setSortType("Usage");
break;
case R.id.id_process_sort_memory:
sortSetting = SortType.SortbyMemory;
settings.setSortType("Memory");
break;
case R.id.id_process_sort_pid:
sortSetting = SortType.SortbyPid;
settings.setSortType("Pid");
break;
case R.id.id_process_sort_name:
sortSetting = SortType.SortbyName;
settings.setSortType("Name");
break;
case R.id.id_process_sort_cputime:
sortSetting = SortType.SortbyCPUTime;
settings.setSortType("CPUTime");
break;
case R.id.id_process_sort_status:
sortSetting = SortType.SortbyStatus;
settings.setSortType("Status");
break;
}
// force update after setting has been changed
if (stopUpdate == true)
stopButton.performClick();
sortMenu.dismiss();
}
});
sortMenu.showAsDropDown(v);
}
}
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
// clear up selected item
if (this.isVisible()) {
if (!isVisibleToUser) {
selectedStatus.clear();
((ProcessListAdapter) getListAdapter()).refresh();
}
}
ipcService.removeRequest(this);
ipcStop = !isVisibleToUser;
if (isVisibleToUser == true) {
byte newCommand[] = { ipcCategory.OS, ipcCategory.PROCESS };
ipcService.addRequest(newCommand, 0, this);
}
}
private void killProcess(int pid, String process) {
CoreUtil.killProcess(pid, getActivity());
((ActivityManager) getActivity().getSystemService(Context.ACTIVITY_SERVICE)).restartPackage(process);
}
// added by Venryx
private void forceCloseProcess(int pid, String process) {
String packageName = process.contains(":") ? process.substring(0, process.indexOf(":")) : process;
String[] commands = {"am force-stop " + packageName};
try {
CoreUtil.runSU(commands);
} catch (Exception e) { }
}
private void watchLog(int pid, String process) {
// pass information
ProcessLogViewFragment newLog = new ProcessLogViewFragment();
Bundle args = new Bundle();
args.putInt(ProcessLogViewFragment.TARGETPID, pid);
args.putString(ProcessLogViewFragment.TARGETNAME,
infoHelper.getPackageName(process));
newLog.setArguments(args);
// replace current fragment
final FragmentManager fragmanger = getActivity()
.getSupportFragmentManager();
newLog.show(fragmanger, "logview");
}
private void switchToProcess(int pid, String process) {
PackageManager QueryPackage = getActivity().getPackageManager();
Intent mainIntent = new Intent(Intent.ACTION_MAIN, null)
.addCategory(Intent.CATEGORY_LAUNCHER);
List<ResolveInfo> appList = QueryPackage.queryIntentActivities(mainIntent,
0);
String className = null;
for (int index = 0; index < appList.size(); index++)
if (appList.get(index).activityInfo.applicationInfo.packageName
.equals(process))
className = appList.get(index).activityInfo.name;
if (className != null) {
Intent switchIntent = new Intent();
switchIntent.setAction(Intent.ACTION_MAIN);
switchIntent.addCategory(Intent.CATEGORY_LAUNCHER);
switchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
| Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
switchIntent.setComponent(new ComponentName(process, className));
startActivity(switchIntent);
getActivity().finish();
}
}
private void switchToAppInfo(int pid, String process) {
try {
//Open the specific App Info page:
Intent intent = new Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
intent.setData(Uri.parse("package:" + process));
startActivity(intent);
} catch ( ActivityNotFoundException e ) {
//Open the generic Apps page:
Intent intent = new Intent(android.provider.Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS);
startActivity(intent);
}
}
private void setPriority(int pid, String process, int priority) {
// pass information
ProcessPriorityFragment newPriority = new ProcessPriorityFragment();
Bundle args = new Bundle();
args.putInt(ProcessPriorityFragment.TARGETPID, pid);
args.putString(ProcessPriorityFragment.TARGETNAME,
infoHelper.getPackageName(process));
args.putInt(ProcessPriorityFragment.DEFAULTPRIORITY, priority);
newPriority.setArguments(args);
// replace current fragment
final FragmentManager fragmanger = getActivity()
.getSupportFragmentManager();
newPriority.show(fragmanger, "priority");
}
@Override
public void onRecvData(byte [] result) {
// check
if (ipcStop == true)
return;
// stop update
if (stopUpdate == true || result == null || result.length == 0) {
byte newCommand[] = { ipcCategory.OS, ipcCategory.PROCESS };
ipcService.addRequest(newCommand, settings.getInterval(), this);
return;
}
// clean up
while (!data.isEmpty())
data.remove(0);
data.clear();
// convert data
ipcMessage ipcMessageResult = ipcMessage.getRootAsipcMessage(ByteBuffer.wrap(result));
for (int index = 0; index < ipcMessageResult.dataLength(); index++) {
try {
ipcData rawData = ipcMessageResult.data(index);
if (rawData.category() == ipcCategory.OS)
info = osInfo.getRootAsosInfo(rawData.payloadAsByteBuffer().asReadOnlyBuffer());
else if (rawData.category() == ipcCategory.PROCESS)
extractProcessInfo(rawData);
} catch (Exception e) {
e.printStackTrace();
}
}
// calculate CPU Usage
float totalCPUUsage = 0;
for (int index = 0; index < data.size(); index++)
totalCPUUsage += data.get(index).cpuUsage();
// sort data
switch (sortSetting) {
case SortbyUsage:
Collections.sort(data, new SortbyUsage());
break;
case SortbyMemory:
Collections.sort(data, new SortbyMemory());
break;
case SortbyPid:
Collections.sort(data, new SortbyPid());
break;
case SortbyName:
Collections.sort(data, new SortbyName());
break;
case SortbyCPUTime:
Collections.sort(data, new SortbyCPUTime());
break;
case SortbyStatus:
Collections.sort(data, new SortbyStatus());
break;
}
processCount.setText("" + data.size());
cpuUsage.setText(UserInterfaceUtil.convertToUsage(totalCPUUsage) + "%");
if (info != null) {
memoryTotal.setText(UserInterfaceUtil.convertToSize(info.totalMemory(), true));
memoryFree.setText(UserInterfaceUtil.convertToSize( info.freeMemory() + info.bufferedMemory() + info.cachedMemory(), true));
}
getActivity().runOnUiThread(new Runnable() {
public void run() {
((ProcessListAdapter) getListAdapter()).refresh();
}
});
// send command again
byte newCommand[] = { ipcCategory.OS, ipcCategory.PROCESS };
ipcService.addRequest(newCommand, settings.getInterval(), this);
}
/**
* Comparator class for sort by usage
*/
private class SortbyUsage implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
if (lhs.cpuUsage() > rhs.cpuUsage())
return -1;
else if (lhs.cpuUsage() < rhs.cpuUsage())
return 1;
return 0;
}
}
/**
* Comparator class for sort by memory
*/
private class SortbyMemory implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
if (lhs.rss() > rhs.rss())
return -1;
else if (lhs.rss() < rhs.rss())
return 1;
return 0;
}
}
/**
* Comparator class for sort by Pid
*/
private class SortbyPid implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
if (lhs.pid() > rhs.pid())
return -1;
else if (lhs.pid() < rhs.pid())
return 1;
return 0;
}
}
/**
* Comparator class for sort by Name
*/
private class SortbyName implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
String lhsName = infoHelper.getPackageName(lhs.name());
if (lhsName == null)
lhsName = lhs.name();
String rhsName = infoHelper.getPackageName(rhs.name());
if (rhsName == null)
rhsName = rhs.name();
if (rhsName.compareTo(lhsName) < 0)
return 1;
else if (rhsName.compareTo(lhsName) > 0)
return -1;
return 0;
}
}
/**
* Comparator class for sort by CPU time
*/
private class SortbyCPUTime implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
if (lhs.cpuTime() > rhs.cpuTime())
return -1;
else if (lhs.cpuTime() < rhs.cpuTime())
return 1;
return 0;
}
}
/**
* Comparator class for sort by Status
*/
private class SortbyStatus implements Comparator<processInfo> {
@Override
public int compare(processInfo lhs, processInfo rhs) {
if (lhs.status() < rhs.status())
return -1;
else if (lhs.status() > rhs.status())
return 1;
return 0;
}
}
/**
* implement viewholder class for process list
*/
private class ViewHolder {
// main information
TextView pid;
ImageView icon;
TextView name;
TextView cpuUsage;
// color
int bkcolor;
// phone layout detail information
LinearLayout detail;
// tablet layout detail information
TextView detailTitle;
ImageView detailIcon;
// common detail information
TextView detailName;
TextView detailStatus;
TextView detailStime;
TextView detailUtime;
TextView detailCPUtime;
TextView detailMemory;
TextView detailPPID;
TextView detailUser;
TextView detailStarttime;
TextView detailThread;
TextView detailNice;
}
private void extractProcessInfo(ipcData rawData)
{
// summary value
float cpuUsage = 0;
long rss = 0;
long vsz = 0;
long startTime = 0;
int threadCount = 0;
long usedSystemTime = 0;
long usedUserTime = 0;
long cpuTime = 0;
// process processInfo
processInfoList psInfoList = processInfoList.getRootAsprocessInfoList(rawData.payloadAsByteBuffer().asReadOnlyBuffer());
for (int count = 0; count < psInfoList.listLength(); count++) {
processInfo psInfo = psInfoList.list(count);
boolean doMerge = false;
if (psInfo.uid() == 0 || psInfo.name().contains("/system/") || psInfo.name().contains("/sbin/"))
doMerge = true;
if (psInfo.name().toLowerCase(Locale.getDefault()).contains("osmcore"))
doMerge = false;
if (settings.isUseExpertMode())
doMerge = false;
// Don't merge data
if (doMerge == false) {
data.add(psInfo);
continue;
}
// Merge process information into a process
cpuUsage += psInfo.cpuUsage();
rss += psInfo.rss();
vsz += psInfo.vsz();
threadCount += psInfo.threadCount();
usedSystemTime += psInfo.usedSystemTime();
usedUserTime += psInfo.usedUserTime();
cpuTime += psInfo.cpuTime();
if (startTime < psInfo.startTime() || startTime == 0)
startTime = psInfo.startTime();
}
if (!settings.isUseExpertMode())
{
// summary all system processes
sysProcess = new FlatBufferBuilder(0);
int [] sysProcessArray = new int[1];
int sysProcessName = sysProcess.createString("System");
int sysProcessOwner = sysProcess.createString("root");
processInfo.startprocessInfo(sysProcess);
processInfo.addPid(sysProcess, 0);
processInfo.addUid(sysProcess, 0);
processInfo.addPpid(sysProcess, 0);
processInfo.addName(sysProcess, sysProcessName);
processInfo.addOwner(sysProcess, sysProcessOwner);
processInfo.addPriorityLevel(sysProcess, 0);
processInfo.addStatus(sysProcess, processStatus.Running);
processInfo.addCpuUsage(sysProcess, cpuUsage);
processInfo.addRss(sysProcess, rss);
processInfo.addVsz(sysProcess, vsz);
processInfo.addStartTime(sysProcess, startTime);
processInfo.addThreadCount(sysProcess, threadCount);
processInfo.addUsedSystemTime(sysProcess, usedSystemTime);
processInfo.addUsedUserTime(sysProcess, usedUserTime);
processInfo.addCpuTime(sysProcess, cpuTime);
sysProcessArray[0] = processInfo.endprocessInfo(sysProcess);
int sysProcessVector = processInfoList.createListVector(sysProcess, sysProcessArray);
int sysProcessList = processInfoList.createprocessInfoList(sysProcess, sysProcessVector);
processInfoList.finishprocessInfoListBuffer(sysProcess, sysProcessList);
processInfoList sysProcessInfoList = processInfoList.getRootAsprocessInfoList(sysProcess.dataBuffer());
data.add(sysProcessInfoList.list(0));
}
}
private void setItemStatus(View v, boolean status) {
// change expand status
if (status) {
ViewHolder holder = (ViewHolder) v.getTag();
if (holder.detail == null) {
// loading view
ViewStub stub = (ViewStub) v
.findViewById(R.id.id_process_detail_viewstub);
stub.inflate();
// prepare detail information
holder.detail = (LinearLayout) v
.findViewById(R.id.id_process_detail_stub);
holder.detailName = ((TextView) v
.findViewById(R.id.id_process_detail_name));
holder.detailStatus = ((TextView) v
.findViewById(R.id.id_process_detail_status));
holder.detailStime = ((TextView) v
.findViewById(R.id.id_process_detail_stime));
holder.detailUtime = ((TextView) v
.findViewById(R.id.id_process_detail_utime));
holder.detailCPUtime = ((TextView) v
.findViewById(R.id.id_process_detail_cputime));
holder.detailMemory = ((TextView) v
.findViewById(R.id.id_process_detail_memory));
holder.detailPPID = ((TextView) v
.findViewById(R.id.id_process_detail_ppid));
holder.detailUser = ((TextView) v
.findViewById(R.id.id_process_detail_user));
holder.detailStarttime = ((TextView) v
.findViewById(R.id.id_process_detail_starttime));
holder.detailThread = ((TextView) v
.findViewById(R.id.id_process_detail_thread));
holder.detailNice = ((TextView) v
.findViewById(R.id.id_process_detail_nice));
} else
holder.detail.setVisibility(View.VISIBLE);
} else {
ViewHolder holder = (ViewHolder) v.getTag();
if (holder.detail != null)
holder.detail.setVisibility(View.GONE);
}
}
private static void setSelectStatus(View v, int position, boolean status) {
if (status)
v.setBackgroundColor(itemColor[selectedItem]);
else if (position % 2 == 0)
v.setBackgroundColor(itemColor[oddItem]);
else
v.setBackgroundColor(itemColor[evenItem]);
}
private class ProcessListAdapter extends BaseAdapter {
private LayoutInflater itemInflater = null;
private ViewHolder holder = null;
public ProcessListAdapter(Context mContext) {
itemInflater = (LayoutInflater) mContext
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public int getCount() {
return data.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;
// Check size to avoid IndexOutOfBoundsException, this issue should only
// happen when data is updating,
// just returning a empty view to prevent exception, it will be fixed
// automatically on next time.
if (data.size() < position)
return (View) itemInflater.inflate(R.layout.ui_process_item, parent,
false);
// get data
processInfo item = data.get(position);
// check cached status
if (!infoHelper.checkPackageInformation(item.name())) {
if (item.name().toLowerCase(Locale.getDefault()).contains("osmcore"))
infoHelper.doCacheInfo(android.os.Process.myUid(), item.owner(),
item.name());
else
infoHelper
.doCacheInfo(item.uid(), item.owner(), item.name());
}
// prepare view
if (convertView == null) {
sv = (View) itemInflater.inflate(R.layout.ui_process_item, parent,
false);
holder = new ViewHolder();
holder.pid = ((TextView) sv.findViewById(R.id.id_process_pid));
holder.name = ((TextView) sv.findViewById(R.id.id_process_name));
holder.cpuUsage = ((TextView) sv.findViewById(R.id.id_process_value));
holder.icon = ((ImageView) sv.findViewById(R.id.id_process_icon));
sv.setTag(holder);
} else {
sv = (View) convertView;
holder = (ViewHolder) sv.getTag();
}
sv.setOnClickListener(new ProcessClickListener(position));
sv.setOnLongClickListener(new ProcessLongClickListener(position));
// check expand status
if (!tabletLayout) {
if (expandStatus.containsKey(data.get(position).name()) == true)
setItemStatus(sv, true);
else
setItemStatus(sv, false);
}
// draw current color for each item
if (selectedStatus.containsKey(data.get(position).name()) == true)
holder.bkcolor = itemColor[selectedItem];
else if (position % 2 == 0)
holder.bkcolor = itemColor[oddItem];
else
holder.bkcolor = itemColor[evenItem];
sv.setBackgroundColor(holder.bkcolor);
// 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;
}
});
// prepare main information
holder.pid.setText(String.format("%5d", item.pid()));
holder.name.setText(infoHelper.getPackageName(item.name()));
holder.icon.setImageDrawable(infoHelper.getPackageIcon(item.name()));
if (sortSetting == SortType.SortbyMemory)
holder.cpuUsage.setText(UserInterfaceUtil.convertToSize((item.rss() * 1024), true));
else if (sortSetting == SortType.SortbyCPUTime)
holder.cpuUsage.setText(String.format("%02d:%02d", item.cpuTime() / 60, item.cpuTime() % 60));
else if (sortSetting == SortType.SortbyStatus)
holder.cpuUsage.setText(UserInterfaceUtil.getSatusString(item.status()));
else
holder.cpuUsage.setText(UserInterfaceUtil.convertToUsage(item.cpuUsage()));
// prepare detail information
if (holder.detail != null)
showProcessDetail(holder, item);
return sv;
}
private void refreshTabletPanel() {
if (selectedPID == -1)
return;
// find target Item
processInfo targetItem = null;
for (int i = 0; i < data.size(); i++) {
if (data.get(i).pid() != selectedPID)
continue;
targetItem = data.get(i);
break;
}
// show
if (targetItem != null && selectedHolder != null) {
selectedProcess = targetItem.name();
selectedPriority = targetItem.priorityLevel();
showProcessDetail(selectedHolder, targetItem);
}
}
private void showProcessDetail(ViewHolder holder, processInfo item) {
if (holder.detailTitle != null)
holder.detailTitle.setText(infoHelper.getPackageName(item.name()));
if (holder.detailIcon != null)
holder.detailIcon.setImageDrawable(infoHelper.getPackageIcon(item
.name()));
holder.detailName.setText(item.name());
holder.detailStime
.setText(String.format("%,d", item.usedSystemTime()));
holder.detailUtime.setText(String.format("%,d", item.usedUserTime()));
holder.detailCPUtime.setText(String.format("%02d:%02d",
item.cpuTime() / 60, item.cpuTime() % 60));
holder.detailThread.setText(String.format("%d", item.threadCount()));
holder.detailNice.setText(String.format("%d", item.priorityLevel()));
// get memory information
MemoryInfo memInfo = infoHelper.getMemoryInfo(item.pid());
String memoryData = UserInterfaceUtil.convertToSize((item.rss() * 1024), true) + " / "
+ UserInterfaceUtil.convertToSize(memInfo.getTotalPss() * 1024, true) + " / "
+ UserInterfaceUtil.convertToSize(memInfo.getTotalPrivateDirty() * 1024, true);
holder.detailMemory.setText(memoryData);
holder.detailPPID.setText("" + item.ppid());
// convert time format
final Calendar calendar = Calendar.getInstance();
final DateFormat convertTool = DateFormat.getDateTimeInstance(
DateFormat.LONG, DateFormat.MEDIUM, Locale.getDefault());
calendar.setTimeInMillis(item.startTime() * 1000);
holder.detailStarttime.setText(convertTool.format(calendar.getTime()));
holder.detailUser.setText(item.owner());
holder.detailStatus.setText(UserInterfaceUtil.getSatusString(item.status()));
}
public void refresh() {
this.notifyDataSetChanged();
if (tabletLayout == true)
refreshTabletPanel();
}
private class ProcessLongClickListener implements OnLongClickListener {
private int position;
public ProcessLongClickListener(int position) {
this.position = position;
}
@Override
public boolean onLongClick(View v) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(
infoHelper.getPackageName(data.get(position).name())).setItems(
R.array.ui_process_menu_item, new ProcessItemMenu(position));
builder.create().show();
return false;
}
}
private class ProcessItemMenu implements DialogInterface.OnClickListener {
private String process;
private int pid;
private int priority;
public ProcessItemMenu(int position) {
this.process = data.get(position).name();
this.pid = data.get(position).pid();
this.priority = data.get(position).priorityLevel();
}
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case 0:
killProcess(pid, process);
break;
case 1:
switchToProcess(pid, process);
break;
case 2:
switchToAppInfo(pid, process);
break;
case 3:
watchLog(pid, process);
break;
case 4:
setPriority(pid, process, priority);
break;
}
}
}
private class ProcessClickListener implements OnClickListener {
private int position;
public ProcessClickListener(int position) {
this.position = position;
}
public void onClick(View v) {
// enter kill mode
if (killSetting == KillMode.Select) {
this.ToogleSelected(v);
return;
}
// phone
if (!tabletLayout) {
this.ToogleExpand(v);
return;
}
// tablet
ViewHolder holder = (ViewHolder) v.getTag();
if (holder != null)
selectedPID = Integer
.parseInt(holder.pid.getText().toString().trim());
// refresh
refreshTabletPanel();
return;
}
private void ToogleSelected(View v) {
// change expand status
if (selectedStatus.containsKey(data.get(position).name()) == false) {
selectedStatus.put(data.get(position).name(), data.get(position)
.pid());
setSelectStatus(v, position, true);
} else {
selectedStatus.remove(data.get(position).name());
setSelectStatus(v, position, false);
}
}
private void ToogleExpand(View v) {
// data must ready to read
if (position > data.size())
return;
// change expand status
if (expandStatus.containsKey(data.get(position).name()) == false) {
expandStatus.put(data.get(position).name(), Boolean.TRUE);
setItemStatus(v, true);
// force redraw single row
ListView list = getListView();
list.getAdapter().getView(position, v, list);
} else {
expandStatus.remove(data.get(position).name());
setItemStatus(v, false);
}
}
}
}
void ShowHelp() {
CoreUtil.showHelp(getActivity(),
"http://eolwral.github.io/OSMonitor/maunal/index.html");
}
}