David Janes’ Code Weblog

January 11, 2010

How to add XCode ‘#pragma mark’-like sections for Python code

python, tips · David Janes · 7:43 am ·

Just add in your Python comments:

# MARK: comment
# TODO: comment
# FIXME: comment
# !!!: comment
# ???: comment

From here.

December 25, 2009

Make INPUT fields select dates using the YUI Calendar Control

code fragments, javascript, tips, yui · David Janes · 3:57 pm ·

Here’s a lengthy Javascript code segment that can be dropped into any of your existing forms that provide for a date selection range. It has the following functionality:

  • clicking a date field pops up a YUI Calendar control to change the date
  • the date field is made read only by this code
  • pressing ESC while a Calendar is displaying dismisses it
  • pressing DEL while a Calendar is displaying clears the input field
  • there is an Input field for the start date and an Input field for the end date
  • you cannot make the end date before the start date, and you cannot make the start date after the end date
  • that the dates are optional
  • dates are displayed in the standard ISO YYYY-MM-DD format

This code sample demonstrates:

  • the YUI Calendar control, including
    • setting the date in the control
    • getting the date from the control (after selection)
    • dynamically displaying and hiding the control
    • manipulating ISO dates into the format that YUI requires
    • setting min and max dates on the fly – specifically, this shows you how to manipulate YUI “config” parameters which the documentation implies are readonly (look for functions in source code named like configMinDate)
    • simple YUI animations (to show that a field value has been changed)
    • detecting keystrokes (DEL and ESC) using YUI
    • adding functionality using Javascript, after the DOM loads

    This assumes:

    • you have an Input field for the start date with DOM id="id_event_start"
    • you have an Input field for the end date with DOM id="id_event_end"
    • that the Form (or a parent element of the Form, up to Body) has DOM class="yui-skin-sam"

    Add the following Script inclusions to your HTML (note: not all of these are probably needed, and there’s more efficient ways to include YUI JS, especially for production deployments):

    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/json/json-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/event/event-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/connection/connection-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/dom/dom-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/element/element-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/button/button-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/dragdrop/dragdrop-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/container/container-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/yuiloader/yuiloader-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/container/container_core-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/calendar/calendar-min.js"></script>
    <script type="text/javascript"
     src="http://yui.yahooapis.com/2.8.0r4/build/animation/animation-min.js"></script>
    

    Add the following CSS inclusions to your HTML:

    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/button/assets/skins/sam/button.css" />
    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/container/assets/skins/sam/container.css" />
    <link rel="stylesheet" type="text/css"
     href="http://yui.yahooapis.com/2.8.0r4/build/calendar/assets/skins/sam/calendar.css" />
    

    Add the following CSS to your HTML:

    <style type="text/css">
    
    #id_calendar {
     position: absolute;
     z-index: 15;
    }
    .yui-skin-sam .yui-panel .bd, .yui-skin-sam .yui-panel .ft {
     background-color:#FFFFFF;
    }
    </style>
    

    Add the following HTML fragment:

    <div id="id_calendar_wrapper">
     <div id="id_calendar"></div>
    </div>
    

    And finally, here’s the JS that makes it all work

    <script type="text/javascript">
    calendar_js = {
     c : null,
     start_e : null,
     end_e : null,
     current_e : 0,
     supress : 0,
    
     create : function() {
     calendar_js.start_e = YAHOO.util.Dom.get("id_event_start");
     calendar_js.end_e = YAHOO.util.Dom.get("id_event_end");
    
     YAHOO.util.Event.addListener(calendar_js.start_e, "click", calendar_js.onclick, this);
     YAHOO.util.Event.addListener(calendar_js.end_e, "click", calendar_js.onclick, this);
    
     YAHOO.util.Dom.get(calendar_js.start_e).readOnly = true;
     YAHOO.util.Dom.get(calendar_js.end_e).readOnly = true;
    
     (new YAHOO.util.KeyListener(document, { keys:27 }, { fn:calendar_js.onesc })).enable();
     (new YAHOO.util.KeyListener(document, { keys:8 }, { fn:calendar_js.ondel })).enable();
     },
    
     onesc : function() {
     calendar_js.current_e = null;
     if (calendar_js.c) {
     calendar_js.c.hide();
     }
    
     },
    
     ondel : function() {
     if (calendar_js.current_e) {
     calendar_js.current_e.value = "";
     calendar_js.current_e = null;
     }
     if (calendar_js.c) {
     calendar_js.c.hide();
     }
    
     },
    
     toydate : function(d) {
     if (!d) {
     return null;
     }
     if (d.value) {
     d = d.value;
     }
     if (d && d.length) {
     d = d.replace(/(\d\d\d\d)-(\d\d)-(\d\d)/, "$2/$3/$1");
     if (d.indexOf('/') == 2) {
     return d;
     }
     }
     return null;
     },
    
     onclick : function(evt, obj) {
     var e_e = YAHOO.util.Event.getTarget(evt);
    
     if (calendar_js.c == null) {
     calendar_js.c = new YAHOO.widget.Calendar(null, "id_calendar");
     calendar_js.c.render(); 
    
     calendar_js.c.selectEvent.subscribe(calendar_js.ondate, calendar_js.c, true);
     }
    
     var c_e = YAHOO.util.Dom.get("id_calendar");
     if (calendar_js.current_e == e_e) {
     calendar_js.current_e = null;
     calendar_js.c.hide();
     } else {
     YAHOO.util.Dom.setAttribute(c_e, "display", "block");
     calendar_js.current_e = e_e;
    
     var region = YAHOO.util.Region.getRegion(e_e);
    
     calendar_js.c.show();
    
     if (e_e.value.length) {
     var d = calendar_js.toydate(e_e);
     if (d) {
     calendar_js.supress = 1;
     calendar_js.c.select(d);
     }
     }
    
     if (e_e == calendar_js.start_e) {
     var d = calendar_js.toydate(calendar_js.end_e);
     if (d) {
     calendar_js.c.configMaxDate(null, [ d ], null);
     calendar_js.c.configMinDate(null, [ "01/01/1970" ], null);
     }
     } else {
     var d = calendar_js.toydate(calendar_js.start_e);
     if (d) {
     calendar_js.c.configMinDate(null, [ d ], null);
     calendar_js.c.configMaxDate(null, [ "01/01/2200" ], null);
     }
     }
    
     calendar_js.c.render();
     YAHOO.util.Dom.setXY(c_e, [ region.right + 2, region.top ]);
     }
     },
    
     ondate : function(type, args, obj) {
     if (calendar_js.supress) {
     calendar_js.supress = 0;
     return;
     }
    
     var date = this.toDate(args[0][0]);
    
     var year = "" + date.getFullYear();
     var month = "" + ( 1 + date.getMonth() );
     while (month.length < 2) {
     month = "0" + month;
     }
     var day = "" + date.getDate();
     while (day.length < 2) {
     day = "0" + day;
     }
    
     var animate = new YAHOO.util.ColorAnim(calendar_js.current_e,
     {backgroundColor: { from: '#ffff99', to: '#FFFFFF' } }
     );
     animate.duration = 0.75;
     animate.method = YAHOO.util.Easing.easeOut;
     animate.animate(); 
    
     calendar_js.current_e.value = year + "-" + month + "-" + day;
     calendar_js.c.hide();
     calendar_js.current_e = null;
     },
    
     end : 0
    };
    YAHOO.util.Event.onDOMReady(calendar_js.oninit);
    </script>
    

    Notes:

    • no apologies for verboseness – I write code so I can understand it in 24 months time
    • my original code is a lot more “tabby” – I flattened to a single space so they’d fit in the space constraints here
    • the code should be fairly self explanatory if you step through it – the hard stuff is probably YUI weirdness and you can probably just accept that “that’s just the way it is”

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

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<0> 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 1 --name david
  • 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

