Android 灯泡); 打破 case Node.DEV_BTN: iv=新图像视图(上下文); iv.setImageResource(R.drawable.按钮); 打破 } 回报四; } 私有最终处理程序=新处理程序(){ @凌驾 公共无效handleMessage(消息消息消息){ 开关(msg.what){ 案例值_已更新: TextView tv=values.get(msg.arg1); tv.setText(node.getValue(msg.arg1)); 打破 } } }; }

Android 灯泡); 打破 case Node.DEV_BTN: iv=新图像视图(上下文); iv.setImageResource(R.drawable.按钮); 打破 } 回报四; } 私有最终处理程序=新处理程序(){ @凌驾 公共无效handleMessage(消息消息消息){ 开关(msg.what){ 案例值_已更新: TextView tv=values.get(msg.arg1); tv.setText(node.getValue(msg.arg1)); 打破 } } }; },android,crash,nullpointerexception,Android,Crash,Nullpointerexception,Node.java /***************************************************************************** * * Copyright(C) 2011, Embedded Artists AB * All rights reserved. * ****************************************************************************** * So

Node.java

/*****************************************************************************
 *
 *   Copyright(C) 2011, Embedded Artists AB
 *   All rights reserved.
 *
 ******************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * Embedded Artists AB assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. Embedded Artists AB
 * reserves the right to make changes in the software without
 * notification. Embedded Artists AB also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
 *****************************************************************************/
package com.embeddedartists.aoa.nodes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


public class Node {

/**************************************************************************
 * Public constants 
 *************************************************************************/ 

public static final int DEV_TEMP  = (0x01 << 4);
public static final int DEV_LIGHT = (0x02 << 4);
public static final int DEV_BTN   = (0x03 << 4);
public static final int DEV_RGB   = (0x04 << 4);
public static final int DEV_LED   = (0x05 << 4);
public static final int DEV_ACCL_X   = (0x06 << 4);
//  public static final int DEV_ACCL_Y   = (0x07 << 4);
//  public static final int DEV_ACCL_Z   = (0x08 << 4);

/**************************************************************************
 * Private variables 
 *************************************************************************/ 

private int nodeId; 

private Map<Integer, Integer> values;
private int[] capabilities;
private AccessoryControl accessoryControl;

private ArrayList<OnValueChangeListener> listeners;

/**************************************************************************
 * Public interface 
 *************************************************************************/ 

public interface OnValueChangeListener {
    public void onValueChange(int capId);
}

/**************************************************************************
 * Constructor 
 *************************************************************************/ 

/**
 * Create a new Node
 * 
 * @param nodeId the unique ID of the Node
 * @param capabilites node capabilities is an array with capability IDs
 *                    DEV_TEMP, DEV_LIGHT, ...
 */
public Node (int nodeId, int[] capabilites, AccessoryControl accessoryControl) {
    this.nodeId = nodeId;
    this.capabilities = capabilites;
    this.accessoryControl = accessoryControl;

    listeners = new ArrayList<OnValueChangeListener>();

    values = new HashMap<Integer, Integer>();
    for (int i = 0; i < capabilities.length; i++) {
        values.put(Integer.valueOf(capabilites[i]), 0);         
    }
}

/**************************************************************************
 * Public methods 
 *************************************************************************/ 

/**
 * Get Node ID
 * @return the node ID
 */
public int getNodeId() {
    return nodeId;
}

/**
 * Get Node capabilities
 * @return node capabilities
 */
public int[] getCapabilities() {
    return capabilities;
}

/**
 * Add value change listener  
 * @param l listener to add
 */
public void addOnValueChangeListener(OnValueChangeListener l) {
    listeners.add(l);
}

/**
 * Remove value change listener
 * @param l listener to remove
 */
public void removeOnValueChangeListener(OnValueChangeListener l) {
    listeners.remove(l);
}   

/**
 * Update the value of a capability in this node object
 *  
 * @param capId capability ID
 * @param value value to set
 */
public void setValue(int capId, int value) {

    values.put(Integer.valueOf(capId), Integer.valueOf(value));

    for (OnValueChangeListener l : listeners) {
        l.onValueChange(capId);
    }

}

/**
 * Get the value of a specific capability
 * @param capId capability ID
 * @return the value of the capability
 */
public String getValue(int capId) {
    String val = "";
    int v = values.get(Integer.valueOf(capId));

    switch (capId) {
    case Node.DEV_TEMP:
        val = Double.toString( ((double)v)/100 );
        break;
    case Node.DEV_LIGHT:
        val = Integer.toString(v);
        break;
    case Node.DEV_BTN:
        val = Integer.toString(v);
        break;  
    case Node.DEV_ACCL_X:
        val = Integer.toString(v);
        break;
/*      case Node.DEV_ACCL_Y:
        val = Integer.toString(v);
        break;
    case Node.DEV_ACCL_Z:
        val = Integer.toString(v);
        break; */

    }

    return val;
}

/**
 * Set the RGB LED on the node. A message will be sent
 * to the attached accessory
 */
private void setRgb(int led, boolean on) {
    byte[] data = new byte[5];
    data[0] = AccessoryControl.MESSAGE_OUT_SET_VALUE;
    data[1] = (byte)this.nodeId;
    data[2] = DEV_RGB;
    data[3] = (byte)led;
    data[4] = (byte)(on ? 1 : 0);
    accessoryControl.writeCommand(data);        
}

/**
 * Set Red LED
 * @param on true if LED should be turned on
 */
public void setRedLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_RED, on);
}

