Skip to content

Posts tagged ‘eclipse’

Android: Grouping onClickListeners together by implementation in an activity

Apr 16 10
by mat

Here is a quick tip for better organisation of your program by grouping all onClickListeners into a simple switch statement.

The problem
If you have lots of buttons or view that you are linking to OnClickListener events you can quickly end up with some cumbersome code. This can be changed very simply by implementing a method in your activity and switching between View ID’s.

Below is an example the typical method for creating an OnClickListener for a View.


final ImageButton Ibutton = (ImageButton) findViewById(R.id.button_1);
Ibutton.setOnClickListener(new OnClickListener() {
	public void onClick(View v) {
		Toast.makeText(testActivity.this, "Button 1 pressed ", Toast.LENGTH_SHORT).show();
	}
});

final ImageButton Ibutton2 = (ImageButton) findViewById(R.id.button_2);
Ibutton2.setOnClickListener(new OnClickListener() {
	 public void onClick(View v) {
		Toast.makeText(testActivity.this, "Button 2 pressed", Toast.LENGTH_SHORT).show();
	}
});
       

The Solution
We can get our activity to handle the OnClick events itself, we do this we implementing OnClickListener:

public class testActivity extends Activity implements OnClickListener {

If your write this in eclipse you can hover over the error this produces and click Add unimplemented methods to automatically create a stub for the next part.

eclipse auto suggesting to implement the onclicklistener method

eclipse auto suggesting to implement the onclicklistener method

We now create our buttons similar to before, but for the setOnClickListener we pass this as an argument, so that our implemented method is called.


ImageButton Ibutton = (ImageButton) findViewById(R.id.button_1);
Ibutton.setOnClickListener(this);      
 
ImageButton Ibutton2 = (ImageButton) findViewById(R.id.button_2);
Ibutton2.setOnClickListener(this);      

We now create the implemented method for our activity (onClick) and we use a switch statement to find which View fired the onclick event and preform an action accordingly.


@Override
public void onClick(View v) {
	switch(v.getId()){
	case R.id.button_1:
		// action to preform on button 1
		Toast.makeText(testActivity.this, "Button 1 pressed ", Toast.LENGTH_SHORT).show();
		break;
	case R.id.button_2:
		// action to preform on button 1
		Toast.makeText(testActivity.this, "Button 2 pressed ", Toast.LENGTH_SHORT).show();
		break;
	}
}		

And it’s as simple as that!
Feel free to post comments or questions

Android: Context menu example (on long press, gridview)

Apr 14 10
by mat

I received several requests regarding how I created a context menu (the menu activated on a long press) using a gridview and how to call functions such as saving a sound file from this. So I have created a quick example to explain this:

Tutorial

To implement a context menu (long press menu) you first need to include the following imports:

import android.view.ContextMenu;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;

We start a very simple project, with the layout having a single button named button_example:

Android context menu example

Android context menu example

We then use registerForContextMenu in the onCreate of the activity to tell android that we want this view to create a menu when it is long pressed. This is not limited to buttons, this will work for other views too. You must register each view that you want to have associated with the context menu.

Button btn = (Button) findViewById(R.id.button_example);
registerForContextMenu(btn);

You then need to override the onCreateContextMenu method to create the menu:

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
	super.onCreateContextMenu(menu, v, menuInfo);
		menu.setHeaderTitle("Context Menu");
		menu.add(0, v.getId(), 0, "Action 1");
		menu.add(0, v.getId(), 0, "Action 2");
	}

android context menu example

android context menu example

And override onContextItemSelected to preform the action when an option is selected from this menu:

    @Override
	public boolean onContextItemSelected(MenuItem item) {
       	if(item.getTitle()=="Action 1"){function1(item.getItemId());}
    	else if(item.getTitle()=="Action 2"){function2(item.getItemId());}
    	else {return false;}
	return true;
	}

function1 and function2 are just place-holders at the moment that toast a message when they are used. In the above example we are choosing between Action 1 and Action 2 which define which function will be run when an item is selected. We have passed the ID of the view through the context menu and into this function so we can tell what the user was pressing when the context menu was created.

