Tag Archive: Android


Android applications are bundled and distributed as apk(s), aka Android application package.To make an APK file, a program for Android is first compiled, and then all of its parts are packaged into one file. An APK file contains all of that program’s code (such as .dex files), resources, assets, certificates, and manifest file. As is the case with many file formats, APK files can have any name needed, provided that the file name ends in “.apk”.

Apk Contents:

An APK file is an archive that usually contains the following files and directories:

  • META-INF directory:
    • MANIFEST.MF: the Manifest file
    • CERT.RSA: The certificate of the application.
    • CERT.SF: The list of resources and SHA-1 digest of the corresponding lines in the MANIFEST.MF file; for example:
 Signature-Version: 1.0
 Created-By: 1.0 (Android)
 SHA1-Digest-Manifest: wxqnEAI0UA5nO5QJ8CGMwjkGGWE=
 ...
 Name: res/layout/exchange_component_back_bottom.xml
 SHA1-Digest: eACjMjESj7Zkf0cBFTZ0nqWrt7w=
 ...
 Name: res/drawable-hdpi/icon.png
 SHA1-Digest: DGEqylP8W0n0iV/ZzBx3MW0WGCA=
  • lib: the directory containing the compiled code that is specific to a software layer of a processor, the directory is split into more directories within it:
    • armeabi: compiled code for all ARM based processors only
    • armeabi-v7a: compiled code for all ARMv7 and above based processors only
    • arm64-v8a: compiled code for all ARMv8 arm64 and above based processors only[7][8]
    • x86: compiled code for x86 processors only
    • x86_64: compiled code for x86 64 processors only
    • mips: compiled code for MIPS processors only
  • res: the directory containing resources not compiled into resources.arsc (see below).
  • assets: a directory containing applications assets, which can be retrieved by AssetManager.
  • AndroidManifest.xml: An additional Android manifest file, describing the name, version, access rights, referenced library files for the application. This file may be in Android binary XML that can be converted into human-readable plaintext XML with tools such as AXMLPrinter2, android-apktool, or Androguard.
  • classes.dex: The classes compiled in the dex file format understandable by the Dalvik virtual machine
  • resources.arsc: a file containing precompiled resources, such as binary XML for example.

Decompilation process:

Our prerequisite would be these 3 tools:

  • dex2jar: Used to convert the apk to jar file. Can be downloaded from here.

  • JD-GUI: Used to view the contents/source from the jar file decompiled in previous step. Details are here.

  • apktool: For reverse engineering the apk to extract files and folders. This can be used to extract the manifest individually and then reading from it. It is available here for download.

dex2jar and JD-GUI are used together. dex2jar converts apk to jar file and JD-GUI provides the editor to browse that jar file. To use dex2jar:

  1. Download dex2jar from here and extract it to a separate folder.
  2.  Execute the following command to decompile an apk:

    sh d2j-dex2jar.sh testapp.apk

  3. It might happen that terminal might show you a permissions error related to d2j_invoke.sh while executing step 2, if that happens then provide d2j_invoke.sh with appropriate permissions by executing:

    sudo chmod +x d2j_invoke.sh

  4. Post above steps, testapp.jar should be generated which can be opened and browsed via JD-GUI. This file contains all the decompiled code(.class files)

screenshot17

We are already able to browse the source code using dex2jar and JD-GUI, however, another important tool in the arsenal is apktool, which is a tool for reverse engineering 3rd party, closed, binary Android apps. It can decode resources to nearly original form and rebuild them after making some modifications. It also makes working with an app easier because of the project like file structure and automation of some repetitive tasks like building apk, etc. Apps/Apks. Decoding of an apk via apktool can be done by using following command:

apktool d test.apk

Repackaging as an apk can be done by:

apktool b test

JD-GUI, d2j and apktool is the essential tooling required to get an effective and deep insight into 3rd party apps which often exist as black boxes. As shown above, the usage is simple and pretty straight forward. I would request you to share your inputs and experiences in comments with these tools or any others that you might have explored for decoding Android or any other platform apps.

 

