I’m internets famous!

It’s been a busy few weeks for me since Remix – surgery, a couple of MahTweets releases, expansion of the MahTweets team, but I’ve become rather internets famous while I was at this year’s Remix in Melbourne a few weeks ago.

First, there is the Hyro blog post from the lovely Kate Carruthers, Windows and Open Source – Social Media Aggregation client. Then Michael Kordahi and Andrew Coates ambushed me and dragged me up on stage for their live Frankly Speaking vod/podcast for FS31: Live from REMIX10

In my defence, I am a shy guy and don’t really like being in the spotlight – but how awesome are my sideburns?*

image

(Image Copyright Nick Ellery)

 

* The answer is: very awesome


 

xAuth is balls

19 June 2010 , , ,    1 Comment

When Twitter originally released their API it had only one form of authentication what is now known as "basic auth". The issue with basic auth is that it requires any applications or websites that ends up using the API to use the username and password. Obviously, this represents a security issue because third parties have full access to the user via API or by just taking those passwords and logging in manually.

Like many other social services, Twitter eventually cottoned on that this was a bad idea, and they’ve adopted OAuth, a token based authentication delegation system.

Some have argued that OAuth is too complex, if you read the Twitter dev mailing list there are numerous requests to keep basic auth but just move it to SSL only. Security and trust shouldn’t be the easiest thing in the world, there needs to be a degree of complexity about them.

Somebody at Twitter got it into their mind that listening to the general whingers and whiners was a good idea. Instead of just relying on open standards, they’d make their own protocol dubbed xAuth which works in conjunction with Oauth. xAuth requires the user to enter in their user name and password, then the application/website can use those credentials to exchange for OAuth access token/secrets. Hang on a moment, their new security measure requires you to enter in "basic auth", essentially? This isn’t a case of two steps forward, one step backward, it’s a case of two steps forward and then a bullet in the head.

The idea of OAuth is so you don’t’ have to have complete trust in the applications – just the service. This method relies on the consumer application to "be a good citizen" and discard the username and password after they’ve got their tokens.

Twitter, please stick to social media, not security.


 

OoO: Demo Solution

28 May 2010 , ,    1 Comment

So it took me a little more than the intial couple of days I estimated, but you can finally download the VS2010 Solution for my OoO (aka OpenID, oData and OAuth together) post.

Please don’t take this as gospel or even an example of good practices.

Fire up the website, then fire up the command line client, which will redirect you to the Login page which requires OpenID credentials. There isn’t a fancy OpenID Relaying Party selector, you’ll just have to type it in yourself. That will then let you to generate a verifier token, which lets the CLI client to access the OData service.

This example ignores the scope values being passed in.

Would I use oData?

Yes and no. I’d use oData if I just wanted people to have LINQ access to various resources – but probably resources I wouldn’t try and protect. Why not? While oData lets you define service methods which let you perform more advanced operations and still return the oData format it doesn’t expose these service methods to the WCF Proxy Client (ie, what is generated from Add Service Reference..), but instead requires you to use magic strings. That is, something like:

DataServiceQuery<Product> q = ctx.CreateQuery<Product>("GetProducts")
                                .AddQueryOption("category", "Boats") ;
List<Product> products = q.Execute().ToList();

WCF Data Services lost me on the magic strings bit. I’d probably go for a plain ole WCF Service with SOAP/JSON endpoints if I needed methods instead of just the data.


 

OoO, aka OpenID, oData and OAuth together

I’ve been busy creating a web service or two for MahTweets 3. The biggest problem I have with creating web services is I generally hate storing user credentials/data. Using a delegated identity system like OpenID makes me feel better about it. It also means users of the service don’t have to sign up for yet another service, and can safely use their existing accounts from one of the thousands of OpenID service providers.

However, I wanted to use the service in a desktop client and OpenID is only a browser tech. That’s where OAuth enters the story. OAuth is getting more an more popular for authentication of web services so that users never have to enter in the username/password on unknown websites or desktop clients. Vimeo, Twitter, Yammer, LinkedIn and many more are already using OAuth today.

As for the service itself, of late I’ve been seeing a bit about Astoria/RIA Data Services/WCF Data Services. It’s had a few names in its time. WCF Data Services pushes out OpenData (aka oData), and lets you use LINQ in your client to query the web service. As always, Scott Hanselman has a great post on the basics of OData.

