/*
* Performance Control - An Android CPU Control application Copyright (C)
* Brandon Valosek, 2011 <bvalosek@gmail.com> Copyright (C) Modified by 2012
* <James Roberts "xoomdevnet@gmail.com">
*
* 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 3 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, see <http://www.gnu.org/licenses/>.
*/
package com.dsht.open;
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.preference.PreferenceManager;
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;
import android.widget.ArrayAdapter;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.dsht.kerneltweaker.Helpers;
import com.dsht.kerneltweaker.MainActivity;
import com.dsht.kerneltweaker.R;
import com.dsht.open.CPUStateMonitor.CPUStateMonitorException;
import com.dsht.open.CPUStateMonitor.CpuState;
public class TimeInState extends Fragment implements Constants {
private LinearLayout mStatesView;
private TextView mAdditionalStates;
private TextView mTotalStateTime;
private TextView mHeaderAdditionalStates;
private TextView mHeaderTotalStateTime;
private TextView mStatesWarning;
private boolean mUpdatingData = false;
private LayoutInflater mInflater;
private int mCpuNum = 1;
private CpuInfoListAdapter mCpuInfoListAdapter;
private CurCPUThread mCurCPUThread;
private CPUStateMonitor monitor = new CPUStateMonitor();
private Context context;
private SharedPreferences preferences;
private List<String> mCpuInfoListData;
public class CpuInfoListAdapter extends ArrayAdapter<String> {
public CpuInfoListAdapter(Context context, int resource, List<String> values) {
super(context, R.layout.cpu_info_item, resource, values);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View rowView = mInflater.inflate(R.layout.cpu_info_item, parent, false);
TextView cpuInfoCore = (TextView) rowView.findViewById(R.id.cpu_info_core);
TextView cpuInfoFreq = (TextView) rowView.findViewById(R.id.cpu_info_freq);
cpuInfoCore.setText(getString(R.string.core) + " " + String.valueOf(position) + ": ");
cpuInfoFreq.setText(mCpuInfoListData.get(position));
return rowView;
}
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getActivity();
preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (savedInstanceState != null) {
mUpdatingData = savedInstanceState.getBoolean("updatingData");
}
loadOffsets();
mCpuNum = Helpers.getNumOfCpus();
setRetainInstance(true);
setHasOptionsMenu(true);
if(MainActivity.menu != null && MainActivity.menu.isMenuShowing()) {
MainActivity.menu.toggle(true);
}
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle savedInstanceState) {
super.onCreateView(inflater, root, savedInstanceState);
View view = inflater.inflate(R.layout.time_in_state, root, false);
mStatesView = (LinearLayout) view.findViewById(R.id.ui_states_view);
mAdditionalStates = (TextView) view.findViewById(R.id.ui_additional_states);
mHeaderAdditionalStates = (TextView) view.findViewById(R.id.ui_header_additional_states);
mHeaderTotalStateTime = (TextView) view.findViewById(R.id.ui_header_total_state_time);
mStatesWarning = (TextView) view.findViewById(R.id.ui_states_warning);
mTotalStateTime = (TextView) view.findViewById(R.id.ui_total_state_time);
mInflater = inflater;
mCpuInfoListData = new ArrayList<String>(mCpuNum);
for (int i = 0; i < mCpuNum; i++) {
mCpuInfoListData.add("Core " + String.valueOf(i) + ": ");
}
mCpuInfoListAdapter = new CpuInfoListAdapter(
context, android.R.layout.simple_list_item_1, mCpuInfoListData);
ListView mCpuInfoList = (ListView) view.findViewById(R.id.cpu_info_list);
mCpuInfoList.setAdapter(mCpuInfoListAdapter);
mCurCPUThread = new CurCPUThread();
mCurCPUThread.start();
return view;
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean("updatingData", mUpdatingData);
}
@Override
public void onResume() {
refreshData();
if (mCurCPUThread == null) {
mCurCPUThread = new CurCPUThread();
mCurCPUThread.start();
}
super.onResume();
}
@Override
public void onPause() {
super.onPause();
if (mCurCPUThread != null) {
if (mCurCPUThread.isAlive()) {
mCurCPUThread.interrupt();
try {
mCurCPUThread.join();
} catch (InterruptedException e) {
}
}
mCurCPUThread = null;
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
if (!getResources().getBoolean(R.bool.config_showPerformanceOnly)) {
inflater.inflate(R.menu.time_in_state_menu, menu);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.refresh:
refreshData();
break;
case R.id.reset:
try {
monitor.setOffsets();
} catch (Exception e) {
// not good
}
saveOffsets();
updateView();
break;
case R.id.restore:
monitor.removeOffsets();
saveOffsets();
updateView();
break;
}
return true;
}
public void updateView() {
mStatesView.removeAllViews();
List<String> extraStates = new ArrayList<String>();
for (CpuState state : monitor.getStates()) {
if (state.duration > 0) {
generateStateRow(state, mStatesView);
} else {
if (state.freq == 0) {
extraStates.add(getString(R.string.deep_sleep));
} else {
extraStates.add(state.freq / 1000 + " MHz");
}
}
}
if (monitor.getStates().size() == 0) {
mStatesWarning.setVisibility(View.VISIBLE);
mHeaderTotalStateTime.setVisibility(View.GONE);
mTotalStateTime.setVisibility(View.GONE);
mStatesView.setVisibility(View.GONE);
}
long totTime = monitor.getTotalStateTime() / 100;
mTotalStateTime.setText(toString(totTime));
if (extraStates.size() > 0) {
int n = 0;
String str = "";
for (String s : extraStates) {
if (n++ > 0)
str += ", ";
str += s;
}
mAdditionalStates.setVisibility(View.VISIBLE);
mHeaderAdditionalStates.setVisibility(View.VISIBLE);
mAdditionalStates.setText(str);
} else {
mAdditionalStates.setVisibility(View.GONE);
mHeaderAdditionalStates.setVisibility(View.GONE);
}
}
public void refreshData() {
if (!mUpdatingData) {
new RefreshStateDataTask().execute((Void) null);
}
}
private static String toString(long tSec) {
long h = (long) Math.floor(tSec / (60 * 60));
long m = (long) Math.floor((tSec - h * 60 * 60) / 60);
long s = tSec % 60;
String sDur;
sDur = h + ":";
if (m < 10)
sDur += "0";
sDur += m + ":";
if (s < 10)
sDur += "0";
sDur += s;
return sDur;
}
private View generateStateRow(CpuState state, ViewGroup parent) {
LayoutInflater inflater = LayoutInflater.from(context);
LinearLayout view = (LinearLayout) inflater.inflate(R.layout.state_row, parent, false);
float per = (float) state.duration * 100 / monitor.getTotalStateTime();
String sPer = (int) per + "%";
String sFreq;
if (state.freq == 0) {
sFreq = getString(R.string.deep_sleep);
} else {
sFreq = state.freq / 1000 + " MHz";
}
long tSec = state.duration / 100;
String sDur = toString(tSec);
TextView freqText = (TextView) view.findViewById(R.id.ui_freq_text);
TextView durText = (TextView) view.findViewById(R.id.ui_duration_text);
TextView perText = (TextView) view.findViewById(R.id.ui_percentage_text);
ProgressBar bar = (ProgressBar) view.findViewById(R.id.ui_bar);
freqText.setText(sFreq);
perText.setText(sPer);
durText.setText(sDur);
bar.setProgress((int) per);
parent.addView(view);
return view;
}
protected class RefreshStateDataTask extends AsyncTask<Void, Void, Void> {
@Override
protected Void doInBackground(Void... v) {
try {
monitor.updateStates();
} catch (CPUStateMonitorException e) {
}
return null;
}
@Override
protected void onPreExecute() {
mUpdatingData = true;
}
@Override
protected void onPostExecute(Void v) {
updateView();
mUpdatingData = false;
}
}
public void loadOffsets() {
String prefs = preferences.getString(PREF_OFFSETS, "");
if (prefs == null || prefs.length() < 1) {
return;
}
Map<Integer, Long> offsets = new HashMap<Integer, Long>();
String[] sOffsets = prefs.split(",");
for (String offset : sOffsets) {
String[] parts = offset.split(" ");
offsets.put(Integer.parseInt(parts[0]), Long.parseLong(parts[1]));
}
monitor.setOffsets(offsets);
}
public void saveOffsets() {
SharedPreferences.Editor editor = preferences.edit();
String str = "";
for (Map.Entry<Integer, Long> entry : monitor.getOffsets().entrySet()) {
str += entry.getKey() + " " + entry.getValue() + ",";
}
editor.putString(PREF_OFFSETS, str).commit();
}
protected class CurCPUThread extends Thread {
private boolean mInterrupt = false;
public void interrupt() {
mInterrupt = true;
}
@Override
public void run() {
try {
while (!mInterrupt) {
sleep(500);
List<String> freqs = new ArrayList<String>();
for (int i = 0; i < mCpuNum; i++) {
String cpuFreq = CPU_PATH + String.valueOf(i) + CPU_FREQ_TAIL;
String curFreq = "0";
if (Helpers.fileExists(cpuFreq)) {
curFreq = Helpers.readOneLine(cpuFreq);
}
freqs.add(curFreq);
}
String[] freqArray = freqs.toArray(new String[freqs.size()]);
mCurCPUHandler.sendMessage(mCurCPUHandler.obtainMessage(0, freqArray));
}
} catch (InterruptedException e) {
//return;
}
}
}
protected Handler mCurCPUHandler = new Handler() {
public void handleMessage(Message msg) {
String[] freqArray = (String[]) msg.obj;
for (int i = 0; i < freqArray.length; i++) {
// Convert freq in MHz
try {
int freqHz = Integer.parseInt(freqArray[i]);
if (freqHz == 0) {
mCpuInfoListData.set(i, getString(R.string.core_offline));
} else {
mCpuInfoListData.set(i, Integer.toString(freqHz / 1000) + " MHz");
}
} catch (Exception e) {
// Do nothing
}
}
mCpuInfoListAdapter.notifyDataSetChanged();
}
};
}