UsbSerial SPI support

UsbSerial already supports most of the common USB to serial chips out there but these chips are pretty “serial standard” and as you probably know there are a lot of different serial architectures.

SPI is one of them. Developed by Motorola in the 80’s allows synchronous communication between one master devices and nth number of slaves devices.

A typical SPI configuration with one SPI master and three SPI slaves.

Someone requested support for CP2130 Usb to SPI bridge and, as always I like to emphasize I am always open to support new devices but this one is not as “serial generic” as the others (Windows even doesn’t see it as serial port). It is specifically designed to bridge USB to SPI. SPI interfaces presents different capabilities compared to a simple UART. Clock signal is provided by the master so greater speeds than using simple UARTS are possible and there are nth SSX lines to select different slaves.

Because of these characteristics a new interface specific for SPI was necessary for UsbSerial but I’ve tried to preserve the same philosophy of use that governs UsbSerial. Keep in mind that SPI support is still in beta state and this interface could change due to adding new devices.

  //Common SPI operations
  boolean connectSPI(); // connect SPI device
  void writeMOSI(byte[] buffer); // send data to the selected slave via MOSI. Asynchronous call
  void readMISO(int lengthBuffer); // read data sent from the selected slave via MISO.  Asynchronous call
  void writeRead(byte[] buffer, int lenghtRead); // write and read at the same time. Asynchronous call
  void setClock(int clockDivider); // set clock speed
  void selectSlave(int nSlave); // select slave
  void setMISOCallback(UsbMISOCallback misoCallback); // set MISO callback
  void closeSPI(); // close SPI device

  // Status information
  int getClockDivider(); // get clock divider
  int getSelectedSlave(); // get selected slave
  
  // Miso callback for received data
  interface UsbMISOCallback
  {
      int onReceivedData(byte[] data);
  }

  // CP2130 speed values
  CP2130SpiDevice.CLOCK_12MHz
  CP2130SpiDevice.CLOCK_6MHz
  CP2130SpiDevice.CLOCK_3MHz;
  CP2130SpiDevice.CLOCK_1_5MHz;
  CP2130SpiDevice.CLOCK_750KHz;
  CP2130SpiDevice.CLOCK_375KHz;
  CP2130SpiDevice.CLOCK_187_5KHz;
  CP2130SpiDevice.CLOCK_93_75KHz;

Previous examples of UsbSerial provides a good start keeping in mind the minor changes.

// Define MISO callback
private UsbSpiInterface.UsbMISOCallback misoCallback = new UsbSpiInterface.UsbMISOCallback()
{

  @Override
  public int onReceivedData(byte[] data) {
     // Your code here
  }
};

//...

// Setup SPI communication
UsbSpiDevice spi = UsbSpiDevice.createUsbSerialDevice(device, connection);
if (serialPort != null) {
  boolean ret = spi.connectSPI();
  if(ret){
    spi.selectSlave(0); //select slave 0
    spi.setClock(CP2130SpiDevice.CLOCK_3MHz);
    spi.setMISOCallback(misoCallback);
  }
}

// Write and read operations
spi.writeMOSI("Hola!".getBytes()); // Write "Hola!" to the selected slave through MOSI (MASTER OUTPUT SLAVE INPUT)
spi.readMISO(5); // Read 5 bytes from the MISO (MASTER INPUT SLAVE OUTPUT) line. Data will be received through UsbMISOCallback
spi.writeRead("Hola!".getBytes(), 15); // Write "Hola!" and read 15 bytes synchronously

//Close spi device
spi.closeSPI();

As I previously said, it is still in beta state and only CP2103 chipset is supported. I would really appreciate any feedback you could give me about this feature to further improvements.

Happy hacking!

Advertisement

Apps and projects using UsbSerial

When I started UsbSerial to fill my own needs I never really expected it would be used for other programmers to their own apps and project but eventually it happened! These are some of the projects where it is used besides DroidTerm and DroidTerm PRO. If you want your project listed here just contact me 🙂

