/* * Copyright (C) 2015-2016 Willi Ye <williye97@gmail.com> * * This file is part of Kernel Adiutor. * * Kernel Adiutor 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. * * Kernel Adiutor 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 Kernel Adiutor. If not, see <http://www.gnu.org/licenses/>. * */ package com.grarak.kerneladiutor.fragments.statistics; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.os.AsyncTask; import android.os.BatteryManager; import android.os.Bundle; import android.support.annotation.Nullable; import android.util.Log; import android.view.Gravity; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.LinearLayout; import android.widget.TextView; import com.bvalosek.cpuspy.CpuSpyApp; import com.bvalosek.cpuspy.CpuStateMonitor; import com.grarak.kerneladiutor.R; import com.grarak.kerneladiutor.fragments.BaseFragment; import com.grarak.kerneladiutor.fragments.RecyclerViewFragment; import com.grarak.kerneladiutor.utils.Utils; import com.grarak.kerneladiutor.utils.kernel.cpu.CPUFreq; import com.grarak.kerneladiutor.utils.kernel.gpu.GPUFreq; import com.grarak.kerneladiutor.utils.root.RootUtils; import com.grarak.kerneladiutor.views.XYGraph; import com.grarak.kerneladiutor.views.recyclerview.CardView; import com.grarak.kerneladiutor.views.recyclerview.DescriptionView; import com.grarak.kerneladiutor.views.recyclerview.RecyclerViewItem; import com.grarak.kerneladiutor.views.recyclerview.StatsView; import com.grarak.kerneladiutor.views.recyclerview.TitleView; import com.grarak.kerneladiutor.views.recyclerview.overallstatistics.FrequencyButtonView; import com.grarak.kerneladiutor.views.recyclerview.overallstatistics.FrequencyTableView; import com.grarak.kerneladiutor.views.recyclerview.overallstatistics.TemperatureView; import java.util.ArrayList; import java.util.List; /** * Created by willi on 19.04.16. */ public class OverallFragment extends RecyclerViewFragment { private static final String TAG = OverallFragment.class.getSimpleName(); private CPUUsageFragment mCPUUsageFragment; private StatsView mGPUFreq; private TemperatureView mTemperature; private CardView mFreqBig; private CardView mFreqLITTLE; private CpuSpyApp mCpuSpyBig; private CpuSpyApp mCpuSpyLITTLE; private double mBatteryRaw; private FrequencyTask mFrequencyTask; @Override protected void init() { super.init(); addViewPagerFragment(mCPUUsageFragment = new CPUUsageFragment()); } @Override protected void addItems(List<RecyclerViewItem> items) { statsInit(items); frequenciesInit(items); } private void statsInit(List<RecyclerViewItem> items) { if (GPUFreq.hasCurFreq()) { mGPUFreq = new StatsView(); mGPUFreq.setTitle(getString(R.string.gpu_freq)); items.add(mGPUFreq); } mTemperature = new TemperatureView(); mTemperature.setFullSpan(mGPUFreq == null); items.add(mTemperature); } private void frequenciesInit(List<RecyclerViewItem> items) { FrequencyButtonView frequencyButtonView = new FrequencyButtonView(); frequencyButtonView.setRefreshListener(new View.OnClickListener() { @Override public void onClick(View v) { updateFrequency(); } }); frequencyButtonView.setResetListener(new View.OnClickListener() { @Override public void onClick(View v) { CpuStateMonitor cpuStateMonitor = mCpuSpyBig.getCpuStateMonitor(); CpuStateMonitor cpuStateMonitorLITTLE = null; if (mCpuSpyLITTLE != null) { cpuStateMonitorLITTLE = mCpuSpyLITTLE.getCpuStateMonitor(); } try { cpuStateMonitor.setOffsets(); if (cpuStateMonitorLITTLE != null) { cpuStateMonitorLITTLE.setOffsets(); } } catch (CpuStateMonitor.CpuStateMonitorException ignored) { } mCpuSpyBig.saveOffsets(getActivity()); if (mCpuSpyLITTLE != null) { mCpuSpyLITTLE.saveOffsets(getActivity()); } updateView(cpuStateMonitor, mFreqBig); if (cpuStateMonitorLITTLE != null) { updateView(cpuStateMonitorLITTLE, mFreqLITTLE); } adjustScrollPosition(); } }); frequencyButtonView.setRestoreListener(new View.OnClickListener() { @Override public void onClick(View v) { CpuStateMonitor cpuStateMonitor = mCpuSpyBig.getCpuStateMonitor(); CpuStateMonitor cpuStateMonitorLITTLE = null; if (mCpuSpyLITTLE != null) { cpuStateMonitorLITTLE = mCpuSpyLITTLE.getCpuStateMonitor(); } cpuStateMonitor.removeOffsets(); if (cpuStateMonitorLITTLE != null) { cpuStateMonitorLITTLE.removeOffsets(); } mCpuSpyBig.saveOffsets(getActivity()); if (mCpuSpyLITTLE != null) { mCpuSpyLITTLE.saveOffsets(getActivity()); } updateView(cpuStateMonitor, mFreqBig); if (mCpuSpyLITTLE != null) { updateView(cpuStateMonitorLITTLE, mFreqLITTLE); } adjustScrollPosition(); } }); items.add(frequencyButtonView); mFreqBig = new CardView(getActivity()); if (CPUFreq.isBigLITTLE()) { mFreqBig.setTitle(getString(R.string.cluster_big)); } else { mFreqBig.setFullSpan(true); } items.add(mFreqBig); if (CPUFreq.isBigLITTLE()) { mFreqLITTLE = new CardView(getActivity()); mFreqLITTLE.setTitle(getString(R.string.cluster_little)); items.add(mFreqLITTLE); } mCpuSpyBig = new CpuSpyApp(CPUFreq.getBigCpu(), getActivity()); if (CPUFreq.isBigLITTLE()) { mCpuSpyLITTLE = new CpuSpyApp(CPUFreq.getLITTLECpu(), getActivity()); } updateFrequency(); } private void updateFrequency() { if (mFrequencyTask == null) { mFrequencyTask = new FrequencyTask(); mFrequencyTask.execute(); } } private class FrequencyTask extends AsyncTask<Void, Void, Void> { private CpuStateMonitor mBigMonitor; private CpuStateMonitor mLITTLEMonitor; @Override protected Void doInBackground(Void... params) { mBigMonitor = mCpuSpyBig.getCpuStateMonitor(); if (CPUFreq.isBigLITTLE()) { mLITTLEMonitor = mCpuSpyLITTLE.getCpuStateMonitor(); } try { mBigMonitor.updateStates(); } catch (CpuStateMonitor.CpuStateMonitorException ignored) { Log.e(TAG, "Problem getting CPU states"); } if (CPUFreq.isBigLITTLE()) { try { mLITTLEMonitor.updateStates(); } catch (CpuStateMonitor.CpuStateMonitorException ignored) { Log.e(TAG, "Problem getting CPU states"); } } return null; } @Override protected void onPostExecute(Void aVoid) { super.onPostExecute(aVoid); updateView(mBigMonitor, mFreqBig); if (CPUFreq.isBigLITTLE()) { updateView(mLITTLEMonitor, mFreqLITTLE); } adjustScrollPosition(); mFrequencyTask = null; } } private void updateView(CpuStateMonitor monitor, CardView card) { if (!isAdded() || card == null) return; card.clearItems(); // update the total state time DescriptionView totalTime = new DescriptionView(); totalTime.setTitle(getString(R.string.uptime)); totalTime.setSummary(sToString(monitor.getTotalStateTime() / 100L)); card.addItem(totalTime); /** Get the CpuStateMonitor from the app, and iterate over all states, * creating a row if the duration is > 0 or otherwise marking it in * extraStates (missing) */ List<String> extraStates = new ArrayList<>(); for (CpuStateMonitor.CpuState state : monitor.getStates()) { if (state.getDuration() > 0) { generateStateRow(monitor, state, card); } else { if (state.getFreq() == 0) { extraStates.add(getString(R.string.deep_sleep)); } else { extraStates.add(state.getFreq() / 1000 + getString(R.string.mhz)); } } } if (monitor.getStates().size() == 0) { card.clearItems(); TitleView errorTitle = new TitleView(); errorTitle.setText(getString(R.string.error_frequencies)); card.addItem(errorTitle); return; } // for all the 0 duration states, add the the Unused State area if (extraStates.size() > 0) { int n = 0; String str = ""; for (String s : extraStates) { if (n++ > 0) str += ", "; str += s; } DescriptionView unusedText = new DescriptionView(); unusedText.setTitle(getString(R.string.unused_frequencies)); unusedText.setSummary(str); card.addItem(unusedText); } } /** * @return A nicely formatted String representing tSec seconds */ private String sToString(long tSec) { int h = (int) (tSec / (60 * 60)); int m = ((int) tSec % (60 * 60)) / 60; int s = ((int) tSec % (60 * 60)) % 60; String sDur; sDur = h + ":"; if (m < 10) sDur += "0"; sDur += m + ":"; if (s < 10) sDur += "0"; sDur += s; return sDur; } /** * Creates a View that correpsonds to a CPU freq state row as specified * by the state parameter */ private void generateStateRow(CpuStateMonitor monitor, CpuStateMonitor.CpuState state, CardView frequencyCard) { // what percentage we've got float per = (float) state.getDuration() * 100 / monitor.getTotalStateTime(); String sFreq; if (state.getFreq() == 0) { sFreq = getString(R.string.deep_sleep); } else { sFreq = state.getFreq() / 1000 + getString(R.string.mhz); } // duration long tSec = state.getDuration() / 100; String sDur = sToString(tSec); FrequencyTableView frequencyState = new FrequencyTableView(); frequencyState.setFrequency(sFreq); frequencyState.setPercentage((int) per); frequencyState.setDuration(sDur); frequencyCard.addItem(frequencyState); } @Override protected void refresh() { super.refresh(); if (mGPUFreq != null) { mGPUFreq.setStat((GPUFreq.getCurFreq() / GPUFreq.getCurFreqOffset()) + getString(R.string.mhz)); } if (mTemperature != null) { mTemperature.setBattery(mBatteryRaw); } if (mCPUUsageFragment != null) { mCPUUsageFragment.refresh(); } } private BroadcastReceiver mBatteryReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { mBatteryRaw = intent.getIntExtra(BatteryManager.EXTRA_TEMPERATURE, 0) / 10D; } }; @Override public void onResume() { super.onResume(); getActivity().registerReceiver(mBatteryReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED)); } @Override public void onPause() { super.onPause(); try { getActivity().unregisterReceiver(mBatteryReceiver); } catch (IllegalArgumentException ignored) { } } public static class CPUUsageFragment extends BaseFragment { private static List<View> mUsages = new ArrayList<>(); private static Thread mThread; private static float[] mCPUUsages; private static int[] mFreqs; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mUsages.clear(); LinearLayout rootView = new LinearLayout(getActivity()); rootView.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); rootView.setGravity(Gravity.CENTER); rootView.setOrientation(LinearLayout.VERTICAL); LinearLayout subView = null; for (int i = 0; i < CPUFreq.getCpuCount(); i++) { if (subView == null || i % 2 == 0) { subView = new LinearLayout(getActivity()); subView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1)); rootView.addView(subView); } View view = inflater.inflate(R.layout.fragment_usage_view, subView, false); view.setLayoutParams(new LinearLayout .LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 1)); ((TextView) view.findViewById(R.id.usage_core_text)).setText(getString(R.string.core, i + 1)); mUsages.add(view); subView.addView(view); } return rootView; } public void refresh() { if (mThread == null) { mThread = new Thread(new Runnable() { @Override public void run() { mCPUUsages = CPUFreq.getCpuUsage(); if (mFreqs == null) { mFreqs = new int[CPUFreq.getCpuCount()]; } for (int i = 0; i < mFreqs.length; i++) { if (getActivity() == null) break; mFreqs[i] = CPUFreq.getCurFreq(i); } if (getActivity() == null) { RootUtils.closeSU(); } mThread = null; } }); mThread.start(); } try { for (int i = 0; i < mUsages.size(); i++) { View usageView = mUsages.get(i); TextView usageOfflineText = (TextView) usageView.findViewById(R.id.usage_offline_text); TextView usageLoadText = (TextView) usageView.findViewById(R.id.usage_load_text); TextView usageFreqText = (TextView) usageView.findViewById(R.id.usage_freq_text); XYGraph usageGraph = (XYGraph) usageView.findViewById(R.id.usage_graph); if (mFreqs[i] == 0) { usageOfflineText.setVisibility(View.VISIBLE); usageLoadText.setVisibility(View.GONE); usageFreqText.setVisibility(View.GONE); usageGraph.addPercentage(0); } else { usageOfflineText.setVisibility(View.GONE); usageLoadText.setVisibility(View.VISIBLE); usageFreqText.setVisibility(View.VISIBLE); usageFreqText.setText(Utils.strFormat("%d" + getString(R.string.mhz), mFreqs[i] / 1000)); usageLoadText.setText(Utils.strFormat("%d%%", Math.round(mCPUUsages[i + 1]))); usageGraph.addPercentage(Math.round(mCPUUsages[i + 1])); } } } catch (Exception ignored) { } } } }