Advertisement
If you have a new account but are having problems posting or verifying your account, please email us on hello@boards.ie for help. Thanks :)
Hello all! Please ensure that you are posting a new thread or question in the appropriate forum. The Feedback forum is overwhelmed with questions that are having to be moved elsewhere. If you need help to verify your account contact hello@boards.ie

Android screen not updating without user interaction, invalidate() not working?

Options
  • 20-12-2012 12:55pm
    #1
    Moderators, Science, Health & Environment Moderators, Social & Fun Moderators, Society & Culture Moderators Posts: 60,092 Mod ✭✭✭✭


    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.

    When I send data the reply to my command comes back into a method from a library onDataReceived(int id, byte[] data). This runs automatically every time I get data back. I can see the data arriving in the log.

    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.

    Here is all the code, I try and run mEmulatorView.invalidate() or mEmulatorView.postInvalidate() in onDataReceieved but neither work, why?
    An emulatorView displays the terminal emulator's screen and is a class in the library that inherits from view http://pastebin.com/MNJ0Zf8P
    I bolded some relevant sections.

    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;
    [B]private EmulatorView mEmulatorView;[/B]
    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<String> mDeviceOutputs;
    private ArrayList<USB2SerialAdapter> mDeviceAdapters;
    private ArrayAdapter<CharSequence> 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<CharSequence> adapter = new ArrayAdapter<CharSequence>(
                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 < tempArray.length; i++) {
            adapter.add(tempArray[i]);
        }
        mBaudSpinner.setSelection(SlickUSB2Serial.BaudRate.BAUD_9600.ordinal());
    
        mDataSpinner = (Spinner) findViewById(R.id.dataSpinner);
        adapter = new ArrayAdapter<CharSequence>(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 < tempArray.length; i++) {
            adapter.add(tempArray[i]);
    
        }
        mDataSpinner
                .setSelection(SlickUSB2Serial.DataBits.DATA_8_BIT.ordinal());
    
        mParitySpinner = (Spinner) findViewById(R.id.paritySpinner);
        adapter = new ArrayAdapter<CharSequence>(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 < tempArray.length; i++) {
            adapter.add(tempArray[i]);
    
        }
        mParitySpinner.setSelection(SlickUSB2Serial.ParityOption.PARITY_NONE
                .ordinal());
    
        mStopSpinner = (Spinner) findViewById(R.id.stopSpinner);
        adapter = new ArrayAdapter<CharSequence>(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 < tempArray.length; i++) {
            adapter.add(tempArray[i]);
    
        }
        mStopSpinner
                .setSelection(SlickUSB2Serial.StopBits.STOP_1_BIT.ordinal());
    
        mDeviceAdapters = new ArrayList<USB2SerialAdapter>();
        mDeviceOutputs = new ArrayList<String>();
    
        mDeviceSpinner = (Spinner) findViewById(R.id.deviceSpinner);
        mDeviceSpinnerAdapter = new ArrayAdapter<CharSequence>(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 && 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.
         */
    
      [B]  /* emulatorView from xml. */
        EmulatorView view = (EmulatorView) findViewById(R.id.emulatorView);
        mEmulatorView = view;[/B]
    
        /* 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");
    
    [B]
        /* Attach the TermSession to the EmulatorView. */
        mEmulatorView.attachSession(mSession);[/B]
    
        //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');
    
    }
    
    [B]public void onDataReceived[/B](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();
        [B] mEmulatorView.invalidate();
         //mEmulatorView.postInvalidate();[/B]
       /* 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<?> parent, View view, int position,
            long id) {
        // TODO Auto-generated method stub
        changeSelectedAdapter(mDeviceAdapters.get(position));
    }
    
    @Override
    public void onNothingSelected(AdapterView<?> 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();
    }
    
    }
    


Comments

  • Registered Users Posts: 18,272 ✭✭✭✭Atomic Pineapple


    Have you tried invalidating your entire layout?


  • Moderators, Science, Health & Environment Moderators, Social & Fun Moderators, Society & Culture Moderators Posts: 60,092 Mod ✭✭✭✭Tar.Aldarion


    No I haven't, not sure how to go about that. I will try that now thanks.


  • Moderators, Science, Health & Environment Moderators, Social & Fun Moderators, Society & Culture Moderators Posts: 60,092 Mod ✭✭✭✭Tar.Aldarion


    Tried that now but no go, I called the layout MainLayout and changed the method to this:

    	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();
    		 View v = findViewById (R.id.MainLayout);
    		 v.invalidate();
    


  • Moderators, Science, Health & Environment Moderators, Social & Fun Moderators, Society & Culture Moderators Posts: 60,092 Mod ✭✭✭✭Tar.Aldarion


    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.


  • Moderators, Science, Health & Environment Moderators, Social & Fun Moderators, Society & Culture Moderators Posts: 60,092 Mod ✭✭✭✭Tar.Aldarion


    I tried handlers and it works:
    Handler viewHandler = new Handler();
    Runnable updateView = new Runnable(){
      @Override
      public void run(){
         mEmulatorView.invalidate();
         viewHandler.postDelayed(updateView, 10);
      }
    };
    
    then call viewHandler.post(updateView); in onDataReceived()

    This updates the screen every 10ms, and works, unless I bring the terminal into focus, it won't update until I hit send or hit the editText field, why is this?

    Also why did the handler work but calling it in the method itself does not? I've never used handlers really so I'm not sure.


  • Advertisement
Advertisement