All posts in category "Code"

The beta quick & dirty: because of reasons, Androblip is being rebuilt. So there’s now a beta-program. This post is about that, check out that linked post for more info about how to join etc.

Beta 3.1.7

While there is a nasty bug I have been ignoring for some time going around (hint: you probably don’t want to change orientation that much :) ), I figured that could be ignored a bit longer in favour of this piece of feature-requesting (thanks by the way)

One thing I’d love is to move the ‘subscription’ view closer to the start, I find myself having to flick through the views every time i open it to see the people that I actually care about.

– Shane

That was bugging me as well, so that will be fixed. This was harder then it looked, but this was a great starting point:

The problem is that the longclick isn’t very intuitive for many users, including some powerusers. For 100% of my alpha-tester it wasn’t very clear what was supposed to be happening (thanks Ilse), meaning that it clearly was a problem.

So I wanted to just move stuff around on the first touch, but sadly the code I have doesn’t quite translate to that very easily. Moving some of the onItemLongClickListener code to the onTouchListeners helped though, but it’s still a bit of a hack. On the other hand, it does like it works good enough, especially for beta testing, so there we are!

Talking with one of the testers (hi Osmo!) about the source, I started thinking on how to proceed with some stuff. Basically, his question was, and I somewhat agree,  why I don’t just open-source it. Now, there is always some sort of code-shyness involved with that, where you have to show the world what sort of mess you made late night, just after missing the Balmer peak, or just stuff you didn’t really fix in a good way. This is only a small I part, and not really stopping me at the moment.

The point is that I don’t know if it will be worth the effort. It will be some effort to get as much as possible in the version control, but not too much (API keys and all). Then my own compile, release, push etc cycle needs to be changed so I can easily work, but get the ‘clean’ version pushed publicly, things like that.

I’m really not sure anyone would be willing to participate, and how much it would help. Anyone at all even have thoughts on that?

New project, new problems.

Another minor fix you “just need to know”. After some server changes (I moved my SVN installation for one), I decided to just start a new Eclipse project as opposed to ‘fixing’ the old one to the new SVN location.

My basic steps are somewhat like:

  • Make a new Android project with some fake settings
  • Connect to SVN
  • Team->revert the hell out of it

Most of the time this works well.

The problem:

There seems to be a class missing, but there isn’t:
After having all the files, the application still doesn’t work. It compiles and goes to the emulator nicely, but then this happens:

ERROR/AndroidRuntime(287): java.lang.RuntimeException:
Unable to instantiate activity ComponentInfo{packageNameGoesHere}:
              java.lang.ClassNotFoundException:
packageName.ClassName in loader
              dalvik.system.PathClassLoader@44e7a8a8

And:

Caused by: java.lang.ClassNotFoundException:
                   packageName.ClassName in
                   loader dalvik.system.PathClassLoader@44e7a8a8
           at dalvik.system.PathClassLoader.findClass
                   (PathClassLoader.java:243)

No fun at all.
This error is quite obvious: it calls a certain class that cannot be found. In this case it was the class of my main activity. Not a thing that would be easily to miss. It was there in the source of course, no errors in sight, etc, but it still didn’t register as being there. This is of course a warning sign that something is wrong with your building path. But what?

The Solution

For some reason Eclipse had decided that –as opposed to every sane default–  my /src folder didn’t need to be in my build-path. Easily fixed if you this is the problem:

  • Open the build-path (right-mouse on the project -> build-path -> configure build path, or project-properties-> Java Build Path)
  • Click the Source tab.
  • Make sure your /gen and your /src paths are listed there.
  • While you’re at it, if you use google analytics, make sure that libGoogleAnalytics.jar is present in the libraries tab!

Scope

Because I was trying Ubuntu 10.10 on my laptop, I had to install a new development environment. Not too much work: I’ve used the installing instructions from Google [1].

This basically boils down to:

  1. Install Eclipse (I just used the ubuntu software center for this).
  2. Download Android SDK and unpack it.
  3. Install Android ADT plugin for Eclipse using the Eclipse install-software feature [2]
  4. Set the path to the android SDK / tools (step 2)  in the Eclipse preferences.

I didn’t install the JDK, because it was on my machine by default. I assume the package manager has that, but I’m not sure. Now I’m all happy with my new toys, installing Subclipse for SVN, getting my source, and coding away. Then the trouble started :)

Symptoms

It seems that I couldn’t  start any Android Virtual Device (AVD). I made a new run configuration, but it didn’t start. Instead I got an error, stating:

‘Launching android’ has encountered a problem.

An internal error occurred during : “Launching android”.

Clicking “details >>” produces an extra line:

