com.tomdignan.jiffymonitor.MonitorActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.tomdignan.jiffymonitor.MonitorActivity.java

Source

/*
Jiffy Resource Monitor
Copyright (C) 2012 Tom Dignan <tom@tomdignan.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.tomdignan.jiffymonitor;

import java.io.FileNotFoundException;
import java.util.ArrayList;

//import android.app.ActivityManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.View;
import android.widget.Button;

import com.tomdignan.analytics.AnalyticsSession;
import com.tomdignan.analytics.AnalyticsSessionFactory;
import com.tomdignan.analytics.Trackable;

/**
 * Main Activity of the application. Responsible for displaying four meters
 * or three depending on whether the user has a dual core processor.
 * 
 * CPU0, CPU1, RAM, and Battery.
 * 
 * @author Tom Dignan
 */
public class MonitorActivity extends FragmentActivity
        implements ResourceMonitorFragment.OnResourcesReceivedListener, Trackable {

    private static final String FRAGMENT_MONITOR = "MONITOR";
    private static final String TAG = "MonitorActivity";

    private ResourceMonitorFragment resourceMonitor;
    //  currently unused but will use for memory
    //    private ActivityManager activityManager;
    private FragmentManager fragmentManager;
    private UpdateResourcesRunnable updateResourcesRunnable;

    /** One session to rule them all. */
    private AnalyticsSession analytics = AnalyticsSessionFactory.makeSession();

    /** List of available resource meters */
    private ArrayList<Fragment> resourceMeters = new ArrayList<Fragment>();

    /** Click handler */
    private MonitorClickHandler monitorClickHandler;

    /** Whether the activity has been paused. */
    private boolean isPaused = false;

    /** {@inheritDoc} */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        this.analytics.create(this, TAG);
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_resource_monitor_layout);

        this.fragmentManager = getSupportFragmentManager();
        this.updateResourcesRunnable = new UpdateResourcesRunnable();

        this.resourceMonitor = (ResourceMonitorFragment) this.fragmentManager.findFragmentByTag(FRAGMENT_MONITOR);

        if (this.resourceMonitor == null) {
            try {
                this.resourceMonitor = new ResourceMonitorFragment();
            } catch (FileNotFoundException e) {
                Intent intent = new Intent(this, ErrorHandlerActivity.class);
                intent.putExtra(ErrorHandlerActivity.EXTRA_MESSAGE,
                        "This app requires a system file that was not found on your device. "
                                + "For the technical, that file is the Linux /proc/stat file.");
                intent.addFlags(Intent.FLAG_ACTIVITY_NO_HISTORY);
                startActivity(intent);
                return;
            }
        }

        this.resourceMonitor.setRetainInstance(true);
        this.fragmentManager.beginTransaction().add(this.resourceMonitor, FRAGMENT_MONITOR).commit();
        this.resourceMonitor.setOnResourcesReceivedListener(this);
    }

    @Override
    public void onResume() {
        this.analytics.resume();
        super.onResume();

        if (this.resourceMonitor != null) {
            this.isPaused = false;

            // set up click handler 
            this.monitorClickHandler = new MonitorClickHandler(this, this.resourceMonitor);
            ((Button) findViewById(R.id.buttonCrank)).setOnClickListener(this.monitorClickHandler);

            Button toggle = (Button) findViewById(R.id.buttonToggle);
            toggle.setOnClickListener(this.monitorClickHandler);
            toggle.setText(getString(R.string.Stop));
            this.resourceMonitor.start();
        }
    }

    /** {@inheritDoc} */
    public void onPause() {
        this.analytics.pause();
        super.onPause();
        if (this.resourceMonitor != null) {
            this.resourceMonitor.stop();
            ((Button) findViewById(R.id.buttonToggle)).setText(getString(R.string.Start));
            this.isPaused = true;
        }
    }

    private static class MonitorClickHandler implements View.OnClickListener {
        private String labelStop;
        private String labelStart;
        private ResourceMonitorFragment monitor;

        public MonitorClickHandler(Context context, ResourceMonitorFragment monitor) {
            this.labelStop = context.getString(R.string.Stop);
            this.labelStart = context.getString(R.string.Start);
            this.monitor = monitor;
        }

        private void toggle(Button button) {
            if (this.monitor.isStarted()) {
                this.monitor.stop();
                button.setText(this.labelStart);
            } else {
                this.monitor.start();
                button.setText(this.labelStop);
            }
        }

        /** Creates a tiny load on the CPU in a separate thread. */
        private static void crank() {
            for (int i = 0; i < 4; i++) {
                new Thread(new Runnable() {
                    public void run() {
                        for (int rev = 0; rev < 10; rev++) {
                            // compute a very high fibonacci number
                            // Leaving off one order of magnitude deliberately (one
                            // missing f)
                            for (long i = 1, j = 1; i + j < 0x7ffffffffffffffl;) {
                                long temp = i + j;
                                j = i;
                                i = temp;

                                Loggy.d(TAG, "Logs are so slow! %l %l\n", i, j);
                            }
                        }
                    }
                }).start();
            }
        }

        public void onClick(View view) {
            int id = view.getId();
            switch (id) {
            case R.id.buttonToggle:
                toggle((Button) view);
                break;

            case R.id.buttonCrank:
                crank();
                break;
            }
        }
    }

    /**
     * Receives results from the resource monitor.
     * 
     * @param long[] cpuUsages -- First result is the number of CPUs
     * available in the array (It is always 8 longs, but doesn't use
     * all of the available space. This is so it can be used when phones
     * with more cores come out.), second result is the aggregate of all cores,
     * all subsequent results are the cpu0->cpuN. Don't read past cpuUsages[0]
     * elements!
     */
    private class UpdateResourcesRunnable implements Runnable {
        public ArrayList<Fragment> resourceMeters;
        public float[] cpuUsages;

        int fragmentSeq = 0;

        public synchronized void run() {
            if (isPaused) {
                Loggy.v(TAG, "UpdateResourcesRunnable::run(): Activity is paused, returning.");
                return;
            }

            // Update memory

            // Update CPU monitors
            synchronized (updateResourcesRunnable) {
                // add any missing meters
                boolean isDirty = false;
                int totalCpus = (int) cpuUsages[0];
                Loggy.d(TAG, "totalCpus=%d", totalCpus);

                while (resourceMeters.size() < totalCpus) {
                    isDirty = true;
                    String tag = String.valueOf(fragmentSeq);

                    Fragment chart = MonitorActivity.this.fragmentManager.findFragmentByTag(tag);

                    if (chart == null) {
                        chart = ChartFragmentFactory.makeDefault();
                        //chart.setRetainInstance(true);
                    }

                    Loggy.d(TAG, "UpdateResourcesRunnable::run(): adding chart %d", chart.getId());

                    // Using commitAllowingStateLoss so that if this happens after 
                    // onSaveInstanceState nothing blows up
                    MonitorActivity.this.fragmentManager.beginTransaction().add(R.id.vgCpuMonitors, chart, tag)
                            .commit();
                    resourceMeters.add(chart);

                    fragmentSeq++;
                }

                if (isDirty) {
                    // Ensure that views are created for any new fragments before attempting
                    // to call setNextValue.
                    MonitorActivity.this.fragmentManager.executePendingTransactions();
                }

                int i;
                for (i = 0; i < totalCpus; i++) {
                    Updatable meter = (Updatable) resourceMeters.get(i);
                    float nextVal = cpuUsages[i + 1];
                    if (meter != null && nextVal >= 0) {
                        meter.setEnabled();
                        meter.setName("cpu" + (i == 0 ? " " : (i - 1) + " "));
                        meter.setNextValue(nextVal);
                    } else if (meter != null) {
                        meter.reset();
                        meter.setName("cpu" + (i == 0 ? " " : (i - 1) + " "));
                        meter.setDisabled();
                    }
                }
            }
        }
    }

    @Override
    public void onResourcesReceived(final float[] cpuUsages) {
        synchronized (updateResourcesRunnable) {
            updateResourcesRunnable.cpuUsages = cpuUsages;
            updateResourcesRunnable.resourceMeters = resourceMeters;
        }
        runOnUiThread(updateResourcesRunnable);
    }

    @Override
    public AnalyticsSession getAnalyticsSession() {
        return this.analytics;
    }
}