Advertisements

Android applications are bundled and installed as android application package(apk) on an Android device. More often then not the stakeholders of an application require confidence that the application that is being developed is secure and does not pose an unacceptable level of risk. Drozer is just the tool for that.

Warning: This is going to be a comprehensive, long and fairly technical post. Once again, nothing is left to reader’s/user’s imagination.

Drozer is a security audit and attack framework for Android which works by allowing you to interact with the Dalvik VM, other apps’ IPC endpoints and the underlying OS. Drozer provides tools to help you use and share public exploits for Android. For remote exploits, it can generate shellcode to help you to deploy the drozer Agent as a remote administrator tool, with maximum leverage on the device.

Now let us go ahead and configure drozer. We will be using a Mac OSX based System, however the instructions are more or less aligned for a Windows based system too. Some prerequisites for configuring dozer are:

  1. Java Development Kit (JDK) 1.6 – (very important! See configuration of Java 6 below for reason)
  2. Python 2.7.
  3. Android SDK
  4. You should ensure that each of these tools are on your path: adb, java

Let us move from easiest to the most difficult prerequisite configuration:

Configuring adb:

On Mac this can be done via homebrew by executing the following at the terminal:

Configuring Java 6:

It is very important that Java 1.6 is installed and used. This is because Android bytecode is only compliant to version 1.6 and not higher versions. Making use of any version of javac other than 1.6 will result in errors during compilation that look similar to the following:

bad class file magic (cafebabe) or version (0033.0000)

You can download legacy Java6 for OSX from here:

https://support.apple.com/kb/DL1572?locale=en_US

Once you have downloaded and installed it. It is necessary to make sure that javac -version returns 1.6.*  on the terminal. See this link for information on how java_home is setup on Mac OSX. If Java 6 was correctly installed at /Library/Java/, then it can be made primary by using the following command at the terminal:

export JAVA_HOME=/Library/Java/JavaVirtualMachines/1.6.0.jdk/Contents/Home/

All the important configurations are done. Python is installed by default on Mac OSX and is accessible via the terminal.

Installing Drozer:

Once the configurations are done, we can now clone drozer directly form git itself. All commands will be run on the terminal.

  1. At appropriate location(a separate folder preferably), clone the drozer repo:

 git clone https://github.com/mwrlabs/drozer/

  1. We will setup drozer from the repo directly. Once the repo is cloned, go to the root of the repo by: cd drozer
  2.  Setup the PYTHONPATH to include/find the drozer:

    export PYTHONPATH=/path/to/drozer/src/:$PYTHONPATH

  3. Now you can run:

    python setup.py build

  4. After setup.py finishes, if you type drozer in the terminal then the command should be executed successfully.

Hacking with Drozer:

Before you start hacking, we will need an Android device(although the steps are valid for emulator too). Also, we will need to install drozer agent and sieve apk(s) on the device. The purpose of these: sieve is a vulnerable application that we would be testing. Agent app is the app which is our gateway into the dalvik vm of the android device we are connected to.The Agent opens a ServerSocket, on port 31415 by default, and awaits incoming connections and commands via those connections.

Both the apps are available here: https://labs.mwrinfosecurity.com/tools/drozer

To connect to drozer agent app:

  1. Connect the android device to the computer via usb.
  2. Launch the agent app on the android device.
  3. Push the “on” button on the app screen. This will make it listen for incoming tcp connections on port 31415
  4. On the terminal type: adb forward tcp:31415 tcp:31415
  5. And after that:

    drozer console connect

    This should launch drozer console:

drozer_screen

 As soon as this happens, we can say safely that we are connected to the android device. The screen of the drozer app should look like the one below and the connectivity indicator fir embedded server should have turned green:

drozer_green