Path for project must have only one segment

And, for full disclosure, the complete log-entry included trailing this post.

Cause & Solution

It seems that this rather cryptic message means nothing more in my case than “please enter a name and project for your run configuration”. I did have a “name”, but left the “Project” field empty. Entering a value in the ‘project’ (the “AndroBlip” you see next to ‘browse’) fixed it. Sources seem to indicate that the same error is produced if you don’t enter a value in the ‘name’-field.

run configurations screenshot

Screenshot

References

  1. http://developer.android.com/sdk/installing.html
  2. https://dl-ssl.google.com/android/eclipse/
  3. http://stackoverflow.com/questions/4961151/android-path-for-project-must-have-only-one-segment

Complete error log

!ENTRY org.eclipse.core.jobs 4 2 2011-02-20 12:12:00.397
!MESSAGE An internal error occurred during: “Launching android”.
!STACK 0
java.lang.IllegalArgumentException: Path for project must have only one segment.
at org.eclipse.core.runtime.Assert.isLegal(Assert.java:63)
at org.eclipse.core.internal.resources.WorkspaceRoot
.getProject(WorkspaceRoot.java:181)
at com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate
.getProject(Unknown Source)
at com.android.ide.eclipse.adt.internal.launch.LaunchConfigDelegate
.launch(Unknown Source)
at org.eclipse.debug.internal.core.LaunchConfiguration
.launch(LaunchConfiguration.java:853)
at org.eclipse.debug.internal.core.LaunchConfiguration
.launch(LaunchConfiguration.java:703)
at org.eclipse.debug.internal.ui.DebugUIPlugin
.buildAndLaunch(DebugUIPlugin.java:866)
at org.eclipse.debug.internal.ui.DebugUIPlugin$8
.run(DebugUIPlugin.java:1069)
at org.eclipse.core.internal.jobs.Worker.run(Worker.java:55)

Indeed, quite simple, but still.

Limiting character amount

The first goal was to limit the amount of characters someone can type in an EditText. Well, this sounds easy and it is. You can set a filter, like so:

editText.setFilters(new InputFilter[]{
         new InputFilter.LengthFilter(MAX_SIZE)
});

Limiting to a certain value

Now to implement this for the setting that stops you from asking for a million pictures to be downloaded.

To be clearer: I have a preference that lets you set the amount of pictures you can download. You should be able to write only numbers* here.. I’ve done this, more or less inelegantly, with the “android:digit” attribute in the xml:

<EditTextPreference
       android:title="Max Amount"
       android:summary="The more, the heavier. Default = 12"
       android:key="viewMax"
       android:defaultValue="12"
       android:digits="0123456789"/>

* no, we’re not entering the amount in HEX.

This works OK, but you can type in a lot of 9’s if you want. That’s no good.

Now to add another LengthFilter is only applicable for numbers  up to (10^y)-1, e.g. up to 999: a LengthFilter(3) will of course limit you to 3 characters, not to an int of max 3.

I have not found a filter for the maximum value, as the standard InputFilter gives us “ALLCAPS” and the previously used LengthFilter. I think the cleanest method might be to write an InputFilter, but it’s rather easy to fix without one, using an OnSharedPreferenceChangeListener.

Simple summary of how to limit an edittext to a certain value

We can add a lot of Listeners, that trigger on a lot of different moments, this is just an example. The moment of warning might be a bit late (after having set the value), but as a proof-of-concept:

Implement OnSharedPreferenceChangeListener

Whenever you’ve changed the preference, trigger a function to check if everything is sane. The activity implements OnSharedPreferenceChangeListener, so you can do this:

getPreferenceScreen().getSharedPreferences()
              .registerOnSharedPreferenceChangeListener(this);

The Listener

Add the listener. It will check for the set value, and correct it if it’s not sane.

public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
		String key) {
  if ("yourPrefName".equals(key)) {
    String valueString = sharedPreferences.getString(key, "");
    int value = Integer.parseInt(valueString);
    if(value > MAX_VALUE){
	EditTextPreference p = (EditTextPreference) findPreference(key);
	p.setText(""+MAX_VALUE);
	Toast t = Toast.makeText(this,"Maximum amount limited to "+MAX_VALUE, Toast.LENGTH_SHORT);
	t.show();
    }else if(value < MIN_VALUE){
	EditTextPreference p = (EditTextPreference) findPreference(key);
	p.setText(""+MIN_VALUE);
	Toast t = Toast.makeText(this,"Minimum amount limited to "+MIN_VALUE, Toast.LENGTH_SHORT);
	t.show();
    }
}

This means that you CAN enter numbers that are too high or too low, but at least you’ll get a message saying you’ve failed, and the preference will remain something sane.

Bonus: a filter on an EditTextPreference

Now for a quick bonus. I was annoyed at first why you cannot simple add a filter to your EditTextPreference like you would with an EditText

yourEditTextPreference.setFilters(yourFilters);

This is because the input you’re getting for a preference actually is an EditText, and you’ve got to find it. Short story even shorter: get the EditText from the EditTextPreference. Duh :)

