0

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);
    }
}
swess
  • 171
  • 19
  • 2
    So if your question is "is it possible" - then the answer would be "yes it is". Have you actually tried? – Egor Apr 18 '16 at 08:18

4 Answers4

1

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;
        }
    }
};   
Victor Nidens
  • 474
  • 1
  • 4
  • 11
  • Yeah! I forgot to mention that (that's a non-UI thread). I do have a handler in Main. Check the edited OP – swess Apr 18 '16 at 08:37
  • Post your `Activity#onCreate` method implementation – Victor Nidens Apr 18 '16 at 08:49
  • Okay! Thanks. Right now it shows "Connected" instead of the voltage. And when I try to write `tv.setText(voltageCommand.getFormattedResult());` it can't resolve the symbol. Maybe i'm doing something wrong here :/ – swess Apr 18 '16 at 09:25
  • Yes, since it handles the `STATE_CONNECTED` event. You need to pass your voltage value to the `Handler` like this: `msg.obj = voltageCommand.getFormattedResult()` in your `ConnectedThread#run()` method – Victor Nidens Apr 18 '16 at 09:29
  • Gotcha. Sorry if i'm being a bit difficult here, but should the `msg.obj...` go in the while loop in ConnectedThread run. Like this: http://i.imgur.com/DpbnWyJ.png – swess Apr 18 '16 at 09:50
  • Tried it, now it shows the sample text "Medium Text". Should the int for VOLTAGE_STATUS be anything in particular? I have set it to `int VOLTAGE_STATUS = 6;` – swess Apr 18 '16 at 10:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/109423/discussion-between-victornlj-and-swess). – Victor Nidens Apr 18 '16 at 10:50
0

Use textView.setText(voltageCommand.getFormattedResult())

user3673952
  • 698
  • 10
  • 30
  • While this code may answer the question, it would be better to include some context, explaining how it works and when to use it. Code-only answers are not useful in the long run. – Bono Apr 18 '16 at 09:26
0

First, you need to map your textView to the resource ID.

   TextView textView = (TextView) findViewById(R.id.textView);

you can place the above line after setContentView("your layout");

Now, you can use this textView and set the data as shown below,

textView.setText(voltageCommand.getFormattedResult());
Natarajan Raman
  • 606
  • 1
  • 4
  • 14
  • I don't have `setContentView("layout");` in that class. Forgot to mention that. This class just handles all the Bluetooth stuff. When I put `TextView textView = (TextView) findViewById(R.id.textView);` under `onCreate` in `MainActivity` and `TextView textView = (TextView) findViewById(R.id.textView);` in the other class (BTHandler) I get the error `Cannot resolve method 'findViewById (int)'` – swess Apr 18 '16 at 08:34
0

This is about Android app development basics, you should read the tutorials at developer.android.com.

You can access the TextView with TextView textView = (TextView) findViewbyId(R.id.textView);.

Then change your text with

textView.setText("new message");

HTH

Sven Menschner
  • 603
  • 5
  • 12