calling a function after a context menu item was pressed

calling a function after a context menu item was pressed

Examples

If you create a gridview and give each item a unique ID then you can use this method to preform actions based on each item. This is the method I use for my soundboard applications to save the sounds to the SD card as a notification or ringtone:

counterstrike 1.6 soundboard context menu

counterstrike 1.6 soundboard context menu


Unreal Tournament soundboard context menu

Unreal Tournament soundboard context menu

Source

Below is the source code:

package com.contextmenu.test;

import android.app.Activity;
import android.os.Bundle;
import android.view.ContextMenu;
import android.view.MenuItem;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.widget.Button;
import android.widget.Toast;

public class test extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        Button btn = (Button) findViewById(R.id.button_example);
        registerForContextMenu(btn);        
    }
    
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {
	super.onCreateContextMenu(menu, v, menuInfo);
		menu.setHeaderTitle("Context Menu");
		menu.add(0, v.getId(), 0, "Action 1");
		menu.add(0, v.getId(), 0, "Action 2");
	}

    @Override
	public boolean onContextItemSelected(MenuItem item) {
       	if(item.getTitle()=="Action 1"){function1(item.getItemId());}
    	else if(item.getTitle()=="Action 2"){function2(item.getItemId());}
    	else {return false;}
	return true;
	}
    
    public void function1(int id){
    	Toast.makeText(this, "function 1 called", Toast.LENGTH_SHORT).show();
    }
    public void function2(int id){
    	Toast.makeText(this, "function 2 called", Toast.LENGTH_SHORT).show();
    }
}

Supporting multiple phones and screensizes in your android applications

Feb 11 10
by mat

When releasing an android application it is often desirable to release your application to the largest amount of users as possible. By developing an application with android 1.5 as the target, later versions are automatically supported (1.6, 2.0, and 2.1). However different screen-sizes were introduced with android 1.6 so by default an application will not support smaller screens (larger screens are automatically supported).

Android Versions Marketshare
The image below shows the current market share for the different version of android:

Android version percentages

Android version marketshare

Android Platform Percentage of Devices
1.1 0.3%
1.5 27.7%
1.6 54.2%
2.0 2.9%
2.0.1 14.8%

Devices with small screens include the following phones T-Mobile G1, Samsung I7500, and the HTC Tattoo. Unfortunately I cannot find any statistics regarding the percentage of small screen android users (if you can please let me know). However as the fix is very simple it seems stupid not to.

Which version of android are you using?

View Results

Loading ... Loading ...

What screen size are you running android on

View Results

Loading ... Loading ...

Adding Support
To add support for different screen sizes we need to add a supports-screens tag in our android_manifest.xml. This should look like the following:

  <supports-screens
          android:largeScreens="true"
          android:normalScreens="true"
          android:smallScreens="true"
          android:anyDensity="false" />

This tells android that the application should work on large, normal and small screens. Note that only setting small to false will prevent it from been visible in the market to small screened phones, for normal and large it will simply change the method of which android interprets the display of this application. anyDensity fix me

However in order to use the supports-screens tag the target of your application must be android 1.6 (or Sdk version 4). We can allow our program which is now targetted for android 1.6 still be avaliable for android 1.5 (Sdk version 3) by adding android:minSdkVersion=”3″ to our uses-sdk tag.

<uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4"/>

Now your application can be built for 1.6 supporting smaller screen sizes and still work on 1.5.

Demonstration
Below are screenshots from two AVD’s, one with a normal screen size, and one with a small screen size:

android small screen wordcube

small screen android AVD running wordcube


android medium screen wordcube

Medium screen android AVD running wordcube

Note: Eclipse bug
Unfortunately eclipse currently does not acknowledge minSdkVersion and will not give you the option to test your application in any android virtual devices (AVD’s), while this is not fixed you can manually install the package into your AVD. For this you need to know the name of your emulator instance, to do this first run the AVD and then use the followin command to find out the name.