yourEditTextPreference.getEditText().setFilters(yourFilters);

Now I’ll see how fun it is to write a real filter. Why not?

Time to catch up on some of the android blogposts! Lets start with a simple one: this annoying bug stops you from opening the very usefull Hierarchy-viewer.

“Failed to get the adb version: Cannot run program “adb.exe”: CreateProcess error=2, The system cannot find the file specified”

There seem to be several issues here, ranging from installing all sorts of new stuff to reinstalling the SDK, but there’s one sollution that has helpt me on 2 different (windows) machines: adding the new location of the “adb.exe” to your environment path.

Step 1: locate adb.exe. It wil probably be in the platform-tools dir (that’s new I guess)

Step 2: add the location to your PATH. In windows 7:

  • Rightclick on “my computer”
  • Advanced system settings (on the left)
  • Button “Environment Variables”
  • Select “PATH” and press “Edit…”
  • Add your path, e.g. add in the end: “;c:\android\sdk\platform-tools
  • press all OK buttons.

Win!

When you have HTML and you want to show it to your users, one of the possibilities is using a WebView. Check the WebView tutorial for more info. You can also use a TextView, but the HTML has to be changed for that. You could use linkify for this. This should be enough, but when you also want (for instance) bold and italic to work, we have to do some more trickery. The first iteration we used was this:

//description is a TextView
description.setText(Html.fromHtml(desc));
description.setMovementMethod(LinkMovementMethod.getInstance());

This works amazingly well, except for this ‘minor’ detail: full links are working perfectly, but a link like this does a force close:

<a href="/dr">Link with relative path</a>

The TextView is not a browser, so it doesn’t even know what protocol we are using, let alone what domain or path we’re supposed to be in. This will result in a force close, with a log that looks like this:

ActivityNotFoundException: No Activity found to handle Intent { act=android.intent.action.View data=/dr}

The intent expects something like “http://” or “mailto:” I presume, but if it gets “/dr”, there is no activity to handle the link.

Elegance aside, a simple solution is to make an absolute link if you know the correct data. This is a quick and dirty solution, but let’s share it anyway because a lot of solutions we found were specifically for linkify, not for complete “fromhtml” purposes.

/**
 * Removes relative a hrefs
 * @param spantext (from Html.fromhtml())
 * @return spanned with fixed links
 */
public Spanned correctLinkPaths (Spanned spantext) {
  Object[] spans = spantext.getSpans(0, spantext.length(), Object.class);
  for (Object span : spans) {
    int start = spantext.getSpanStart(span);
    int end = spantext.getSpanEnd(span);
    int flags = spantext.getSpanFlags(span);
    if (span instanceof URLSpan) {
      URLSpan urlSpan = (URLSpan) span;
      if (!urlSpan.getURL().startsWith("http")) {
        if (urlSpan.getURL().startsWith("/")) {
          urlSpan = new URLSpan("http://domain+path" + urlSpan.getURL());
        } else {
          urlSpan = new URLSpan("http://domain+path/" + urlSpan.getURL());
        }
      }
      ((Spannable) spantext).removeSpan(span);
      ((Spannable) spantext).setSpan(urlSpan, start, end, flags);
    }
  }
  return spantext;
}

We traverse the objects in the Spanned. If we find a URLSpan we copy the span and fix that one. Then we remove the old span, and insert the new one. The copy move might not be necessary, but as it doesn’t bother us at the moment I’ll leave it like this. This has taken up quite enough time, thankyouverymuch. If you want to do something extra, and instead of opening a browser do your own onClick magic, you can take a look at this anddev forumthread.

Finally, our assign has to look like this obviously:

//description is still a TextView
description.setText(correctLinkPaths(Html.fromHtml(desc)));
description.setMovementMethod(LinkMovementMethod.getInstance());

Another interesting link is this bugreport on code.google.com. I’d say that your browser shouldn’t know where to point itself, but apparently the project members don’t agree.

It seems that a WebView element in Android reserves some space for a vertical scrollbar by default. This shows up as a small, vertical strip of white space on the right, which is not padding or margin but some space reserved for a scrollbar.

To get rid of this you have to set the scrollbar style to display the scrollbars inside the content area. This can be done with the following code:

webview.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY);

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 :)