Aprsdroid
APRSdroid is an Android application for Amateur Radio operators that allows to report your position to the APRS (Automatic Packet Reporting System). UsbSerial is used to connect TNC’s (Terminal node controllers) to the Android device. I am not sure if this feature is in release mode but there are interesting developments in the APRSDroid github.


Mouse4All
The guys from BLECentral have developed a project to allow people with severe movement limitations use an Android tablet. I personally saw it and it is an awesome example of how technology can enhance people’s life! UsbSerial is used to connect the special mouse to the tablet.

Akvo Caddisfly, the water quality testing kit
Akvo is a not-for-profit foundation that creates open source, internet and mobile software and sensors. One of their projects is Akvo Caddisfly, an Android application to test water quality in a very low cost way. UsbSerial is used to communicate with the sensors to test water’s conductivity. The app is open-source and its code is hosted on Github

Musethereal

MuseTheReal is an amazing project that shows how science, engineering and fashion can be put together to create something amazing. MuseTheReal showcases the relationship between music and consciousness. This project performs in the MakeFashion gala in Calgary, Canada. A gala where fashion and wearables is combined with fashion. The code is available on Github.

Check out this interview with Angie, Ksenia and the rest of the team!

A DIY Android Auto

This is a great example of what a hacker really is. Dennys from Ukraine is developing a DIY Android Auto for his 2009 Kia Ceed. A great DIY example.

A UsbSerial wrapper for Delphi

A UsbSerial wrapper for the Basic4Android development

DroidTerm PRO 1.2: Multi Character Encoding Support

I am happy to announce that DroidTerm PRO finally supports different character encodings which can be useful to connect with some legacy systems that still uses things like the CodePage 437 to extend ASCII. Current character encodings are:

– ASCII
– ISO 8859-1
– UTF-8 (set by default)
– UTF-16
– CP437

To change the character encoding, press the menu button in the upper right corner and select character encoding.
Screenshot_2015-09-24-18-39-35

There are tons of legacy encodings there so I probably will add some of them in next months.

More information about DroidTerm Pro and its free version
Happy crafting!

DroidTerm 7.1 and DroidTerm PRO 1.2: Usb Serial port terminal for Android

Finally new updates on DroidTerm! I managed to fix the first and foremost bug that was present on previous releases of DroidTerm, the unresponsive scroll when data was received. The text renderer is completely new and it offers and smooth scroll and keeps the app responsive.

After much thought I’ve decided to split DroidTerm into two apps, one free (with ads) that contains the same features, plus the scroll fix, than previous versions and other paid version with no ads and more features.

DroidTerm FREE
DroidTerm PRO

My intention is to keep the Free version stable as it is useful for the common user and add more complex features to the PRO version.
Try first the free sure to be sure it fits your needs, if you need the PRO features or you find DroidTerm useful and you want to help in its further development consider buying the PRO version.

If you prefer you can donate via Paypal tu support further improvements