OAuth securing an oData service using OpenID for identity – OoO!

The flow from the client side is request access via OAuth, open browser and login with OpenID, "allow" client via OAuth, copy generated PIN verifier into the client, then have full access to web service. The OpenID portion of it could easily be replaced with username/password, FacebookConnect, or even another OAuth service that can be used for identity like Twitter.

Server Side

The server side stuff is mostly the same as what is found in the DotNetOpenAuth samples. I had problems getting it to work in an MVC Application, but that was solved by just adding SubmitChanges() at the end of ExpireRequestTokenAndStoreNewAccessToken and AuthorizeRequestToken within DatabaseTokenManager.

I’m not going to cover the DotNetOpenAuth setup, its long winded and mostly copy & pasting code.

Create a new WCF Data Service, open up the svc markup (ie, api.svc, not api.svc.cs) and modify the Factory to something like:

<%@ ServiceHost Language="C#"  Factory="MahTweetsWebsite.Application.WCFRestOAuthFactory" Service="MahTweetsWebsite.api" %>

Then create WCFRestOAuthFactory.cs

public class WCFRestOAuthFactory : DataServiceHostFactory
{
    protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new WCFRestOAuth(serviceType, baseAddresses);
    }
}

public class WCFRestOAuth : DataServiceHost
{
    public WCFRestOAuth(Type serviceType, Uri[] baseAddresses) : base(serviceType, baseAddresses)
    {
        this.Authorization.ServiceAuthorizationManager = new OAuthAuthorizationManager();
    }
}

All WCFRestOAuthFactory does is create a new WCF service of the Service specified and assigns the ServiceAuthorizationManager, which in this case is OAuthAuthorizationManager (copied from DNOA). There are other ways you can intercept calls in WCF Data Services to attach security – notably overriding OnStartProcessingRequest(ProcessingRequestArgs args) in the service class itself.

The next dilemma is what if you want one service to provide certain data without authentication? In the MahTweets webservice, things like a list of available Plugins to download doesn’t really need to be authenticated. I’ll admit that I’ve not really sorted this out, but as a basic guess, inspecting the RequestUri inside of OAuthAuthorizationManager is one possibility.

protected override bool CheckAccessCore(OperationContext operationContext)
{
    if (!base.CheckAccessCore(operationContext))
    {
        return false;
    }

    HttpRequestMessageProperty httpDetails = operationContext.RequestContext.RequestMessage.Properties[HttpRequestMessageProperty.Name] as HttpRequestMessageProperty;
    Uri requestUri = operationContext.RequestContext.RequestMessage.Properties["OriginalHttpRequestUri"] as Uri;
    ServiceProvider sp = Constants.CreateServiceProvider();

    if (requestUri.AbsolutePath == "/Api.svc/$metadata")
        return true;

    if (requestUri.AbsolutePath.StartsWith("/Api.svc/Plugins"))
        return true;

// other auth bits

}

I’m not saying this is a great way to do it, but the best I’ve come up with so far.

Client Side

The client side consists of two parts – the OAuth "dance" to get the tokens, much like any OAuth accessing service. Again, I’ll use DotNetOpenAuth for this. This is my console application.