October 24, 2009

Fizz Buzz in one line of Python

code fragments, python · David Janes · 5:58 am ·

Since Libin points to “Fizz Buzz” in one line of Ruby, I feel it’s only fair to do it in one line of Python:

print [ not i % 15 and "Fizz Buzz" or not i % 5 and "Buzz" or not i % 3 and "Fizz" or i for i in xrange(1, 101) ]

My preference is to really have a few more brackets in there, for clarity but apparently terseness is considered a virtue in and off itself sometimes. There’s other implementations of this in one line of Python:

October 22, 2009

hAtom hits the big time

microformats, semantic web · David Janes · 2:14 pm ·

From the Read/Write Web:

Earlier this year, the Associated Press, together with the Media Standards Trust, introduced hNews, a new microformat for describing news content. HNews allows publishers to easily attach machine-readable news semantics to content on the web. Today, the AP announced the completion of the first draft of hNews. In addition, TownNews, announced that is will support hNews in its BLOX content management system, which is being used by over 1,500 newspapers in the US.

HNews, which is an extension of the hAtom format, only requires content users to specify information about the source organization. In addition, publishers can specify geo-information, a dateline element, license information and information about the code of ethics that governed the behavior of the author of a given site. At its most basic level, hNews, just like other microformats like hCard or hCalendar, allows search engines spiders to identify and read semantic information that would otherwise be buried within a text and would be hard to identify for search engines.

