David Janes' Code Weblog

November 28, 2009

UIImage imageNamed: and caching

code fragments,iphone · David Janes · 7:07 am ·

There may be some temptation to improve caching for UIImage imageNamed:

static UIImage* starred = nil;
if (starred == nil) starred = [UIImage imageNamed:@"starred.png"];

Don’t do this, it’s really stupid. It’s already being held by a cache, and maybe dropped out of it at any time, leading to miraculous crashes.

November 23, 2009

Animated Slideshow on Android

android,code fragments,java · David Janes · 7:20 pm ·

After a brutal day of running into many Android bugs and misdocumentations, I’ve finally figured out how to create a slideshow of images for Android, with each imaging fading to the next. This is not unlike AnimationDrawable, except with a smoother and slower transition between images.

First, you need a resource file with something like this in it. The FrameLayout is the clever bit: it stacks everyone of its children on top of each other.

<FrameLayout
 android:id="@+id/frame"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
>
 <ImageView
 android:id="@+id/slide_1"
 android:layout_gravity="center_vertical"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
 <ImageView
 android:id="@+id/slide_2"
 android:layout_gravity="center_vertical"
 android:layout_width="wrap_content"
 android:layout_height="wrap_content"
 />
</FrameLayout>

Then you need code that looks like this:

public class TopListActivity
  extends Activity
{
  private static class AnimationAlphaTimer
    extends TimerTask
    implements Animation.AnimationListener
  {
    TopListActivity topList;
    Vector<BitmapDrawable> images;
    int count = 0;

    public AnimationAlphaTimer(TopListActivity _topList)
    {
      this.topList = _topList;

      this.images = new Vector<BitmapDrawable>();
      for (int i = 0; ; i++) {
      // LOAD IMAGES HERE
      }

      if (this.images.size() > 0) {
        this.topList.slide_0.setBackgroundDrawable(this.images.get(0));

        if (this.images.size() > 1) {
          this.topList.slide_1.setBackgroundDrawable(this.images.get(1));
        }
      }

      this.count = 1;
    }

    public void launch()
    {
      if (this.images.size() >= 2) {
        (new Timer(false)).schedule(this, 100);
      }
    }

    @Override
    public void run()
    {
      this.doit();
      this.cancel();
    }

    private void doit()
    {
      if ((this.count % 2) == 0) {
        AlphaAnimation animation = new AlphaAnimation(1.0f, 0.0f);
        animation.setStartOffset(3000);
        animation.setDuration(3000);
        animation.setFillAfter(true);
        animation.setAnimationListener(this);

        this.topList.slide_1.startAnimation(animation);
      } else {
        AlphaAnimation animation = new AlphaAnimation(0.0f, 1.0f);
        animation.setStartOffset(3000);
        animation.setDuration(3000);
        animation.setFillAfter(true);
        animation.setAnimationListener(this);

        this.topList.slide_1.startAnimation(animation);
      }
    }

    public void onAnimationEnd(Animation animation)
    {
      if ((this.count % 2) == 0) {
        this.topList.slide_1.setBackgroundDrawable(
          this.images.get((this.count + 1) % (this.images.size()))
        );
      } else {
        this.topList.slide_0.setBackgroundDrawable(
          this.images.get((this.count + 1) % (this.images.size()))
        );
      }

      this.count++;
      this.doit();
    }

    public void onAnimationRepeat(Animation animation)
    {
    }
    public void onAnimationStart(Animation animation)
    {
    }
  }

  @Override
  public void onResume()
  {
    super.onResume();

    (new AnimationAlphaTimer(this)).launch();
  }
}

The “create a Timer trick” in onResume is courtesy of Diego Torres. If you just try to run the animation, it’s likely just to choke.

November 20, 2009

How to use XCode for Android Projects

android,code fragments,ideas,macintosh · David Janes · 6:07 pm ·

Let’s assume you already have an Android project on your Mac.

Create the XCode Project

  • start XCode
  • select File > New Project…
  • select External Build System
  • go to the parent directory of your Android Project
  • in the Save As: field, enter the directory name of your Android Project
  • select the scarily-misnamed Replace option [not in XCode 4 -- thanks Jusin]

Add Files

In your new XCode project:

  • select first item in the left hand column, which is the name of your project
  • right-click, select Add > Existing Files…
  • select add files (don’t select the Copy option)
  • organize as desired (I like to do a lot of grouping). You should be probably adding at least your Java files and your Layout resources.

Configure your Build Target

In your new XCode project:

  • look for Targets
  • inside will be a target for your project’s name
  • double click on it
    • change Build Tool to ant
    • change Arguments to install

Clicking ⌘B should now compile your project.

Note: if you figure out how to have a Build vs. Build & Install (e.g. ⌘ENTER) please let me know!.

Getting XCode to Recognize Java errors

  • Reconfigure the Build Target, changing ant to ./xant
  • Make a file xant in the project’s home directory, using the code below
  • do (from a Terminal) chmod a+x xant
