title: Android screen not updating without user action
body: <p>I am trying to connect to a terminal emulator using a library in android, this will connect to a serial device and should show me sent/received data. I should be able to send data over the connection via an text box below the terminal  or by typing in the terminal itself and hitting enter on the keyboard in both cases. </p>
  
  <p>When I send data the reply to my command comes back into a method from a library <code>onDataReceived(int id, byte[] data)</code>. This runs automatically every time I get data back. I can see the data arriving in the log. </p>
  
  <p>My problem is whenever data comes back from the serial device the screen does not update until I either bring the text box into focus, or hit the enter button on the keyboard. How can I make the screen update whenever anything is received. I know it is receiving the data and all data received will be displayed when I press the text box or the enter button.</p>
  
  <p>Here is what is called when I receive data, a <code>ByteArrayInputStream</code> is updated with the new data.  <code>appendToEmulator</code> writes something directly to the terminal emulator. <code>notifyUpdate</code> lets the emulator know the screen has changed.</p>
  
  <pre><code>public void onDataReceived(int id, byte[] data)
  {
      String str = new String(data);
      Log.d(TAG, "in data received " + str);
      ((MyBAIsWrapper)bis).renew(data);
  
      mSession.appendToEmulator(data, 0, data.length);
      mSession.notifyUpdate();
  }
  </code></pre>
  
  <p>Here is where I send something:</p>
  
  <pre><code>mEntry = (EditText) findViewById(R.id.term_entry);
  mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
  
          @Override
          public boolean onEditorAction(TextView v, int actionId,
                  KeyEvent event) {
  
              /* Ignore enter-key-up events. */
              if (event != null &amp;&amp; event.getAction() == KeyEvent.ACTION_UP) {
  
                  return false;
              }
  
  
              /* Don't try to send something if we are not connected yet. */
              TermSession session = mSession;
  
              if (mSession == null) {
              Log.d(TAG, "null session ");
              return true;
  
               }
  
              Log.d(TAG, "in click event ");
              Editable e = (Editable) v.getText();
  
              // call original sendData to send data over serial
              String data = e.toString() + "\r\n";
              Log.d(TAG, "edittext data is  " + data);
              //overridden sendData
              sendData(data.getBytes());
  
              // send data over serial using original sendData() method
              mSelectedAdapter.sendData(data.getBytes());
              TextKeyListener.clear(e);
              return true;
          }
      });
  </code></pre>
  
  <p>what can I do to make the screen update in real time and not just update on user action?</p>
  
  <p>EDIT: I think invalidate() is what I need but it doesn't work. I call it on emulatorView. An emulatorView displays the terminal emulator's screen and is a class in the library that inherits from view <a href="http://pastebin.com/MNJ0Zf8P" rel="nofollow">http://pastebin.com/MNJ0Zf8P</a></p>
  
  <p>EDIT: So invalidating the whole view does nothing but I noticed that when I invalidate mEmulatorView the screen updates when I unlock it. also the first time after it has been updated, I can tap the terminal itself and it will update. Further taps do nothing. Very strange, so it clearly is altering behaviour but is not doing anything automatically when I call it.</p>
  
  <p>EDIT: How come the handler worked and calling it did not?</p>
  
  <p>EDIT, my full activity:</p>
  
  <pre><code>import java.io.ByteArrayInputStream;
  import java.io.ByteArrayOutputStream;
  import java.io.InputStream;
  import java.io.IOException;
  import java.io.OutputStream;
  import java.net.Socket;
  import java.util.ArrayList;
  import java.util.Arrays;
  
  import slickdevlabs.apps.usb2seriallib.AdapterConnectionListener;
  import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial;
  import slickdevlabs.apps.usb2seriallib.USB2SerialAdapter;
  import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.BaudRate;
  import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.DataBits;
  import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.ParityOption;
  import slickdevlabs.apps.usb2seriallib.SlickUSB2Serial.StopBits;
  import slickdevlabs.apps.usb2seriallib.USB2SerialAdapter.DataListener;
  
  import android.app.Activity;
  import android.app.AlertDialog;
  import android.content.DialogInterface;
  import android.content.Intent;
  import android.os.Bundle;
  import android.os.Handler;
  import android.os.Message;
  import android.text.Editable;
  import android.text.method.TextKeyListener;
  import android.util.DisplayMetrics;
  import android.util.Log;
  import android.view.KeyEvent;
  import android.view.View;
  import android.view.View.OnClickListener;
  import android.view.Window;
  import android.widget.AdapterView;
  import android.widget.AdapterView.OnItemSelectedListener;
  import android.widget.ArrayAdapter;
  import android.widget.Button;
  import android.widget.EditText;
  import android.widget.Spinner;
  import android.widget.TextView;
  import android.widget.Toast;
  
  import jackpal.androidterm.emulatorview.EmulatorView;
  import jackpal.androidterm.emulatorview.TermSession;
  
  public class SerialTerminalActivity extends Activity implements
      OnClickListener, OnItemSelectedListener, AdapterConnectionListener,
      DataListener {
  
  private static final String TAG = "SerialTerminalActivity";
  private EditText mEntry;
  private EmulatorView mEmulatorView;
  private TermSession mSession;
  private OutputStream bos;
  private InputStream bis;
  private InputStream in;
  private OutputStream out;
  private Spinner mBaudSpinner;
  private Spinner mDataSpinner;
  private Spinner mParitySpinner;
  private Spinner mStopSpinner;
  private Spinner mDeviceSpinner;
  private Button mConnect;
  private ArrayList&lt;String&gt; mDeviceOutputs;
  private ArrayList&lt;USB2SerialAdapter&gt; mDeviceAdapters;
  private ArrayAdapter&lt;CharSequence&gt; mDeviceSpinnerAdapter;
  private USB2SerialAdapter mSelectedAdapter;
  private TextView mCurrentSettings;
  boolean attached = false;
  
  private Button mUpdateSettings;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      requestWindowFeature(Window.FEATURE_NO_TITLE);
      setContentView(R.layout.activity_serial_terminal);
  
      mConnect = (Button) findViewById(R.id.deviceConnect);
      mConnect.setOnClickListener(this);
      mUpdateSettings = (Button) findViewById(R.id.updateSettings);
      mUpdateSettings.setOnClickListener(this);
  
      mBaudSpinner = (Spinner) findViewById(R.id.baudSpinner);
      ArrayAdapter&lt;CharSequence&gt; adapter = new ArrayAdapter&lt;CharSequence&gt;(
              this, android.R.layout.simple_spinner_item);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      mBaudSpinner.setAdapter(adapter);
      String[] tempArray = SlickUSB2Serial.BAUD_RATES;
      for (int i = 0; i &lt; tempArray.length; i++) {
          adapter.add(tempArray[i]);
      }
      mBaudSpinner.setSelection(SlickUSB2Serial.BaudRate.BAUD_9600.ordinal());
  
      mDataSpinner = (Spinner) findViewById(R.id.dataSpinner);
      adapter = new ArrayAdapter&lt;CharSequence&gt;(this,
              android.R.layout.simple_spinner_item);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      mDataSpinner.setAdapter(adapter);
      tempArray = SlickUSB2Serial.DATA_BITS;
      for (int i = 0; i &lt; tempArray.length; i++) {
          adapter.add(tempArray[i]);
  
      }
      mDataSpinner
              .setSelection(SlickUSB2Serial.DataBits.DATA_8_BIT.ordinal());
  
      mParitySpinner = (Spinner) findViewById(R.id.paritySpinner);
      adapter = new ArrayAdapter&lt;CharSequence&gt;(this,
              android.R.layout.simple_spinner_item);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      mParitySpinner.setAdapter(adapter);
      tempArray = SlickUSB2Serial.PARITY_OPTIONS;
      for (int i = 0; i &lt; tempArray.length; i++) {
          adapter.add(tempArray[i]);
  
      }
      mParitySpinner.setSelection(SlickUSB2Serial.ParityOption.PARITY_NONE
              .ordinal());
  
      mStopSpinner = (Spinner) findViewById(R.id.stopSpinner);
      adapter = new ArrayAdapter&lt;CharSequence&gt;(this,
              android.R.layout.simple_spinner_item);
      adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      mStopSpinner.setAdapter(adapter);
      tempArray = SlickUSB2Serial.STOP_BITS;
      for (int i = 0; i &lt; tempArray.length; i++) {
          adapter.add(tempArray[i]);
  
      }
      mStopSpinner
              .setSelection(SlickUSB2Serial.StopBits.STOP_1_BIT.ordinal());
  
      mDeviceAdapters = new ArrayList&lt;USB2SerialAdapter&gt;();
      mDeviceOutputs = new ArrayList&lt;String&gt;();
  
      mDeviceSpinner = (Spinner) findViewById(R.id.deviceSpinner);
      mDeviceSpinnerAdapter = new ArrayAdapter&lt;CharSequence&gt;(this,
              android.R.layout.simple_spinner_item);
      mDeviceSpinnerAdapter
              .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
      mDeviceSpinner.setAdapter(mDeviceSpinnerAdapter);
      mDeviceSpinner.setOnItemSelectedListener(this);
  
      mCurrentSettings = (TextView) findViewById(R.id.currentSettings);
  
      SlickUSB2Serial.initialize(this);
  
      /*
       * Text entry box at the bottom of the activity. Note that you can also
       * send input (whether from a hardware device or soft keyboard) directly
       * to the EmulatorView.
       */
      mEntry = (EditText) findViewById(R.id.term_entry);
      mEntry.setOnEditorActionListener(new TextView.OnEditorActionListener() {
  
          @Override
          public boolean onEditorAction(TextView v, int actionId,
                  KeyEvent event) {
  
              /* Ignore enter-key-up events. */
              if (event != null &amp;&amp; event.getAction() == KeyEvent.ACTION_UP) {
  
                  return false;
              }
  
  
              /* Don't try to send something if we are not connected yet. */
              TermSession session = mSession;
  
              if (mSession == null) {
              Log.d(TAG, "null session ");
              return true;
  
               }
  
              Log.d(TAG, "in click event ");
              Editable e = (Editable) v.getText();
  
              // call original sendData to send data over serial
              String data = e.toString() + "\r\n";
              Log.d(TAG, "edittext data is  " + data);
              //doLocalEcho(data.getBytes());
              sendData(data.getBytes());
  
              // send data over serial using original sendData() method
              mSelectedAdapter.sendData(data.getBytes());
  
              /* Write to the terminal session. */
              session.write(e.toString());
              //Log.d(TAG, "edittext to string is  " + editText.toString());
              session.write('\r');
              TextKeyListener.clear(e);
              return true;
          }
      });
  
      /*
       * Sends the content of the text entry box to the terminal, without
       * sending a carriage return afterwards
       */
      Button sendButton = (Button) findViewById(R.id.term_entry_send);
      sendButton.setOnClickListener(new View.OnClickListener() {
  
          @Override
          public void onClick(View v) {
  
              /* Don't try to send something if we are not connected yet. */
              TermSession session = mSession;
              if (mSession == null) {
                  Log.d(TAG, "mSession == NULLLLLLLLLLLLL ");
                  return;
              }
  
              Editable editText = (Editable) mEntry.getText();
              session.write(editText.toString());
              Log.d(TAG, "edittext is " + editText.toString());
              TextKeyListener.clear(editText);
              Log.d(TAG, "send pressed ");
          }
      });
  
      /**
       * EmulatorView setup.
       */
  
      /* emulatorView from xml. */
      EmulatorView view = (EmulatorView) findViewById(R.id.emulatorView);
      mEmulatorView = view;
  
      /* Let the EmulatorView know the screen's density. */
      DisplayMetrics metrics = new DisplayMetrics();
      getWindowManager().getDefaultDisplay().getMetrics(metrics);
      view.setDensity(metrics);
  
      /* Create a TermSession. */
      // TermSession session = mSession;
      mSession = new TermSession();
  
      //byte[] a=new byte[]{1,1,1};
      byte[] a = new byte[]{'h','e', 'l', 'l', 'o'};
      byte[] b = new byte[4096];
      //bis = new ByteArrayInputStream(a);
      bis = new MyBAIsWrapper(b);
      bos = new ByteArrayOutputStream();
      mSession.write("testTWO");
      //bis = new ByteArrayInputStream(b);
      mSession.setTermIn(bis);
      mSession.setTermOut(bos);
      //session.setTermIn(in);
      //session.setTermOut(out);
  
      mSession.write("testONE");
  
  
      /* Attach the TermSession to the EmulatorView. */
      mEmulatorView.attachSession(mSession);
  
      //mSession = session;
  //  mSession.write("abc");
      //session.write("test");
      try {
          bos.write(b);
          bos.flush();
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
      }
  
      /* TODO Monday:  ByteArrayInputStream() can only be used once.  The data in it at creation is all that'll ever be in.
       *               Find a way to update what bis is pointing to without breaking the bind that bis has to the terminal.
       *               Recover from Saturday's hangover.
       */
  
  
      /*
       * That's all you have to do! The EmulatorView will call the attached
       * TermSession's initializeEmulator() automatically, once it can
       * calculate the appropriate screen size for the terminal emulator.
       */
  
  }
  
  public void sendData(byte[] data) {
      String str = new String(data);
      Log.d(TAG, "send data method value is: " + str);
  
      // this should echo what I send to the terminal in the correct format
      //bos = new ByteArrayOutputStream(data.length);
      mSession.write(data, 0, data.length);
      try {
          bos.write(data);
      } catch (IOException e) {
          // TODO Auto-generated catch block
          e.printStackTrace();
          Log.d(TAG, "EXCEPTION in data sent ");
      }
      // mSession.write(data, 0, data.length);
      // mSession.write('\r');
  
  }
  
  public void onDataReceived(int id, byte[] data) {
  
      String str = new String(data);
      Log.d(TAG, "in data received " + str);
        ((MyBAIsWrapper)bis).renew(data);
  
       mSession.appendToEmulator(data, 0, data.length);
       mSession.notifyUpdate();
       //mEmulatorView.invalidate();
       mEmulatorView.postInvalidate();
     /* bis = new ByteArrayInputStream(data);
      SerialTerminalActivity.this.runOnUiThread(new Runnable() {
          public void run() {
  
                serialSession();
          }
        });*/
  
       //cast added to keep original code structure 
      //I recommend defining the bis attribute as the MyBAIsWrapper type in this case
     // ((MyBAIsWrapper)bis).renew(data);
  
  
      //mSession.write(data, 0, data.length);
      //mSession.write('\r');
  
  }
  
  public void serialSession() {
      Log.d(TAG, "in serial session");
      mSession.setTermIn(bis);
      mSession.setTermOut(bos);
      /* Attach the TermSession to the EmulatorView. */
      mEmulatorView.attachSession(mSession);
  }
  
  @Override
  public void onItemSelected(AdapterView&lt;?&gt; parent, View view, int position,
          long id) {
      // TODO Auto-generated method stub
      changeSelectedAdapter(mDeviceAdapters.get(position));
  }
  
  @Override
  public void onNothingSelected(AdapterView&lt;?&gt; arg0) {
      // TODO Auto-generated method stub
  }
  
  public void changeSelectedAdapter(USB2SerialAdapter adapter) {
      Toast.makeText(this, "in changeselectedadapter", Toast.LENGTH_SHORT)
              .show();
      // if(mSelectedAdapter!=null){
      // mDeviceOutputs.set(mDeviceSpinnerAdapter.getPosition(mSelectedAdapter.getDeviceId()+""),mReceiveBox.getText().toString());
  
      mSelectedAdapter = adapter;
      mBaudSpinner.setSelection(adapter.getBaudRate().ordinal());
      mDataSpinner.setSelection(adapter.getDataBit().ordinal());
      mParitySpinner.setSelection(adapter.getParityOption().ordinal());
      mStopSpinner.setSelection(adapter.getStopBit().ordinal());
  
      updateCurrentSettingsText();
  
      // mReceiveBox.setText(mDeviceOutputs.get(mDeviceSpinner.getSelectedItemPosition()));
      Toast.makeText(this,
              "Adapter switched toooo: " + adapter.getDeviceId() + "!",
              Toast.LENGTH_SHORT).show();
  }
  
  @Override
  public void onAdapterConnected(USB2SerialAdapter adapter) {
      adapter.setDataListener(this);
      mDeviceAdapters.add(adapter);
      mDeviceOutputs.add("");
      mDeviceSpinnerAdapter.add("" + adapter.getDeviceId());
      mDeviceSpinner.setSelection(mDeviceSpinnerAdapter.getCount() - 1);
  
      Toast.makeText(this,
              "Adapter: " + adapter.getDeviceId() + " Connected!",
              Toast.LENGTH_SHORT).show();
      // Toast.makeText(this, "Baud: "+adapter.getBaudRate()+" Connected!",
      // Toast.LENGTH_SHORT).show();
  }
  
  @Override
  public void onAdapterConnectionError(int error, String msg) {
      // TODO Auto-generated method stub
      if (error == AdapterConnectionListener.ERROR_UNKNOWN_IDS) {
          final AlertDialog dialog = new AlertDialog.Builder(this)
                  .setIcon(0)
                  .setTitle("Choose Adapter Type")
                  .setItems(new String[] { "Prolific", "FTDI" },
                          new DialogInterface.OnClickListener() {
                              public void onClick(DialogInterface dialog,
                                      int optionSelected) {
                                  if (optionSelected == 0) {
                                      SlickUSB2Serial
                                              .connectProlific(SerialTerminalActivity.this);
                                  } else {
                                      SlickUSB2Serial
                                              .connectFTDI(SerialTerminalActivity.this);
                                  }
                              }
                          }).create();
          dialog.show();
          return;
      }
      Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
  }
  
  public void onClick(View v) {
  
      if (v == mConnect) {
          SlickUSB2Serial.autoConnect(this);
          if (mSelectedAdapter == null) {
              Toast.makeText(this, "no adapters detected", Toast.LENGTH_SHORT)
                      .show();
              return;
          }
      }
  
      else if (v == mUpdateSettings) {
          if (mSelectedAdapter == null) {
              return;
          }
  
          mSelectedAdapter.setCommSettings(BaudRate.values()[mBaudSpinner
                  .getSelectedItemPosition()], DataBits.values()[mDataSpinner
                  .getSelectedItemPosition()],
                  ParityOption.values()[mParitySpinner
                          .getSelectedItemPosition()],
                  StopBits.values()[mStopSpinner.getSelectedItemPosition()]);
  
          updateCurrentSettingsText();
          Toast.makeText(this, "Updated Settings", Toast.LENGTH_SHORT).show();
  
      }
  
  }
  
  private void updateCurrentSettingsText() {
      mCurrentSettings.setText("Current Settings Areeee: "
              + mBaudSpinner.getSelectedItem().toString() + ", "
              + mDataSpinner.getSelectedItem().toString() + ", "
              + mParitySpinner.getSelectedItem().toString() + ", "
              + mStopSpinner.getSelectedItem().toString());
  }
  
  
  
  /* Echoes local input from the emulator back to the emulator screen. */
  private void doLocalEcho(byte[] data) {
  
          Log.d(TAG, "echoing " +
                  Arrays.toString(data) + " back to terminal");
      //I added mSession, is it right?
      mSession.appendToEmulator(data, 0, data.length);
      mSession.notifyUpdate();
  }
  
  
  
  @Override
  protected void onResume() {
      super.onResume();
  
      /*
       * You should call this to let EmulatorView know that it's visible on
       * screen.
       */
      mEmulatorView.onResume();
  
      mEntry.requestFocus();
  }
  
  @Override
  protected void onPause() {
      /*
       * You should call this to let EmulatorView know that it's no longer
       * visible on screen.
       */
      mEmulatorView.onPause();
  
      super.onPause();
  }
  
  @Override
  protected void onDestroy() {
      /**
       * Finish the TermSession when we're destroyed. This will free
       * resources, stop I/O threads, and close the I/O streams attached to
       * the session.
       * 
       * For the local session, closing the streams will kill the shell; for
       * the Telnet session, it closes the network connection.
       */
      if (mSession != null) {
          mSession.finish();
      }
      SlickUSB2Serial.cleanup(this);
      super.onDestroy();
  }
  
  }
  </code></pre>