public static class MahTweetsOAuthService
{
    public static readonly ServiceProviderDescription Description = new ServiceProviderDescription
    {
        RequestTokenEndpoint = new MessageReceivingEndpoint("http://localhost:57500/OAuth.ashx", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        UserAuthorizationEndpoint = new MessageReceivingEndpoint("http://localhost:57500/OAuth.ashx", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        AccessTokenEndpoint = new MessageReceivingEndpoint("http://localhost:57500/OAuth.ashx", HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
        TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
        ProtocolVersion = ProtocolVersion.V10a,
    };
}

public class OAuthWrapper
{
    public DotNetOpenAuth.OAuth.DesktopConsumer consumer { get; set; }

    private String RequestToken = "";
    public String ConsumerKey { get; set; }
    public String ConsumerSecret { get; set; }

    public OAuthWrapper(String ConsumerKey, String ConsumerSecret)
    {
        this.ConsumerKey = ConsumerKey;
        this.ConsumerSecret = ConsumerSecret;

        consumer = new DotNetOpenAuth.OAuth.DesktopConsumer(MahTweetsOAuthService.Description,
            new OAuthTokenManager()
            {
                ConsumerKey = this.ConsumerKey,
                ConsumerSecret = this.ConsumerSecret
            });
    }

    public String BeginAuth()
    {
        var requestArgs = new Dictionary<string, string>();
        requestArgs["scope"] = "http://tempuri.org/IDataApi/GetName|http://tempuri.org/IDataApi/GetAge|http://tempuri.org/IDataApi/GetFavoriteSites";
        return this.consumer.RequestUserAuthorization(requestArgs, null, out this.RequestToken).AbsoluteUri;
    }
    public String CompleteAuth(String Verifier)
    {
        var response = this.consumer.ProcessUserAuthorization(this.RequestToken, Verifier);
        return response.AccessToken;
    }

    public IConsumerTokenManager TokenManager
    {
        get { return (IConsumerTokenManager)consumer.TokenManager; }
    }
}
class Program
{
    static void Main(string[] args)
    {
        //Oauth Authentication
        OAuthWrapper t = new OAuthWrapper("123", "123");
        System.Diagnostics.Process.Start(t.BeginAuth());
        Console.WriteLine("Please enter the PIN");
        t.CompleteAuth(Console.ReadLine());
        Console.WriteLine("Press anykey to continue");
    }
}

The very basic wrapper is designed to make the initial OAuth dance as easy and reusable as possible.

The next part is the WCF Data Services/OData specific bits. OData itself doesn’t specify any standards for authentication, which is a little confusing at first. OAuth requires several headers, one part of that is the OAuth signature which requires the address of the resource you’re requesting to generate.

From what I can see (and I very well may be wrong), there isn’t a really awesome way to do this with WCF Data Services on the client side – if you create an extension on the service (DataServiceContext), it doesn’t yet know the full address (it only has the base address), and if you create an extension for DataServiceQuery<T>, it doesn’t know about the Context so you can’t hijack the SendingRequest event to insert the OAuth header. The best solution I could come up with was creating the extension for DataServiceQuery<T> and passing in the service.

public static class ODataExtensions
{
    public static DataServiceQuery<T> AsOAuth<T>(this DataServiceQuery<T> Query, DataServiceContext context, IConsumerTokenManager t)
    {
        context.SendingRequest += (s, e) =>
        {
            var serviceEndpoint = new MessageReceivingEndpoint(Query.RequestUri.ToString(), HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest);
            var wcf = new DesktopConsumer(MahTweetsOAuthService.Description, t);

            WebRequest httpRequest = wcf.PrepareAuthorizedRequest(serviceEndpoint, ((OAuthTokenManager)t).AccessToken);
            HttpRequestMessageProperty httpDetails = new HttpRequestMessageProperty();
            httpDetails.Headers[HttpRequestHeader.Authorization] = httpRequest.Headers[HttpRequestHeader.Authorization];

            e.Request.Headers = httpDetails.Headers;
        };

        return Query;
    }
}

Usage:

mahtweetsdbEntities api = new ServiceReference2.mahtweetsdbEntities(new Uri("http://localhost:57500/Api.svc/"));
var y = api.Screencasts.AsOAuth(api, t.TokenManager).Execute();

Immediately you’ll see some less than ideal code in there. The default IConsumerTokenManager doesn’t expose the AccessToken, but my OAuthTokenManager does. You could modify the extension to include a string token parameter, or just modify token manager to expose it. The next less-than-generic snippet is the ServiceProviderDescription, which is using the service description from a static class. Again, you could pass in another parameter containing the service description. The last issue is the request method in the service endpoint – this obviously won’t work for other HttpVerbs, so it might be worth passing in that parameter.

It’ll take me a day or three, but I’ll get a sample project together for OoO.


 

MicroISV/OSS Project Tools & Services

5 May 2010 , ,    1 Comment

MahTweets started out as a simple hobby project, but has grown from just a open source project to the first application of the MahApps MicroISV. As such, we’ve used a boat load of tools and services, but given we’re not exactly making a killing (ie, $0) in the Twitter market, we’ve looked for primarily free or very cheap services.

All of these run on Windows/Windows Server, but several of these (such as the web services and some of the apps written in Java) will work cross platform. There are also a surprising amount of tools and services available if you’re creating an open source project. This obviously won’t work for all MicroISV, but for us it has been a wallet saver.

A quick note on the Altassian software mentioned. Altassian have a range of software available for $10 each. They give you less users/repositories/plans/etc, but for $10 each, it really is incredible value. We would probably be using more of their software, but apart from JIRA and Fisheye they have horrible install experiences on Windows Server above 2003. And by horrible, I mean after a day wrestling with getting Confluence working, I gave up and installed ScrewTurn Wiki via Web Platform Installer in a matter of minutes. For other operating systems (OS X, Linux/BSD derivatives), I’m sure it’s fine since they’re all Java.

Software Packages

If you qualify and can afford the $100USD in three years time, Microsoft’s BizSpark is amazing value. Amazing enough to get its own section? Heck yes. So what’s included?

  • All the software included in the Visual Studio Team System Team Suite (VSTS) with MSDN Premium subscription Expression Studio (Version 2), plus VSTS Team Foundation Server Standard Edition – for the entire development team
    • The development tools provided are the same as those in MSDN Premium subscription, plus VSTS Team Foundation Server. However, there are features of MSDN Premium (such as support and internal use licenses) that are not included in BizSpark
  • Production license use rights, to deploy, host and support Startup’s "Software-plus-Services" applications for delivery over the Internet, using the most current releases of the following products: Windows Server (all editions), SQL Server (all editions), BizTalk Server, and Office SharePoint Server for hosting. To deploy in production, a Startup may self-host or select an authorized BizSpark Hosting Partner through the BizSpark Network Partner directory.

That’s a crap load of very expensive software (VS2010 Ultimate!) for your entire team.

Continuous Integration

TeamCity 5 (free)
The limitation of the free version is 20 user accounts, 20 build configurations, and 3 build agents. Frankly, that’s more than enough for our usage, but given I’m now running it on my personal Windows Home Server machine, I might start using it for various other projects.

Cross platform, requires Java.


Source Code Management/Version Control System

VisualSVN Server (free)
It’s free, its dead simple to setup, there isn’t really need to have a comparison for hosting SVN on Windows. If we were to start again, we’d strongly consider Mercurial or Git, depending on integration with other tools.

TortoiseSVN (free)
Much like VisualSVN Server, TortoiseSVN is kinda "the bomb" for SVN on Windows.

Fisheye ($10)
Fisheye integrates well with JIRA, and might seem useless at first, but being able to explore code/diffs/etc without using a SVN client (which are all horribly slow) can be a life saver.

Cross platform, requires Java.

Bug Tracking

UserVoice (Service) (Free)
Very simple signup, gives a sort of forum approach to bug tracking/user feedback, with voting ala StackOverflow/Digg/etc. It works well for a public facing bug tracker.

Has free, paid and OSS accounts (you may need to sign up for free, then "upgrade" the plan to OSS). Alternative would be GetSatisifaction, but I’ve heard some nasty things about them in the past.

JIRA ($10)
Although we also use UserVoice, JIRA is the "internal" bug tracking system – that is, bug tracking that’s more technical detailed than what we maintain on UserVoice.  The separation of the two is nice as it means we don’t get spam/duplicates/etc on JIRA, but it’d also be nice to import from UserVoice to JIRA.

Cross platform, requires Java.

Wiki

ScrewTurn (free)
ASP.NET Wiki, can use databases or flat file. Easy to setup if you’re using Web Platform Installer, has neat ‘template/snippet’ editor which I’ve used for embedding Silverlight videos.

MediaWiki (free) is probably a good alternative for LAMP servers, or Confluence ($10) if you can afford it.

Mailing Lists

MailChimp (Service) (Free)
Mailing lists? Really? Yes! Email is still a great way to get out information to a bunch of people (in this case, our beta testers), and MailChimp is great. Being able to send out HTML emails with plaintext fallback isn’t all that easy to do from a desktop email client, but MailChimp makes it relatively easy.

The free account gives you up to 500 users on your list, and 3000 emails/month. Requires "Powered by MailChimp" logo down the bottom.

Alternatively there is CampaignMonitor, which is $5/campaign + 1c/recipient.

IDE/IDE Tools

Visual Studio Express (Free)
If you don’t qualify for BizSpark, WebsiteSpark or DreamSpark, VSExpress is the next best thing for .NET devs.

NetBeans (Free)
Wait, what? Java development tools? You’re crazy Paul, get out of here! I’ve occasionally dabbled in Android development for MahTweets/MahApps as well. I’ve settled on NetBeans.
Alternatively, Eclipse is also free, and is officially pushed by Google as the Android

CodeRush Xpress  (Free)
"Coding Assistance Add-in" – has some neat code navigation and code refactoring tools not built into VS2010. Like Resharper I believe.

VisualSVN (Plugin for Visual Studio)  (Free for OSS or MVPs, $49 for individuals)
VisualSVN integrates SVN into Visual Studio so you don’t have to leave VS to update/commit/etc. Also shows you which files, projects, etc have been modified in the Solution Explorer window.

Requires TortoiseSVN. Also free for MVP’s.

Resharper (Free for OSS, $199 for personal)
I’ve not actually applied for an OSS license of Resharper – one of the MahTweets dev team has a personal license and swears by it. I’ve tried it before, and have been less than wow’d – and the free version of CodeRush does enough for me.

Media

Expression Encoder 3 (Free)
xEncoder comes in two versions – free and bundled-with-other-software, but for screencasting or video encoding, the free version is great.

IconDock’s Blockie Icons ($50)
If you’re not a designer, don’t design your icons. Icon’s can really make or break your app – be it web, desktop or mobile.

There are plenty of icons for free available, just make sure you’re consistent and try and use one pack. There is a long thread on StackOverflow on How does a developer with no design skill make his/her application icons look pretty


There is a bit of overlap between these tools and Scott Hanselman’s 2009 Ultimate Developer and Power Tools List For Windows, and I strongly recommend you check out that list. Scott’s list should almost be considered mandatory before you start looking into further tools and services!

So that’s my list of software and services I’ve been using as a MicroISV – what’s have I missed? I’d love to grow this list.


 

Android: Eclipse to NetBeans

23 April 2010 , ,    3 Comments

To be perfectly honest, I’ve not been overly happy with Eclipse. Yes, its free, yes its the recommended IDE for Android dev from Google, but it’s not the easiest IDE to adapt to. I’m sure its super powerful and whatnot, but there are several things that really irk me. My major complaints are:

I thought I’d give NetBeans (6.9Beta) a shot, having used so few Java IDE’s before. Like Eclipse, NetBeans is free, and has an Android plugin.

NetBeans has the ability to import Eclipse workspaces, but unfortunately will convert it to a standard Java project rather than an Android project. Never fear, Paul is to the rescue.

Converting Eclipse/Android Workspace to NetBeans/Android Workspace

  1. Install Netbeans Android plugin following the instructions from the NetBeans Wiki
  2. Fire up Netbeans, File –> Import –> Eclipse Workspace, and import as normal.
  3. Close Netbeans and locate the ‘nbproject’ folder for your new project – it should be <location of your project>\nbproject.
  4. First up, its worth replacing the build-impl.xml for an Android specific one. From what I gather, this file defines all the rules for compiling. Here’s the build-impl.xml that NetBeans 6.9 (beta) generated for me.
  5. Open up project.xml in your favourite text editor. You’ll need to change a few lines here.

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://www.netbeans.org/ns/project/1">
        <type>org.netbeans.modules.java.j2seproject</type>
        <configuration>
            <data xmlns="3"http://www.netbeans.org/ns/j2se-project/3">
                <name>MahTweetsMobile</name>
                <source-roots>
                    <root id="src.dir" name="src"/>
                    <root id="gen.dir"/>
                </source-roots>
                <test-roots/>
            </data>
        </configuration>
    </project>

    Change the first red line to: org.netbeans.modules.android.project
    and the second line to  http://www.netbeans.org/ns/android-project/1

  6. Open up project.properties in a text editor, find the line starting with dist.jar – Android doesn’t use jar, but apk, so change dist.jar to dist.apk and the extension to .apk as well.

    ie Change dist.jar=${dist.dir}/MahTweetsMobile.jar to dist.apk=${dist.dir}/MahTweetsMobile.apk

  7. Next up, still inside project.properties, you have to add a few properties that don’t exist.
    Add the following lines to the end of the file:

    resource.dir=res
    test.src.dir=test
    intermediate.dex=${build.dir}/classes.dex

  8. Save those files, fire up NetBeans and you should be good to go! If you get a build path error when opening the project, right click on the project (in NetBeans) –> Properties –> Libraries –> make sure the correct Android is selected from the drop down.


 

MahTweets Dev Screencasts

17 April 2010    1 Comment

Over on MahTweets.com, we’ve just launched the first developer preview screencast. The idea of these screencasts is to get feedback – positive or negative – from the community on features and changes which will help shape the direction MahTweets takes.

image

So far there is only the one video up, and its a bit long at 10minutes (~28meg), however this is likely to be the longest video as its a general overview of where we’re up to. We’ll upload smaller, more specific videos over the coming weeks.

If you all behave and are lucky, I may even upload some of the earlier videos of the prototypes.

We need to add more features to the screencast subsite, such as..


 

Review: C&C4 Tiberium Twilight

1 April 2010 , ,    1 Comment
command-and-conquer-4
Kane presumably looking on in horror
at what has become of the franchise

After playing most of the C&C games both in the Tiberium and Red Alert universe, you’d think I’d be eager to play C&C4.

I have no good things to say about C&C4. I played one round in the multiplayer beta, that was enough to put me off buying the game. Sadly, a friend of mine didn’t have a chance to play the beta, purchased it, and later lent the game to me.

This is not Command and Conquer.

Heck, I’d barely consider it an RTS. Why would I think a game series that helped create the RTS genre, is no longer an RTS? EA LA (developers) decided that

Instead, you have one of three types of  ‘crawler’ (giant carrier where you can dispense units), each type allowing you to build different types of units or use different powers. The goal is to capture various control nodes – if this is sounding similar, its because Dawn of War II got there first, but did it “right” with squad based play.

While the C&C cinematics have always been a little on the campy side, C&C4 just leaves a bad taste in your mouth with poor props, worse CGI, poor dialog and a very lack lustre performance by the usually brilliant Joseph Kuncan (who has always played Kane in the series). The game isn’t helped by laughable DRM (you have to be online to play…even single player) and restrictions (no LAN play).

While this might sound like me being bitter about change, the game simply isn’t fun. In singleplayer you’re either bombarded with three or four enemies all with higher level stuff than you or you’re waiting for stuff to happen, in the form of waiting for a timer to tick down or for nodes to be captured.

The last C&C game, Red Alert 3 wasn’t the mind blowingly awesome game that everybody wanted, but C&C4 retroactively boots its rating. This is the sad, final chapter in the “Kane Saga”, and the last game I’ll play from EA LA for a long time.

No star rating can be given for this monstrosity.


 

Why Even Bother: Part II

18 March 2010 , ,    8 Comments

Where is part one? Well a few months ago, Brendan wrote part one outlining the frustration we feel about everybody loving the "big boys" in the Twitter client space, never giving the smaller, (seemingly) the non-VC funded, open source clients a chance to shine.

It’s happened again, but this time its worse. Seesmic were yet again presenting at a major Microsoft conference (MIX10), banging on about how they got early access to the WinPhone7 Series hardware and SDK – how wonderful, another platform we simply can’t compete with because come day one, they’ll have not only had the emulator for longer, but physical devices to try everything on. And if we were to move to WP7S, we’d have to break the mindset of everybody who saw MIX/associated articles that "WP7S + Twitter = Seesmic". They are in the phone from before day one.  You just can’t compete against that.

Oh, and something about updated desktop client that will include plugins – turns out via MEF, just like us. The one consolation I have is that all those promised features they keep banging on about – we’ve got them today. Now. 3 months ago. Not "in the near future" (anybody seen their iPhone client they promised? oh wait, doesn’t exist)

However the final crushing blow came when news broke that Silverlight (in beta form?) has now been released on Nokia’s S60 platform – and guess what the two example apps are? Bing and Seesmic.

Unlike Brendan, I’m not venting at the general noisy ‘industry’ and how hard it is to get a break, but how the favourites of PDC will continue being the favourites on every platform Microsoft releases, so long as Microsoft keep handing out early access to the same developer again and again. This isn’t so much about MahTweets, but the frustration that Microsoft pick a favourite and will run forever with them – what about the other, smaller clients in this field? Where is the love for Witty, Blu, Halfwit, Sobees, or Gadfly?

It feels less like "Developers, Developers, Developers" and more like "Developers – those guys, the rest of you don’t matter"

Why even bother?

After MahTweets 3 (which is a very major version/update), I won’t be bothering – I’ll be getting out of the "game". MahTweets will be maintained/major bug fixes, the others may continue working on it, but I have no more interest in creating Twitter clients.

Why is it so personal?

For lack of a better cliché, I am MahTweets. I don’t mean to diminish the role or contributions of others, far from it, when I say I am MahTweets, I mean that I’ve poured a lot of myself into it, I’m the public face, I’m the "overlord", I started the project. For the others, it’s a pet project – for me, I’m disabled and unable to get a ‘real job’ – it’s as close to a job as I’ll have for a long time.


 

Android’s event design sucks

11 March 2010 , ,    No Comments

While delving further into Android, I’ve been creating a “hello world” type of app – its not as complex as what MahTweetsMobile will be, but it is encompassing a lot of different elements (custom ListAdapter, SQLite for persistent storage, different types of menus, loading external intents, etc).

One “difficult” element has been events. I say “difficult” because by design, its freaking nuts.

Scenario

I’ve got a custom ListViewAdapter, for my custom list elements, where the ListView items include a checkbox. This checkbox is to indicate the status of the items (for reference, its a shopping list app, you tick the checkboxes to show which items you’ve bought while shopping, the items then go grey in colour).

device

I wanted to add a “long press” context menu so that you could easily bring up a menu (with options such as Edit Item or Delete Item)

The Problem

Normally when “attaching” event listeners with ListViews, you use setOnCreateContextMenuListener on the ListView itself. You then override your Activity’s onContextItemSelected, figure out what menu button was pressed as well as what item it was pressed on.

ListView x = (ListView)findViewById(R.id.ListView01);
x.setOnCreateContextMenuListener(new OnCreateContextMenuListener()
{
    public void onCreateContextMenu(ContextMenu contextMenu, View view,
ContextMenuInfo arg2)
    {
        contextMenu.add(0,CMENU_DELETE, 0, "Delete Item");
    }
});

public boolean onContextItemSelected(MenuItem item)
{
    AdapterContextMenuInfo menuInfo =
    (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
    switch (item.getItemId())
    {
        case CMENU_DELETE:
//Use menuInfo.position along with the adapter.getItem
//ie Product p = (Product)adapter.getItem(menuInfo.position);
            return true;
    }

    return true;
}

As you can see, its ugly code, but it works. Sort of. The problem was the checkboxes.

As soon as you add checkboxes to the individual items view, any listener created on the ListView never fires – the CheckBox swallows the event!

The Apparent Solution

Apparently this is a known bug (or more accurately, this is by design!). The solution is to set the event handlers on the rows themselves; inside the custom ListAdapter, when you’re inflating the various UI elements – look at my previous post on Custom ListAdapter, it’s under the getView method – on that “rowLayout” use the same setOnCreateContextMenuListener as the code used on ListView above, except only apply it to the individual row’s.

That would be fine if you didn’t care which item was pressed. That’s right, the cast to AdapterContextMenuInfo of item.getMenuInfo() in your Activity’s (yes, your activity has to override this, nowhere else works. argh!) onContextItemSelected always returns null because item.getMenuInfo() is always null.

The Actual Solution

I have no idea how I stumbled across this solution – I think I was in the process of undo-ing lines of code to try and figure out “where I’d gone wrong”.

Fact: To get the event to fire with checkboxes you need it to be set on the rowLayout.

Fact: To get menuInfo to be anything other than null (ie, tell you where it came from), you need it set on the ListView

Solution? Combine them. Wait, what? Two event listeners to get them to work? Yup.

On the rowLayout, set the listener:

rowLayout.setOnCreateContextMenuListener(new OnCreateContextMenuListener()
{
    public void onCreateContextMenu(ContextMenu contextMenu, View view,

ContextMenuInfo arg2)
        {

        }
});

Notice the lack of code inside the onCreateContextMenu? Well, any ContextMenu items added inside this method won’t have AdapterContextMenuInfo – so don’t set anything, no menus will appear.

Now also set the adapter on the ListView using the code above.

Now it all works. WTF doesn’t begin to describe this.


 
← Newer PostsPrevious Posts →