Thursday, January 29, 2015

Finding Papercut Bugs/Improvements on github

Trying out a new programming language and looking for some real life practice? On the lookout for some Open Source projects to take part in? Look no further.

Simply execute one of theses searches on github:

Issues with label papercut

Issues with title papercut

Issues with label easy

Issues with label simple

These will search for so called papercuts - issues which are quickly to fix without too much knowledge of the code base and/or the programming language. You can even filter the results by the language of your desire.

There you go! No excuses left!

Sunday, January 11, 2015

Handling LTR text in RTL environments

Recently we finally managed to solve the layouting problems in our Android app caused by RTL user names in LTR language environments. Here's how.

But first things first: what is RTL/LTR? Most languages including English are written from left to right (LTR). However there are some languages - Arabic and Hebrew being the most important of them - which are written from right to left (RTL). This of course has implications on layouting especially in mixed environments.

Consider an Android device whose language is set to English. And now there's an app displaying some user names in a simple ListView. One of those user names is written in Hebrew. This is what happens:

    Arthur, 42 y/o
    Trillian, 39 y/o
  عليّ, y/o 56
    Ford Prefect, 111 y/o

Somewhat ugly, huh? It doesn't fit into the overall layout and the complete line is LTR instead of just the name. Why is that?

Trying to display RTL strings in LTR environments isn't as simple as one would think. Especially on Android. The reason for this is the automatic detections algorithm Android OS uses to render TextViews with RTL text. During the layouting phase of a TextView Android checks the first character of the TextView's String content. Depending on the position of this character in the unicode table Android decides whether to lay out the TextView according to RTL or LTR. This mainly has implications on the gravity which is applied to the TextViews content and how some layout_* parameters (like align_parentLeft/Right) are interpreted leading to the phenomenon depicted above. What we actually want to achieve is that the LTR text is still rendered from left-to-right (as this is how the name is written in it's original form), but with left gravity. To make things a little more interesting we're also appending an LTR string containing the user's age.

Okay, that's enough theory for now - how did we actually solve the problem? Simply by taking a close look at Android's LTR detection algorithm and using some text layout direction control characters. Take a look at this code snippet:

    /**
     * Reformat the String as LTR to ensure that it is laid out from
     * left to right, but it doesn't affect overall layouting of
     * TextViews etc..
     */
    public static String makeLtr ( String string ) {
       if (checkRtl(string)) {
            /* prepend the string with an LTR control sign (so
               that Android's RTL check returns false) and an RTL
               control sign (so that the string itself is printed in
               RTL) and append an LTR control sign (so that if we
               append another String it is laid out LTR). */
            return "\u200E" + "\u200F" + string + "\u200E";
        } else {
            return string;
        }
    }

    /**
     * Check if the given String is probably written in RTL by
     * checking if the very first character is within the range of
     * RTL unicode characters.
     */
    public static boolean checkRtl ( String string ) {
        if (TextUtils.isEmpty(string)) {
            return false;
        }
        char c = string.charAt(0);
        return c >= 0x590 && c <= 0x6ff;
    }

By applying makeLtr() to every user name String we can be sure, that the TextView is laid out correctly, that is like so:

    Arthur, 42 y/o
    Trillian, 39 y/o
    عليّ(RTL!), 56 y/o (yeah, LTR and gravity left!)
    Ford Prefect, 111 y/o

The TextView itself doesn't have to be modified in any way. Of course this little trick can also be applied to other programming languages and frameworks or LTR Strings in RTL environments just by encapsulating the String in the corresponding unicode control characters.

Sunday, March 30, 2014

Using JSON requests in QML

Together with a friend I'm currently taking part in the Ubuntu App Showdown. We decided to implement a simple QML Pocket client. However, the pocket API is JSON based while the Ubuntu SDK (which is based on QML/QT) just provides means to work with XML HTTP requests. Here is how to work with JSON requests in QML.

To leverage working with JSON requests in QML, kromain implemented a simple ListModel based way to handle JSON requests and responses. It works great for GET requests, but doesn't support POST requests and also misses some other commonly used features like setting the content type header. Therefore we extended his implementation with POST support and some other neat features. As of now he didn't merge our pull request, but you can of course use the code directly off from the patch branch in my repository.

I will walk you through how to set up basic JSON based request/response handling using the extended version of kromain's QML. There are two approaches. Which one you choose depends on how you want to use the response data.

Using response data in a ListView

If you want to use the response data in a QML ListView you need to provide the JSONListModel as a model for your list view like outlined in the following example which shows how to implement a simple GET request for the unread objects on a user's pocket list (I will not bother with how you can acquire the consumer key and the access token as this is depends heavily on which service you plan to use):

import QtQuick 2.0
import "../json" // this folder contains JSONListModel.qml and jsonpath.js


ListView {
    id: items

    property string accessToken: ""

    model: ListModel {}
    
    JSONListModel { 
       id: pocketModel
       source: "https://getpocket.com/v3/get?consumer_key=YOUR_CONSUMER_KEY&access_token=" + accessToken
       query: "$.list.*" // simple XPATH query over the JSON result
       requestType: "GET" // this is optional as GET is default

       Component.onCompleted: {
           items.model = model; // assign JSON model to ListView model (this only happens once when the component is created)
       }
    }

    delegate: Subtitled {
        text: resolved_title
        subText: excerpt

    }

    onAccessTokenChanged: {
        if (accessToken !== "") {
            refresh();
        }
    }

    function refresh() {
        pocketModel.refresh();
    }
}

This code will trigger a request for the user's reading list as soon as the access token is set. As soon as the model has changed the ListView's content will adapt automatically.

Using the response as-is

If you want to handle the response yourself you simply omit adding the model to a ListView. In the following example we shorten a URL using the Google URL Shortener JSON API and post the shortened URL to the clipboard. At first we create a ShortenRequest component for better reusability.

import QtQuick 2.0
import "../json"

JSONListModel {
    property string longUrl: ""

    source: "https://www.googleapis.com/urlshortener/v1/url"
    contentType: "application/json"
    requestType: "POST"
    query: "$"
    postData: "{\"longUrl\":\"" + longUrl + "\"}"
 

    onListModelUpdateChanged: {
        var shortUrl = model.get(0).id;
        Clipboard.push(shortUrl);
    }
}

When we want to send the request we just have to call the following method which creates a new instance of our ShortenRequest:

function shorten(longUrl) {
    var request = Qt.createComponent("path/to/ShortenRequest.qml").createObject(null, {"longUrl": longUrl};
    request.refresh();
}

Sunday, April 21, 2013

Sending files with XMPPFramework and TurnSockets

This is a short example about how to send files from Objective-C applications via XMPP. For that I use the XMPP client library XMPPFramework and it's TURNSocket class (a XEP-0065: SOCKS5 Bytestreams implementation).

This code solely handles actual file sending. It assumes an established XMPP connection. myJid is the JID the application uses for connection to the XMPP server. Below you find the the methods needed for sending of files via XMPP. You actually just have to call the sendToOtherDevice method from your code.


- (void)sendToOtherDevice:(NSData *)fileData receiverJid:(NSString *)receiverJid {    
    XMPPJID *jid = [XMPPJID jidWithString:receiverJid];
    if ([jid.domain isEqualToString:myJid.domain]) {
        [TURNSocket setProxyCandidates:[NSArray arrayWithObjects:jid.domain, nil]];
    } else {
        [TURNSocket setProxyCandidates:[NSArray arrayWithObjects:jid.domain, myJid.domain, nil]];
    }
    DataAwareTurnSocket *socket = [[DataAwareTurnSocket alloc] initWithStream:xmppStream toJID:jid];
    [socket setDataToSend:fileData];
    [socket startWithDelegate:self delegateQueue:dispatch_get_main_queue()];
}

- (void)turnSocket:(DataAwareTurnSocket *)sender didSucceed:(GCDAsyncSocket *)socket {
    [socket writeData:sender.dataToSend withTimeout:60.0f tag:0];
    [socket disconnectAfterWriting];
}

- (void)turnSocketDidFail:(TURNSocket *)sender {
    NSLog(@"Couldn't set up bytestream for file transfer!");
}

DataAwareTurnSocket is a TURNSocket subclass which holds a reference to the file which shall be transmitted via XMPP. Its header class looks like this:

#import "TURNSocket.h"

@interface DataAwareTurnSocket : TURNSocket {
    NSData *dataToSend;
}

@property (nonatomic, readwrite) NSData *dataToSend;

@end


The actual implementation is also fairly simple (it actually consists of no real code^^):

#import "DataAwareTurnSocket.h"

@implementation DataAwareTurnSocket

@synthesize dataToSend;

@end

Keep in mind, that I'm a Objective-C beginner and the code might be not optimal. However it should give you a hint of how to accomplish sending a file via XMPP from Objective-C code.


Saturday, May 26, 2012

SVN commit problem: no working copy

During developing I often use SVN as CVS. However, sometimes when trying to commit I get an error message stating that a specific folder/file is no working copy which leads to a failure of the commit process.

But don't worry: it is easy to fix in a few steps:

  1. Create a copy of the affected folder and move it to somewhere outside your working copy.
  2. Clear the copied folder of all SVN-references (e.g by deleting all .svn folders).
  3. SVN-delete the affected folder (either be SVN CLI or from the context-menu when using tools like Tortoise/Rabbit-SVN) - if the folder contains subfolders make sure to also SVN-delete these as the SVN-delete command is not recursive.
  4. Commit the deleted (and only the deleted!) folder.
  5. Delete the folder from the filesystem.
  6. Copy the aforementioned copied folder back into the working copy.
  7. SVN-add the folder.
  8. Commit.
That should make sure, that you're working copy is usable again. Another way would be to just SVN-revert the folder to the last revision, but thus you would loose all changes you've made since the last commit/update.

Monday, May 14, 2012

How to properly restore a backed-up Eclipse workspace

This morning my Ubuntu crashed while I was working in Eclipse. Unfortunately this also corrupted my workspace state. But, no problem - I had a backup. However, restoring the backup wasn't as easy as I had thought...

I had backed up my home partition with deja-dup - the built in backup solution in Ubuntu. After the recovery deja-dup's status dialog gave me quite a few error messages stating that SVN related files couldn't be restored due to missing permissions. I assume, this was due to the fact, that rabbitcvs, the tool I access my SVN repositories with besides Eclipse, still was holding locks on these files.

Stopping rabbitvcs's status checker service didn't help. So I fired up deja-dup's recovery dialog again by right clicking on the folder and choosing the "Revert to previous version..." entry. After the dialog appeared (and deja-dup knew the path of the folder it should recover) I renamed the workspace folder (could also have deleted it, but just in case...) and let deja-dup do it's job. And guess what, it worked! As deja-dup doesn't seem to just restore the changed files, but everything, that is under the marked folder, there is also no performance loss in acting like this.

"Easy-peasy!" you may shout out now, but for me it took nearly 10 minutes to work this out.

Friday, April 13, 2012

Adding CXF runtime environment to Eclipse

Another quick tip for JAX-WS development in Eclipse:

When adding a CXF-Runtime via Window->Preferences->CXF 2.x Preferences don't forget to set a checkmark to the left of the entry, otherwise Eclipse won't use the environment and will complain about a missing CXF jar.