/**
 * Set Blue LED
 * @param on true if LED should be turned on
 */
public void setBlueLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_BLUE, on);
}

/**
 * Set Green LED
 * @param on true if LED should be turned on
 */
public void setGreenLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_GREEN, on);
}   

/**
 * Set LED state. A message will be sent to the Accessory
 * 
 * @param on true if the LED should be turned on
 */
public void setLed(boolean on) {
    byte[] data = new byte[5];
    data[0] = AccessoryControl.MESSAGE_OUT_SET_VALUE;
    data[1] = (byte)this.nodeId;
    data[2] = DEV_LED;
    data[3] = 0;
    data[4] = (byte)(on ? 1 : 0);
    accessoryControl.writeCommand(data);
}
}
/*****************************************************************************
*
*版权所有(C)2011,嵌入式艺术家AB
*版权所有。
*
******************************************************************************
*本文描述的软件仅用于说明目的
*为客户提供有关
*产品。本软件按“原样”提供,不作任何保证。
*Embedded Artists AB对以下内容不承担任何责任:
*使用本软件,不转让任何专利下的许可或所有权,
*版权,或产品的版权。嵌入式艺术家公司
*保留对软件进行更改的权利,恕不另行通知
*通知。嵌入式美术师AB也不进行任何表示或修改
*保证此类应用将适用于指定用途
*无需进一步测试或修改即可使用。
*****************************************************************************/
包com.embeddedartists.aoa.nodes;
导入java.util.ArrayList;
导入java.util.HashMap;
导入java.util.Map;
公共类节点{
/**************************************************************************
*公共常数
*************************************************************************/ 

public static final int DEV_TEMP=(0x01我没有看到
新节点(int nodeId,int[]capabilites,AccessoryControl AccessoryControl);
任何地方,我认为您需要先构造它,然后才能从值中获取任何内容,因为其中没有值。

节点视图中的171行是什么?我猜它的
tv.setText(Node.getValue(msg.arg1))
是的,我预先确定它是作为它的handleMessage所以是的……你真的在创建节点吗?就像你在调用构造函数一样,我能看到的唯一可能为null的是散列值map@user1539284:如果您希望得到答案,请发布导致错误的实际代码。将在的第171行引发异常
NodeView.java
但我刚刚将您发布的代码复制/粘贴到编辑器中,只有160行。很抱歉,Tim和Squonk在代码开头删除了一些版权信息,从而减少了行数。我正在另一个文件中构建节点,我将复制粘贴整个代码。再次道歉!
case Node.DEV_ACCL_X:
val = Integer.toString(v);
break; 
/*****************************************************************************
 *
 *   Copyright(C) 2011, Embedded Artists AB
 *   All rights reserved.
 *
 ******************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * Embedded Artists AB assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. Embedded Artists AB
 * reserves the right to make changes in the software without
 * notification. Embedded Artists AB also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
 *****************************************************************************/
package com.embeddedartists.aoa.nodes;

import java.util.HashMap;
import java.util.Map;

import com.embeddedartists.aoa.nodes.R;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

public class NodeView extends LinearLayout implements Node.OnValueChangeListener {

/**************************************************************************
 * Private variables 
 *************************************************************************/ 

private TextView nodeIdView;
private Node node;
private Map<Integer, TextView> values;

private static final int VALUE_UPDATED = 0;

/**************************************************************************
 * Constructor 
 *************************************************************************/ 

/**
 * Create a NodeView
 * @param context context
 * @param node node associated with this view
 */
public NodeView(Context context, Node node) {
    super(context);
    this.node = node;

    this.setOrientation(VERTICAL);      

    values = new HashMap<Integer, TextView>();
    node.addOnValueChangeListener(this);

    nodeIdView = new TextView(context);
    nodeIdView.setTextSize(24);
    nodeIdView.setText("Node: " + node.getNodeId());

    addView(nodeIdView, new LinearLayout.LayoutParams(
            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

    LinearLayout capLayout = new LinearLayout(context);
    capLayout.setOrientation(HORIZONTAL);
    addView(capLayout, new LinearLayout.LayoutParams(
            LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

    int[] caps = node.getCapabilities();


    for (int i = 0; i < caps.length; i++) {
        LinearLayout vl = new LinearLayout(context);
        vl.setOrientation(HORIZONTAL);          
        vl.setMinimumWidth(110);


        ImageView iv = getImageView(context, caps[i]);
        if (iv == null) continue;
        iv.setPadding(0, 0, 10, 5);
        vl.addView(iv, new LinearLayout.LayoutParams(
                LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));

        TextView tv = new TextView(context);
        tv.setText("" + node.getValue(caps[i]));
        vl.addView(tv, new LinearLayout.LayoutParams(
                LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));


        // Mapping capability with the TextView that holds its value.
        // A limitation with using the capability type as key is
        // that a node can only have one capability of each type
        values.put(Integer.valueOf(caps[i]), tv);   
        capLayout.addView(vl, new LinearLayout.LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));


    }


}

/**************************************************************************
 * Public methods 
 *************************************************************************/ 

/**
 * Get Node associated with this NodeView
 * @return
 */
public Node getNode() {
    return this.node;
}

/**
 * OnValueChangeListener
 */
public void onValueChange(int capId) {

    Message m = Message.obtain(handler, VALUE_UPDATED);
    m.arg1 = capId;
    handler.sendMessage(m);     

}   

/**************************************************************************
 * Private methods 
 *************************************************************************/ 

/**
 * Get ImageView given a capability
 * @param context
 * @param capability capability ID
 * @return ImageView
 */
private ImageView getImageView(Context context, int capability) {
    ImageView iv = null;
    switch (capability) {
    case Node.DEV_TEMP:
        iv = new ImageView(context);
        iv.setImageResource(R.drawable.temperature);
        break;
    case Node.DEV_LIGHT:
        iv = new ImageView(context);
        iv.setImageResource(R.drawable.lightbulb);          
        break;
    case Node.DEV_BTN:
        iv = new ImageView(context);
        iv.setImageResource(R.drawable.button);         
        break;

    }

    return iv;
}

private final Handler handler = new Handler() {

    @Override
    public void handleMessage(Message msg) {

        switch(msg.what) {
        case VALUE_UPDATED: 

            TextView tv = values.get(msg.arg1);
            tv.setText(node.getValue(msg.arg1));                

            break;                                      
        }
    }

};      


}
/*****************************************************************************
 *
 *   Copyright(C) 2011, Embedded Artists AB
 *   All rights reserved.
 *
 ******************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * Embedded Artists AB assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. Embedded Artists AB
 * reserves the right to make changes in the software without
 * notification. Embedded Artists AB also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
 *****************************************************************************/
package com.embeddedartists.aoa.nodes;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;


public class Node {

/**************************************************************************
 * Public constants 
 *************************************************************************/ 

public static final int DEV_TEMP  = (0x01 << 4);
public static final int DEV_LIGHT = (0x02 << 4);
public static final int DEV_BTN   = (0x03 << 4);
public static final int DEV_RGB   = (0x04 << 4);
public static final int DEV_LED   = (0x05 << 4);
public static final int DEV_ACCL_X   = (0x06 << 4);
//  public static final int DEV_ACCL_Y   = (0x07 << 4);
//  public static final int DEV_ACCL_Z   = (0x08 << 4);

/**************************************************************************
 * Private variables 
 *************************************************************************/ 

private int nodeId; 

private Map<Integer, Integer> values;
private int[] capabilities;
private AccessoryControl accessoryControl;

private ArrayList<OnValueChangeListener> listeners;

/**************************************************************************
 * Public interface 
 *************************************************************************/ 

public interface OnValueChangeListener {
    public void onValueChange(int capId);
}

/**************************************************************************
 * Constructor 
 *************************************************************************/ 

/**
 * Create a new Node
 * 
 * @param nodeId the unique ID of the Node
 * @param capabilites node capabilities is an array with capability IDs
 *                    DEV_TEMP, DEV_LIGHT, ...
 */
public Node (int nodeId, int[] capabilites, AccessoryControl accessoryControl) {
    this.nodeId = nodeId;
    this.capabilities = capabilites;
    this.accessoryControl = accessoryControl;

    listeners = new ArrayList<OnValueChangeListener>();

    values = new HashMap<Integer, Integer>();
    for (int i = 0; i < capabilities.length; i++) {
        values.put(Integer.valueOf(capabilites[i]), 0);         
    }
}

/**************************************************************************
 * Public methods 
 *************************************************************************/ 

/**
 * Get Node ID
 * @return the node ID
 */
public int getNodeId() {
    return nodeId;
}

/**
 * Get Node capabilities
 * @return node capabilities
 */
public int[] getCapabilities() {
    return capabilities;
}

/**
 * Add value change listener  
 * @param l listener to add
 */
public void addOnValueChangeListener(OnValueChangeListener l) {
    listeners.add(l);
}

/**
 * Remove value change listener
 * @param l listener to remove
 */
public void removeOnValueChangeListener(OnValueChangeListener l) {
    listeners.remove(l);
}   

/**
 * Update the value of a capability in this node object
 *  
 * @param capId capability ID
 * @param value value to set
 */
public void setValue(int capId, int value) {

    values.put(Integer.valueOf(capId), Integer.valueOf(value));

    for (OnValueChangeListener l : listeners) {
        l.onValueChange(capId);
    }

}

/**
 * Get the value of a specific capability
 * @param capId capability ID
 * @return the value of the capability
 */
public String getValue(int capId) {
    String val = "";
    int v = values.get(Integer.valueOf(capId));

    switch (capId) {
    case Node.DEV_TEMP:
        val = Double.toString( ((double)v)/100 );
        break;
    case Node.DEV_LIGHT:
        val = Integer.toString(v);
        break;
    case Node.DEV_BTN:
        val = Integer.toString(v);
        break;  
    case Node.DEV_ACCL_X:
        val = Integer.toString(v);
        break;
/*      case Node.DEV_ACCL_Y:
        val = Integer.toString(v);
        break;
    case Node.DEV_ACCL_Z:
        val = Integer.toString(v);
        break; */

    }

    return val;
}

/**
 * Set the RGB LED on the node. A message will be sent
 * to the attached accessory
 */
private void setRgb(int led, boolean on) {
    byte[] data = new byte[5];
    data[0] = AccessoryControl.MESSAGE_OUT_SET_VALUE;
    data[1] = (byte)this.nodeId;
    data[2] = DEV_RGB;
    data[3] = (byte)led;
    data[4] = (byte)(on ? 1 : 0);
    accessoryControl.writeCommand(data);        
}

/**
 * Set Red LED
 * @param on true if LED should be turned on
 */
public void setRedLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_RED, on);
}

/**
 * Set Blue LED
 * @param on true if LED should be turned on
 */
public void setBlueLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_BLUE, on);
}

/**
 * Set Green LED
 * @param on true if LED should be turned on
 */
public void setGreenLed(boolean on) {
    setRgb(AccessoryControl.MESSAGE_RGB_VAL_GREEN, on);
}   

/**
 * Set LED state. A message will be sent to the Accessory
 * 
 * @param on true if the LED should be turned on
 */
public void setLed(boolean on) {
    byte[] data = new byte[5];
    data[0] = AccessoryControl.MESSAGE_OUT_SET_VALUE;
    data[1] = (byte)this.nodeId;
    data[2] = DEV_LED;
    data[3] = 0;
    data[4] = (byte)(on ? 1 : 0);
    accessoryControl.writeCommand(data);
}
}
/*****************************************************************************
 *
 *   Copyright(C) 2011, Embedded Artists AB
 *   All rights reserved.
 *
 ******************************************************************************
 * Software that is described herein is for illustrative purposes only
 * which provides customers with programming information regarding the
 * products. This software is supplied "AS IS" without any warranties.
 * Embedded Artists AB assumes no responsibility or liability for the
 * use of the software, conveys no license or title under any patent,
 * copyright, or mask work right to the product. Embedded Artists AB
 * reserves the right to make changes in the software without
 * notification. Embedded Artists AB also make no representation or
 * warranty that such application will be suitable for the specified
 * use without further testing or modification.
 *****************************************************************************/
package com.embeddedartists.aoa.nodes;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
import android.util.Log;

import com.android.future.usb.UsbAccessory;
import com.android.future.usb.UsbManager;

public class AccessoryControl { 

public static final String ACTION_USB_PERMISSION = 
        "com.embeddedartists.aoa.nodes.MainActivity.action.USB_PERMISSION"; 

/*
 * Message indexes for messages sent from the Accessory
 */
public static final byte MESSAGE_IN_NODE_ADD    = 0;
public static final byte MESSAGE_IN_NODE_REMOVE = 1;    
public static final byte MESSAGE_IN_NODE_VALUE  = 2;

/*
 * Message indexes for messages sent to the Accessory
 */
public static final byte MESSAGE_OUT_SET_VALUE    = 10;

/*
 * Special messages to/from the accessory
 */


/** 
 * Sent to the accessory to indicate that the application is 
 * ready to receive data.
 */
private static final byte MESSAGE_CONNECT = 98; 
/** 
 * Sent to the accessory to indicate that the application is 
 * closing. The accessory acks the message using the same
 * ID/index.
 */
private static final byte MESSAGE_DISCONNECT = 99;

/*
 * 
 */
public static final int MESSAGE_RGB_VAL_RED = 0x01;
public static final int MESSAGE_RGB_VAL_BLUE = 0x02;
public static final int MESSAGE_RGB_VAL_GREEN = 0x04;


/** Manufacturer string expected from the Accessory */
private static final String ACC_MANUF = "Embedded Artists AB";
/** Model string expected from the Accessory */
private static final String ACC_MODEL = "AOA Board - Nodes";

/** Tag used for logging */
private static final String TAG = "EA AOA";

public enum OpenStatus {
    CONNECTED, REQUESTING_PERMISSION, UNKNOWN_ACCESSORY, NO_ACCESSORY, NO_PARCEL
}


private boolean permissionRequested = false;    
private boolean isOpen = false;
private UsbManager usbManager;
private Context context;

private ParcelFileDescriptor parcelFileDescriptor; 
private FileOutputStream accOutputStream;   
private Receiver receiver;
private NodeList nodeList;

private static AccessoryControl instance;


/**
 * Private constructor
 */
private AccessoryControl() {        
    nodeList = NodeList.getInstance();
}


/**
 * Get instance of AccecssoryControl
 * @return instance
 */
public static AccessoryControl getInstance() {
    if (instance == null) {
        instance = new AccessoryControl();
    }

    return instance;
}

/**
 * Initialize with context
 * 
 * @param context
 */
public void init(Context context) {
    this.context = context;
    usbManager = UsbManager.getInstance(context);
}

/**
 * Open the accessory and establish a connection. This method will
 * check if there is any accessory connected to the device, request
 * permissions if necessary and then establish input and output 
 * streams to/from the accessory.
 * 
 * @return status of the Open call. 
 */
public OpenStatus open() {

    if (isOpen) {
        return OpenStatus.CONNECTED;
    }

    UsbAccessory[] accList = usbManager.getAccessoryList();
    if (accList != null && accList.length > 0) {

        if (usbManager.hasPermission(accList[0])) {
            return open(accList[0]);
        }
        else if (!permissionRequested) {

            PendingIntent permissionIntent = PendingIntent.getBroadcast(
                    context, 0, new Intent(ACTION_USB_PERMISSION), 0);              

            Log.i(TAG, "Requesting USB permission");

            usbManager.requestPermission(accList[0], permissionIntent);
            permissionRequested = true;

            return OpenStatus.REQUESTING_PERMISSION;
        }
    }

    return OpenStatus.NO_ACCESSORY;
}

/**
 * Open an accessory. This method should be called if an accessory
 * object has already been obtained. The method will establish the 
 * connection and start receiver thread.
 * 
 * @param accessory an instance of UsbAccessory
 * @return status of the Open call
 */
public OpenStatus open(UsbAccessory accessory) {

    if (isOpen) {
        return OpenStatus.CONNECTED;
    }

    // check if it is a known and supported accessory 
    if (!ACC_MANUF.equals(accessory.getManufacturer()) 
            || !ACC_MODEL.equals(accessory.getModel())) {

        Log.i(TAG, "Unknown accessory: " + accessory.getManufacturer() 
                + ", " + accessory.getModel());

        return OpenStatus.UNKNOWN_ACCESSORY;
    }

    parcelFileDescriptor = usbManager.openAccessory(accessory); 
    if (parcelFileDescriptor != null) {
        byte[] data = new byte[1];

        accOutputStream = new FileOutputStream(parcelFileDescriptor.getFileDescriptor());       
        receiver = new Receiver(new FileInputStream(parcelFileDescriptor.getFileDescriptor()));

        isOpen = true;
        new Thread(receiver).start();


        // notify the accessory that we are now ready to receive data
        data[0] = MESSAGE_CONNECT;
        writeCommand(data);

        return OpenStatus.CONNECTED;
    }

    Log.i(TAG, "Couldn't get any ParcelDescriptor");
    return OpenStatus.NO_PARCEL;
}

/**
 * Close the connection with the accessory.
 */
public void close() {

    if (!isOpen) {
        return;
    }

    permissionRequested = false;
    isOpen = false;

    try {
        receiver.close();
        accOutputStream.close();
        parcelFileDescriptor.close();
    } catch (IOException ioe) {
        Log.w(TAG, "Got exception when closing", ioe);
    }
}

/**
 * Call this method when the application is closing. This method
 * will notify the accessory that the application is about to
 * be closed.
 */
public void appIsClosing() {
    byte[] data = new byte[1];
    if (!isOpen) {
        return;
    }

    Log.i(TAG, "Sending Disconnect message to accessory");
    data[0] = AccessoryControl.MESSAGE_DISCONNECT;
    writeCommand(data);
    long t = System.currentTimeMillis() + 5000;
    try {
        while (!receiver.done && System.currentTimeMillis() < t) {
            Thread.sleep(200);
        }
    } catch(InterruptedException ie) {  
    }

}

/** 
 * Write/send a command to the accessory. For simplicity all messages
 * are 3 bytes long; command index and two data bytes.
 * 
 * @param cmd - command index
 * @param hiVal - first data byte
 * @param loVal - second data byte
 */
public void writeCommand(byte[] buffer) {

    if (!isOpen) {
        return;
    }

    try {
        synchronized(accOutputStream) {
            accOutputStream.write(buffer);
        }
    } catch(IOException ioe) {      
    }

}


/*
 * The receiver thread. This Thread is responsible for reading
 * data from the Accessory and dispatching messages to the Handler
 * (UI thread)
 */

private class Receiver implements Runnable  {

    private FileInputStream inputStream;
    private boolean done = false;

    Receiver(FileInputStream inputStream) {
        this.inputStream = inputStream;
    }

    public void run() {

        int msgLen = 0;
        int numRead = 0;
        int pos = 0;
        byte[] buffer = new byte[16384];

        Log.i(TAG, "Receiver.run");

        try {

            while(!done) {
                numRead = inputStream.read(buffer);
                pos = 0;


                while(pos < numRead) {
                    int len = numRead - pos;

                    switch(buffer[pos]) {

                    case AccessoryControl.MESSAGE_IN_NODE_ADD:

                        msgLen = 3;
                        if (len >= 3) {


                            int nodeId = (int) (buffer[pos+1] & 0xff);
                            int capLen = (int) (buffer[pos+2] & 0xff);

                            Log.i(TAG, "Recv Node Add: id="+nodeId+", capLen="+capLen);

                            msgLen += capLen;
                            int[] caps = new int[capLen];
                            int maxLen = caps.length;
                            if (maxLen > len-3) {
                                maxLen = len-3;
                            }
                            for (int i = 0; i < maxLen; i++) {
                                caps[i] = (int) (buffer[pos+3+i] & 0xff);
                            }

                            Node n = new Node(nodeId, caps, AccessoryControl.this);
                            nodeList.addNode(n);
                        }
                        pos += msgLen;
                        break;
                    case AccessoryControl.MESSAGE_IN_NODE_REMOVE:

                        Log.i(TAG, "Recv Node Remove: id="+(buffer[pos+1] & 0xff));

                        if (len >= 2) {
                            nodeList.removeNode((int) (buffer[pos+1] & 0xff));
                        }
                        pos += 2;
                        break;              

                    case AccessoryControl.MESSAGE_IN_NODE_VALUE:
                        if (len >= 5) {
                            int nodeId = (int) (buffer[pos+1] & 0xff);
                            int capId = (int) (buffer[pos+2] & 0xff);

                            Node n = nodeList.getNodeWithId(nodeId);
                            if (n != null) {
                                Log.i(TAG, "Recv Node id="+nodeId+", CapId= "+capId+", data="+toInt(buffer[pos + 3], buffer[pos + 4]));
                                n.setValue(capId, toInt(buffer[pos + 3], buffer[pos + 4]));
                            }
                        }
                        pos += 5;
                        break;              

                    case AccessoryControl.MESSAGE_DISCONNECT:

                        Log.i(TAG, "Received Disconnect (ACK) message from accessory");

                        // We want to make sure the Receive thread ends at this point
                        // and doesn't start reading data
                        done = true;
                        pos = numRead;
                        break;              

                    default:
                        // invalid message (or out of sync)

                        Log.w(TAG, "Unknown command: " + buffer[pos]);
                        pos += len;
                        break;
                    }

                }


            }

        } catch (IOException ioe) {
        }

    }

    /**
     * Close the receiver thread.
     */
    public void close() {
        done = true;

        try {
            inputStream.close();
        } catch(IOException ioe) {              
        }
    }

    /** Convert two bytes to an integer */
    private int toInt(byte hi, byte lo) {
        return (( (int)(hi&0xff) << 8) | (int)(lo&0xff));
    }

};
}