DroidTerm PRO new features: VT100 Terminal emulator
DroidTerm PRO allows to send a subset of the ANSI control escape sequences. Designing a good interface to support this feature has been more complicated than I thought. Instead of relying purely on the Android keyboard the ‘ESC’ and the ‘[‘ button are check buttons that can be set to ON or OFF, the rest of the command must be written in a field. When the command ready, press the ‘Send’ button.

Screenshot_2015-09-10-22-51-07
ESC[2J escape sequence will erase the screen and move the cursor to home

Some systems echo back whatever you send but in some configurations need local echo. Local echo can be ON/OFF easily by the checkboxes below the the ANSI escape sequences.

DroidTerm Pro new Feature: Different character encodings

DroidTerm features explained in previous posts

USB Viewer
Logs and Hex viewer
Log viewer and Bulk transfer
Connection profiles
Profiles automatically create a default log file. LF CR and LF-CR End of line options added
CH340/CH341 supported, those cheap Arduino clones should be working now 🙂

Both versions use UsbSerial to handle serial port which is free, open source and can boost your projects too!

DroidTerm started as a little serial port terminal, it was buggy as hell in the first release with a very slow scroll and a lack of features but now it is unrecognizable! I started this because other options for serial ports were disappointing and I can state that right now is probably the best out there for Android, probably still not the PuTTy replacement for Android but who knows.. 🙂

Happy crafting!

DroidTerm v4.0 USB Serial port for Android

Last information about the current state of DroidTerm. Please check it out

Some weeks ago I uploaded a new version of DroidTerm with some improvements. One of these improvements was a log system to save your session. Well I made some changes that allow view your logs inside your app. Press “Log Viewer” and choose your log.
Screenshot_2014-08-28-21-18-33

I added a new way to send data. In prior versions users had to click on terminal and send one character at a time. This way is not gone but now it is possible to send more data at a time.
Just connect your device, start your session with your settings, press option button and select “Send bulk data”
Screenshot_2014-08-28-21-19-17

DroidTerm is getting more complete each month and I almost lack of missing features. If you are using it and you have a good idea I would love to hear it 🙂

Happy crafting!

Speeding up SQLite insertions for Android developers

SQLite is a very important asset of every Android programmer. It offers a really easy to use solution to cache all your data. As a local database, most of the time, it is not going to suffer of heavy insertions. But sometimes you are going to have to insert a lot of rows and, if you are not very well informed, you are going to discover in a painful way that the common way of insert data using Android api is astoundingly slow. I had to deal with that blow when programming DroidTerm because I needed a local database of USB vids and pids (Naively I started to look for REST api or something like that. Eventually I found something…that was merely a web hosted text file).

These examples are based on code I wrote working on this and code is available here.

Mainstream insertions
This is what you are going to find first time you have to populate a SQLite database in Android.

private SQLiteDatabase db;
/*
...............
*/
public long insertEntryVersion(String version, String date)
{
    ContentValues values = new ContentValues();
    values.put(VERSION_COLUMN, version);
    values.put(DATE_COLUMN, date);
    values.put(ID_VERSION, currentVersion);
    return db.insert(VERSION_TABLE, null, values);
}

It is an easy way using Bundle-like ContentValues class to store data per column and inserting through SQLiteDatabase.insert(String table, String nullHack, ContentValues values) without messing with your own SQL statement, nice huh? Well, most of the times I use this and works fine but if you are going to perform a heavy insertion you are going to discover that inserting…let´s say… thousands of rows can last for more than a minute or even more!

But it is not a big deal when you know what you need to speed up insert operations. Compiled statements, and even more important, SQL transactions are going to save the day.

Not all is lost
Compile your insert instructions as follows:

private SQLiteDatabase db;
/*...*/
private static final String INSERT_VENDOR = "insert into vendorTable(vid,vid_name) values(?,?)";
/*... */
insertVendor = db.compileStatement(INSERT_VENDOR);

Our SQL statement is now compiled so We are going to create a function to handle it.

public void insertEntryVendor(String vid, String vidName)
{
    insertVendor.bindString(1, vid);
    insertVendor.bindString(2, vidName);
    insertVendor.execute();
    insertVendor.clearBindings();
}

And it is all ready to insert all our data much faster!

String[] vidPid = new String[2];
db.beginTransaction();
while(vidPid != null)
{
    vidPid = vidPidGenerator(); // Returns null when no more vid and pids are available
    if(vidPid != null)
    {
        insertEntryVendor(vidPid[0], vidPid[1]);
    }
}
db.setTransactionSuccessful();
db.endTransaction();

It is that simple! I hope you found this little help useful 🙂

Happy crafting!

DroidTerm v3.0 USB Serial Port for Android

Last information about the current state of DroidTerm. Please check it out

I uploaded a new version of DroidTerm some days ago with some changes I would like to share with you. First of all, I decided to drop Bluetooth SPP (Serial Port Profile) support from DroidTerm. I did not consider it my first goal when I started to develop my app. I was struggling to find a great replacement in Android for Putty as a serial terminal so that became my first objective and, eventually, I decided to add support of Bluetooth SPP because it would be a not difficult task. Months later I spent hours refining the USB serial interface of DroidTerm adding devices, fixing bugs… so now It is pretty stable. Unfortunately I almost forgot completely about bluetooth. So it was not so stable at all and I have decide to focus on USB stuff.

Now the good things! You can generate logs of your sessions and keep them for further study. Saving a log of your session is easy:
– Connect your USB-Serial device and configure it as you want.
– Press options button whenever you want.
Screenshot_2014-08-14-17-19-24
– Select “Save Log” and your session will be saved in a folder called DroidTerm in your SD card. Files follow this name convention:

YYYY_MM_DD__HH__MM_SS.txt

The text files generated by DroidTerm save not only raw data (Text or Hex representation as We will see next) but metadata of session too.

#Date: 2014/08/06 13:58:19
#Device Name: FT232 USB-Serial (UART) IC
#Baud rate: 9600
#Data bits: 8
#Stop bits: 1
#Parity: None
#Flow: None

0x6C 0x6B 0x6B 0x6C 0x6B 0x6B 0x6C 0x6A 0x64 0x64 0x61 0x61 0x61 0x61 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x41 0x20 0x4D 0x65 0x20 0x6C 0x6C 0x61 0x6D 0x6F 0x20 0x46 0x65 0x6C 0x69 0x70 0x65 0x20 0x79 0x20 0x65 0x6F 0x73 0x20 0x66 0x69 0x66 0x6A 0x64 0x6E 0x20 

The next improvement, as you may noticed before, is a Hexadecimal representation of received data. I missed that really useful feature when you are debugging, or gaining understanding of a protocol and you need the raw data.

Here an example received from a scrollable LED with String representation:

Screenshot_2014-08-14-17-51-36

Obviously except first bytes information is not encoded at all. So Hex mode is necessary to study how this scrollable Led communicates with host:

Screenshot_2014-08-14-17-52-12

I have more improvements in mind but They will have to wait for a next release.

Happy craft!

Hunting down memory leaks in Android

One of the most common myths They told us about programming in a Garbage collector environment is there is no need to worry about memory management. Ok, that is usually right most of the time but it is not complete truth (in fact, memory leaks in GC environments are pretty difficult to spot). There are some potencial sources of memory leaks and some of them are not obvious and they can lead to a huge memory leakage.

A visual representation of an Android app with leaks

There is a well-known potential cause of memory leaks on Android which it is usually called “Leaking the Context” and it is a very problematic source of leaking memory because, as you know, an Activity (inherited from Context) is a potential god-object with a lot of references and it has to be destroyed and re-created every time you change the orientation of the screen.

Although it is a common issue is not usually well explained, or explained at all in Android tutorials or introductory books. There are some good links with amazing information about this issue but they usually lack of examples.

So I am going to take a “learning by example” approach to make you and extraordinary and merciless hunter of memory leaks.

Elmer Fudd, a pioneer of leak hunting and deeply misunderstood as almost all pioneers

The one you are going to see in every Memory leak example out there

This is the most common and easiest example of a memory leak you are going to find. First let´s take a look at this piece of code

public class MainActivity extends Activity
{
    static LeakMeNow leak; // static classes can be GC roots

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        if(leak == null)
        {
            leak = new LeakMeNow();
            leak.logSomethingLeaky();
        }
    }

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

    private class LeakMeNow
    {
        // This class holds a reference of Activity
        public void logSomethingLeaky()
        {
            Log.i("LeakExamples", "I know you are going to leak something here, just shake   your phone");
        }
   }
}

It looks pretty simple and unoffensive isn’t it? It is not so innocent, it hides a memory leak. LeakMeNow class is an inner class and all inner classes keep a reference of their enclosing classes. This leads to a memory leak if, as it is happening in this example, a static reference of this inner class has been declared. Fortunately this example is really simple to solve. We have two options.

Make the reference non-static:

LeakMeNow leak; // Non static class, safe now

If you really need a static reference and need access to variables from Activity, you have to make your inner class static and pass a WeakReference to your activity.

private static class LeakMeNow
{
    // It is not going to leak now
    private WeakReference upperClassReference;

    private LeakMeNow(MainActivity activity)
    {
        upperClassReference = new WeakReference(activity);
    }

    // This class holds a reference of Activity
    public void logSomethingLeaky()
    {
        Log.i("LeakExamples", "I know you are going to leak something here, just shake your  phone");
    }
}

Threads are dangerous too!

Threads are a potential cause of leaks too. Let’s see this code:

public class MainActivity extends Activity
{

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        new LeakyThread().start();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) 
    {
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    public class LeakyThread extends Thread
    {
        public void run()
        {
            synchronized(this)
            {
                try
                {
                    wait();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }
    }
}

Every time onCreate is called a new thread is going to be created and started. When orientation is changed those threads will remain and, consequently the Activity is not available for collection.

Best solution is being careful with your thread lifecycle and stop them when necessary. In this particular example is really easy:

public class MainActivity extends Activity
{
    private LeakyThread thread;

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_main);

       thread = new LeakyThread();
       thread.start();

    }

    @Override
    public void onDestroy()
    {
        super.onDestroy();
        thread.stopThread();
    }

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

    public class LeakyThread extends Thread
    {
        public void run()
        {
            synchronized(this)
            {
                try
                {
                    wait();
                } catch (InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        }

    public void stopThread()
    {
        synchronized(this)
        {
            notify();
        }
    }
  }
}

Basically you don’t have to create threads as anonymous classes and it is imperative to stop them before the destruction of the Activity.

A really subtle one (based on real events)

The third leak is going to be more difficult to spot. Although it involves a thread and We already know they are prone to problems, the thread involved here is not a subclass or an anonymous class of the Activity, it is completely separated of it. It means this thread should not be a GC root of the Activity (threads are potential candidates to be roots) but IT IS THE GC ROOT!!! Some code:

public class SemperVigilans 
{
	// This class, as techie new form of inquisition, it is going to check the incoming arrival of the end
	
	private ViewGroup viewGroup;
	private SemperVigilansThread thread;
	
	public SemperVigilans(ViewGroup viewGroup)
	{
		this.viewGroup = viewGroup;
		this.thread = new SemperVigilansThread();
		this.thread.start();
	}
	
	
	private class SemperVigilansThread extends Thread
	{
		
		public void run()
		{
			while(viewGroup.getWidth() != 666 && viewGroup.getHeight() != 666)
			{
				// World is save...at least for now
			}
			
			// Here a callback to warn the world about incoming apocalypse would be awesome
		}
	}

}

Our Activity:

public class MainActivity extends Activity
{
	private RelativeLayout layout;
	private SemperVigilans semperVigilans;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		LayoutInflater inflater = getLayoutInflater();
		layout = (RelativeLayout) inflater.inflate(R.layout.activity_main, null);
		semperVigilans = new SemperVigilans(layout);
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
	}
 
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
}

When this code is executed and orientation is changed you leak all the Activity but..Why? Well it is happening the same problem as before, but in a more subtle way. SemperVigilansThread is getting a reference from Activity through the Layout reference passed when SemperVigilans is created. Because Views objects have always a reference to the Context and in this case this reference reaches this thread. The solution is the same, control the lifecycle of your thread. If you remove all references to the Context but still your lifecycle is not controlled, you are still leaking but not the entire Activity.
View objects and Drawables objects are dangerous objects because both of them hold references to the Context. Drawables are even more dangerous because their references to their context are even less obvious Be careful with them.

Memory leaks are not exclusive of Activities

Leaking the context is probably the worse of all memory leaks you can get in Android but it is worth to mention that is not the only kind of leak you can get. As seen before, inner classes and anonymous classes are going to be naughty here:

public class LeakGenerator 
{
	// A leak generator
	
	public LeakGenerator()
	{
		
	}
	
	public Leak generateLeak()
	{
		return new Leak();
	}
	
	
	public class Leak
	{
		public Leak()
		{
			
		}
		
	}
}

MainActivity:

public class MainActivity extends Activity
{
	private Button firstTimeButton, everyTimeButton;
	private List<Leak> listOfLeaks;
	private LeakGenerator generator;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		
		firstTimeButton = (Button) findViewById(R.id.buttonfirsttime);
		everyTimeButton = (Button) findViewById(R.id.buttoneverytime);
		
		listOfLeaks = new ArrayList<Leak>();
		
		
		
		firstTimeButton.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				if(generator == null)
				{
				    generator = new LeakGenerator();
				}
				    
				listOfLeaks.add(generator.generateLeak());
			}
			
		});
		
		everyTimeButton.setOnClickListener(new View.OnClickListener()
		{
			@Override
			public void onClick(View v)
			{
				listOfLeaks.add(new LeakGenerator().generateLeak());
			}
			
		});
		
		
	}
	
	@Override
	public void onDestroy()
	{
		super.onDestroy();
	}
 
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
}