The RRW article then goes on to posit some ideas about this being related to AP’s efforts to track use of their web content across the web. This strikes me as rather farfetched, as stripping out the microformat tags is beyond trivial. What makes this exciting for me is that it makes it more likely that search engines will start recognizing hAtom tags and thus will start properly indexing blogs and other microcontent properly into search engines.

In other exciting hAtom-related news, WordPress 2.7 has the post_class function to allow (new) templates to automatically include the hentry tag on blog posts! Also see the Smashing Magazine article on this.

September 26, 2009

UIPageControl + UIScrollView

code fragments, iphone, tips · David Janes · 7:58 am ·

This is an example of how to combine a UIPageControl and a UIScrollView together to create a “snap to page”-like effect that is seen on the iPhone’s home screen. This sample is partially based on Apple’s UIPageControl example.

XCode Sample Project

August 10, 2009

Travel Websites & Web 3.0

Discover Anywhere Mobile, ideas, semantic web · David Janes · 2:53 pm ·

On my Discover Anywhere Mobile blog, I’ve posted a list of recommendations about how travel websites can use information to extend their reach.

July 1, 2009

RIM Application Development

Discover Anywhere Mobile, rim · David Janes · 10:07 am ·

The Boy Genius wrote a blog post on the state of RIM BlackBerry application development this morning that’s getting more than a little traction:

One word is where RIM fails so miserably it isn’t even imaginable: software.

You have to look at the big picture here… for what RIM is working with (an incredibly miserable Java OS with so much security and encryption and smoke-blowing APIs) they’ve hit the jackpot. Their OS architecture is fantastic, their use of security is what makes them so trustworthy. But, as each handset release comes closer and closer, people start to see the bigger picture. And that’s the fact that RIM’s OS is more than antiquated, it’s borderline laughable. But it works, you’re thinking, so what’s wrong? I’ve been saying this for years, but it wasn’t designed to do anything the BlackBerry does now. Imagine scotch taping car parts to a 200hp engine and see how far that gets you. Obviously, it’s just a viciously rough metaphor, but we believe a correct one.

There’s so many limitations to RIM’s OS, and even RIM’s data network that it offsets all the wonderful things they’ve managed to accomplish. Remember when people were so excited over leaked shots of OS 4.6 and I said somewhere it was just a theme? Well, was I wrong? Oh, look! OS 5.0! What changed? 99% nothing. Some functionality is added here and there, but the mobile phone landscape has changed so drastically in the last two years, that RIM, admittedly known to planning “three years out” looks to be unable to see the proper direction to head.

You can throw $1,000,000,000 at developers but you won’t get any if your OS, tools, and documentation are so bad, and that’s really in the end a lot of what I’m getting at. I was laying in bed at around 3AM early one morning recently, looking through the iPhone App Store and I came across EA’s Tiger Woods Golf. $4.99, why not? Wait, it’s 150MB? Wow, it must be good. I clicked purchase and literally 4 minutes later, Tiger Woods was installed and up on my screen. Granted I was on a high-speed Wi-Fi connection, but it made me realize more than ever that RIM has the most uphill battle of their lifetimes. When a BlackBerry application over 500k is considered “large”, something’s wrong. When TweetGenius is one of the first BlackBerry applications to do fun, unique things like transparent overlays, consistent shortcuts, and a straight forward UI, something is wrong.

The reason why this is so frustrating to me and I’m guessing many is because RIM literally almost has it all. They’ve got it! They are 90% there but that last 10% has become the most important. If you take Apple for example, and see their shortcomings, and then what they’ve done to fix them, it’s remarkable. It’s a completely different DNA than RIM’s but it’s working. In two years Apple has practically matched Research In Motion in almost every consumer area while having the most advanced mobile operating system with the most advanced mobile SDK on the planet. If Apple can do this in just two years and RIM has stood still, no one thinks that’s a problem?

Larry Dignan of ZDNet chimes in (I suspect he was going to the iPhone anyway):

The BlackBerry operating system issue struck home for me while on vacation. My Storm was my only ramp to the Web. Under stress and heavy usage the BlackBerry OS was clearly struggling. You could almost feel it choke when switching between apps, browsing and handling basic tasks. The interface was fine, but under the hood something is off.

Will these OS limitations affect my next phone choice when my Verizon Wireless contract is up? Possibly. The device is only 25 percent of the smartphone game these days. The operating system is everything. Apple gets it. Palm gets it. Google gets it. I’m not sure that Microsoft gets it. For RIM, it remains to be seen if the company gets OS religion.

