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!

AndroBlip 2.0

On 2011/01/09, in Releases, by Nanne Huiges

AndroBlip in the Android MarketA long time since there was an update! We’ve been changing things like crazy, sometimes two or three times over, so it took a little more time than planned.

First of all: how to get it, second: what’s new?

Getting AndroBlip 2.0

1. Download AndroBlip on the Android market (small fee of 0,99 euro)
2. Download from the Website (apk): Androblip 2.0 (free).

Be adviced: you’ll probably have to re-authenticate to your account after updating. Really sorry about that.

ChangeLog

Apart from the extra features, the layout changes and bugfixes, there are a lot of changes going on “under the hood”. That’s why this major release is baptized “2.0”! We haven’t thought of a spiffy name for this release, sorry bout that :). Check out the new layout (List style) and tell us what you think!

Features

  • Added Search. Press the search key on your phone, or select it as a view
  • Notifications added. Press menu->notifications
  • Page up/down buttons on the comments screen: easy scrolling! Long-click on these buttons for top/bottom

Layout

  • The Grid should render a lot better now (no more 1-pixel-too-large). Major thanks to Thomas from Frozen Fractal for that!
  • New layout: List view (go to menu->prefs to set this style)
  • Preferences are now according to Android standards
  • Easier usable buttons (e.g. view-choosing dialog, links at the bottom of entries)
  • Fixed ‘add date’ difficulties in upload screen
  • Trying to find the last texts without the proper font
  • Numerous smaller changes

Bugfixes & other changes

  • Bugfix for relative paths in links
  • Loading screens are dismissable
  • Cache uses standard (browser shared) cache
  • Grid layout now works for all(?) aspect ratios
  • Work on the back-button behaviour (we’ve seen that one before :)  )
  • Entry date visible
  • After posting comment view that comment

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.

No drives in export dialog

A bit offtopic from the usual ‘I had trouble with android’ posts, but this morning as I was trying to export some pictures, and only my ‘user dir’ and ‘desktop’ where available for choice. No disks at all. Annoying.

The reason it happened was, I think, the last export location (a removable device) wasn’t there anymore. Don’t know this for sure though.Anyway, teh google helps! It’s about the same symptoms, maybe not the same cause but still. Some sidenotes to that post are neccessairy, so this is what I did:

First, find the preferences file.

This is the location on my windows 7 , but the forum post also states this article for more location possibilities on other systems like Vista and all.

C:\Users\–YOUR USERNAME–\AppData\Roaming\Adobe\Lightroom\Preferences

It could be you try to open the “Application Data” dir, but for some reason this doesn’t work in Windows 7 installs. Just go to that AppData dir. Just try to find the file called “”Lightroom 2 Preferences.agprefs”.

Find the line for the export dialog.

Find this line:

AgMRUPopupList_AgExport_destinationFolderPathPrefix

Fix that line.

With me there was a line in there that was not a real option anymore. But just removing that line didn’t work. I don’t know why, but I had to fiddle around with it a bit. It seems to me that there should be more than 1 line, and they all should exist. My file part looks like this now:

AgMRUPopupList_AgExport_destinationFolderPathPrefix = “s = {\
\”C:\”,\
\”E:\\\\fotos\”,\
}\

Finished!

Again, props to this adobe forums link , which by the way also describes the lines to edit for a similar problem with the import dialog.

http://forums.adobe.com/message/2746280

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

AndroBlip 1.4

On 2010/08/29, in AndroBlip, Releases, by Nanne Huiges

AndroBlip in the Android MarketAs the end of life.turns. is come and gone we can safely say that the quick development and release circle for that feature didn’t do too much harm. Now these features have to be removed as the project is at it’s end. Together with some small layout changes and some requests this makes for version 1.4!

Getting AndroBlip 1.4

1. Download AndroBlip on the Android market (small fee of 0,99 euro)
2. Download from the Website (apk): Androblip 1.4 (free).

ChangeLog

Removed life.turns. options

As the options and upload were specifically for the life.turns. project that has ended, the clutter has been reduced by removing the files and menu option for it.

Long-press on entry shows EXIF

A reasonable request, and easy enough to do: long-press on an entry shows you the EXIF information, just as you would see when you click the link a bit lower on the same page.

Autorefresh

The first second comment in the market (by ‘Andrew’) requested this quite quickly and it has been requested by mail too. We’ve made this a setting, because it can be a real pain when you have a bad data connection. It starts ‘off’ by default, and you can find it in the preferences. We also changed the ‘loading’ behaviour a bit, so to have a better reponse on slow loading systems. If you’re checking out the preferences, also check out the new layout of the about page, where we now list some new application statistics.

Stats

We’ve added the new resource ‘statistics’ on the about page as you can see below.

File location Bugfix

The previous update tried to fix the fact that all files were saved in the root of your sdcard. But that didn’t work. This update should fix that persistent bug and place everything in the .AndroBlip folder. You can remove all the files starting with “.ABlipCache” if you want because they should and will be stored in the .ABlipCache folder. (Yes, you can figure out why I didn’t fix the bug in the first place from those filenames and that foldername :)  )

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"

AndroBlip in the Android MarketIn short: this brings you a new feature for life.turns and some minor bug fixes. The life.turns feature is a one-time only deal for next months project, and will be removed in the release after this one.

The camera functionality might stay though, if it turns out to be stable.

Getting AndroBlip 1.3

1. Download AndroBlip on the Android market (small fee of 0,99 euro)
2. Download from the Website (apk): Androblip 1.3 life.turns (free).

New Feature

life turnedYes! All the hip kids are doing it, and so are we! What you mean by ‘it’ you ask? Well: life.turns ofcourse. You can read all about it trough that link, but in short: We’re making a zoetrope by uploading one of eight pre-chosen poses as your daily blip. Blipfoto central is going to go all the way with this, and there’s going to be all sorts of big events during the Edinburgh art festival.

Now the official blipfoto iPhone app™ can do all sorts of stuff and is really hip, but we were a little late to the game. But as uploading to this project should at least be possible for us cutting edge android users™ we have added some basic functionality to align a person with one of the poses, and upload it using the special magic life.turns methods, so it gets flagged as an entry!

This was a bit of a difficult process, because of the time frame and of the possible trouble the different handsets can come up with, so I’m afraid this is it for life.turns.

Bugfixes

* The data AndroBlip caches isn’t dumped in the root of the sdcard anymore. Thank you Ronan for pointing out that bug :)
* The back behaviour that was heavily changed in 1.2 is now a bit better: you can press back to go to you homescreen in the main activity, like the default behaviour should be in Android. Thanks for BigLaurens amongst others for that report.

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