This example is really easy, two buttons, one of them leaks one LeakGenerator() the first time you pressed and the other leaks a new LeakFactory() when pressed.

The first button only leaks the reference to LeakGenerator(). Even if the generator is not going to be use anymore it is going to live because every single leak reference has a reference to the generator. We can infer from this example that Objects from inner classes can’t outlive the outer class object which they hold a reference

The second button is a worse thing because every Leak object has a reference to a different generator.

That is all I have to say about this ugly issue I know you will face as an Android developer, it is better to be prepared before hunt them down.

Here some great essential links about Memory leaks:
Google I/O 2011: Memory management for Android Apps

Activities, Threads, & Memory Leaks

I hope you find useful this post. If you have some suggestions or you found some bugs just let me now 🙂

Happy Craft!

DroidTerm: A serial port terminal emulator for Android

Last information about the current state of DroidTerm. Please check it out

During my most recent work I had to deal a lot with serial ports and Usb to serial converters. Most of the work used an Android device as a host of a usb-serial converter to send commands through a serial connection to a custom hardware we developed. I certainly missed a good replacement of PuTTY for Android. Some apps I encountered are faulty or does not support the converters I am using for. That is the reason because I started to develop my own replacement.
Finally I shipped the first version of this app that I called DroidTerm.
Here it is the link if you are interested