$adb devices
List of devices attached
emulator-5554   device

So we can see that the emulator instance I need is called emulator-5554. We can now use adb to install a package into this emulator. The below example shows installing wordcubefree from the bin folder in my project to my AVD using adb.

adb -s emulator-5554 install ~/android/workspace/WordCubeFree/bin/WordCubeFree.apk
1019 KB/s (39683 bytes in 0.038s)
Can't dispatch DDM chunk 46454154: no handler defined
Can't dispatch DDM chunk 4d505251: no handler defined
        pkg: /data/local/tmp/WordCubeFree.apk
Success

Note that this will fail if the application is already installed and you will need to uninstall the application before using this command (if anyone knows a switch to force install or alternative method please let me know)

Going Further
It is possible to delve into this is more detail by providing layouts for small, medium and large screen sizes. This can be explored in more detail in the google dev support guide

Android: Using SVN with your app’s project (and eclipse)

Feb 11 10
by mat

When creating any non-trivial program using a versioning system is essential, especially when working in part of a group. This guide aims to be a quick tutorial to the SVN (subversion) tool for versioning and how to use it with an android project.

Assumptions

  • You will need SVN installed on your computer. This can be done using your package manager or by the following command in ubuntu / debian based systems:

    sudo apt-get install subversion
  • You already have an SVN repository configured. If not please view a tutorial like this or if you have a nice webhost like me (thanks dreamhost :P ) there may be a simple tool to do this automatically for you in your panel.

Note
Don’t include the files inside /bin or /gen as they are just build from the source code and will simply fill up a lot space in your SVN. But do include the folders themselves as the project will fail to build without them.

Command line (recommended)
For SVN, I am a great fan of the command line. From the few SVN GUI applications that I have used in the past I can recommend Turtoise SVN for windows and kdesvn for linux (kde) but I still prefer the command line.

The following code will checkout the project from your server. This will create a new folder called “projectname” on your computer and download the project from your server (at this point it is most likely an empty folder).

# Checkout the SVN directory
svn co svn.yourdomain.com/projectname projectname

You can then copy or create your android project in this directory. In our project folder there are two folders which contain generated files (as opposed to source files) there is no point uploading these to the svn as you will simply take up space and bandwidth. Before you decided to upload your changes to the server you should empty the bin and gen folders:

# Empty bin and gen folders
rm -rf ./projectname/bin/*
rm -rf ./projectname/gen/*

Each time you add a new file to the project you will need to add (`svn add filename` for single files or `svn add *` for all files):

# Tell SVN we want to be versioning these files
svn add projectname/*

When you are happy with your changes you can commit (`svn commit -m “message”`) your changes to the svn to create a new version, it is mandatory to include a message with each revision and it is best to be as detailed as possible with the changes made. This makes it much easier to hunt down where a bug or regression was introduced.

# Save the changes and upload to repository
svn commit -m "Initial import of projectname"

Each time you commit or wish to upgrade what is stored locally to the latest version on the server you need to use the following:

# Update the locally stored version
svn update projectname

Further points
Eclipse has a plugin to manage SVN download and install instructions can be found here.

How to tell android which volume (media/ringtone etc.) should be controlled by your app

Feb 1 10
by mat

In my android apps I was getting annoying problems that whilst playing sound the volume buttons would control the media volume, but when no sound was playing they would control the ringtone volume. I found the following by trial and error, hopefully this post will help people with the same problem.

This is done by placing a call of setVolumeControlStream in the onCreate part of your activity which takes on of the following values

  • AudioManager.STREAM_MUSIC
  • AudioManager.STREAM_RING
  • AudioManager.STREAM_ALARM
  • AudioManager.STREAM_NOTIFICATION
  • AudioManager.STREAM_SYSTEM
  • AudioManager.STREAM_VOICECALL

Below shows the code required to set your applications default volume control to any of the above values:

Media Volume (Music/Video etc…)

this.setVolumeControlStream(AudioManager.STREAM_MUSIC);

Ringtone Volume

this.setVolumeControlStream(AudioManager.STREAM_RING);

Alarm Volume

this.setVolumeControlStream(AudioManager.STREAM_ALARM);

Notification Volume

this.setVolumeControlStream(AudioManager.STREAM_NOTIFICATION);

System Volume

this.setVolumeControlStream(AudioManager.STREAM_SYSTEM);

Voicecall Volume

this.setVolumeControlStream(AudioManager.STREAM_VOICECALL);

I imagine this is also possible via and XML settings, if anyone knows of this I’d be very grateful to know how!

Android: Saving a sound file to SD from resource and setting as ringtone

Jan 26 10
by mat

Quite a few people have been asking how to save a file to the SD card in order to register it as a ringtone. The following example creates a function that will save a resource to the SD card (ie: from R.raw.soundfile to /sdcard/media/audio/ringtones/soundfile.ogg) and register it as a ringtone.

I have split this example into two parts, the first part goes through the code a section at a time with a brief explanation of what it does, the second half is just the code that you can copy and paste and then edit to your hearts content.

Parts

We first setup our function to return a boolean depicting if we have failed or if we are successful. We accept in an integer which corresponds to the raw sound file.

public boolean saveas(int ressound){

}

So this function would be called in the following fashion:

saveas(R.raw.soundfile);

or utilising its boolean return:

if (saveas(R.raw.soundfile)){
// Code if successful
}
else
{
// Code if unsuccessful
}

The following chunk of code creates an inputstream from the raw sound resource and loads it into a buffer. We add in the mandatory try/catch clause around these operations and return false if an exception is raised (to indicate failure to the rest of our program and to prevent trying to continue act upon this sound).

 byte[] buffer=null;
 InputStream fIn = getBaseContext().getResources().openRawResource(ressound);
 int size=0;

 try {
  size = fIn.available();
  buffer = new byte[size];
  fIn.read(buffer);
  fIn.close();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  return false;
 }

The following saves the buffer to a file on the SD card. It first ensures the folder exists and if not it is created. Then as before the writing operations are surrounded with try/catches

 String path="/sdcard/media/audio/ringtones/";
 String filename="examplefile"+".ogg";

 boolean exists = (new File(path)).exists();
 if (!exists){new File(path).mkdirs();}

 FileOutputStream save;
 try {
  save = new FileOutputStream(path+filename);
  save.write(buffer);
  save.flush();
  save.close();
 } catch (FileNotFoundException e) {
  // TODO Auto-generated catch block
  return false;
 } catch (IOException e) {
  // TODO Auto-generated catch block
  return false;
 }    

The following code sends an intent to tell the Media Scanner that we have added a new file, and sets up its properties in the media database:

 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+path+filename)));

 File k = new File(path, filename);

 ContentValues values = new ContentValues();
 values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());
 values.put(MediaStore.MediaColumns.TITLE, "exampletitle");
 values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/ogg");
 values.put(MediaStore.Audio.Media.ARTIST, "cssounds ");
 values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
 values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
 values.put(MediaStore.Audio.Media.IS_ALARM, true);
 values.put(MediaStore.Audio.Media.IS_MUSIC, false);

 //Insert it into the database
 this.getContentResolver().insert(MediaStore.Audio.Media.getContentUriForPath(k.getAbsolutePath()), values);

Final code
Putting all this code together gives us our final functions:

public boolean saveas(int ressound){
 byte[] buffer=null;
 InputStream fIn = getBaseContext().getResources().openRawResource(ressound);
 int size=0;

 try {
  size = fIn.available();
  buffer = new byte[size];
  fIn.read(buffer);
  fIn.close();
 } catch (IOException e) {
  // TODO Auto-generated catch block
  return false;
 }

 String path="/sdcard/media/audio/ringtones/";
 String filename="examplefile"+".ogg";

 boolean exists = (new File(path)).exists();
 if (!exists){new File(path).mkdirs();}

 FileOutputStream save;
 try {
  save = new FileOutputStream(path+filename);
  save.write(buffer);
  save.flush();
  save.close();
 } catch (FileNotFoundException e) {
  // TODO Auto-generated catch block
  return false;
 } catch (IOException e) {
  // TODO Auto-generated catch block
  return false;
 }    

 sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, Uri.parse("file://"+path+filename)));

 File k = new File(path, filename);

 ContentValues values = new ContentValues();
 values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());
 values.put(MediaStore.MediaColumns.TITLE, "exampletitle");
 values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/ogg");
 values.put(MediaStore.Audio.Media.ARTIST, "cssounds ");
 values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
 values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
 values.put(MediaStore.Audio.Media.IS_ALARM, true);
 values.put(MediaStore.Audio.Media.IS_MUSIC, false);

 //Insert it into the database
 this.getContentResolver().insert(MediaStore.Audio.Media.getContentUriForPath(k.getAbsolutePath()), values);


 return true;
}

Comments, suggestions etc. are always welcome. Hope this has been helpful.

Android: Eclipse and problems with dynamic tables (adding rows)

Jan 20 10
by mat

When using TableLayout with LayoutParams Eclipse will automatically (and wrongly) add the following import:

import android.view.ViewGroup.LayoutParams;

However if you try to use this you will notice that nothing changes. You need to replace the import with the ollowingL

import android.widget.TableRow.LayoutParams;

Now if you rerun your code, you will see that your tables are updating. See below for an example of the code to generate a table

int sizey=10;
int sizex=10;
int btnwidth=24;
TableLayout tl = (TableLayout)findViewById(R.id.MyTableLayout); 

for (int y=0;y<sizey;y++){

// Rows
TableRow tr = new TableRow(this); 
tr.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.WRAP_CONTENT));

//Cells
for (int x=0;x<sizex;x++){
// Create new cell
	// new button
	Button b = new Button(this); 
	b.setText(x+""+y); 
	b.setLayoutParams(new LayoutParams( 
		LayoutParams.FILL_PARENT, 
		LayoutParams.WRAP_CONTENT));
	b.setHeight(btnwidth);
	b.setBackgroundResource(R.drawable.btn_blue);
	b.setOnClickListener(new PegOnClickListener(y,x));
	// add button to row
	tr.addView(b); 
	}
// add row to layout
tl.addView(tr,new TableLayout.LayoutParams( 
LayoutParams.FILL_PARENT, 
LayoutParams.WRAP_CONTENT));
}
}

Android: Blurring and dimming background windows from dialogs

Jan 17 10
by mat

The android SDK has lots of nice goodies built in to make your applications look sexier. One such feature is the blurring of windows. This effect looks particularly nice if a background window is blurred while a dialog box is shown above which can really make it stand out. Below shows the application such an example; on the left is the default about box (for WordCube Pro) and on the right is with added blur and no dimming.

android blur dim, before, after

android bluring and dimming effect before and after

I am using the AlterDialog.Builder to create my dialog, however this method will work with all kinds of dialog providing you can access it via getWindow.

   dialog = new AlertDialog.Builder(WordCube.this)
   .setTitle(WordCube.this.getResources().getString(R.string.app_name))
   .setMessage(s)
   .setIcon(R.drawable.logo)
   .setPositiveButton(R.string.btn_close, null)
   .show();

Below shows the code needed to add blur and remove dimming of the background (as I think the blur looks nicer when the background is well lit).

WindowManager.LayoutParams lp = dialog.getWindow().getAttributes();
lp.dimAmount=0.0f;
dialog.getWindow().setAttributes(lp);
dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

The blur is simply created using the last line (line 4) which sets a flag for the dialog telling android that we want windows below this one to be blurred. To achieve the dimming, we need to retrieve the layout parameters for the dialog window, set the dim amount to zero, update these parameters with setAttributes (lines 1-3).

Any comments, questions, or improvements please let me know.

Android: Improving Interfacing with twitter applications

Jan 16 10
by mat

This post builds upon the ideas shown in this post to create a complete function that will do the follow:

  • Accept a string input for the message
  • Try to open twidroidpro to send this tweet
  • If failed, try to open twidroid to send this tweet
  • If failed, bring up a list of applications that can send this information
  • Return a boolean indicating success of displaying the intent

Code

public boolean intentTwitter(String message){
 // Boolean to show if we succeeded or not
 // we assume we did until proven otherwise.
 boolean success=true;

 //Try twidroidpro first
 Intent intent = new Intent("com.twidroidpro.SendTweet");
 intent.putExtra("com.twidroidpro.extra.MESSAGE", message); 
 intent.setType("application/twitter"); 
   try{
    startActivityForResult(intent, 1);
   }
   catch(ActivityNotFoundException e){
    success=false;
   }

 // Then twidroid if we failed
 if (!success){
  success=true;
  intent = new Intent("com.twidroid.SendTweet");
  intent.putExtra("com.twidroid.extra.MESSAGE", message); 
  intent.setType("application/twitter"); 
    try{
     startActivityForResult(intent, 1);
    }
    catch(ActivityNotFoundException e){
     success=false;
    }
 }
   
   
 //Then send general intent if we failed again
 if (!success){
  success=true;
   try {
    intent = new Intent(Intent.ACTION_SEND); 
    intent.putExtra(Intent.EXTRA_TEXT, d); 
    intent.setType("application/twitter"); 
    startActivity(Intent.createChooser(intent, null));
   } catch (ActivityNotFoundException e) {
    success=false;
   }
 }
// return indicating if we were successful in bringing up an intent
// of some description
return success;
}

Example
Below is a screenshot from the wordcube (available on market) application using this function to make the application interact with twitter.

twidroid intent screenshot

twidroid intent called from wordcube

What’s next?
In a future post I will be demonstrating how to interact with the android market, this can be use in conjunction with something similar to above to try and run a program and if the program is not installed the user can be taken to the market to download a program.

Android: Counter-Strike 1.6 SoundBoard released

Jan 15 10
by mat

A soundboard is a very simple application to make, and I was given the idea to make a counter-strike 1.6 version by a friend. It seemed like a good idea as no one had made one yet, and it’s quite hard to find something that hasn’t already been done before.

counter-strike 1.6 soundboard promo image

counter-strike 1.6 soundboard

The buttons are aligned in a grid view, with the buttons made transparent so that the background can be seen.

screenshot of counter-strike 1.6 soundboard android application

screenshot of counter-strike 1.6 soundboard


another screenshot of counter-strike 1.6 soundboard android application

screenshot of counter-strike 1.6 soundboard

I am very pleased with the final look of the app, however it was getting poor reviews due to people wanting to save the sounds for use as a ringtone. To alleviate this problem I have updated the app to have this option as a context menu avaliable by long pressing on a button. This will then save the file on the sd card and update the mediastore to tell android a new ringtone and notification has been added.

The code to load a ringtone into android from an application is shown below. If you see any problems with this code, or have an improvements please let me know!

File k = new File(path, filename);

ContentValues values = new ContentValues();
values.put(MediaStore.MediaColumns.DATA, k.getAbsolutePath());
values.put(MediaStore.MediaColumns.TITLE, filesnames[pos]);
values.put(MediaStore.MediaColumns.MIME_TYPE, "audio/wav");
values.put(MediaStore.Audio.Media.ARTIST, "cssounds ");
values.put(MediaStore.Audio.Media.IS_RINGTONE, true);
values.put(MediaStore.Audio.Media.IS_NOTIFICATION, true);
values.put(MediaStore.Audio.Media.IS_ALARM, true);
values.put(MediaStore.Audio.Media.IS_MUSIC, false);

//Insert it into the database
this.getContentResolver().insert(MediaStore.Audio.Media.getContentUriForPath(k.getAbsolutePath()), values);

Ratings, reviews, feedback and feature requests are most welcome.

Download Link

Android Market Link (For android phones)