All posts in category "Android"

This morning I woke up to this little message:

Error generating final archive: Debug certificate expired on 8/23/10 8:45 AM!    AndroBlip.coffee        Unknown    Android Packaging Problem

Well, that was annoying. According to google this is expected after your debug key is a year old. I have no clue why this is; it doesn’t seem to have any purpose, but luckily they do find us a solution:

To fix this problem, simply delete the debug.keystore file. The default storage location for AVDs is in ~/.android/avd on OS X and Linux, in C:\Documents and Settings\\.android\ on Windows XP, and in C:\Users\\.android\ on Windows Vista.

The next time you build, the build tools will regenerate a new keystore and debug key

This also seems to happen if you’re on a non-gregorian locale, but that’s not the case for me. Check out that google link for that one.

Minor note: running the program on the same virtual machine, gives you trouble with a new signing/debug key:

installation failed due to different application signatures.
You must perform a full uninstall of the application. WARNING: This will remove the application data!
Please execute ‘adb uninstall com.huiges.AndroBlip’ in a shell.
Launch canceled!

Just uninstalling the application in the virtual machine works fine.
It does however mean that if you work with a lot of test-data in your app, you’ll have to find a way to keep that around when uninstalling your app. As far as I know this is unavoidable when your key expires. This site comes up with a solution: create your own key with a much larger expiration date:

keytool -genkey -keypass android -keystore debug.keystore -alias androiddebugkey -storepass android -validity 10000 -dname "CN=Android Debug,O=Android,C=US"

For the life.turns blipfoto project we wanted to quickly push out a feature to be able to add to the fun.

The extension would be that an image had is shown on top of a camera ‘preview’, so you could align the person on your screen with one of the prescribed poses. The trouble started because the final picture would be a lot different from the image you saw in the preview. As the alignment over at blipfoto had to be as good as possible, this was a big problem.

Making some sort of preview-screen based upon the CameraPreview.java example is trivial, although if you are using 1.5 (api lvl 3) you cannot use the newest version. The main difference is getSupportedPreviewSizes(), that doesn’t exist. So getOptimalPreviewSize() doesn’t work here, and basically you have no setPreviewSize() in the surfaceChanged(). But the basics are there.

Well, if you ignore the difference in surfaceChanged, it all seems to work just fine. All we have to do now is add a button or use the camera button to call the “take a picture” function, aptly named “takePicture()”. The former isn’t a problem, the latter I did like this:

	@Override
	public boolean onKeyDown(int keyCode, KeyEvent event) {
		if (keyCode == KeyEvent.KEYCODE_CAMERA) {
			if (!bIsPictureTaking) {
				this.onClick(null);
				return true;
			}else{
				//ignore buttons while taking pics.
				return true;
			}
		}else{
			return super.onKeyDown(keyCode, event);
		}
	}

It’s not complete, but that’s the basic proof of concept. Now the onClick we call is ofcourse the same onClick you got set as onclicklistener for the button you might have made so it’s all a bit of patchwork, but again, this is to keep this example basic. Now for the code to take the picture:

	public void onClick(View v) {
		myCamera.takePicture(null, null, this);//nulpoint
	}

If you want to use possible autofocus, you can take a look over here

But now for the real trouble: It seems that on the G1 the standard previewsize you get is 320 x 480, while the picture size is something like 1536 x 2048. A quick thinker sees that the preview is 2:3, while the picture is ofcourse 3:4. Yes. Trouble. The Motorola Milestone (european Droid) does NOT have this difference. (And do not look at the virtual machine in the SDK, it’s completely useless with the ‘camera’.)

Now because the preview has to be defaulted to some sort of standard, and we cannot get a list of these sizes in the old SDK, there is no real solution sadly. What you can learn from this, is that the difference in the vertical preview is 3/4 * 2/3.

