/*
* Copyright (C) 2013 - 2014 Alexander "Evisceration" Martinz
*
* 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 org.namelessrom.devicecontrol.modules.cpu;
import android.app.Activity;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v7.widget.SwitchCompat;
import android.text.TextUtils;
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.CompoundButton;
import android.widget.LinearLayout;
import org.namelessrom.devicecontrol.ActivityCallbacks;
import org.namelessrom.devicecontrol.App;
import org.namelessrom.devicecontrol.DeviceConstants;
import org.namelessrom.devicecontrol.R;
import org.namelessrom.devicecontrol.actions.ActionProcessor;
import org.namelessrom.devicecontrol.actions.extras.MpDecisionAction;
import org.namelessrom.devicecontrol.models.BootupConfig;
import org.namelessrom.devicecontrol.models.DeviceConfig;
import org.namelessrom.devicecontrol.modules.bootup.BootupItem;
import org.namelessrom.devicecontrol.preferences.AutoEditTextPreference;
import org.namelessrom.devicecontrol.preferences.AutoSwitchPreference;
import org.namelessrom.devicecontrol.preferences.CustomPreferenceCategoryMaterial;
import org.namelessrom.devicecontrol.utils.PreferenceUtils;
import org.namelessrom.devicecontrol.utils.ShellOutput;
import org.namelessrom.devicecontrol.utils.Utils;
import org.namelessrom.devicecontrol.views.AttachMaterialPreferenceFragment;
import org.namelessrom.devicecontrol.views.CpuCoreView;
import java.util.ArrayList;
import java.util.List;
import at.amartinz.execution.RootShell;
import at.amartinz.hardware.cpu.CpuCore;
import at.amartinz.hardware.cpu.CpuCoreMonitor;
import at.amartinz.hardware.cpu.CpuInformation;
import at.amartinz.hardware.cpu.CpuInformationListener;
import at.amartinz.hardware.cpu.CpuReader;
import alexander.martinz.libs.materialpreferences.MaterialListPreference;
import alexander.martinz.libs.materialpreferences.MaterialPreference;
import alexander.martinz.libs.materialpreferences.MaterialSwitchPreference;
import butterknife.BindString;
import butterknife.BindView;
import butterknife.ButterKnife;
public class CpuSettingsFragment extends AttachMaterialPreferenceFragment implements CpuCoreMonitor.CoreListener, MaterialPreference.MaterialPreferenceChangeListener, MaterialPreference.MaterialPreferenceClickListener, CpuInformationListener {
@BindView(R.id.cpu_pref_max) MaterialListPreference mMax;
@BindView(R.id.cpu_pref_min) MaterialListPreference mMin;
@BindView(R.id.cpu_pref_cpu_lock) MaterialSwitchPreference mCpuLock;
@BindView(R.id.cpu_pref_governor) MaterialListPreference mGovernor;
@BindView(R.id.cpu_pref_governor_tuning) MaterialPreference mGovernorTuning;
@BindView(R.id.cpu_pref_gov_lock) MaterialSwitchPreference mCpuGovLock;
private MaterialSwitchPreference mMpDecision;
private MaterialListPreference mCpuQuietGov;
@BindView(R.id.cpu_info_hide) SwitchCompat mStatusHide;
@BindView(R.id.cpu_info) LinearLayout mCpuInfo;
@BindString(R.string.core) String coreString;
private static final int ID_MPDECISION = 200;
private final ShellOutput.OnShellOutputListener mShellOutputListener = new ShellOutput.OnShellOutputListener() {
@Override public void onShellOutput(final ShellOutput shellOutput) {
if (shellOutput != null) {
switch (shellOutput.id) {
case ID_MPDECISION:
if (mMpDecision != null) {
mMpDecision.setChecked(!TextUtils.isEmpty(shellOutput.output));
mMpDecision.setOnPreferenceChangeListener(
CpuSettingsFragment.this);
}
break;
default:
break;
}
}
}
};
@Override protected int getLayoutResourceId() {
return R.layout.preferences_cpu;
}
@Override protected int getFragmentId() { return DeviceConstants.ID_CTRL_PROCESSOR; }
@Override public void onResume() {
super.onResume();
if (mStatusHide != null && mStatusHide.isChecked()) {
CpuCoreMonitor.getInstance(App.HANDLER).start(this, 750);
}
CpuReader.getCpuInformation(CpuSettingsFragment.this);
}
@Override public void onPause() {
super.onPause();
CpuCoreMonitor.getInstance(App.HANDLER).stop();
}
@Override public void onDestroy() {
CpuCoreMonitor.getInstance(App.HANDLER).destroy();
super.onDestroy();
}
@Override public void onCores(@NonNull final List<CpuCore> cores) {
final int count = cores.size();
if (count != 0) {
for (int i = 0; i < count; i++) {
generateRow(i, cores.get(i));
}
}
}
@Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.menu_refresh, menu);
}
@Override public boolean onOptionsItemSelected(MenuItem item) {
final int id = item.getItemId();
switch (id) {
case R.id.menu_action_refresh: {
CpuReader.getCpuInformation(CpuSettingsFragment.this);
return true;
}
}
return super.onOptionsItemSelected(item);
}
@NonNull @Override public View onCreateView(LayoutInflater inflater, ViewGroup root, Bundle savedState) {
setHasOptionsMenu(true);
final View view = super.onCreateView(inflater, root, savedState);
ButterKnife.bind(this, view);
final DeviceConfig deviceConfig = DeviceConfig.get();
mStatusHide.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override public void onCheckedChanged(final CompoundButton button, final boolean b) {
if (b) {
mCpuInfo.setVisibility(View.VISIBLE);
CpuCoreMonitor.getInstance(App.HANDLER).start(CpuSettingsFragment.this, 750);
} else {
CpuCoreMonitor.getInstance(App.HANDLER).stop();
mCpuInfo.setVisibility(View.GONE);
}
deviceConfig.perfCpuInfo = b;
deviceConfig.save();
}
});
mStatusHide.setChecked(deviceConfig.perfCpuInfo);
if (mStatusHide.isChecked()) {
mCpuInfo.setVisibility(View.VISIBLE);
} else {
mCpuInfo.setVisibility(View.GONE);
}
CpuCore tmpCore;
final int numCpus = CpuReader.readAvailableCores();
for (int i = 0; i < numCpus; i++) {
tmpCore = new CpuCore(i, "0", "0", "0");
generateRow(i, tmpCore);
}
mMax.setOnPreferenceChangeListener(CpuSettingsFragment.this);
mMin.setOnPreferenceChangeListener(CpuSettingsFragment.this);
mGovernor.setOnPreferenceChangeListener(CpuSettingsFragment.this);
mCpuLock.getSwitch().setChecked(deviceConfig.perfCpuLock);
mCpuLock.setOnPreferenceChangeListener(this);
mGovernorTuning.setOnPreferenceClickListener(this);
mCpuGovLock.getSwitch().setChecked(deviceConfig.perfCpuGovLock);
mCpuGovLock.setOnPreferenceChangeListener(this);
if (Utils.fileExists(getResources().getStringArray(R.array.directories_intelli_plug))
|| Utils.fileExists(getString(R.string.directory_mako_hotplug))
|| Utils.fileExists(getString(R.string.file_cpu_quiet_base))
|| Utils.fileExists(MpDecisionAction.MPDECISION_PATH)) {
setupHotpluggingPreferences();
}
view.postDelayed(new Runnable() {
@Override public void run() {
CpuReader.getCpuInformation(CpuSettingsFragment.this);
view.postDelayed(new Runnable() {
@Override public void run() {
CpuReader.getCpuInformation(CpuSettingsFragment.this);
}
}, 500);
}
}, 250);
return view;
}
private void setupHotpluggingPreferences() {
CustomPreferenceCategoryMaterial category = null;
AutoSwitchPreference togglePref;
AutoEditTextPreference editPref;
String[] paths;
String path;
//------------------------------------------------------------------------------------------
// General
//------------------------------------------------------------------------------------------
boolean mpdecision = Utils.fileExists(MpDecisionAction.MPDECISION_PATH);
boolean cpuQuiet = Utils.fileExists(getString(R.string.file_cpu_quiet_base))
&& Utils.fileExists(getString(R.string.file_cpu_quiet_avail_gov))
&& Utils.fileExists(getString(R.string.file_cpu_quiet_cur_gov));
boolean hotplug = mpdecision || cpuQuiet;
if (hotplug) {
category = createCustomPreferenceCategoryMaterial("hotplug", getString(R.string.hotplug));
addPreference(category);
}
if (mpdecision) {
mMpDecision = createSwitchPreference(false, "mpdecision",
getString(R.string.mpdecision), getString(R.string.mpdecision_summary), false);
category.addPreference(mMpDecision);
Utils.getCommandResult(mShellOutputListener, ID_MPDECISION, "pgrep mpdecision 2> /dev/null;");
}
if (cpuQuiet) {
final String[] govs = Utils.readOneLine(getString(R.string.file_cpu_quiet_avail_gov)).split(" ");
final String gov = Utils.readOneLine(getString(R.string.file_cpu_quiet_cur_gov));
mCpuQuietGov = new MaterialListPreference(getActivity());
mCpuQuietGov.setAsCard(false);
mCpuQuietGov.init(getActivity());
mCpuQuietGov.setKey("pref_cpu_quiet_governor");
mCpuQuietGov.setTitle(getString(R.string.cpu_quiet));
mCpuQuietGov.setAdapter(mCpuQuietGov.createAdapter(govs, govs));
mCpuQuietGov.setValue(gov);
category.addPreference(mCpuQuietGov);
mCpuQuietGov.setOnPreferenceChangeListener(this);
}
//------------------------------------------------------------------------------------------
// Intelli-Plug
//------------------------------------------------------------------------------------------
paths = getResources().getStringArray(R.array.directories_intelli_plug);
path = Utils.checkPaths(paths);
if (!TextUtils.isEmpty(path)) {
category = createCustomPreferenceCategoryMaterial("intelli_plug", getString(R.string.intelli_plug));
addPreference(category);
// setup intelli plug toggle
if (Utils.fileExists(path + "intelli_plug_active")) {
togglePref = new AutoSwitchPreference(getActivity());
togglePref.setAsCard(false);
togglePref.init(getActivity());
togglePref.setCategory(BootupConfig.CATEGORY_INTELLI_HOTPLUG);
togglePref.setKey("intelli_plug_intelli_plug_active");
togglePref.setTitle(getString(R.string.enable));
togglePref.setPath(path + "intelli_plug_active");
togglePref.initValue();
category.addPreference(togglePref);
}
// setup touch boost toggle
if (Utils.fileExists(path + "touch_boost_active")) {
togglePref = new AutoSwitchPreference(getActivity());
togglePref.setAsCard(false);
togglePref.init(getActivity());
togglePref.setCategory(BootupConfig.CATEGORY_INTELLI_HOTPLUG);
togglePref.setKey("intelli_plug_touch_boost_active");
togglePref.setTitle(getString(R.string.touch_boost));
togglePref.setPath(path + "touch_boost_active");
togglePref.initValue();
category.addPreference(togglePref);
}
// add the other files
final String[] files = Utils.listFiles(path, false);
for (final String file : files) {
final int type = PreferenceUtils.getType(file);
if (PreferenceUtils.TYPE_EDITTEXT == type) {
editPref = new AutoEditTextPreference(getActivity());
editPref.setAsCard(false);
editPref.init(getActivity());
editPref.setCategory(BootupConfig.CATEGORY_INTELLI_HOTPLUG);
editPref.setKey("intelli_plug_" + file);
editPref.setTitle(file);
editPref.setPath(path + file);
editPref.initValue();
category.addPreference(editPref);
}
}
}
path = Utils.checkPath(getString(R.string.directory_mako_hotplug));
if (!TextUtils.isEmpty(path)) {
category = createCustomPreferenceCategoryMaterial("mako_hotplug",
getString(R.string.mako_hotplug));
addPreference(category);
// setup mako_hotplug toggle
if (Utils.fileExists(path + "enabled")) {
togglePref = new AutoSwitchPreference(getActivity());
togglePref.setAsCard(false);
togglePref.init(getActivity());
togglePref.setCategory(BootupConfig.CATEGORY_MAKO_HOTPLUG);
togglePref.setKey("mako_enabled");
togglePref.setTitle(getString(R.string.enable));
togglePref.setPath(path + "enabled");
togglePref.initValue();
category.addPreference(togglePref);
}
final String[] files = Utils.listFiles(path, true);
for (final String file : files) {
final int type = PreferenceUtils.getType(file);
if (PreferenceUtils.TYPE_EDITTEXT == type) {
editPref = new AutoEditTextPreference(getActivity());
editPref.setAsCard(false);
editPref.init(getActivity());
editPref.setCategory(BootupConfig.CATEGORY_MAKO_HOTPLUG);
editPref.setKey("mako_" + file);
editPref.setTitle(file);
editPref.setPath(path + file);
editPref.initValue();
category.addPreference(editPref);
}
}
}
}
private CustomPreferenceCategoryMaterial createCustomPreferenceCategoryMaterial(String key, String title) {
final Activity activity = getActivity();
final CustomPreferenceCategoryMaterial preference = new CustomPreferenceCategoryMaterial(activity);
preference.init(activity);
preference.setKey(key);
preference.setTitle(title);
return preference;
}
@Override public boolean onPreferenceChanged(MaterialPreference preference, Object o) {
if (preference == mMax) {
final String selected = String.valueOf(o);
mMax.setValue(selected);
final String other = String.valueOf(mMin.getValue());
final boolean updateOther = Utils.parseInt(selected) < Utils.parseInt(other);
if (updateOther) {
onPreferenceChanged(mMin, selected);
}
ActionProcessor.processAction(ActionProcessor.ACTION_CPU_FREQUENCY_MAX, selected, true);
return true;
} else if (preference == mMin) {
final String selected = String.valueOf(o);
mMin.setValue(selected);
final String other = String.valueOf(mMax.getValue());
final boolean updateOther = Utils.parseInt(selected) > Utils.parseInt(other);
if (updateOther) {
onPreferenceChanged(mMax, selected);
}
ActionProcessor.processAction(ActionProcessor.ACTION_CPU_FREQUENCY_MIN, selected, true);
return true;
} else if (preference == mCpuLock) {
DeviceConfig.get().perfCpuLock = (Boolean) o;
DeviceConfig.get().save();
return true;
} else if (preference == mGovernor) {
final String selected = String.valueOf(o);
ActionProcessor.processAction(ActionProcessor.ACTION_CPU_GOVERNOR, selected, true);
return true;
} else if (preference == mCpuGovLock) {
DeviceConfig.get().perfCpuGovLock = (Boolean) o;
DeviceConfig.get().save();
return true;
} else if (preference == mMpDecision) {
final boolean value = (Boolean) o;
new MpDecisionAction(value ? "1" : "0", true).triggerAction();
return true;
} else if (preference == mCpuQuietGov) {
final String path = getString(R.string.file_cpu_quiet_cur_gov);
final String value = String.valueOf(o);
RootShell.fireAndForget(Utils.getWriteCommand(path, value));
BootupConfig.setBootup(new BootupItem(BootupConfig.CATEGORY_EXTRAS, mCpuQuietGov.getKey(), path, value, true));
return true;
}
return false;
}
@Override public boolean onPreferenceClicked(MaterialPreference preference) {
if (preference == mGovernorTuning) {
final Activity activity = getActivity();
if (activity instanceof ActivityCallbacks) {
((ActivityCallbacks) activity).shouldLoadFragment(DeviceConstants.ID_GOVERNOR_TUNABLE);
}
return true;
}
return false;
}
public View generateRow(final int core, final CpuCore cpuCore) {
if (!isAdded() || mCpuInfo == null) {
return null;
}
View rowView = mCpuInfo.getChildAt(core);
if (rowView == null) {
rowView = new CpuCoreView(getActivity());
mCpuInfo.addView(rowView);
}
if (rowView instanceof CpuCoreView) {
final boolean isOffline = cpuCore.current == 0;
((CpuCoreView) rowView).core.setText(String.format("%s %s:", coreString, cpuCore.core));
((CpuCoreView) rowView).freq.setText(isOffline
? getString(R.string.core_offline)
: CpuInformation.toMhz(String.valueOf(cpuCore.current))
+ " / " + CpuInformation.toMhz(String.valueOf(cpuCore.max))
+ " [" + cpuCore.governor + ']');
((CpuCoreView) rowView).bar.setMax(cpuCore.max);
((CpuCoreView) rowView).bar.setProgress(cpuCore.current);
}
return rowView;
}
@Override public void onCpuInformation(@NonNull final CpuInformation cpuInformation) {
final Integer[] availableFrequencies = cpuInformation.freqAvail.toArray(new Integer[cpuInformation.freqAvail.size()]);
final ArrayList<String> entries = new ArrayList<>();
for (final Integer availableFreq : availableFrequencies) {
entries.add(CpuInformation.toMhz(String.valueOf(availableFreq)));
}
final ArrayList<String> entryValues = new ArrayList<>();
for (final Integer availableFreq : availableFrequencies) {
entryValues.add(String.valueOf(availableFreq));
}
final String[] entryArray = entries.toArray(new String[entries.size()]);
final String[] entryValuesArray = entryValues.toArray(new String[entryValues.size()]);
final String[] govAvail = cpuInformation.govAvail.toArray(new String[cpuInformation.govAvail.size()]);
mMax.post(new Runnable() {
@Override public void run() {
mMax.setAdapter(mMax.createAdapter(entryArray, entryValuesArray));
mMax.setValue(String.valueOf(cpuInformation.freqMax));
mMax.setEnabled(true);
mMin.setAdapter(mMin.createAdapter(entryArray, entryValuesArray));
mMin.setValue(String.valueOf(cpuInformation.freqMin));
mMin.setEnabled(true);
mGovernor.setAdapter(mGovernor.createAdapter(govAvail, govAvail));
mGovernor.setValue(cpuInformation.govCur);
mGovernor.setEnabled(true);
}
});
}
}