The drozer Console is a command line environment, which should be familiar to anybody who has used a bash shell or Windows terminal. Now we can go ahead and assess sieve.

The first step in assessing Sieve is to find it on the Android device. Apps installed on an Android device are uniquely identified by their ‘package name’. We can use the `app.package.list` command to find the identifier for Sieve:

dz> run app.package.list -f sieve

com.mwr.example.sieve

We can ask drozer to provide some basic information about the package using the `app.package.info` command:

dz> run app.package.info -a com.mwr.example.sieve

Package: com.mwr.example.sieve

Process Name: com.mwr.example.sieve

Version: 1.0

Data Directory: /data/data/com.mwr.example.sieve

APK Path: /data/app/com.mwr.example.sieve-2.apk

UID: 10056

GID: [1028, 1015, 3003]

Shared Libraries: null

Shared User ID: null

Uses Permissions:

– android.permission.READ_EXTERNAL_STORAGE

– android.permission.WRITE_EXTERNAL_STORAGE

– android.permission.INTERNET

Defines Permissions:

– com.mwr.example.sieve.READ_KEYS

– com.mwr.example.sieve.WRITE_KEYS

This shows us a number of details about the app, including the version, where the app keeps its data on the device, where it is installed and a number of details about the permissions allowed to the app.

We can ask drozer to report on Sieve’s attack surface:

dz> run app.package.attacksurface com.mwr.example.sieve

Attack Surface:

  3 activities exported

  0 broadcast receivers exported

  2 content providers exported

  2 services exported

    is debuggable

We can drill deeper into this attack surface by using some more specific commands. For instance, we can ask which activities are exported by Sieve:

dz> run app.activity.info -a com.mwr.example.sieve

Package: com.mwr.example.sieve

  com.mwr.example.sieve.FileSelectActivity

  com.mwr.example.sieve.MainLoginActivity

  com.mwr.example.sieve.PWList

The PWList activity is exported and does not require any permission, we can ask drozer to launch it:

dz> run app.activity.start –component

com.mwr.example.sieve com.mwr.example.sieve.PWList

This formulates an appropriate Intent in the background, and delivers it to the system through the `startActivity` call. Sure enough, we have successfully bypassed the authorization and are presented with a list of the user’s credentials:

screen-shot-2017-01-15-at-21-26-58

Next we can gather some more information about the content providers exported by the app. Once again we have a simple command available to request additional information:

dz> run app.provider.info -a com.mwr.example.sieve

Package: com.mwr.example.sieve

  Authority: com.mwr.example.sieve.DBContentProvider

    Read Permission: null

    Write Permission: null

    Content Provider: com.mwr.example.sieve.DBContentProvider

    Multiprocess Allowed: True

    Grant Uri Permissions: False

    Path Permissions:

     Path: /Keys

       Type: PATTERN_LITERAL

       Read Permission: com.mwr.example.sieve.READ_KEYS

       Write Permission: com.mwr.example.sieve.WRITE_KEYS

  Authority: com.mwr.example.sieve.FileBackupProvider

    Read Permission: null

   Write Permission: null

   Content Provider: com.mwr.example.sieve.FileBackupProvider

   Multiprocess Allowed: True

   Grant Uri Permissions: False

 

This shows the two exported content providers that the attack surface alluded to in Section 3.3. It confirms that these content providers do not require any particular permission to interact with them, except for the /Keys path in the DBContentProvider.

Also, We identified that Sieve exported two services. As with activities and content providers, we can ask for a little more detail:

dz> run app.service.info -a com.mwr.example.sieve

Package: com.mwr.example.sieve

  com.mwr.example.sieve.AuthService

    Permission: null

  com.mwr.example.sieve.CryptoService

    Permission: null

Once again, these services are exported to all other apps, with no permission required to access them.

And that is it. We did configure, install and scratched the surface of an app using dozer. There is much more to dozer which we would be covering in subsequent posts. I hope this was useful and informative