For my next phone, I’ll be buying and OS instead of a device. If the iPhone comes to Verizon Wireless it’s most likely a no-brainer for me. I have more than a year to see if Palm’s Pre and WebOS is the real deal. Even the Motorola Android devices may hold promise for me. My last phone choice was basically an escape from Windows Mobile. If RIM doesn’t get its OS strategy together I may ditch the BlackBerry too. All I really need is to browse and tether to my laptop as a wireless card on a good network.

The real power behind the RIM brand is all the relationships they’ve built with communication providers and lock-in to the IT departments in business and government around the world. That said, there is a medium term danger that RIM is going to be undermined from the bottom by consumer-friendly devices such as the iPhone, in the same way that the PC undermined big iron’s lock on the enterprise market in the 80’s and 90’s.

I’m actively developing for the BlackBerry right now and I have to say it’s a struggle:

My current application approach is to create an entirely self contained browser application and bypass almost all RIM UI features. We should know soon whether this is a sane approach (I may open source this code if there’s interest).

Update: Alec Saunders of Calliflower adds comments about Nokia.

June 27, 2009

How iPhone OS 3.0 delivers location services

iphone, maps · David Janes · 6:38 am ·

Alec Saunders has a seriously cool article on his blog about how your iPhone knows where it is. I got to experience how well this works yesterday when we used TimmyMe in the McCowan Road/Ellesmere Avenue neighborhood to grab a coffee pre-meeting:

Many folks assume that iPhone’s location services are GPS based. In fact, they’re not. [...]

Skyhook’s innovation is to augment that with WiFi hotspot locations. Why WiFi? It can be wonderfully accurate, to within 30 to 60 feet, or 10 to 20m which is roughly the same as GPS. WiFi can establish a fix within less than a second. And, WiFi is not vulnerable to overhead obstructions, so it can be used indoors.

Skyhook’s technology relies on a database of WiFi access points in over 2,000 cities (and growing). Much like Google, they use vehicles to drive cities, and using a laptop in the vehicle, they chart the location of WiFi access points. In addition, on iPhone as users use the mapping application, new locations and newly discovered WiFi access points are automatically added to the database. It’s a kind of automated crowdsourcing, based on usage.

My emphasis on that last sentence. Very cool.

June 24, 2009

Discover Anywhere Mobile

Discover Anywhere Mobile · David Janes · 7:45 am ·

OK, it’s time to officially unveil my new company: Discover Anywhere Mobile. We’re creating mobile guides for the tourism and hospitality sector, and similar products for advanced store locators, conference guides, etc.. Platform-wise, we’re creating iPhone & BlackBerry applications, and then a mobile web browser version to catch other devices. Our belief is that over the next three years this is going to be the way tourists and travelers consume information – they want everything in the palm of their hands. Why would you want to carry a tour guide book with you anymore?

Note, and this is important, that we are not a content company. Our target clients are Destination Marketing Organizations (DMOs), festival & event organizers, large corporations, and so forth. We will write apps on their behalf using their data and the app will be released under their names. Discover Anywhere Mobile will handle all the bolts and nuts of making this happen: importing & managing data, creating & customizing apps, updating user’s apps, look & feel, etc.. DMOs provide the marketing and data, we provide the infrastucture and services to make it all happen.

Here’s the start of our official blurb:

Discovery Anywhere Mobile creates customizable mobile travel guide applications for Destination Management Organizations. Discover Anywhere Mobile can quickly and cost effectively compile your DMO’s event and listing information into mobile applications for the iPhone, BlackBerry and mobile web browsers. Applications created with Discover Anywhere Mobile’s services will meet traveler’s need for concise, relevant, timely destination information at the exact time and place they need it most – at your destination.

Go to our website and read more, and if you have your own site, please provide a link!

June 6, 2009

Lack of posting

administrivia · David Janes · 5:52 am ·

Sorry for the lack of posting – I’m busy applying my ideas rather than coming up with new ones ;-)

April 22, 2009

Coffee & iPhone IV

iphone · David Janes · 4:12 pm ·

We’ll be holding the next Coffee & iPhone tomorrow between 4 and 6p again at the Dark Horse Cafe, 215 Spadina Avenue for Coffee and iPhone.

  • What: Coffee & iPhone
  • When: Thursday, April 23 from 4 – 6p
  • Where: Dark Horse Cafe, 215 Spadina Avenue (at Sullivan Street)

Older Posts »

Powered by WordPress