/* (C) 2012 Pragmatic Software This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/ */ package com.googlecode.networklog; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.widget.ListView; import android.widget.ArrayAdapter; import android.widget.AdapterView; import android.widget.TextView; import android.widget.CheckedTextView; import android.widget.ImageView; import android.widget.Spinner; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.AdapterView.OnItemClickListener; import android.graphics.drawable.Drawable; import android.view.View; import android.view.ViewGroup; import android.view.Window; import android.view.LayoutInflater; import android.util.Log; import java.lang.Runnable; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.HashMap; import java.util.List; import com.jjoe64.graphview.GraphView.*; public abstract class GraphActivity extends Activity { public MyGraphView graphView; public ArrayList<LegendItem> legendData; public double interval; public double viewsize; public abstract void buildSeries(double timeFrameSize, double viewSize); public class LegendItem { Drawable mIcon; int mHashCode; String mName; boolean mEnabled; double size; } private CustomAdapter adapter; private Spinner intervalSpinner; private Spinner viewsizeSpinner; private String[] intervalValues; private double[] intervalValuesDouble; private int spinnerInit = 0; private int lastViewsizePos = -1; private double viewportStart; protected class InstanceData { int spinnerInit; int lastViewsizePos; double interval; double viewsize; ArrayList<LegendItem> legendData; String[] intervalValues; double[] intervalValuesDouble; double viewportStart; List<GraphViewSeries> graphSeries; } public InstanceData instanceData = null; @Override public Object onRetainNonConfigurationInstance() { instanceData = new InstanceData(); instanceData.lastViewsizePos = lastViewsizePos; instanceData.spinnerInit = spinnerInit; instanceData.interval = interval; instanceData.viewsize = viewsize; instanceData.legendData = legendData; instanceData.intervalValues = intervalValues; instanceData.intervalValuesDouble = intervalValuesDouble; instanceData.viewportStart = graphView.viewportStart; instanceData.graphSeries = graphView.graphSeries; return instanceData; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.graph_main); instanceData = (InstanceData) getLastNonConfigurationInstance(); if(instanceData != null) { intervalValues = instanceData.intervalValues; intervalValuesDouble = instanceData.intervalValuesDouble; legendData = instanceData.legendData; viewsize = instanceData.viewsize; interval = instanceData.interval; spinnerInit = instanceData.spinnerInit; lastViewsizePos = instanceData.lastViewsizePos; } else { legendData = new ArrayList<LegendItem>(); interval = NetworkLog.settings.getGraphInterval(); viewsize = NetworkLog.settings.getGraphViewsize(); intervalValues = getResources().getStringArray(R.array.interval_values); intervalValuesDouble = new double[intervalValues.length]; for(int i = 0; i < intervalValues.length; i++) { intervalValuesDouble[i] = Double.valueOf(intervalValues[i]); } } graphView = (MyGraphView) findViewById(R.id.graph); graphView.setEnableMultiLineXLabel(true); graphView.setOnScaleChangeListener(new OnScaleChangeListener() { @Override public void scaleChanged(double newViewportSize) { viewsize = newViewportSize; for(int i = intervalValuesDouble.length - 1; i >= 0; i--) { if(newViewportSize >= intervalValuesDouble[i]) { lastViewsizePos = i; // force onItemSelected() listener to ignore programmatical call viewsizeSpinner.setSelection(i); NetworkLog.settings.setGraphViewsize((long) intervalValuesDouble[i]); break; } } } }); /* Unused code commented out and retained for example purposes */ /* graphView.setOnScrollChangeListener(new MyOnScrollChangeListener() { @Override public void scrollChanged(double newViewportStart) { // unused in this app } }); */ graphView.setScrollable(true); graphView.setScalable(true); graphView.setShowLegend(false); graphView.setLegendSorter(new Runnable() { public void run() { sortLegend(); } }); ListView listView = (ListView) findViewById(R.id.graph_legend); adapter = new CustomAdapter(this, R.layout.graph_legend_item, legendData); listView.setAdapter(adapter); listView.setOnItemClickListener(new CustomOnItemClickListener()); listView.setFastScrollEnabled(true); MyOnItemSelectedListener listener = new MyOnItemSelectedListener(); intervalSpinner = (Spinner) findViewById(R.id.intervalSpinner); intervalSpinner.setOnItemSelectedListener(listener); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( this, R.array.interval_entries, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); intervalSpinner.setAdapter(adapter); viewsizeSpinner = (Spinner) findViewById(R.id.viewsizeSpinner); viewsizeSpinner.setOnItemSelectedListener(listener); adapter = ArrayAdapter.createFromResource( this, R.array.interval_entries, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); viewsizeSpinner.setAdapter(adapter); int length = intervalValues.length; String intervalString = String.valueOf((int)interval); String viewsizeString = String.valueOf((int)viewsize); for(int i = 0; i < length; i++) { if(intervalString.equals(intervalValues[i])) { spinnerInit++; intervalSpinner.setSelection(i); break; } } for(int i = 0; i < length; i++) { if(viewsizeString.equals(intervalValues[i])) { spinnerInit++; viewsizeSpinner.setSelection(i); break; } } } public class MyOnItemSelectedListener implements OnItemSelectedListener { @Override public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { if(spinnerInit > 0) { // Don't process selection event if spinner is initializing spinnerInit--; return; } if(parent == intervalSpinner) { interval = Double.parseDouble(intervalValues[pos]); MyLog.d("Setting interval " + pos + ", " + interval); NetworkLog.settings.setGraphInterval((long)interval); } else { if(lastViewsizePos == pos) { // Skip programmatical call to setSelected() return; } viewsize = Double.parseDouble(intervalValues[pos]); MyLog.d("Setting viewsize " + pos + ", " + viewsize); NetworkLog.settings.setGraphViewsize((long)viewsize); lastViewsizePos = pos; } buildSeries(interval, viewsize); graphView.invalidateContent(); } @Override public void onNothingSelected(AdapterView parent) { // do nothing } } private Comparator<LegendItem> legendSorter = new Comparator<LegendItem>() { public int compare(LegendItem o1, LegendItem o2) { return o1.size > o2.size ? -1 : (o1.size == o2.size) ? 0 : 1; } }; HashMap<Integer, Double> legendMap = new HashMap<Integer, Double>(); public void sortLegend() { for(GraphViewSeries series : graphView.graphSeries) { legendMap.put(series.id, series.size); } // FIXME: update hashcode when resolving host addr Double size; synchronized(legendData) { for(LegendItem item : legendData) { size = legendMap.get(item.mHashCode); item.size = size == null ? 0 : size; } Collections.sort(legendData, legendSorter); adapter.notifyDataSetChanged(); } } private class CustomOnItemClickListener implements OnItemClickListener { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { LegendItem item = legendData.get(position); item.mEnabled = !item.mEnabled; CheckedTextView ctv = (CheckedTextView) view.findViewById(R.id.legendName); ctv.setChecked(item.mEnabled); graphView.setSeriesEnabled(item.mHashCode, item.mEnabled); graphView.invalidateLabels(); graphView.invalidateContent(); } } public void refreshLegendAdapter() { adapter.notifyDataSetChanged(); } private class CustomAdapter extends ArrayAdapter<LegendItem> { LayoutInflater mInflater = (LayoutInflater) getSystemService(Activity.LAYOUT_INFLATER_SERVICE); public CustomAdapter(Context context, int resource, List<LegendItem> objects) { super(context, resource, objects); } @Override public View getView(int position, View convertView, ViewGroup parent) { ViewHolder holder = null; ImageView icon; CheckedTextView name; LegendItem item = getItem(position); if(convertView == null) { convertView = mInflater.inflate(R.layout.graph_legend_item, null); holder = new ViewHolder(convertView); convertView.setTag(holder); } else { holder = (ViewHolder) convertView.getTag(); } icon = holder.getIcon(); icon.setImageDrawable(item.mIcon); name = holder.getName(); name.setText(item.mName); name.setChecked(item.mEnabled); return convertView; } } private class ViewHolder { private View mView; private ImageView mIcon; private CheckedTextView mName; public ViewHolder(View view) { mView = view; } public ImageView getIcon() { if(mIcon == null) { mIcon = (ImageView) mView.findViewById(R.id.legendIcon); } return mIcon; } public CheckedTextView getName() { if(mName == null) { mName = (CheckedTextView) mView.findViewById(R.id.legendName); } return mName; } } }