In the life.turns case this means that the overlay on the preview has to be stretched with 9/8, but you can calculate this difference using this code:

	Camera.Parameters parameters = myCamera.getParameters();
	int picH = parameters.getPictureSize().height;
	int picW = parameters.getPictureSize().width;
	int preH = parameters.getPreviewSize().height;
	int preW = parameters.getPreviewSize().width;
	float scale = ((float)(picH*preW)) / ((float)(picW*preH));

Not pretty. But development time for this feature was really short, so shortcuts had to be taken :). I located this in the onSurfaceChange function of the surfaceview that would make the cameraPreview, because all these facts had to be known there, but I guess you can do this anyplace you can get your fingers on those parameters :)

There are enough sites that help you to get a CameraPreview going. Most of them seem to be based on this resource from the apidemos.

If you implement this uncarefully, you might see this error, or something like it:

ERROR/AndroidRuntime(): Uncaught handler: thread main exiting due to uncaught exception
ERROR/AndroidRuntime(): java.lang.RuntimeException: Out of memory
ERROR/AndroidRuntime():     at android.hardware.Camera.native_setup(Native Method)
ERROR/AndroidRuntime():     at android.hardware.Camera.<init>(Camera.java:82)
ERROR/AndroidRuntime():     at android.hardware.Camera.open(Camera.java:64)

My google-fu was a bit off maybe, but the rather simple explanation took some time to find. There are some interesting discussion about the stopPreview() and release() function of the Camera class, but that wasn’t the basic problem in this case; here it is, in all its simplicity:

Add the camera permission to your manifest.

Yes. that simple. I’m not saying that fixes ALL of your problems, but I am saying that you’ll get problems if you don’t :)
I have no clue why you seem to get a memory error when the permissions are missing, but you do :)

This is the line you’re supposed to add tot the AndroidManifest.xml:

<uses-permission android:name="android.permission.CAMERA"/>

So yesterday I finished with this:

As said, it might turn out to be a too big memory hog, but for now this is fine.

Right. well.

A simple bit of math: A shot with resolution 2145×3217 (o.k., I know that’s not a standard phone-shot, but that shouldn’t be lethal). That’s 6900465 pixels, resulting in an allocation of 13800930 bytes. With 16MB heap that’s a bit steep.  This results in ye olde memory error:

ERROR/dalvikvm-heap(1064): 13800930-byte external allocation too large for this process.
ERROR/(1064): VM won’t let us allocate 13800930 bytes
WARN/dalvikvm(1064): threadid=17: thread exiting with uncaught exception (group=0x4000fe70)
ERROR/AndroidRuntime(1064): Uncaught handler: thread Thread-9 exiting due to uncaught exception
ERROR/AndroidRuntime(1064): java.lang.OutOfMemoryError: bitmap size exceeds VM budget

I’ve tried to skip the whole bitmap and just play with a drawable, but that doesn’t help either. BitmapFactory still needs to get into the mix and allocate your pixels.

In the end the solution was of course really simple, and everyone will be thinking “duh”, but still; for the thumbnail all those pixels are a waste, so we can resample without any trouble:

BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize=8;
thumb = BitmapFactory.decodeStream(in,null,options);

I’ve used the stream here because that was the quickest, but there is a decode for everybody:
decodeFile, decodeResource…Just pick your flavour :)

This works perfectly to show “a small version of the picture” aka thumbnail, so I’m happy with that :). Only possible trouble might be that this doesn’t work on higher API levels, but I’ll test that in a sec and don’t expect any trouble.

Another small post, for anyone interested.

A lot of requests were for showing a small thumbnail while uploading a picture. This is all fine and dandy, but I do not have this thumbnail available. There is a thumbnail in the system (the gallery shows them I guess), but there seems to be a problem for certain handsets (HTC Hero ?) with these.

Still, I wanted to use the lovely ‘getThumbnail‘ from MediaStore.Images.Thumbnails, but although much is quite old in that resource, this thumbail functions is ‘Since: API Level 5’. So another workaround needed for the oldscool Android 1.5 development. I’m thinking I might want to move to a newer SDK…

Anyway, I’m making my own thumbnail, pretty simple actually: for a 40×40 thumb:

