/*
* (C) Copyright 2015 by fr3ts0n <erwin.scheuch-heilig@gmx.at>
*
* 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 2 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, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*/
package com.fr3ts0n.ecu.gui.androbd;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.PorterDuff;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.ProgressBar;
import android.widget.TextView;
import com.fr3ts0n.ecu.Conversion;
import com.fr3ts0n.ecu.EcuDataItem;
import com.fr3ts0n.ecu.EcuDataPv;
import com.fr3ts0n.ecu.NumericConversion;
import com.fr3ts0n.ecu.prot.obd.ObdProt;
import com.fr3ts0n.pvs.IndexedProcessVar;
import com.fr3ts0n.pvs.PvChangeEvent;
import com.fr3ts0n.pvs.PvChangeListener;
import com.fr3ts0n.pvs.PvList;
import org.achartengine.model.XYSeries;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Set;
/**
* Adapter for OBD data items (PVs)
*
* @author erwin
*/
public class ObdItemAdapter extends ArrayAdapter<Object>
implements PvChangeListener
{
transient protected PvList pvs;
transient protected boolean isPidList = false;
transient protected LayoutInflater mInflater;
transient public static final String FID_DATA_SERIES = "SERIES";
/** allow data updates to be handled */
public static boolean allowDataUpdates = true;
transient SharedPreferences prefs;
public ObdItemAdapter(Context context, int resource, PvList pvs)
{
super(context, resource);
prefs = PreferenceManager.getDefaultSharedPreferences(context);
mInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
setPvList(pvs);
}
/**
* set / update PV list
*
* @param pvs process variable list
*/
@SuppressWarnings("unchecked")
public synchronized void setPvList(PvList pvs)
{
this.pvs = pvs;
isPidList = (pvs == ObdProt.PidPvs);
// get set to be displayed (filtered with preferences */
Collection<Object> filtered = getPreferredItems(pvs, SettingsActivity.KEY_DATA_ITEMS);
// make it a sorted array
Object[] pidPvs = filtered.toArray();
Arrays.sort(pidPvs, pidSorter);
clear();
// add all elements
addAll(pidPvs);
if (this.getClass() == ObdItemAdapter.class)
addAllDataSeries();
}
@SuppressWarnings("rawtypes")
static Comparator pidSorter = new Comparator()
{
public int compare(Object lhs, Object rhs)
{
// criteria 1: ID string
int result = lhs.toString().compareTo(rhs.toString());
// criteria 2: description
if(result == 0)
{
result = String.valueOf(((IndexedProcessVar)lhs).get(EcuDataPv.FID_DESCRIPT))
.compareTo(String.valueOf(((IndexedProcessVar) rhs).get(EcuDataPv.FID_DESCRIPT)));
}
// return compare result
return result;
}
};
/**
* get set of data items filtered with set of preferred items to be displayed
* @param pvs list of PVs to be handled
* @param preferenceKey key of preference to be used as filter
* @return Set of filtered data items
*/
public Collection getPreferredItems(PvList pvs, String preferenceKey)
{
// filter PVs with preference selections
Set<String> pidsToShow = prefs.getStringSet( SettingsActivity.KEY_DATA_ITEMS,
(Set<String>)pvs.keySet());
return getMatchingItems(pvs, pidsToShow);
}
/**
* get set of data items filtered with set of preferred items to be displayed
* @param pvs list of PVs to be handled
* @param pidsToShow Set of keys to be used as filter
* @return Set of filtered data items
*/
public Collection<Object> getMatchingItems(PvList pvs, Set<String> pidsToShow)
{
HashSet<Object> filtered = new HashSet<Object>();
for(Object key : pidsToShow)
{
IndexedProcessVar pv = (IndexedProcessVar) pvs.get(key);
if(pv != null)
filtered.add(pv);
}
return(filtered);
}
/*
* (non-Javadoc)
*
* @see android.widget.ArrayAdapter#getView(int, android.view.View,
* android.view.ViewGroup)
*/
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
// get data PV
EcuDataPv currPv = (EcuDataPv) getItem(position);
if (convertView == null)
{
convertView = mInflater.inflate(R.layout.obd_item, parent, false);
}
// fill view fields with data
// description text
TextView tvDescr = (TextView) convertView.findViewById(R.id.obd_label);
tvDescr.setText(String.valueOf(currPv.get(EcuDataPv.FID_DESCRIPT)));
CheckBox cbChecked = (CheckBox) convertView.findViewById(R.id.check);
TextView tvValue = (TextView) convertView.findViewById(R.id.obd_value);
TextView tvUnits = (TextView) convertView.findViewById(R.id.obd_units);
ProgressBar pb = (ProgressBar) convertView.findViewById(R.id.bar);
cbChecked.setVisibility(isPidList ? View.VISIBLE : View.GONE);
// format value string
String fmtText;
Object colVal = currPv.get(EcuDataPv.FID_VALUE);
Object cnvObj = currPv.get(EcuDataPv.FID_CNVID);
Number min = (Number) currPv.get(EcuDataPv.FID_MIN);
Number max = (Number) currPv.get(EcuDataPv.FID_MAX);
int pid = currPv.getAsInt(EcuDataPv.FID_PID);
try
{
if ( cnvObj != null
&& cnvObj instanceof Conversion[]
&& ((Conversion[])cnvObj)[EcuDataItem.cnvSystem] != null
)
{
Conversion cnv;
cnv = ((Conversion[])cnvObj)[EcuDataItem.cnvSystem];
// set formatted text
fmtText = cnv.physToPhysFmtString((Number)colVal,
(String)currPv.get(EcuDataPv.FID_FORMAT));
// set progress bar only on LinearConversion
if( min != null
&& max != null
&& cnv instanceof NumericConversion)
{
pb.setVisibility(ProgressBar.VISIBLE);
pb.getProgressDrawable().setColorFilter(ChartActivity.getItemColor(pid), PorterDuff.Mode.SRC_IN);
pb.setProgress((int)(100 * ((((Number)colVal).doubleValue() - min.doubleValue()) / (max.doubleValue() - min.doubleValue()))));
}
else
{
pb.setVisibility(ProgressBar.GONE);
}
} else
{
fmtText = String.valueOf(colVal);
}
} catch (Exception ex)
{
fmtText = String.valueOf(colVal);
}
// set value
tvValue.setText(fmtText);
tvUnits.setText(currPv.getUnits());
return convertView;
}
/**
* Add data series to all process variables
*/
protected synchronized void addAllDataSeries()
{
for (IndexedProcessVar pv : (Iterable<IndexedProcessVar>) pvs.values())
{
XYSeries series = (XYSeries) pv.get(FID_DATA_SERIES);
if (series == null) {
series = new XYSeries(String.valueOf(pv.get(EcuDataPv.FID_DESCRIPT)));
pv.put(FID_DATA_SERIES, series);
pv.addPvChangeListener(this, PvChangeEvent.PV_MODIFIED);
}
}
}
@Override
public void pvChanged(PvChangeEvent event)
{
if (allowDataUpdates)
{
IndexedProcessVar pv = (IndexedProcessVar) event.getSource();
XYSeries series = (XYSeries) pv.get(FID_DATA_SERIES);
if (series != null)
{
if(event.getValue() instanceof Number)
{
series.add(event.getTime(),
((Number)event.getValue()).doubleValue());
}
}
}
}
}