#!/usr/bin/env python

import sys
import re
import subprocess

av = list(sys.argv)
av[0] = "ant"

p = subprocess.Popen(av, stdout = subprocess.PIPE)

javac_rex = re.compile(" +[[]javac[]] +")
line_rex = re.compile("[.]java:[\d]+:")

pending = ""
while True:
    d = p.stdout.read(128)
    if not d:
        break

    d = pending + d

    nx = d.rfind('\n')
    if nx == -1:
        pending = d
        continue
    else:
        d, pending = d[:nx + 1], d[nx + 1:]

        d = javac_rex.sub("", d)
        d = line_rex.sub(r"\g error: ", d)
        sys.stdout.write(d)
        sys.stdout.flush()

sys.stdout.write(pending)
p.wait()
sys.exit(p.returncode)

Note: this code has been updated from the original post. It now reads little chunks and outputs them immediately rather than post-processing the ant output.

November 16, 2009

PIL, libjpeg, jpeg and Mac OS/X Snow Leopard

macintosh,python,tips · David Janes · 7:47 am ·

If you want to the use the Python Imaging Library on Mac OS/X Snow Leopard, these instructions appear to be the best way to to get libjpeg installed:

1. Download the source from http://libjpeg.sourceforge.net/

2. Extract, configure, make:

tar zxvf jpegsrc.v6b.tar.gz
cd jpeg-6b
cp /usr/share/libtool/config/config.sub .
cp /usr/share/libtool/config/config.guess .
./configure --enable-shared --enable-static
make

3. You may need to create the following directories:

sudo mkdir -p /usr/local/include
sudo mkdir -p /usr/local/lib
sudo mkdir -p /usr/local/man/man1

4. Now you can install it as usual.

sudo make install

I used to use Fink on Leopard, but it didn’t seem to work to well this time. If you’ve previously made an attempt at installing PIL, make sure to rm -rf build.

Django 1.1 and ImageField

code fragments,django,python,tips · David Janes · 7:42 am ·

Having recently upgraded to Django 1.1, I suddenly started getting the error messages that look like:

  File "/Library/Python/2.6/site-packages/django/db/models/fields/related.py", line 257, in __get__
    rel_obj = QuerySet(self.field.rel.to).get(**params)
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 300, in get
    num = len(clone)
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 81, in __len__
    self._result_cache = list(self.iterator())
  File "/Library/Python/2.6/site-packages/django/db/models/query.py", line 251, in iterator
    obj = self.model(*row[index_start:aggregate_start])
  File "/Library/Python/2.6/site-packages/django/db/models/base.py", line 324, in __init__
    signals.post_init.send(sender=self.__class__, instance=self)
  File "/Library/Python/2.6/site-packages/django/dispatch/dispatcher.py", line 166, in send
    response = receiver(signal=self, sender=sender, **named)
  File "/Library/Python/2.6/site-packages/django/db/models/fields/files.py", line 368, in update_dimension_fields
    (self.width_field and not getattr(instance, self.width_field))
AttributeError: 'Icon' object has no attribute 'width'

The issue turns out to be that you can’t just define the ImageField in your model, you also have to explicitly define the fields that will store the width and height fields for the image field. The sql generation tools for Django don’t do it for you.

For various reasons, I can’t do that this at this moment so I made the following I hack which I strongly recommend you don’t use (for efficiency reasons, as with this the height & width have to be computed every time you access the image). This is added to site-packages/django/db/models/fields around line 367.

if self.width_field and not hasattr(instance, self.width_field):
     dimension_fields_filled = False
else:
     dimension_fields_filled = not(
          (self.width_field and not getattr(instance, self.width_field))
          or (self.height_field and not getattr(instance, self.height_field))
     )

The proper solutions probably involve:

  • not adding the hack above and explicitly adding the fields, as per here
  • updating the documentation (here and here) to say “you also have to add the fields to the DB”
  • making syncdb/sql automatically generate the width & height fields

November 14, 2009

Twitter uses WOEIDs – why you should care

ideas,maps · David Janes · 5:43 am ·

Because I don’t think this announcement got sufficient attention: Twitter is using Yahoo’s WOEIDs to identify locations in its Geolocation API.

WOEIDs are:

  • a numeric identifier for geographical places or regions, from a “place of interest” all the way up to continent
  • uniquely defined, for each place
  • hierarchically nested
  • related to each other by natural concepts, such as neighbor-of, sibling-of, etc.
  • managed, by Yahoo
  • a language independent way of talking about place
  • not necessarily tied to political boundaries, i.e. there’s a WOEID for the Bay Area

This announcement is important because:

  • it orders tweets into “containers” that can be used to find those tweets easily. In particular, there’s now codes that a machine can easily work with to find human concepts. Previous attempts at identification depended upon things such user entered hash tags for things such as a nearby airport, postal code, zip code, etc. and other concepts that don’t necessarily reflect what’s really going on
  • it encourages others to start building on WOEIDs, a non-Google way of identifying places