thumb = Bitmap.createScaledBitmap(myBitmap, 40, 40, false);

Of course I didn’t have a bitmap ready, I just had a URI, but using ‘getBitmap’  you can simply retrieve it. This will use some more memory I guess, but in this case that shouldn’t be a big problem. Final code looks like this:

myBitmap = MediaStore.Images.Media.getBitmap(cr,contentURI);
thumb = Bitmap.createScaledBitmap(myBitmap, 40, 40, false);

As said, it might turn out to be a too big memory hog, but for now this is fine.

According to the internet it is really easy to change the behaviour of the standard Android back button:

public void onBackPressed() {
   // Your code goes here.
   return;
}

This is documented of course and all fine and happy.

Except it didn’t work for me. This is because it was introduced in Android 2.0, and I’m developing for 1.5 and above. Sad, I know :P

The sollution isn’t all that hard: Just override the keypress, filter out the backbutton, and call the parent function for the rest. Easy as that.

public boolean onKeyDown(int keyCode, KeyEvent event)  {
    if (keyCode == KeyEvent.KEYCODE_BACK
        && event.getRepeatCount() == 0) {
        //Your code goes here.
        return true;
    }
    //call parent function for other keys
    return super.onKeyDown(keyCode, event);
}

This seems reasonably forward enough, and doesn’t seem to be too breakable in future updates.

For some apps that handle web content, like AndroBlip, it’s better to view (the equivalent of) certain pages in that app, then in the browser. A good example is youtube: at time of writing you cannot view youtube movies in the browser, but you can in the youtube app. When you click on a youtube link you get the “complete action using” dialog (right).

Luckily, this isn’t very hard to do.

Add an intent to your manifest

You simply have to tell the system you want to catch some urls and use your activity to open them. A couple of minor points you might want to consider, but it looks like this:

<activity android:name=".YourActivity">
	<intent-filter>
		<action android:name="android.intent.action.VIEW"></action>
		<category android:name="android.intent.category.DEFAULT"></category>
		<category android:name="android.intent.category.BROWSABLE"></category>
		<data android:scheme="http" android:host="www.site.com" ></data>
	</intent-filter>
</activity>

And now you are showing the chooser dialog for all the links to “www.site.com”. The small print is here:

  • You’re filtering http://www.site.com, but not http://site.com or any https requests. Add a seperate data item for those!
  • You might want to filter a subset of urls. Use pathPrefix or pathPattern for that situation. Check out the google android resources for more info on that.

Get your URL in you Activity

Now we’ve caught that URL and opened the activity, all you need to do is grab it from the intent. You can do a lot of complicated stuff, but I reckon this suffices:

String data = getIntent().getDataString();

Simple, eh? You can do all sorts of other things as you can see in the reference guide but this serves as a simple example; this little mock-up filters a variable from the query string a user might find in the URL:

String data = getIntent().getDataString();
Pattern pat = Pattern.compile("id=([0-9]*)");
Matcher mat = pat.matcher(data);
if (mat.find()){
	Log.d("yourTag", "group1"+mat.group(1));
}

You can now start to do something with the ID you found.

Fallback

You can try to add a fallback to your code and if you have no clue how to parse the received url (the pathPattern is a bit limited) just try and restart the browser. This is far from ideal, because the user will just get the same choice-screen, and has to figure out for him/herself that the browser should be chosen after several tries. Even worse, if a default is chosen, it might go completely wrong. But for anyone willing to play with this, you can just start an Intent to view a URL like this:

startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));

Reading this post on how to avoid OutOfMemory (OOM) triggered a little search for places where Bitmaps might be causing trouble. In one place the following seemed to be in need of update:

//fOut is a FileOutputStream
mBitmap = BitmapFactory.decodeStream(url.openStream());
mBitmap.compress(Bitmap.CompressFormat.PNG, 100, fOut);

This downloads a bitmap and saves it to a stream. (Don’t use this code. the openStream has no timeout, so you will get funky stuff when your stream doesn’t work like you’d like it to).