Security is now a prime concern when writing the apps for handheld devices, specifically on an Android based system where a root user can gain access to almost everything which is there or was there on the device(Now please reimagine listing your device on craiglist for a sale :)).

A quick refresher, Mobile Devices provide the following mechanisms and storages to securely store information(Since username and passwords are the most stored ones, we would be specifically covering and referring those):

The following Storages are available:

  • Sqlite
  • Shared Preferences
  • In Code
  • Keystore/Keychain storage

The following mechanisms are most followed(In the order of least to most recommended):

  • Store as cleartext in Sqlite/Shared preferences
  • Store encrypted using a symmetric key
  • Using Android Key Store
  • Store encrypted using asymmetric key

Cleartext storage is least recommended as there is no protection of user’s data. Anyone can extract the prefs/sqlite file using the adb backup command. There can be argument to set android:allowBackup flag to false in AndroidManifest.xml, but since Android is Linux based even then root can have access to all files.

A much better idea is to encrypt the data before it is stored. However, in this case it is very important that the encryption key is not stored in the code itself as the code can be decompiled.

Symmetric vs Asymmetric Encryption:

Symmetric encryption uses a single key to encrypt and decrypt data. Asymmetric encryption uses 2 keys, a public key(for encryption) and private key(for decryption). Asymmetric encryption is a better option due to the involvement of 2 keys. Apps, especially critical ones(Medical/Banking) at no point of time should have the password visible on the phone. A use case can be: User enters the password on the mobile device, it is encrypted using the public key and sent to the server where the decryption happens using the private key(private key being stored on server). After the password is verified a token is sent back to the app to allow the user to login. This flow is essentially important for HIPAA compliant apps where a quick check for one of the HIPAA regulations is to put your device in flight mode and check if the user can still login. If the user can still login then probably the app is not HIPAA compliant.

Symmetric VS Asymmetric Algorithms

Symmetric VS Asymmetric Algorithms

 

On the apps which do not connect to a backend but yet require encrypted information to be stored, Android Keystore is a good place to store the public and private keys.

 

 

 

 

  • X items on the visible list means x items inflated/created means x times getView() gets called with convertView as null.
  • Items in recycler at this time: 0
  • User scrolls up.
  • One more time getView() gets called, 2 things happen.
  • getView() gets called with convertView as null. getView() inflates and returns the view that just became visible.
  • The view that just disappeared gets dumped in the recycler.
  • Total views : number of views on screen+1 in recycler.
  • User scrolls further, View recycling kicks in, convertView is no longer null.

listview_recycler

This post outlines the decryption of the popular Whatsapp Crypt8 database file. To accomplish this we will need a few tools:

  • PC with either MAC or Windows installed. For this tutorial, I executed the steps on a Windows 7 system.
  • Cygwin(For Windows based system, With Mac you probably won’t need anything else).
  • Basic knowing of hexdump, openssl, gzip.
  • A rooted Android device.

Whatsapp stores the decryption key at location data/data/com.whatsapp/files/key on the phone. Extract this.

Whatsapp periodically backs up data on the SD-Card at sdcard/Whatsapp/Databases/msgstore.db.crypt8. Extract that.

Now, fire up the cygwin shell and run the following commands in the order mentioned below:

Firstly, extract the aes and the initialization vector,

#hexdump -e '2/1 "%02x"' key | cut -b 253-316 > aes.txt
#hexdump -e '2/1 "%02x"' key | cut -b 221-252 > iv.txt

Now strip down the 67 bytes header
#dd if=msgstore.db.crypt8 of=msgstore.db.crypt8.nohdr ibs=67 skip=1

Decrypt and convert to gzip
#openssl enc -aes-256-cbc -d -nosalt -nopad -bufsize 16384 -in msgstore.db.crypt8.nohdr -K aes.txt -iv iv.txt > msgstore.gz

Extract from Gzip
#gzip -cdq msgstore.gz > msgstore.db