Features:
– Allows serial connections over Bluetooth Serial Port Profile and Usb.
– It supports FTDI chipsets (nice for Arduino stuff), CP210x family of chipsets and soon Prolific pl2303 chipsets. Not only defaults VID and PID, it supports custom VID and PIDS of other manufacturers too.
– In Usb serial connection, baud rate, data bits, stop bits, parity can be configurable before connection. Flow control is still not supported.

There is plenty of room for new features and improvements:
– Flow control and almost more importantly, a interface to handle it manually.
– Send through serial port a selected filed from a file explorer.
– Macros (Implement a simple BASIC-like language and a simple interface to code on mobile devices would be awesome).
– Add more not supported devices, although that must be done on Usb Serial android library
– Design improvement, that is not my best area so do not expect something much better than the retro style it has now.

If you use it, you find it useful and you have some new ideas or improvements I would love to hear them.

Happy craft!

UPDATE: Devices with PL2303 chipsets are now supported. I would like some feedback about this new feature 🙂
https://lh3.ggpht.com/Ms-VlbxWea5n3yUDqsx1ZZcjswxA-cpR4xC35XcYOszlszUUfXvDq8tkozQ9vKihXvOt=h900-rw

https://lh5.ggpht.com/RSMTKtvYHBNPkMuwiGlRju8iCjq8JW8VQvOVphDYf0gsa-nu7Vk8RNCbXaMqhDGkfA-J=h900-rw

