/*
* 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/.
*
* Copyright (c) 2014 Digi International Inc., All Rights Reserved.
*/
package com.digi.android.wva.adapters;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import com.digi.android.wva.R;
import com.digi.android.wva.model.EndpointConfiguration;
import com.digi.android.wva.model.VehicleData;
import com.digi.android.wva.util.VehicleDataList;
import java.text.DecimalFormat;
import java.util.Collection;
import java.util.List;
/**
* Adapter used by the
* {@link com.digi.android.wva.fragments.VariableListFragment
* VariableListFragment}
* to display the vehicle data which has been received.
*
* @author mwadsten
*
*/
public class VariableAdapter extends ArrayAdapter<VehicleData> {
private final int resourceId;
private final Context ctx;
private final VehicleDataList data;
private static VariableAdapter instance;
/**
* Initialize the singleton VariableAdapter instance
* @param context the context to use
* @param list the backing {@link VehicleDataList} to use
*/
public static void initInstance(Context context, VehicleDataList list) {
if (instance == null)
instance = new VariableAdapter(context, list);
}
/**
* Fetch the singleton VariableAdapter instance, if there is one
* @return the singleton instance, or null if none exists
*/
public static VariableAdapter getInstance() {
return instance;
}
/** Constructor is private to enforce singleton model. */
private VariableAdapter(Context context,
VehicleDataList list) {
super(context, R.layout.variable_layout);
this.ctx = context;
this.resourceId = R.layout.variable_layout;
data = list;
}
// Overrides of ArrayAdapter methods to interact with the backing
// VehicleDataList
/**
* Updates the VehicleData object inside the adapter's backing
* list if found, otherwise adds `object` to the end of the list.
*/
@Override
public void add(VehicleData object) {
// If 'object' can correspond to something already in the
// backing list, update its data rather than blindly inserting
// it at the end of the list.
data.update(object);
notifyDataSetChanged();
}
/**
* Iterates over the backing data list to find any data whose name
* matches that passed in to this method, and removes that data from
* the list
* @param endpoint endpoint name whose corresponding {@link VehicleData}
* shall be removed
*/
public void removeEndpoint(String endpoint) {
List<VehicleData> lis = data.getList();
VehicleData toRemove = null;
for (VehicleData d : lis)
if (d.name.equals(endpoint)) {
toRemove = d;
break;
}
if (toRemove != null)
data.getList().remove(toRemove);
notifyDataSetChanged();
}
/**
* Calls {@link #add(VehicleData) add()} for each VehicleData in
* collection.
*/
@Override
public void addAll(Collection<? extends VehicleData> collection) {
for(VehicleData object : collection) {
add(object);
}
}
/**
* Does nothing. Don't use this.
*/
@Override
public void addAll(VehicleData... objects) {
}
/**
* Clears the backing {@link VehicleDataList} instance.
*/
@Override
public void clear() {
data.getList().clear();
notifyDataSetChanged();
}
@Override
public int getCount() {
return data.getList().size();
}
@Override
public VehicleData getItem(int position) {
return data.getList().get(position);
}
@Override
public int getPosition(VehicleData item) {
int pos = -1;
for(int i = 0; i < getCount(); i++) {
if (item.name.equals(data.getList().get(i).name))
pos = i;
}
return pos;
}
@Override
public void insert(VehicleData object, int index) {
data.update(object, index);
notifyDataSetChanged();
}
@Override
public void remove(VehicleData object) {
VehicleData toRemove = null;
for(VehicleData o : data.getList()) {
if (object.name.equals(o.name)) {
toRemove = o;
break;
}
}
if (toRemove != null) {
data.getList().remove(toRemove);
notifyDataSetChanged();
}
}
@Override
public View getView(int pos, View view, ViewGroup parent) {
if (view == null)
view = LayoutInflater.from(ctx).inflate(resourceId, null);
assert view != null;
TextView name = (TextView)view.findViewById(R.id.var_name);
TextView val = (TextView)view.findViewById(R.id.var_value);
VehicleData i = getItem(pos);
String n = i.name,
v = roundToThree(i.value);
name.setText(n != null ? n : "Blah");
val.setText(v != null ? v : "50");
// Gray out the entry if we're not subscribed to the endpoint.
name.setEnabled(true);
val.setEnabled(true);
EndpointsAdapter endpoints = EndpointsAdapter.getInstance();
if (endpoints != null) { // a sanity check
EndpointConfiguration config = endpoints.findEndpointConfiguration(n);
if (config == null || !config.isSubscribed()) {
name.setEnabled(false);
val.setEnabled(false);
}
}
return view;
}
/**
* Given a Double value, return the same value formatted as a string, with up to three digits
* following the decimal point.
*
* <p>This method is protected, rather than private, due to a bug between JaCoCo and
* the Android build tools which causes the instrumented bytecode to be invalid when this
* method is private:
* http://stackoverflow.com/questions/17603192/dalvik-transformation-using-wrong-invoke-opcode
* </p>
*
* @param value the value to stringify
* @return the value, rounded to three digits
*/
protected String roundToThree(Double value) {
return new DecimalFormat("#.###").format(value);
}
}