And you are done. You can view the file now in Sqlite Browser.

Custom Android, Custom USB

I have been working with a custom Android device for sometime now. This is a decently powerful 2.3.4 device(It is not a phone). It has USB capabilities too, but for some arcane reasons the vendor does not want to support the USB accessory api and hence one has to rely on native linux to handle the USB drive. The vendor’s app runs as a System app and needs to have the RW access to the USB. 

Image

We were easily able to map the USB to mnt/udisk by having a quick look at the vol.fstab, which showed:

dev_mount usb /mnt/udisk auto /devices/platform/ehci-omap.0/usb1/

Since auto-mount was already enabled so it was not much of a hassle and things looked Vanilla and easy. But, Alas, everything went well until the code responsible for R/W was deployed. The Java code simply refused to acknowledge that the USB was plugged in and had data in it. Other apps, eg: Eclipse File Explorer, And Explorer, shell(OK, let’s leave this one out as it has root privileges), were easily able to acknowledge the existence of the USB. Now, what could have gone wrong with one small piece of File R/W Java code. The permissions looked pretty much Ok(other apps were already accessing the drive).

Few hours and 3 cups of coffee later, we had an aha moment when we released that the app was running as a System App and not a normal one. Putting the theory to test we ran the app as a normal one i.e. we immediately updated the debug.keystore to use the default one plus the manifest and yes everything was sunny again.

Image

Hmmm….The issue at hand was solved now and the Vendor has been notified about the glitch, but This makes us think about the Android Security mechanisms. There should be a way to make system apps access USB but I haven’t found the answer to that yet. Will update as soon as I stumble on something.

Android Preference Framework is among st the mostly widely used building blocks of Android. Almost every Android application makes use of the Preferences file in some way or the other and our app is no different.

Our app uses the Android Preferences to store some general keys like,

  • Hardware Make
  • Hardware Model
  • Software Version
  • Date
  • Time
  • Connection State

All the above mentioned values are being retrieved from a sensor and are stored by the App in the preferences, to be displayed to the User in a User Friendly Manner.  However, we ran into a weird problem which had its roots in the Android Preferences during one of our test-runs.

Superficially speaking about the problem, whenever the user tried to retrieve the above mentioned data from the Sensor, the process seemingly happened fine, but whenever the app was restarted after any such scenario, all of the previously stored data in the Preferences file was lost, i.e., the App behaved as a freshly installed app and being launched for the first time! We tried various lenses to view this problem, our most prominent lens(wild guess actually) being that somehow something was overwriting the preferences file to default settings. Another programmer also suggested that “Somehow the Preference object was being null“. We tried to be nice and please everybody by incorporating their hypothesis and applying the suggested fixes and ended up wasting like 2 hours over this so called conventional wisdom!

After blaming the Preference Framework for those 2 hours and interrogating the data associated with it, we could not extract anything meaningful out of it except the OnSharedPreferenceChangeListener. So we decided to find somebody else to blame for the mess being created and turned our attention to the Sensor framework(as the bug always popped out whenever the Sensor data was retrieved). After tracking the changes in the preferences via OnSharedPreferenceChangeListener, it became clear that the Sensor Framework was not only retrieving the data, but was also storing like 10 of those values in the Preferences in a rapid succession and the code doing this looked something like,

updatePreferences(key1, value1);

updatePreferences(key2, value2);

………..

updatePreferences(key10, value10);

As soon as this code was commented, the app behaved normally :-).

This made us realize that it was not about Android Preferences but about the manner in which we were updating those and hence we derived a few cardinal rules about the same,

  • Shared Preferences are NOT designed for storing data which changes often and/or large amounts of data.
  • Shared Preferences are just a file – it has to be opened, parsed, updated and closed every time we do an update operation on it. Doing update operation in a rapid succession might just corrupt the file and restore it to default values which was our case exactly.
  • Do not access Shared Preferences from multiple threads.
  • ‘Conventional wisdom’ can be wrong.