Catch soft keyboard show/hidden events in Android

Android offers you an overwhelming api loaded with tons of overridable functions representing typical events you may face as a mobile developer. That is the reason because you are not usually worry about catching and handling a new event that you have never meet before, you guess, normally right, those smart kids from Mountain View implemented an event handler sometime ago. Well, this is not the case of soft keyboard. It can be easily show and hide with convenient methods but there is not an implemented way of catching when user choose to hide it. Due to this limitations I coded a simple snippet that I hope it will be useful. It is available here. It is pretty easy to use.

/*
Somewhere else in your code
*/
RelativeLayout mainLayout = findViewById(R.layout.main_layout); // You must use your root layout
InputMethodManager im = (InputMethodManager) getSystemService(Service.INPUT_METHOD_SERVICE);
	
/*
Instantiate and pass a callback
*/
SoftKeyboard softKeyboard;
softKeyboard = new SoftKeyboard(mainLayout, im);
softKeyboard.setSoftKeyboardCallback(new SoftKeyboard.SoftKeyboardChanged()
{
 
	@Override
	public void onSoftKeyboardHide() 
	{
		// Code here
	}
 
	@Override
	public void onSoftKeyboardShow() 
	{
		// Code here
	}	
});
	
/*
Open or close the soft keyboard programatically
*/
softKeyboard.openSoftKeyboard();
softKeyboard.closeSoftKeyboard();

