Search code examples
javaandroidobd-ii

How to show values other than the sample text that's on textView in Android


I'm making a OBDII reader app and would like to see the (in this case) voltage on text rather than in the Logcat which is what I currently see. You can see in the code below where I read the voltage in my while loop. Is it possible to update the textView everytime the loop runs?

Any help is appreciated!

.java

public class BTHandler {

    public static final int STATE_NONE = 0;       // we're doing nothing
    public static final int STATE_CONNECTING = 2; // now initiating an outgoing connection
    public static final int STATE_CONNECTED = 3;  // now connected to a remote device
    private final BluetoothAdapter mAdapter;
    private final Handler mHandler;
    BluetoothAdapter mBluetoothAdapter;
    UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
    private String status;
    private BluetoothDevice mmDevice;
    private ConnectedThread mConnectedThread;
    private ConnectThread mConnectThread;
    private int pPlatform = Constants.SPA;
    private boolean connectionStatus = false;

    public BTHandler(Context context, Handler handler) { // Konstruktor
        mAdapter = BluetoothAdapter.getDefaultAdapter();
        mHandler = handler;
    }

    public void write(String s) {
        mConnectedThread.sendRawCommand(s);
        Log.v("write", "write");
    }

    public void close() {
        try {
            mConnectedThread.cancel();
        } catch (NullPointerException e) {

        }
    }

    public void connect(String deviceAddress) {
        mConnectThread = new ConnectThread(deviceAddress);
        mConnectThread.start();
    }

    private void guiHandler(int what, int arg1, String obj) {
        Message msg = mHandler.obtainMessage();
        msg.what = what;
        msg.obj = obj;
        msg.arg1 = arg1;
        msg.sendToTarget();
    }

    private class ConnectThread extends Thread {
        private final BluetoothDevice mmDevice;
        BluetoothSocket tmp = null;
        private BluetoothSocket mmSocket;

        public ConnectThread(String deviceAddress) {
            mmDevice = mAdapter.getRemoteDevice(deviceAddress);

            try {
                // MY_UUID is the app's UUID string, also used by the server code
                tmp = mmDevice.createRfcommSocketToServiceRecord(MY_UUID);
            } catch (IOException e) {
            }
            mmSocket = tmp;
        }

        public void run() {
            // Cancel discovery because it will slow down the connection
            mAdapter.cancelDiscovery();

            try {
                // Connect the device through the socket. This will block
                // until it succeeds or throws an exception
                mmSocket.connect();
            } catch (IOException connectException) {
                // Unable to connect; close the socket and get out
                try {
                    mmSocket.close();
                } catch (IOException closeException) {
                }
                guiHandler(Constants.TOAST, Constants.SHORT, "Connection Failed");
                return;
            }
            // Do work to manage the connection (in a separate thread)
            guiHandler(Constants.CONNECTION_STATUS, Constants.STATE_CONNECTED, "");
            mConnectedThread = new ConnectedThread(mmSocket);
            mConnectedThread.start();

        }
    }

    private class ConnectedThread extends Thread {
        private final InputStream mmInStream;
        private final OutputStream mmOutStream;
        private BluetoothSocket mmSocket;
        private ObdMultiCommand multiCommand;

        public ConnectedThread(BluetoothSocket socket) {
            connectionStatus = true;
            mmSocket = socket;
            InputStream tmpIn = null;
            OutputStream tmpOut = null;


            switch (pPlatform) {
                case Constants.SPA:
                    multiCommand = new ObdMultiCommand();
                    multiCommand.add(new OdbRawCommand(SPA.VOLTAGE));
                    break;
            }

            try {
                tmpIn = socket.getInputStream();
                tmpOut = socket.getOutputStream();
            } catch (IOException e) {
                Log.v("e", "e");
            }

            mmInStream = tmpIn;
            mmOutStream = tmpOut;
        }

        public void run() {
            OBDcmds();

            ModuleVoltageCommand voltageCommand = new ModuleVoltageCommand();
            //TextView textView = (TextView) findViewById(R.id.textView);

            while (!Thread.currentThread().isInterrupted()) {
                try {
                    voltageCommand.run(mmInStream, mmOutStream);
                    voltageCommand.getFormattedResult();
                    Log.d("Log", "Voltage:" + voltageCommand.getFormattedResult());
                    textView.setText(voltageCommand.getFormattedResult());
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

        // CALL this to MainActivity
        public void sendRawCommand(String s) {

            try {

            } catch (Exception e) {
                Log.v("sendRawCommand", "e");
            }
        }


        /*
        // Call this from the main activity to send data to the remote device
        public void write(byte[] bytes) {
            try {
                mmOutStream.write(bytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        */


        private void OBDcmds() { // execute commands

            try {
                new EchoOffCommand().run(mmInStream, mmOutStream);
                new LineFeedOffCommand().run(mmInStream, mmOutStream);
                new TimeoutCommand(100).run(mmInStream, mmOutStream);
                new SelectProtocolCommand(ObdProtocols.AUTO).run(mmInStream, mmOutStream);
                //ISO_15765_4_CAN

            } catch (Exception e) {
                Log.v("OBDcmds", "e");
                // handle errors
            }
        }

        /* Call this from the main activity to shutdown the connection */
        public void cancel() {
            try {
                connectionStatus = false;
                mmSocket.close();

                guiHandler(Constants.CONNECTION_STATUS, Constants.STATE_NOT_CONNECTED, "");
            } catch (IOException e) {
            }
        }
    }
}

.xml

<TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="178dp"
        android:text="Medium Text"
        android:textAppearance="?android:attr/textAppearanceMedium" />

MainActivity

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button b1;
    BluetoothAdapter mAdapter;
    BTHandler btHandler;

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constants.MESSAGE_STATE_CHANGE:
                    switch (msg.arg1) {
                        case BTHandler.STATE_CONNECTED:
                            setContentView(R.layout.activity_connected);
                            Toast.makeText(getApplicationContext(), R.string.title_connected_to, Toast.LENGTH_SHORT).show();
                            Log.v("Log", "Connected");
                            break;
                        case BTHandler.STATE_NONE:
                            Toast.makeText(getApplicationContext(), R.string.title_not_connected, Toast.LENGTH_SHORT).show();
                            break;
                    }
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        TextView textView = (TextView) findViewById(R.id.textView);
        btHandler = new BTHandler(MainActivity.this, mHandler);
        b1 = (Button) findViewById(R.id.connect);
        b1.setOnClickListener(this);
        mAdapter = BluetoothAdapter.getDefaultAdapter();

        if (mAdapter == null) {
            Toast.makeText(getApplicationContext(), R.string.device_not_supported, Toast.LENGTH_LONG).show();
            finish();
        } else {
            if (!mAdapter.isEnabled()) {
                Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
                startActivityForResult(intent, 1);
            }
        }
    }