Happy Coding!

Android ScrollView Trick

Recently I had a problem with one of my layouts, where I was trying to put a variable length content in a ScrollView. All I wanted was to keep the height of the ScrollView fixed, inspite of the length of the content. I was trying to avoid hardcoding the height parameter due to the obvious choice of multitude of the devices I wanted to cover with my layout(shown in figure below).
My initial attempts with the ScrollView(shown with a green highlight), were about messing with(a combination of) the height/width/fillViewPort parameters, but nothing helped. Since the content inside the ScrollView was getting rendered dynamically, the ScrollView used to extend and occupy more than the top-half of the screen and the price was payed by the controls contained in the bottom LinearLayout which just dissappeared. This was fixed by wrapping the bottom LinearLayout in a ScrollView(shown below) and weirdly, now the top ScrollView always rendered the dynamic content within the top half boundries, which was what I initially intended.

Android, Life of a ListView

The Listview in android could need a little getting used to if you are a newbie. I first met the listview while looking to design a custom list of items, which exhibits a unique behaviour classified into 2 categories,

1. All of items in the list would have a radiobutton and would be uniquely selectable.

2. A specific set of items would have an edit button(along with the radiobutton) & the item title would become editable if the edit button is clicked.

All in all I wanted my layout to look like,

list1

Above Image is a collection of those items which dos not have an edit button. Other set of that this list contains would look like,

list2

On selecting a particular item and then pressing the button, user would be greeted with an EditText Dialog as shown below,

list3

Let’s see step by step that how one should go about achieving this functionality

The primary requirement of the list in question is a listview. We can define a simple ListView in a listlayout.xml file,

<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:choiceMode="singleChoice"
android:id="@+id/triggerList"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:cacheColorHint="#00000000"
/>

Now the listview contains several row items, so to accomplish this we would be needing a row template which would define the layout of each row item. This can be defined in a file listrowtemplate.xml(Both the xml files will go inside the layout folder),

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
	android:layout_width="fill_parent" android:layout_height="wrap_content"
	android:padding="5dip" android:orientation="horizontal"
	android:gravity="center_vertical">
	<RadioButton android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:id="@+id/btn_radio" />

	<TextView android:id="@+id/triggerLabel" android:layout_width="fill_parent"
		android:layout_height="wrap_content" style="@style/BodyTextBigGrey"
		android:padding="5dip" android:gravity="center_horizontal|center_vertical"
		android:textSize="20dip" android:layout_weight="1" />
	<Button android:id="@+id/buttonEdit" android:layout_width="wrap_content"
		android:layout_height="wrap_content" android:text="Edit"
		android:layout_gravity="right" />
</LinearLayout>

Now,To take this further to the next level, One must know that a ListView exhibits an arcane property called as View Recycling i.e. The rows that are not getting displayed are recycled to save memory. Although sounding simple, this can sometimes become a bit dicey especially for custom lists(the one like ours) as we will need to maintain the state of our selected radio button if it undergoes the recycling process.

There can be numerous approaches to tackle this problem. We discuss one such approach here. We will need a model to main tain the current state of our rows & this is where the developer meets the Trigger class(Shakespere said, What’s in a Name?). The Trigger class is our model to maintain the state of various trigger rows added to our list. This is what the trigger class looks like,(Please mind that I’m posting the development code here and not the release one so one can always encounter a few trivial redundancies),

public class Trigger {

    private String triggerName;
    private boolean triggerStatus;
    protected TextView text;
    protected RadioButton radiobutton;
    protected Button button;

    public String getTriggerName() {
        return triggerName;
    }
    public void setTriggerName(String triggerName) {
        this.triggerName = triggerName;
    }
    public boolean getStatus() {
        return triggerStatus;
    }
    public void setStatus(boolean triggerStatus) {
        this.triggerStatus = triggerStatus;
    }
}