Read more:

Note that I have some concerns about how well WOEIDs will work when we start wanting them being dynamically/crowd defined down to the business level (e.g. sort of like what foursquare is doing; I don’t think they use WOEIDs though.)

November 13, 2009

18 Hours of JAR Hell with Android & Google Maps

android,tips · David Janes · 7:39 am ·

Yesterday, my experiments came with Android programming came to a crashing halt when I tried to use the Google Maps libraries. No matter what I tried – adding every possible Google Maps SDK, manually adding the jar to the project’s libs directory, a few more things too foolish to talk about – I would get one of these errors:

    [javac] .../LocationMapActivity.java:21: package com.google.android.maps does not exist
    [javac] import com.google.android.maps.*;
    [javac] ^
    [javac] .../LocationMapActivity.java:24: cannot find symbol
    [javac] symbol: class MapActivity
    [javac] 	extends MapActivity

or

W/dalvikvm(  247): Unable to resolve superclass of Lcom/.../LocationMapActivity; (89)
W/dalvikvm(  247): Link of class 'Lcom/.../LocationMapActivity;' failed
D/AndroidRuntime(  247): Shutting down VM
W/dalvikvm(  247): threadid=3: thread exiting with uncaught exception (group=0x4001b188)

or

Failure [INSTALL_FAILED_MISSING_SHARED_LIBRARY]

Well. Here’s what you need to know:

  • the emulator you’re running must match the target you set up your project for
  • the emulator actually (as I understand it) actually has a lot of code built into it, so it’s not enough to link against the jar file
  • if you don’t have the right target specified, ant won’t look for jar files and just give you the linking errors
  • the Google Maps SDK is tied into Android’s concept of a target

In particular, this is what you want to do to fix your problem – it’s all about “targets”:

  • run android list targets to find a suitable Google Maps-enabled target
  • make an avd (“Android Virtual Device”) that has that target. This is really easy to do in the UI provided by running android &. Don’t be fooled by seeing just “Platform” and “API Level” listed there – the target determines what these are
  • run the appropriate emulator to use the avd that you created, that has that target, that includes the Google Maps SDK: e.g. emulator -avd david_6 &
  • update your project to reflect the new target: android update project --path HelloAndroid --target 7

If you’re an Eclipse user, I’m sure there’s some easy way to do this, but the command line works just fine for me. I’m not the first person to see this problem, but I’ve never really seen this spelled out so explicitly how to solve it so hopefully you fine this of use.

November 8, 2009

How to install Android SDK on your Mac

android,mobile · David Janes · 5:56 am ·

I was interested in trying the Android emulators yesterday on my Mac, so here’s what I did. It’s much simpler than the documentation makes it out to be:

  • download and unpack the SDK zip file from http://developer.android.com/sdk/index.html
  • add the SDK tools directory to your path (in ~/.bashrc):
    export PATH=${PATH}:${HOME}/…/android-sdk-mac/tools
  • start Terminal
  • run the “Android SDK and AVD Manager”:
    android &

    • select Settings on the left
      • select Force https://… sources to be fetched using http://…
    • select Available Packages on the left
      • select the checkboxes for the various SDK Platforms
      • press Install Selected
  • make an “Android Virtual Device”:
    android create avd --target 6 --name david -c 200M
  • run the emulator (note: it can take minutes to boot):
    emulator -avd david

Next Generation iPhone to include RFID reader?

ideas,iphone · David Janes · 5:38 am ·

This was something I was hoping would be in the 3GS. Rumor: Next generation iPhone to be RFID enabled:

A highly reliable source has informed me that Apple has built some prototypes of the next gen iPhone with an RFID reader built in and they have seen it in action. So its not full NFC but its a start for real service discovery and I’m told that the reaction was very positive that we can expect this in the next gen iPhone.

If Apple does it, expect every phone manufacturer and their sister to begin pumping out NFC enabled phones, at least for service discovery and sync.

This just reinforces what we knew based on the two separate patents Apple submitted that had the iPhone enabled to read RFID tags. I’m told that the touch project video and the BT SIG’s specs were all driving forces to push this forward as well as other factors.

Guess I’ll be touching my iPhone to my Mac to link them together to sync iTunes by next year.

My thoughts:

  • this will be a game changer for RFID, bringing applications down to the small business and hobbyist layer
  • RFID + applications is a wicked combination, making the mobile phone an Anything Device; Microsoft, Motorola and RIM should all try to get the jump on this
  • RFID + in app purchase is a wicked combination; put your thinking cap on for this one
  • RFID + Augmented Reality makes context sensitive layers start popping up
  • I’m not a game person, but you know there’s got to be gaming implications for this
  • what are the implications for big chains — groceries, consumer electronics — when anyone can walk in the door and get a better price buy waving their phone at a shelf

Powered by WordPress

Switch to our mobile site