Looking for a simple way to do that decodeStream with a Drawable instead of a BitmapFactory gave me no help. I did find this post on the google groups: Store drawable locally as a file . This in turn links to this excellent howto.

Just a minor addition from my side if you are using that code on Android:

FileOutputStream out = new FileOutputStream(name);

That’s all fun and games, untill someone loses an eye. Or tries to read the file :) Be sure to make that file readable enough for your convenience and all, so use the openFileOutput from your context:

FileOutputStream out = openFileOutput(name, Context.MODE_WORLD_READABLE);

I had to add certain iterations of the same type of data to a certain View. You can do all of the adding in code, but you’re probably better off doing all the layout in a seperate place (XML), and just including multiple instances of that XML.

How to proceed: first, you’ll need an inflator (or is it inflater?) to get your View in code. In this example there is an XML with a LinearLayout as top element, and we want to add some of the TextViews that are present in another XML.

LinearLayout wrapper = (LinearLayout) findViewById(R.id.newCommentWrapper);

This View is present in the current Layout. Lets get our “to include” View:

View inflatedView = View.inflate(this, R.layout.test, null);
wrapper.addView(inflatedView);

This is all good, but keep in mind that if you want to add this multiple times you’ll have to avoid this error:

The specified child already has a parent. You must call removeView() on the child’s parent first.

You CAN just add 2 of the same Views, but you must be careful not to add the same instance twice.

View inflatedView;
inflatedView = View.inflate(this, R.layout.test, null);
wrapper.addView(inflatedView);

inflatedView = View.inflate(this, R.layout.test, null);
wrapper.addView(inflatedView2);

It’s really simple to add a single View, but we do want to change some of the stuff. No fear: you can just point to the id’s in the inflated view with findViewById! As an example: the View in the XML that is going to be “included” has a child called “comment”, that has to get a specific content (text):

inflatedView = (LinearLayout) View.inflate(this, R.layout.comment, null);
((TextView) inflatedView.findViewById(R.id.comment)).setText("you commentString goes here");

You can add the final inflatedView to your current Layout with “addview”.
Wrapping it all up, you’ll have a master layout XML, an XML with your ‘child’ that you want to add and of course some code.

MasterLayout.xml:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" style="@style/yourStyle1">
	<LinearLayout android:orientation="vertical" style="@style/yourStyle1" android:id="@+id/commentWrapper">
	</LinearLayout>
</ScrollView>

SingleComment.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" style="@style/yourStyle1" android:id="@+id/comment">
	<TextView android:id="@+id/comment" style="@style/yourStyle1"/>
</LinearLayout>

Java Snippet:

setContentView(R.layout.MasterLayout);
LinearLayout wrapper = (LinearLayout) findViewById(R.id.CommentWrapper);
LinearLayout inflatedView;

int j = nmbrOfCmnts;
for(int i=0;i < j ;i++){
	String content = getYourComment(i);
	inflatedView = (LinearLayout) View.inflate(this, R.layout.SingleComment, null);
	((TextView) inflatedView.findViewById(R.id.comment)).setText(commentUser);
	wrapper.addView(inflatedView);
}

I don’t know if this is the best method, but it let’s me keep the layout of one comment in a nice and seperate XML, while I am still able to programmatically add instances of that View to my current layout.

It was quite easy to make some of the input fields use the “auto caps” feature of android. Now while deadly for some people and a bit annoying for others, this was one of the first requests I got for AndroBlip .I’m not going to go for the autocomplete stuff, as this feels like a bridge to far.

Anyway, we were talking about android coding, not AndroBlip.
So there is an EditText view in the layout called “editTextId“, and we set it for Capitalization like so:

EditText myEditText = (EditText) findViewById(R.id.editTextId);
myEditText.setInputType(InputType.TYPE_CLASS_TEXT |
        InputType.TYPE_TEXT_FLAG_CAP_SENTENCES);

Now i don’t know if you can do this in XML, but this works fine ;).