Now, Our ListView can maintain an ArrayList of these Triggers and we can also take care of the View Recycling process using the various getters and setters defined in our Trigger Class.
The bond between the ListView and its Data(Trigger items) is forged by something called as an Adapter. We would be needing a custom adapter class, to override its its getView method which has a signature like public View getView(final int position, View convertView, ViewGroup parent). Doing this would give us a fine control over the view recycling process because if a view has been recycled then the android system call the getView with convertView as null and this is how we would come to know that we need to reinstantiate that view and also restore it to its previous state i.e. as it was before recycling. Considering this, below is the implementation of our Trigger Adapter,

public class TriggerAdapter extends ArrayAdapter<Trigger> {

	private ArrayList<Trigger> items = null;
	private Context context = null;
	private String tracker = null;
	private ArrayList<Boolean> itemChecked = new ArrayList<Boolean>();

	public TriggerAdapter(Context context, int textViewResourceId,
			ArrayList<Trigger> items) {
		super(context, textViewResourceId, items);
		this.context = context;
		this.items = items;
		for (int i = 0; i < this.items.size(); i++) {
			itemChecked.add(i, false); // initializes all items value with false
		}
	}

public View getView(final int position, View convertView, ViewGroup parent) {
View v = convertView;
if (v == null) {
	LayoutInflater vi = (LayoutInflater) context
	.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
	v = vi.inflate(R.layout.listrowtemplate, null);
}
final Trigger trigger = items.get(position);
if (trigger != null) {
	trigger.text = (TextView) v.findViewById(R.id.triggerLabel);
	trigger.radiobutton = (RadioButton) v.findViewById(R.id.btn_radio);
	trigger.button = (Button) v.findViewById(R.id.buttonEdit);
	if (trigger.text != null) {

		trigger.text.setText(trigger.getTriggerName());
	}
	if (trigger.button != null) {
		if (position > 10) {
			trigger.button.setVisibility(View.VISIBLE);
		} else {
			trigger.button.setVisibility(View.INVISIBLE);
		}
	trigger.button.setOnClickListener(new OnClickListener() {
		public void onClick(View v) {
			final Trigger trigger = items.get(position);
			if (!trigger.getStatus()) {
				return;
			}
			AlertDialog.Builder alert = new AlertDialog.Builder(
					context);
			alert.setTitle("Title");
			alert.setMessage("Message");
			// Set an EditText view to get user input
			final EditText input = new EditText(context);
			alert.setView(input);
			alert.setPositiveButton("Ok",
					new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog,
						int whichButton) {
					String value = input.getText()
					.toString();
					// Do something with value!
					final Trigger trigger = items
					.get(position);
					trigger.setTriggerName(value);
					trigger.text.setText(value);
					items.remove(position);
					items.add(position, trigger);
				}
			});
			alert.setNegativeButton("Cancel",
					new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog,
						int whichButton) {
					// Canceled.
				}
			});

			alert.show();
			}
		});
	}
	if (trigger.radiobutton != null) {
		trigger.radiobutton.setChecked(trigger.getStatus());
		trigger.radiobutton.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
		     for (int i = 0; i < items.size(); i++) {
			final Trigger trig = items.get(i);
			if (trig.getStatus()) {
			trig.setStatus(false);
			trig.radiobutton.toggle();
			trig.radiobutton.invalidate();
			ListView list = (ListView) (((LinearLayout) v
						.getParent()).getParent());
			for (int j = 0; j < list.getChildCount(); j++) {
			LinearLayout l = (LinearLayout) list
			.getChildAt(j);
			RadioButton rd = (RadioButton) l
			.getChildAt(0);
			if (rd.isChecked()) {
				rd.setChecked(false);
			}
			}
			}
		}
				((RadioButton) v).setChecked(true);
				final Trigger trigger = items.get(position);
				trigger.setStatus(true);
				trigger.radiobutton.setChecked(true);
				items.remove(position);
				items.add(position, trigger);
			}
		});
	}
}
return v;

}