    public void onClick(View v) {
        int id = v.getId();
        //String voltage = ("ATRV");

        switch (id) {
            case R.id.connect:
                onConnect(); //Operation
                Log.v("Log", "Pressed onClick");
                break;
            case R.id.getValue:
                btHandler.write(SPA.VOLTAGE);
                Log.v("getValue","" + SPA.VOLTAGE);
                break;

        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == RESULT_CANCELED) {
            Toast.makeText(getApplicationContext(), R.string.enable_bluetooth, Toast.LENGTH_SHORT).show();
            finish();
        }
    }

    private void onConnect() {
        ArrayList deviceStrs = new ArrayList();
        final ArrayList<String> devices = new ArrayList();

        BluetoothAdapter mAdapter = BluetoothAdapter.getDefaultAdapter();
        Set pairedDevices = mAdapter.getBondedDevices();
        if (pairedDevices.size() > 0) {
            for (Object device : pairedDevices) {
                BluetoothDevice bdevice = (BluetoothDevice) device;
                deviceStrs.add(bdevice.getName() + "\n" + bdevice.getAddress());
                devices.add(bdevice.getAddress());
            }
        }

        // show list
        final AlertDialog.Builder alertDialog = new AlertDialog.Builder(this);

        ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.select_dialog_singlechoice,
                deviceStrs.toArray(new String[deviceStrs.size()]));

        alertDialog.setSingleChoiceItems(adapter, -1, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                int position = ((AlertDialog) dialog).getListView().getCheckedItemPosition();
                String deviceAddress = devices.get(position);

                btHandler.connect(deviceAddress);
            }
        });
        alertDialog.setTitle("Paired devices");
        alertDialog.show();
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

Solution

  • Your object extends the Thread so I assume that it would be started like this

    ConnectedThread ct = new ConnectedThread(socket);
    ct.start();
    

    In Android you can't update View from non-UI thread. You would need to create a Handler and pass the data to it. In you Handler implementation you would be able to call the setText() method to update your TextView.

    See this link for more details about communication with UT thread.

    For the updated post

    I personally don't like the idea to change the layout of the Activity in the Handler implementation - I would go for Fragments usage.

    Anyway after calling the setContentView(R.layout.activity_connected) in your Handler you need to call the findViewById again to find the TextView in your activity_connected layout:

    case BTHandler.STATE_CONNECTED:
        setContentView(R.layout.activity_connected);
        Toast.makeText(getApplicationContext(), R.string.title_connected_to, Toast.LENGTH_SHORT).show();
        Log.v("Log", "Connected");
        TextView tv = (TextView)findViewById(R.id.textView);
        tv.setText("Connected");
        break;
    

    Code snippet update

    ConnectedThread#run()

    public void run() {
            OBDcmds();
    
            ModuleVoltageCommand voltageCommand = new ModuleVoltageCommand();
            //TextView textView = (TextView) findViewById(R.id.textView);
    
            while (!Thread.currentThread().isInterrupted()) {
                try {
                    voltageCommand.run(mmInStream, mmOutStream);
                    Log.d("Log", "Voltage:" + voltageCommand.getFormattedResult());
                    guiHandler(Constants.VOLTAGE_STATUS, 0, voltageCommand.getFormattedResult())
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    

    Handler implementation private Handler mHandler = new Handler() { TextView textView = null;

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case Constants.MESSAGE_STATE_CHANGE:
                    switch (msg.arg1) {
                        case BTHandler.STATE_CONNECTED:
                            setContentView(R.layout.activity_connected);
                            Toast.makeText(getApplicationContext(), R.string.title_connected_to, Toast.LENGTH_SHORT).show();
                            Log.v("Log", "Connected");
                            textView = (TextView)findViewById(R.id.textView);
                            break;
                        case BTHandler.STATE_NONE:
                            Toast.makeText(getApplicationContext(), R.string.title_not_connected, Toast.LENGTH_SHORT).show();
                            break;
                    }
                    break;
                case Constants.VOLTAGE_STATUS:
                    if(msg.obj != null && textView != null){
                        textView.setText((String)msg.obj)
                    }
                    break;
            }
        }
    };