/*
SoftKeyboard can catch keyboard events when an EditText gains focus and keyboard appears
*/

/* Prevent memory leaks:
*/
@Override
public void onDestroy()
{
    super.onDestroy();
    softKeyboard.unRegisterSoftKeyboardCallback();
}

Besides this, you must add this line to your activity reference in your manifest.xml.

android:windowSoftInputMode="adjustResize"

While I were coding this snippet I encountered with another snipper which is pretty awesome. It is basically the same, involves subclassing your main layout but I think is a very elegant solution. Use which solution fits better with your needs. Happy coding!

UPDATE (06/15/14): This snippet has been updated with some improvements but It can be used in the same straightforward way and it handles keyboard events from EditText too
This snippet was too specific and it could not catch open keyboard events from EditText, it only worked if you opened and closed keyboard with provided functions and EditText opens keyboard as default behavior. Obviously this was a heavy shortcoming I did not notice before. So here it is my fix: The constructor of SoftKeyboard has been changed from:

public SoftKeyboard(View layout, InputMethodManager im)

to:

public SoftKeyboard(ViewGroup layout, InputMethodManager im)

This means it is necessary to pass a reference of main layout now. Maybe it is more strict now but it is necessary for something we are going to see next and, really, it was not working with whatever view.

SoftKeyboard class, when instantiated, it is going to try to get a reference, if possible, of every EditText available. This will allow it to handle focus changes when a EditText is selected and keyboard appears.

private void initEditTexts()
{
    editTextList = new ArrayList<EditText>();
    int childCount = layout.getChildCount();
    for(int i=0;i<=childCount -1;i++)
    {
        View v = layout.getChildAt(i);
        if(v instanceof EditText)
	    {
	       EditText editText = (EditText) v;
	       editText.setOnFocusChangeListener(this);
	       editText.setCursorVisible(false);
	       editTextList.add(editText);
	    }
    }
}

SoftKeyboard implements the interface View.OnFocusChangeListener to handle properly focus changes on our EditTexts.

@Override
public void onFocusChange(View v, boolean hasFocus) 
{   
    if(hasFocus && !isKeyboardShow)
    {
        layoutBottom = getLayoutCoordinates();
        softKeyboardThread.keyboardOpened();
        isKeyboardShow = true;
        tempView = v;
    }
}

I noticed the resize process when keyboard appears through an EditText gaining focus has some differences and it was breaking the logic of the thread which is checking the bottom position of the layout. So i added this silly loop to handle it.

// When keyboard is opened from EditText, initial bottom location is greater than layoutBottom
// and at some moment equals layoutBottom.
// That broke the previous logic, so I added this new loop to handle this.
while(currentBottomLocation >= layoutBottom)
{
    currentBottomLocation = getLayoutCoordinates();
}

There are some minor changes too but I think these are the most important changes. I have tested it but nothing is 100% bugs-free. I would love read your feedback about this modification.

UPDATE (10/07/14): Thanks to Francesco Verheye(verheye.francesco@gmail.com) this snippet can handle EditTexts attached to nested subViews. initEditText modifications:

private void initEditTexts(ViewGroup viewgroup) 
{
    if(editTextList == null)
        editTextList = new ArrayList<EditText>();
		
    int childCount = viewgroup.getChildCount();
    for(int i=0; i<= childCount-1;i++) 
    {
        View v = viewgroup.getChildAt(i);

        if(v instanceof ViewGroup) 
        {
	    initEditTexts((ViewGroup) v);
	}

        if(v instanceof EditText) 
        {
            EditText editText = (EditText) v;
            editText.setOnFocusChangeListener(this);
            //editText.setCursorVisible(false);
            editTextList.add(editText);
        }
    }
}