All in all, our list is now complete and we just need an activity with a button to display it(It actually depends on your choice how you want to display it), Mine is a simple activity with a button and on the click of the button the list is displayed,

On the click of the button, we actually create an AlertDialog and set the ListView as its content after populating it,

public class CustomListActivity extends Activity {

	private ArrayList<Trigger> mTriggers = null;
	private TriggerAdapter mAdapter = null;
	ListView list = null;

	/** Called when the activity is first created. */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		((Button) findViewById(R.id.btn_list))
		.setOnClickListener(mClickListener);
	}

	protected void onStart() {
		super.onStart();
		String[] trigger_name = getResources().getStringArray(R.array.triggers);
		mTriggers = new ArrayList<Trigger>();
		for (int i = 0; i < trigger_name.length; i++) {
			final Trigger trigger = new Trigger();
			trigger.setTriggerName(trigger_name[i]);
			trigger.setStatus(false);
			mTriggers.add(trigger);
		}
		list = (ListView) getLayoutInflater()
		.inflate(R.layout.listlayout, null);
		list.setItemsCanFocus(true);
		list.setChoiceMode(ListView.CHOICE_MODE_SINGLE);

	};

	OnClickListener mClickListener = new OnClickListener() {
		@Override
		public void onClick(final View view) {
			final AlertDialog.Builder builder = new AlertDialog.Builder(view
					.getContext());
			builder.setTitle(getResources().getString(R.string.trigger));
			mAdapter = new TriggerAdapter(CustomListActivity.this,
					R.layout.listrowtemplate, mTriggers);
			list.setAdapter(mAdapter);
			builder.setView(list);
			LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
					LinearLayout.LayoutParams.FILL_PARENT,
					LinearLayout.LayoutParams.WRAP_CONTENT);
			builder.setView(list);
			final AlertDialog alert = builder.create();
			if (!alert.isShowing()) {
				alert.show();
			}
		}
	};
}

So this was a bit about creating Custom Lists in android. Hope you enjoyed it.

If you have any questions/doubts/improvements, please feel free to post those.

A blob refers to a binary large object. A BLOB usually comes into picture with Sqlite, ususally it is some data(image, audio file) which needs to be stored in a database purely on need basis. Although it is not a very good approach to do this but some times it is just the requirement. So actually, I demonstrate here to store an icon image into the android sqlite as a BLOB(byte array) and then retrieve and show it on the screen.

    • Step 1 would be to convert the image into a byte array,

 

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.icon); ByteArrayOutputStream bos = new ByteArrayOutputStream(); bitmap.compress(CompressFormat.PNG, 0 /*ignored for PNG*/, bos); byte[] bitmapdata = bos.toByteArray();

    • Step 2 would be to store this in the database,

 

SQLiteDatabase db=this.openOrCreateDatabase(“imagedatabase”, this.MODE_PRIVATE, null); db.execSQL(“CREATE TABLE IF NOT EXISTS imagetable (“ + “_id INTEGER PRIMARY KEY AUTOINCREMENT,” + “image BLOB” + “);”); ContentValues values = new ContentValues(); values.put(“image”, bitmapdata); long row_id=db.insert(“imagetable”, null, values);

    • Step 3 would be to retrieve and display it back on screen,

 

Cursor cursor = db.query(“imagetable”, new String[]{“image”}, null, null, null, null,null); System.out.println(“—–getcolumn count”+cursor.moveToFirst()); //get it as a ByteArray byte[] mybyte=cursor.getBlob(0); //the cursor is not needed anymore cursor.close(); //convert it back to an image ByteArrayInputStream imageStream = new ByteArrayInputStream(mybyte); Bitmap theImage = BitmapFactory.decodeStream(imageStream); ((ImageView)findViewById(R.id.view_image)).setImageBitmap(theImage);

A simple 3 step process!