It’s difficult not to interpret the latest Twitter API announcement by Ryan Sarver as anything other than a clear instruction that competition with Twitter’s client applications will not be tolerated. For me, the stand-out quote was:
More specifically, developers ask us if they should build client apps that mimic or reproduce the mainstream Twitter consumer client experience. The answer is no.
I personally think that this public statement has irrevocably set Twitter down an inferior path; what investor will now seriously fund third-party applications that augment Twitter? I’m not saying that this move will necessarily stop Twitter from being a successful company, but I do feel that its scope as a platform has been diminished.
I don’t think it ever needed to take this route. Here’s one business model I would have loved to see Twitter adopt…
This would have required extensive monitoring of third party applications (though ad networks already face similar challenges), but it seems like a ‘maximizing’ strategy to me: third-party clients continue to proliferate, have an incentive to increase user engagement and to generate revenue for Twitter.
Of course, it’s easy to comment from the sidelines.
A poor bit of user interface design in the twitter widget for android in an otherwise a well designed and intuitive application.
What do you think the “Update” button is going to do?
Yes, it’s 3.
It can’t be 1. As android developers will understand, you can’t usefully put an editable text view into a widget (the Google Search is not a bona-fide widget). So the designers/developers have followed the pattern of showing the “Create Tweet” activity when the text view is clicked. This works really nicely.
And it isn’t 2 either. Even though the twitter website, and the rest of the android application, refer to the action as “Tweet”, the “Update” button does exactly the same thing here. It duplicates the action of clicking in the textbox. This is wrong on three counts:
What prompted me to post this is actually a second (and I feel more significant) issue with the app: there is no “About” screen, no information about how to contact the developer, not even any confirmation that it was developed or licenced by twitter.
I would have emailed my observations instead of posting them, but they weren’t courteous enough to provide their contact details.
Last night I decided to knock out a quick app to test how straightforwardly one can upload twitter images. With the exception of one hiccup:
http://code.google.com/p/twitter-api/issues/detail?id=697
it went very smoothly.
Essentially you need to introduce two libraries into your project that are absent from Android:
I used versions 4.0-beta2 and 0.5 respectively.
Here’s my very basic implementation:
//sample method for uploading a user profile image to twitter
int uploadImage(InputStream in, String type, String fileName, Credentials credentials) throws IOException {
try {
//convert the input stream into a byte array - this is just a convenient way
InputStreamEntity ise = new InputStreamEntity(in, -1L);
byte[] bytes = EntityUtils.toByteArray(ise);
//establish the credentials
DefaultHttpClient client = new DefaultHttpClient();
client.getCredentialsProvider().setCredentials(new AuthScope("twitter.com", 80), credentials);
//create the multipart entity
MultipartEntity entity = new MultipartEntity();
ByteArrayBody body = new ByteArrayBody(bytes, type, fileName);
entity.addPart("image", body);
//assign the entity to the request and disable the Expect header
//see http://code.google.com/p/twitter-api/issues/detail?id=697
HttpPost request = new HttpPost("http://twitter.com/account/update_profile_image.xml");
request.setEntity(entity);
request.getParams().setBooleanParameter( "http.protocol.expect-continue", false );
//execute the request and return the response
HttpResponse response = null;
try {
response = client.execute(request);
} finally {
//exhaust the response
if (response != null) {
HttpEntity re = response.getEntity();
if (re != null) re.consumeContent();
}
}
return response.getStatusLine().getStatusCode();
} finally {
try {
in.close();
} catch (IOException e) {
/* ignored */
}
}
}
I haven’t investigated properly, but disabling the “expect-continue” feature appears to cause the request entity to be read twice. So the method above depends on a helper class that provides a repeatable entity.
private class ByteArrayBody extends AbstractContentBody {
private final byte[] bytes;
private final String fileName;
public ByteArrayBody(byte[] bytes, String mimeType, String fileName) {
super(mimeType);
this.bytes = bytes;
this.fileName = fileName;
}
@Override
public String getFilename() {
return fileName;
}
@Override
public void writeTo(OutputStream out, int mode) throws IOException, MimeException {
out.write(bytes);
}
@Override
public String getCharset() {
return null;
}
@Override
public long getContentLength() {
return bytes.length;
}
@Override
public String getTransferEncoding() {
return MIME.ENC_BINARY;
}
}
The Java code in this post is in the public domain.
I’ve bowed to the inevitable and now have a twitter account. Being on the geeky end of the human spectrum, the first thing I wanted to do was write a some Java code that will push new profile images.
I’m running along nicely, and the first problem I hit comes as I finish my coding and start testing. Twitter responds with:
The expectation given in the Expect request-header field could not be met by this server.
The client sent
Expect: 100-Continue
but we only allow the 100-continue expectation
That such a widely used service can’t stick to one of the plainest elements of the HTTP 1.1 specification is depressing.
Comparison of expectation values is case-insensitive for unquoted tokens (including the 100-continue token), and is case-sensitive for quoted-string expectation-extensions.
But what is worse is that the Apache HTTP library I’m using doesn’t allow this header to be amended in any way — it gets magically added at the point at which the request is made so there’s no opportunity to remove it — and something as simple as this multipart image upload becomes a trial…