Skip to content

Posts tagged ‘java’

Nexus Revamped Live Wallpaper: Update 1.98.01 (Multi-touch and more!) #android

Feb 1 11
by mat

To celebrate getting over 1/4 million downloads of my live wallpaper I decided to put extra effort into fixing problems some of my users were having. A common gripe with live wallpapers is the amount of battery they use and the associated performance hit when navigating on the home screen. I used to use java drawables to create and manipulate all of the particles on the screen, but I have successfully moved over to OpenGL-ES. This has an impressive benefit in performance (back of the envelope calculation shows >13x performance increase) and also improves the overall sexyness as OpenGL handles the blending nicely.

Anyway enough of the boring stuff, let’s see what’s new. Something I’m REALLY excited about (hence the caps) is multi-touch! It is so much fun just playing with a blank home screen creating particles! Unfortunatly this isn’t something which translates well into a screenshot, so you’ll just have to try it out for yourself.

Nexus Revamped 1.98.01 screenshot

Nexus Revamped 1.98.01 screenshot


New Particle Appearance Screen

New Particle Appearance Screen

I’d like to thank all of my beta testers for the very helpful feedback I have got over the past couple of weeks. There is much more to mention but I am now absolutely knackered. I’m sure I’ll follow this post up soon with some more interesting things. Enjoy!

Changelog

1.98.01

  • Improved: Now using OpenGL to draw the live wallpaper, which should give approximately a 16x performance increase (awesome eh?)
  • Added: MULTI-TOUCH!!! :O
  • Improved: Lowered file size from over 1Mb to less than 300Kb
  • Added: Option to not spawn particles when dragging / swiping across screens etc, set by default
  • Added: Anayltics see which areas need improving
  • Added: Sexy Particle appearance dialog to set saturation and tail length
  • Removed: Tail length and particle saturation (see above)
  • Added: Custom colors for battery level colored particles
  • Added: Speed now allows you to set the minimum and maximum speed
  • Removed: Removed speed deviation (see above)
  • Improved: Scaling can go negative now (i.e. shrinking)
  • Improved: Lots of default values changed to make prettier on first run
  • Improved: Speed and Spawning decoupled from FPS settings, should be uniform now
  • Improved: Max FPS setting now 80 (25 is recommended still)
  • Improved: Max speed increased
  • Improved: Max tail size increased
  • Improved: Max particle number increased
  • Improved: Added some nice icons in the about menu
  • Bug Fix: Particle alignment works more consistently on different screen sizes and scales

Android Market Links
Either click the following android market links (in android phone) or search the market for nexus revamped.
Pro Version
Nexus Revamped Pro Live Wallpaper (com.stealthcopter.nexusrevampedpro)

qr

Free Version

The free version is the same as the pro version but with some of the fancier features are missing, and some non-intrusive adverts. Both versions are fully supported.

Nexus Revamped Live Wallpaper (com.stealthcopter.nexusrevamped)

Download link

qr

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

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: Opening a webpage in your app using Intents

Jan 25 10
by mat

Opening up a website from an application in android is very simple thanks to “Intents”. An Intent is a request to android use an application to preform a task. The code below shows a very simple example of launching a browser to go to the wordcube website.

Context context = getApplicationContext();
String url = "http://www.stealthcopter.com/wordcube";
Intent i = new Intent(Intent.ACTION_VIEW); 
Uri u = Uri.parse(url); 
i.setData(u); 
try {
  // Start the activity
  startActivity(i);
} catch (ActivityNotFoundException e) {
  // Raise on activity not found
  Toast toast = Toast.makeText(context, "Browser not found.", Toast.LENGTH_SHORT);
}

As pointed out in the comments, the context can be replaced by the activity itself, such as TestApp.this. eg:

Toast toast = Toast.makeText(TestApp.this, "Browser not found.", Toast.LENGTH_SHORT);

We have surrounded the activity with a try/catch which will be raised if android cannot find an application that will accept this intent, in this case a web-browser. It is highly unlikely that an android phone will not have a web-browser installed but it is a good practise to get into.

Android: Requesting root access in your app

Jan 17 10
by mat

This snippet shows how root access can be requested inside an application in order to write a file into a place we do not have permission to access usually. Requesting root access will only work if your phone allows it, or it has been ‘rooted’ (hacked to allow superuser permissions).

Process p; 
try { 
   // Preform su to get root privledges
   p = Runtime.getRuntime().exec("su"); 
   
   // Attempt to write a file to a root-only 
   DataOutputStream os = new DataOutputStream(p.getOutputStream()); 
   os.writeBytes("echo \"Do I have root?\" >/system/sd/temporary.txt\n");
   
   // Close the terminal
   os.writeBytes("exit\n"); 
   os.flush(); 
   try { 
      p.waitFor(); 
           if (p.exitValue() != 255) { 
        	  // TODO Code to run on success
              toastMessage("root");
           } 
           else { 
        	   // TODO Code to run on unsuccessful
        	   toastMessage("not root");    
           } 
   } catch (InterruptedException e) { 
      // TODO Code to run in interrupted exception
	   toastMessage("not root"); 
   } 
} catch (IOException e) { 
   // TODO Code to run in input/output exception
	toastMessage("not root"); 
}

Where my “toastMessage” is just a function which creates a toast to display on the screen. On phones with superuser permissions installed (root access) this will display a dialog asking the user to allow or deny the application permission to have root access:

android root access dialog

android request root access dialog

Ref
anddev.org

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)