edu.nd.darts.cimon.CimonListView.java Source code

Java tutorial

Introduction

Here is the source code for edu.nd.darts.cimon.CimonListView.java

Source

/*
 * Copyright (C) 2013 Chris Miller
 *
 * This file is part of CIMON.
 * 
 * CIMON 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.
 *  
 * CIMON 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 CIMON.  If not, see <http://www.gnu.org/licenses/>.
 *  
 */
package edu.nd.darts.cimon;

//import java.text.DecimalFormat;
import edu.nd.darts.cimon.contentprovider.CimonContentProvider;
import edu.nd.darts.cimon.database.MetricInfoTable;
import edu.nd.darts.cimon.database.MetricsTable;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.IBinder;
import android.os.RemoteException;
import android.support.v4.app.LoaderManager;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.util.Log;
import android.util.SparseArray;
import android.widget.ExpandableListView;

/**
 * List view tab for administration app.
 * Provides list view of all potential metrics for monitoring activity in CIMON.
 * This is an expandable list, which groups metrics which are acquired through the
 * same process. Used for the System, Sensor, and User activity tabs.  
 * 
 * @author darts
 * 
 * @see NDroidAdmin
 * @see CimonListAdapter
 *
 */
public class CimonListView extends ExpandableListActivity
        implements LoaderManager.LoaderCallbacks<Cursor>, AdminUpdate {

    private static final String TAG = "NDroid";

    private static final long TWOFIFTY_MILLISECONDS = 250;
    //   private static final long TWOHUNDRED_MILLISECONDS = 200;
    private CimonInterface mCimonInterface = null;
    private ExpandableListView list;
    private CimonListAdapter adapter;
    private AdminObserver adminObserver;
    private Handler handler;
    private int category;
    //   private int counter = 0;
    //   private final DecimalFormat formatter = new DecimalFormat("#.##");

    private SparseArray<MonitorReport> monitorReports;
    private Handler backgroundHandler = null;
    //   private List<MetricService<?>> serviceList = null;
    private final HandlerThread backgroundThread = new HandlerThread("listhelp") {

        @Override
        protected void onLooperPrepared() {
            if (DebugLog.DEBUG)
                Log.d(TAG, "CimonListView.onLooperPrepared - listhelp ");
            backgroundHandler = new Handler(getLooper());

            super.onLooperPrepared();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.cimonlist);
        category = getIntent().getExtras().getInt("key");

        list = this.getExpandableListView();
        list.setGroupIndicator(null);
        handler = new Handler();
        switch (category) {
        case Metrics.TYPE_SYSTEM:
            adminObserver = SystemObserver.getInstance();
            break;
        case Metrics.TYPE_SENSOR:
            adminObserver = SensorObserver.getInstance();
            break;
        case Metrics.TYPE_USER:
            adminObserver = UserObserver.getInstance();
            break;
        default:
            if (DebugLog.ERROR)
                Log.e(TAG, "CimonListView.onCreate - unknown category:" + category);
        }
        //      this.setListAdapter(new SystemAdapter(this, ActiveMonitor.getInstance().getSystems()));
        getSupportLoaderManager().initLoader(0, null, this);
        adapter = new CimonListAdapter(this, adminObserver);
        this.setListAdapter(adapter);

        backgroundThread.start();
        if (getApplicationContext().bindService(new Intent(NDroidService.class.getName()), mConnection,
                Context.BIND_AUTO_CREATE)) {
            if (DebugLog.DEBUG)
                Log.d(TAG, "CimonListView.onCreate - bind service. category:" + category);

        } else {
            if (DebugLog.DEBUG)
                Log.d(TAG, "CimonListView.onCreate - bind service failed. category:" + category);

        }
        monitorReports = new SparseArray<MonitorReport>();
        /*      backgroundHandler = new Handler(backgroundThread.getLooper());
              backgroundHandler.post(new Runnable() {
            
                 public void run() {
        serviceList = MetricService.getServices(category);
                 }
              });*/
    }

    @Override
    protected void onPause() {
        super.onPause();
        //      handler.removeCallbacks(updateViews);
        if (DebugLog.DEBUG)
            Log.d(TAG, "NDroidSystem.onPause - unregister observers");
        adminObserver.unregisterObserver(this);
    }

    @Override
    protected void onResume() {
        super.onResume();
        //      handler.postDelayed(updateViews, TWOHUNDRED_MILLISECONDS);
        if (DebugLog.DEBUG)
            Log.d(TAG, "NDroidSystem.onResume - register observers");
        adminObserver.registerObserver(this, handler, TWOFIFTY_MILLISECONDS);
        /*      backgroundHandler.post(new Runnable() {
            
                 public void run() {
        for (MetricService<?> mService : serviceList) {
           mService.registerObserver(adminObserver, TWOFIFTY_MILLISECONDS);
        }
                 }
              });*/
    }

    /**
     * Class for interacting with the main interface of the service.
     * On connection, acquire binder to {@link CimonInterface} from the
     * CIMON background service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the service object we can use to
            // interact with the service.  We are communicating with our
            // service through an IDL interface, so get a client-side
            // representation of that from the raw service object.
            if (DebugLog.DEBUG)
                Log.d(TAG, "NDroidSystem.onServiceConnected - connected");
            mCimonInterface = CimonInterface.Stub.asInterface(service);
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected -- that is, its process crashed.
            if (DebugLog.DEBUG)
                Log.d(TAG, "NDroidSystem.onServiceDisconnected - disconnected");
            mCimonInterface = null;

        }
    };

    /*
     * Handler for update messages received from CIMON service.
     * Handles messages which provided periodic updated values for monitored metrics.
     */
    /*   private static Handler mHandler = new Handler() {
          @Override
          public void handleMessage(Message msg) {
     if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.handleMessage - what: " + msg.what);
    //         Bundle b = msg.getData();
    //         Long val = b.getLong("value", -1);
     switch (msg.what) {
        case Metrics.BATTERY_PERCENT:
    / *               int temperature = ((ContentValues)msg.obj).getAsInteger(
                 NDroidProvider.Constants.TEMPERATURE);
           int tens = temperature * 18 / 100;
              
           String ft = Integer.toString( tens + 32 ) + "." //$NON-NLS-1$
                 + ( temperature * 18 - 100 * tens )
                 + "\u00B0F"; //$NON-NLS-1$
           
           int voltage = ((ContentValues)msg.obj).getAsInteger(
                 NDroidProvider.Constants.VOLTAGE);
           String vStr = String.valueOf( voltage ) + "mV"; //$NON-NLS-1$
                     
           battLevel.setText(String.valueOf(((ContentValues)msg.obj).getAsInteger(
                 NDroidProvider.Constants.LEVEL)));
           battTemp.setText(ft);
           battVolt.setText(vStr);
    * ///               mCallbackText.setText("Received from service: " + msg.arg1);
           break;
        case Metrics.MEMORY_AVAIL:
           if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.handleMessage - memory avail");
    //               memValue.setText(String.valueOf(val));
           break;
        case Metrics.CPU_LOAD1:
           if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.handleMessage - cpu load1");
    //               cpuValue.setText(String.valueOf(val));
           break;
           default:
           super.handleMessage(msg);
           break;
     }
          }
              
       };*/

    /*
     * Target we register for service to send messages to mHandler.
     */
    //   final Messenger mMessenger = new Messenger(mHandler);

    /**
     * Register a new periodic update when metric is enabled through administration activity.
     * This method is called by the onCheckedChanged listener for the Enable button
     * of the administration rows when the state is changed to enable.
     * 
     * @param metric    integer representing metric (per {@link Metrics}) to register
     * @param period    period between updates, in milliseconds
     * @param duration    duration to run monitor, in milliseconds
     * @param metadata    include metadata at top of csv report file
     * @param email    send csv report file to email
     * @param dropbox    send csv report file to Dropbox account folder
     * @param box    send csv report file to Box account folder
     * @param drive    send csv report file to Google Drive account folder
     */
    public void registerPeriodic(int metric, long period, long duration, boolean metadata, boolean email,
            boolean dropbox, boolean box, boolean drive) {
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.registerPeriodic - metric:" + metric + " period:" + period + " duration:"
                    + duration);
        if (mCimonInterface == null) {
            if (DebugLog.INFO)
                Log.i(TAG, "CimonListView.OnClickListener - register: service inactive");
        } else {
            try {
                if (!adminObserver.getStatus(metric)) {
                    int monitorId = mCimonInterface.registerPeriodic(metric, period, duration, false, null); //mMessenger
                    adminObserver.setActive(metric, monitorId);
                    monitorReports.append(monitorId, new MonitorReport(this, metric, monitorId, backgroundHandler,
                            adminObserver, metadata, email, dropbox, box, drive));
                }
            } catch (RemoteException e) {
                if (DebugLog.INFO)
                    Log.i(TAG, "CimonListView.OnClickListener - register failed");
                e.printStackTrace();
            }
        }
    }

    /**
     * Unregister periodic update monitor which was registered through administration activity.
     * This method is called by the onCheckedChanged listener for the Enable button
     * of the administration rows when the state is changed to disable.
     * 
     * @param metric    integer representing metric (per {@link Metrics}) to unregister
     */
    public void unregisterPeriodic(int metric) {
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.OnClickListener - unregister periodic");
        if (mCimonInterface != null) {
            try {
                int monitorId = adminObserver.getMonitor(metric);
                if (monitorId >= 0) {
                    mCimonInterface.unregisterPeriodic(metric, monitorId);
                    adminObserver.setInactive(metric, monitorId);
                }
            } catch (RemoteException e) {
                if (DebugLog.INFO)
                    Log.i(TAG, "CimonListView.OnClickListener - unregister failed");
                e.printStackTrace();
            }
        }
    }

    /*
     * Holder object for view objects related to a group or child view
     * in the ListView.  
     * 
     * @author darts
     *
     */
    /*   static class ViewHolder {
          protected int childPosition;
          protected TextView title;
          protected TextView value;
          protected TextView fails;
          protected TextView status;
          protected TextView power;
          protected ProgressBar valueBar;
          protected ToggleButton toggle;
          protected SystemData metric;
       }*/

    /*
     * Update the rows which are visible in the list view for System Activity tab.
     * This task is executed at a fixed frequency, and only updates the views for 
     * visible rows in the list view. This is done rather than invalidating the view
     * when there is an update to the underlying data, since updates to the underlying
     * data may occur very frequently, which would cause the system to be sluggish if
     * the views were recreated that frequently. 
     */
    /*   Runnable updateViews = new Runnable() {
        
          public void run() {
    /*         if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.updateViews - first visible group " + 
           ExpandableListView.getPackedPositionGroup(
                 list.getExpandableListPosition(
                       list.getFirstVisiblePosition())));
     if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.updateViews - first visible group " + 
           ExpandableListView.getPackedPositionGroup(
                 list.getExpandableListPosition(
                       list.getLastVisiblePosition())));
    *         
     ViewHolder holder;   //SystemAdapter.ViewHolder
     boolean updated = true;
    //         if (DebugLog.DEBUG) Log.d(TAG, "NDroidSystem.updateViews - child count " + list.getChildCount());
     for (int i = 0; i < list.getChildCount(); i++) {
        holder = (ViewHolder)list.getChildAt(i).getTag();
        if (holder == null) continue;
        // if current view is a group view
        if (holder.childPosition < 0) {
           if (!holder.metric.setUpdated(false)) {
              updated = false;
              continue;
           }
           updated = true;
           holder.value.setText(formatter.format(holder.metric.getValue()));
           holder.status.setText(holder.metric.getStatus()?
              String.format("Frequency: %.3f Hz", 1000.0/holder.metric.getPeriod()):
                 "inactive");
           holder.fails.setText("Fails: " + holder.metric.getFails());
           holder.valueBar.setProgress((int)(holder.metric.getValue() * 100 / holder.metric.getMax()));
        }
        // if current view is a child view which is not the administrator row
        //    (an individual metric row)
        else if (holder.childPosition > 0) {
           if (!updated) continue;
    //               value.setText(String.valueOf(systemD.getValue()));
           final double value = holder.metric.getField(holder.childPosition - 1).getValue();
           holder.value.setText(formatter.format(value));
           holder.valueBar.setProgress((int)(value * 100 / holder.metric.getMax()));
        }
     }
    //         counter++;
     handler.postDelayed(updateViews, TWOHUNDRED_MILLISECONDS);
          }
       };*/

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.onCreateLoader - id: " + id);
        if (id == 0) {
            /*         String[] projection = {MetricInfoTable.COLUMN_TITLE, 
                           MetricInfoTable.COLUMN_DESCRIPTION,
                           MetricInfoTable.COLUMN_POWER,
                           MetricInfoTable.COLUMN_SUPPORTED};*/
            CursorLoader cursorLoader = new CursorLoader(this,
                    Uri.withAppendedPath(CimonContentProvider.CATEGORY_URI, String.valueOf(category)), null, null,
                    null, MetricInfoTable.COLUMN_ID + " ASC");
            //         cursorLoader.setUpdateThrottle(250);
            return cursorLoader;
        } else {
            /*         String[] projection = {MetricsTable.COLUMN_ID, 
                           MetricsTable.COLUMN_METRIC,
                           MetricsTable.COLUMN_MAX,
                           MetricsTable.COLUMN_UNITS};*/
            CursorLoader cursorLoader = new CursorLoader(this,
                    Uri.withAppendedPath(CimonContentProvider.GRP_METRICS_URI, String.valueOf(id)), null, null,
                    null, MetricsTable.COLUMN_ID + " ASC");
            //         cursorLoader.setUpdateThrottle(250);
            return cursorLoader;
        }
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        int id = loader.getId();
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.onLoadFinished - id: " + id + " data:" + data);
        if (id == 0) {
            adapter.swapGroupCursor(data);
        } else {
            adapter.swapChildrenCursor(id, data);
        }
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        int id = loader.getId();
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.onLoaderReset - id: " + id);
        if (id == 0) {
            adapter.swapGroupCursor(null);
        } else {
            adapter.swapChildrenCursor(id, null);
        }
    }

    @Override
    public void onGroupCollapse(int groupPosition) {
        int groupID = (int) adapter.getGroupId(groupPosition);
        getSupportLoaderManager().destroyLoader(groupID);
    }

    @Override
    public void onGroupExpand(int groupPosition) {
        int groupID = (int) adapter.getGroupId(groupPosition);
        getSupportLoaderManager().initLoader(groupID, null, this);
    }

    /**
     * Initiate update of group view in adapter when new data is available.
     */
    public void updateGroup(int groupId) {
        if (DebugLog.DEBUG)
            Log.d(TAG, "CimonListView.updateGroup - group: " + groupId);
        adapter.updateGroup(groupId);
    }

}