Nominally, you can make Android View or Layout transparent or semi-transparent by changing the android:background color definition in the layout XML. However, this doesn’t work quite as you might expect on Android 1.5.
For example, attempting to create a layout which includes an image as a background with a semi-transparent overlay like this:
<!-- Use a relative layout with background image, with a semi-transparent overlay in an enclosed LinearLayout -->
<RelativeLayout android:id="@+id/MainLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:background="@drawable/default_bg">
<LinearLayout android:id="@+id/LinearLayout01"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="vertical"
android:background="#99FFFFFF"
>
.....
</LinearLayout>
</RelativeLayout>
On Android 1.5, you will end up with a view with a white background. On Android 1.6, you (correctly) have the background image with a semi-transparent white overlay. I guess that is why the Android example code for translucent background uses a transparent PNG rather than setting the alpha in the background colour - there is a bug in 1.5 which prevents it working!
This is the fifth and final part of a series of blog posts about our experiences in using Qt on Symbian. The earliest posts in the series were published earlier this week.
Actions and softkeys
Once we had our main view and model, we needed to add the rest of the UI.
The first step was to add a few commands to the main view. In Qt, the notion of a UI command is encapsulated by the QAction class. This gathers together the menu items, toolbar buttons and keyboard shortcuts connected with a particular command on a given parent widget, and supports a basic set of signals and slots. Once you’ve created a QAction object, you can set its keyboard shortcuts, attach it to button widgets and insert it into a menu.
Unfortunately, S60 doesn’t make much use of these UI elements, relying instead on softkeys, a mixture of menus and buttons, to capture user commands. To cope with this, Qt maps top level menus to items on the left softkey pop-up menu by default. This works well for situations where you have a large number of actions, but when you only have a couple it feels rather awkward and overcomplicated.
As a way of getting round this awkwardness, Qt also offers the alternative mechanism of specifying a particular soft key role for a QAction. This has changed slightly between 4.5.3 and 4.6.0, but the basic principle is the same for both versions: you have a limited set of possible roles, which you assign to QAction objects using the setSoftKeyRole method, and it’s up to Qt to automatically determine the mapping of keys when the parent widget gets the focus.
The only problem with this is that Qt assumes that only two softkeys are available, whereas some touchscreen s60v5 devices support up to five. This meant that some of our screens ended up wasting a large amount of screen space on extra buttons, whilst leaving the middle three softkeys blank.
Other screens: dialogs or frames or views or…?
Once we had some basic actions working, we moved on to implementing the other views and dialogs. When implementing the main view, it was fairly clear what we needed to do once we had chosen to use the MVC model. We were displaying the main view of the application, so it should clearly be embedded in the main window of the application. For the other views, the choice was not so clear.
Our first thought was to implement the views with dialog semantics as modal QDialogs, but we were stymied by the lack of a method that displays the dialog in full screen mode. The standard QDialog::exec() modal method shows the dialog at its natural size in the middle of the screen, which looks odd on S60 devices. The show() method suffers from the same problem, but this can be worked round by using the showMaximized() method, as mentioned above. Unfortunately, there is no execMaximized() equivalent, so we had to abandon the use of the modal dialog pattern. Instead, we explicitly displayed the dialogs using showMaximized() and explicitly handled their OK and cancel signals.
The individual message view didn’t fit the modal dialog model, as we wanted the user to be able to navigate between adjacent messages without leaving the view. So, guessing that we wouldn’t need any functionality provided specifically by QDialog, we chose to inherit it from the QFrame class instead, which seemed to work well. So well, in fact, that I experimented with using QFrame for the dialogs as well, which also worked without any obvious problems. I then just used the QWidget and that seem to work fine as well! Now, it’s possible that they are all effectively equivalent on S60 platforms, but it would be nice if there were some more guidance on what to use in which circumstance.
The one serious problem we did run into was the behaviour of setWindowTitle, the QWidget method used to set the main title of the currently displayed window. This does work, in that calling it changes the text in the title bar, but it doesn’t tie the set title to the window in question. So, if you close or hide a window after calling setWindowTitle, its title remains in the title bar, even though the window is no longer visible. This means that if we wanted the title to change when we showed different windows, we had to manually reset the title every time a window was shown or hidden, a remarkably complex process.
In retrospect, the best way to solve our particular set of problems would have been to manually swap dialogs and views in and out of the main widget manually. This would have enabled us to get round the setWindowTitle problem and made for nice consistent handling of view swapping. Having said that, I have no real idea if this is a reasonable thing to do in the general case.
Conclusions
First of all, the good news: Qt is much more pleasant to work with than the native Symbian APIs. It’s more orthogonal, simpler and doesn’t suffer from the unusual design of some of its central classes that Symbian does. Signals and slots provide a cleaner and more consistent communication mechanism than the jumble of active objects and callback interfaces used in Symbian. The well-named QString and QByteArray are a joy to work with compared to the mess1 that is the descriptor hierarchy and the CBuf* classes. Instead of Symbian’s proliferation of semi-redundant array classes and the fiddly RHashMap classes, Qt has a sensible set of containers, including associative arrays that you can just pick up and use. The lack of explicit memory management is an enormous boon, meaning more than anything else that you can write code that expresses your intent clearly, rather than having it obscured with a nest of clean up stack manipulations. Qt feels like it’s an API designed for writing applications, rather than one designed for developing low-level device software.
Unfortunately, it’s not quite there yet as a complete platform for developing polished UI applications on Series 60. Firstly, it doesn’t yet have a sufficiently developed set of APIs for accessing phone functionality such as messaging, telephony and PIM info. Secondly, perhaps more surprisingly, the current mapping of its UI functionality in general to the native Symbian framework feels a little awkward. To some extent this is to do with the lack of guidance on best practise, but other things, like the behaviour of setWindowTitle, are simply broken. Hopefully this will improve with the introduction of the new S60 widget set.
1. Even after seven years of writing Symbian C++ code, the mess around HBufC::Des still annoys me. If I squint hard enough, the rest of the descriptor hierarchy makes sense, but having a non-constant constant object like that remains ridiculous.
This is the fourth in a series of posts covering our experiences in implementing a Universal Inbox application on Symbian using a Qt library. Parts 0, 1 and 2 were published over the last three days and part 4 will appear tomorrow.
SMS: extensions and interfacing
We needed at least three pieces of functionality from the message store: detection of incoming messages (or, more generally, notification of changes to the inbox), a view of the current contents of the inbox, and the ability to send new messages. It’s anticipated that there will eventually be a complete cross-platform module implementing exactly this sort of functionality, called QtMobility. Unfortunately, this isn’t complete yet, and at the point I started the project there was no support for messaging in its development release.1.
To fill the gap, Nokia have supplied a set of Symbian extension modules. These are a collection of APIs specifically written to allow access to Symbian functionality in a Qt friendly way. I haven’t had the chance to investigate them all thoroughly, but as it stood, the messaging API wasn’t a good match to our requirements. While it offered the ability to receive and send SMSes and MMSes, there was no way of examining the contents of the messaging store.
It’s also worth pointing out that while these extensions are specifically intended for use in Symbian apps, they are not a very good fit to the standard epoc tree. In particular, you cannot simply export the supplied headers into the epoc32/include directory, as they themselves include files found in the source directory.
So, I ended up writing a class from scratch to bridge the gap between the Qt code and the messaging server.
Talking to Symbian code
On the whole, communicating with Symbian code is fairly straightforward. However, there are three significant mismatches between Qt and standard Symbian code that have to be dealt with: type conversions, error handling and memory management.
Type conversion is partially handled by the limited set of functions provided in the XQConversions extension library:
static QString s60DescToQString(const TDesC& desc);
static HBufC* qStringToS60Desc(const QString& string);
static QString s60Desc8ToQString(const TDesC8& desc);
static HBufC8* qStringToS60Desc8(const QString& string);
static QByteArray s60Desc8ToQByteArray(const TDesC8& desc);
static HBufC8* qByteArrayToS60Desc8(const QByteArray& string);
These offer a way of converting between ‘S60′(!) descriptors and QStrings/QByteArrays, but aren’t the set I would have chosen myself. The first and last pairs are useful, but the inclusion of the middle pair is questionable. You should never be converting unicode QStrings to and from 8-byte descriptors without specifying an encoding, and supplying officially sanctioned functions to do just that seems highly dubious. In an ideal world, I’d also like functions for converting the various date and time classes. As it was I ended up writing my own date converters, which while trivial, had just enough scope for error that providing API functions would be worthwhile.
Qt mostly uses return codes to propagate errors, but it also supports exceptions being thrown from third party code. Symbian’s error propagation is usually done via the leave mechanism, which since 9.1 is implemented using exceptions. However, these exceptions are not derived from the standard Exception class, making them incompatible with the Qt catch mechanism. The solution to this, as you might have guessed, is to wrap all Symbian leaving code in TRAPs and then re-raise the errors as full blown exceptions or convert them into error codes. To help with this, Qt provides a couple of macros and functions, by far the most useful being QT_TRAP_THROWING which does the trapping and throwing of a new exception in one fell swoop.
The only frustrating thing is the conversion functions, which don’t come in a sufficient variety of flavours. Ideally you’d want versions that could leave and versions that could throw an exception, so that you could use conversions in both directions in both types of code.
There’s a lot more detail on this in the official docs, and even more thorough discussion on the Symbian Wiki2, which also gives a full example of how to wrap a significant chunk of native functionality using the Pimpl pattern.
Memory management is actually pretty simple if you follow the writing-your-own API model and make sure that any object that you expect to be manipulated or deleted by the Qt side of the interface is not left on the clean up stack. It’s fine to follow Symbian conventions inside objects, but you mustn’t let the Symbian-ness leak out in the Qt code. Again, see the wiki for more details.
Pushing the data into the model
Having extracted the two sets of data from their sources, we needed to get both sets into the model. We’d decided early on that the main list view would be in date order, with the most recent dates at the top, so it made sense to hold the messages in a sorted list. However, in the name of modularity, I wanted to make each message source responsible for its own messages, with the main view list being an automatically aggregated version of this. I also wanted to load all the messages at least semi-lazily, so it made sense as a first step to fill the main list from the source-specific ones on demand. This led me to a slightly strange set up, where the main list was a list of pointers to objects owned by the source-specific lists. This main list was populated lazily as the view requested items by adding a pointer to the next most recent message in all of the source-specific lists.
From a Qt point of view, the interesting thing was the containers and algorithms used. For the type specific lists, a sorted list or set type would have been ideal, but Qt doesn’t have one. Of the existing Qt sequential containers, none seemed particularly suited, but since QList is the generally recommended sequence type, I chose that as my basic container for both type specific collections.
I was initially frustrated by the lack of a sort or an insert in order method, as I would have used in a Symbian RArray, but I quickly found the QAlgorithms module, which supplies a collection of basic algorithms, loosely based on a subset of the STL algorithms. These include qSort, qLowerBound, qUpperBound and qBinarySearch generic functions for dealing with sorted sequences and the qSort and qUpperBound were easy to adapt to my ends. With operator< defined on the message class such that later messages are effectively less than earlier messages, insert in order could be defined as:
iMessages.insert(qUpperBound(iMessages.begin(),
iMessages.end(),
newMessage),
newMessage);
which is more verbose than the Symbian equivalent, but with the benefit of being closer to standard C++.
To keep track of which source-specific list contained the next available message to be added to the main list, each message source held an index into the next available message that hadn’t been added into the main list. This enabled me to treat the sources as stacks, with the top of the stack being the next available message for that source. I then sorted the sources themselves after each message was added using qSort and a specialised comparison function. This was complete overkill for two lists, and rather inefficient to boot, but there was some method behind this apparent madness. If I’d had time, I was intending to replace the qSort with a qHeapify function that would maintain the list as a heap rather an entirely sorted list. This would have made for a more efficient data structure, and also enabled me to investigate the writing of generic Qt algorithms, but I ran out of time before I had the chance to implement one.
Coming up next…
The final part of the series, going back to the UI before drawing some conclusions.
1. While there still hasn’t been an official release of the entire library, the technology preview now has support for messaging.
2. Thanks to Hamish Willee for linking to this in a comment on the first post in this series.
This is the third in a series of posts covering our experiences in implementing a Universal Inbox application on Symbian using a Qt library. Parts 0 and 1 were published on Monday and Tuesday; parts 3-4 will appear over the next couple of days.
Actually getting the data
To start with we decided to only implement two of the possible message types, Twitter and SMS. We chose these two because they were simple to display, but still covered the three different ways data was likely to be brought into the app: HTTP requests to a remote server, a message being received by the message store, or the app requesting the set of already used messages in the app.
In today’s post, I’ll cover how we handled the Twitter feed; in tomorrow’s, I’ll go into the issues thrown up when wanted to watch for and read SMSes.
Talking to the Twitter server
Fetching the Twitter data from the internet proved to be remarkably straightforward. Qt provides a very simple API for network connections, consisting of a QNetworkConnection class that handles the sending and receiving of requests, and QNetworkRequest and QNetworkResponse classes that encapsulate the requests and responses respectively. To implement HTTP handling in our TwitterMessageStore class I just added a QNetworkConnection member to the class declaration, constructing it and connecting its finished signal to the TwitterMessageStore::reply slot in the constructor. Then it was just a case of implementing a request function, like this:
void TwitterMessageStore::request()
{
QByteArray authHeader = authHeaderValue();
if(!authHeader.isEmpty())
{
QNetworkRequest request(
QUrl("http://twitter.com/statuses/friends_timeline.xml?count=80"));
request.setRawHeader("Authorization",authHeader);
ipConnection->get(request);
}
}
and a reply slot implementation like this:
void TwitterMessageStore::reply(QNetworkReply* apReply)
{
QXmlStreamReader reader(apReply);
TwitterStatusParser parser;
parser.parse(reader);
//...
}
The contrast between this and the amount of boilerplate required by the Symbian API is staggering - if the authentication header wasn’t required, the request method could be a one or two line function
Of course, we did need the authentication header, Twitter’s basic authentication consisting of the notoriously insecure base64-encoded username and password mechanism. To allow the user to supply these, we added a settings dialog and menu item to the main view, which stored the username and password as strings using the QSettings class. This class implements a dictionary storage mechanism, serializing objects to a file held in the application’s private directory. We used the same mechanism to deserialize the username and password from disk whenever we needed to make a request to the server. Caching these in memory would have been a bit more efficient, but at the price of some potentially complex extra code to invalidate the cache.
After deserialization, the username and password then needed to be concatenated and turned into Base64 utf8 byte strings. Again, this was almost absurdly trivial in Qt:
QString usernamepassword;
QTextStream(usernamepassword) << username << ':' << password;
return QByteArray("Basic " + usernamepassword.toUtf8().toBase64());
To give some idea of how complicated an operation like this could be, the equivalent Symbian code would have looked something like this:
HBufC* pUsernamepassword = HBufC::NewLC(username.Length() + 1 + password.Length());
pUsernamepassword->Des().AppendFormat(_L("%S:%S"),&username,&password);
HBufC8* pUtf8 = CnvUtfConverter::ConvertFromUnicodeToUtf8L(pUsernamepassword);
CleanupStack::PushL(pUtf8);
HBufC8* pB64 = HBufC8::NewLC((pUtf8->Length() + 2) * 4/3);
TImCodecB64 b64encoder;
b64encoder(pB64->Des());
_LIT8(KBasic,"Basic ");
HBufC8* pAuth = HufC8::NewLC(KBasic().Length() + pB64->Length());
pAuth->Des().Append(KBasic);
pAuth->Des().Append(*pB64);
CleanupStack::Pop(pAuth);
CleanupStack::PopAndDestroy(pB64,3);
return pAuth;
At was at this point in the project that it really dawned on me how much of a revolution in Symbian application development the Qt implementation could be when it’s complete. By simply removing the need to do all this clean up stack juggling and descriptor converting, a huge amount of mental overhead is removed at a stroke. It’s not just that there’s less code, it’s that there are fewer decisions to make. These decisions are fairly trivial once you’re familiar with Symbian conventions, but each one still incurs a few mental cycles each time it’s made, and can still be disastrous if made wrongly.
Parsing the data
Once we had the data, it was necessary to parse it. Qt offers three ways of doing this: DOM, SAX and their own, recommended, highly efficient pull parser. The DOM parser is relatively memory hungry, and the Twitter XML relatively large, so I discounted it immediately, leaving the choice of parser between the SAX and pull parsers. As I was trying to get to grips with Qt, I chose to use their recommended parser, rather than the SAX option I’d usually go for.
This was fairly easy to use, but I am not convinced that it’s necessarily the best choice in all cases. In the situation I was in, where I was only interested in a few subtrees of the document being parsed, it felt rather verbose, requiring me to write code that handled all the parents of the tags I was interested in, rather than just the tags themselves. I realised once I’d finished that I could trivially create a SAX-like interface on top of the pull parser, but I was concerned that in doing so I would re-introduce the inefficiencies that the pull parser was supposed to eliminate.
Coming up next…
Tomorrow, I deal with the issues encountered when trying to watch for and read SMSes, and take a brief look at our use of containers and algorithms.
This is the second in a series of posts covering our experiences in implementing a Universal Inbox application on Symbian using a Qt library. Part 0 was published yesterday, and parts 2-4 will appear over the next few days.
My first application
Writing a basic Qt app is very simple, as our main function shows:
int main(int argc, char **argv)
{
QApplication app(argc, argv);
app.setKeypadNavigationEnabled(true);
MainWindow window;
window.showMaximized();
return app.exec();
}
QApplication is the main Qt application class that handles the basic Qt application machinery, and MainWindow is the user defined main window class, derived from QMainWindow. Note that both are created on the stack, and so will be cleaned up when the program exits.
The function calls are simple enough: setKeypadNavigationEnabled enables navigation around the application using the joypad, showMaximized ensures that the window occupies the normal app screen space (excluding the softkey and title/icon panes), and exec starts the main application’s run loop.
The main window’s role seems rather ill-defined in S60 applications. In conventional desktop applications, it’s the top level window which contains the menu, tool and status bar, as well as the central content. But in S60 land, the equivalent soft key and title panes aren’t part of the conventional main window in the same way, so it feels a little redundant. I haven’t actually attempted this, but it might be interesting to replace the QMainWindow derived class with a simple view or a dialog and see what happens.
We used the Carbide wizard to create our project, and it generated a main function very much like the one above, and a nearly empty main window class. The only thing we initially altered in the main function was to change the generated window.show() to window.showMaximized(), as recommended. By contrast, we quickly completely changed the main window class.
The only line that actually did anything in the generated main window class was a single call in the constructor:
ui.setupUi(this);
This initialised the UI object that we were then expected to generate using the graphical UI designer in Carbide. To begin with, we attempted to use the designer to lay out out the basic structure of the application, but it quickly proved more work than manually laying out the components ourselves. This almost certainly had something to do with the style of the app we were writing - one main list view, plus an item view and a few dialogs isn’t that much work to hand code, after all - but I wonder if it’s going to be much use for S60 apps until more work is done on the designer. Right now, it feels much more targetted towards the design of generic desktop apps rather than S60 mobile applications. Much of the benefit of resource editors is in their WYSWYG nature, but as of the current release WYS is most definitely not WYG.
So, we removed all the auto-generated UI code, and set to work adding things by hand.
The main list view and MVC
The first job was to define our main list view, the one that users would see when the app was opened. Given that the app was to have at least one other type of view (the detailed view of each entry), it seemed sensible to use an MVC pattern if one was available.
Qt does have support for Model/View separation, but not in the fully general way you might expect. Rather than the generic document and view classes other frameworks offer, it instead has a collection of different view and model classes specialised for different data structures. These include QAbstractListModel and QListView, QAbstractTableModel and QTableView, and QAbstractItemModel and QTreeView. Each pair of these offers a sensible default behaviour for the data structure in question, with only a few simple abstract methods that need implementing for each model.
Because all of these views and models are granular, Qt offers an orthogonal way of customising the appearance of the individual item, as opposed to the way the items are collectively laid out. This takes the form of Delegates, classes derived from AbstractItemDelegate which implement different ways of painting the individual items. The QItemView derived classes all use a basic, stylable delegate by default, but this can be replaced using the view’s setItemDelegate method.
Given that our application is inherently listy, going with a straight list view and model initially seemed like the obvious thing to do. But after some fiddling, we ended up with the slightly odd combination of a QListView derived view talking to a QAbstractTableModel derived model instance. How we got there requires a bit of explanation.
The problem arose when considering what we wanted each individual item in the list to look like. Given that this was intended to be a universal inbox, it made sense to emulate the appearance of the built in inbox, as found in the messaging app. So, each item should have a bold from line, followed by a truncated subject/description line in a normal weight font. We also decided to show the type of the message using an icon to the left of the pair of lines.
Now, the standard list view doesn’t have any support for multiple text widgets per item - you basically get an icon and a label, and that’s it. So it quickly became apparent that we would have to write our own delegate. How best to do this wasn’t immediately obvious - we needed to implement the paint method to get both chunks of text on screen, but the standard list model only supported the single piece of text. For a while it looked as though the role mechanism might provide an answer.
To fetch the item data to display to the user, the view indirectly calls the model’s data method, which returns the data in the form of a QVariant object, and accepts two arguments, the model index of the item and a role. These roles determine the type of data the view is asking for, including the text data, the icon and what to display when editing an item, but also things such as the preferred font and background colour to use.
This demonstrates one of the stranger aspects of the Qt model/view relationship - the model is expected to provide a non-trivial amount of display information to the view. Which, while not the end of the world, is antithetical to conventional MVC, where the model doesn’t care how the data is displayed.
Anyway, our hope was that we might be able to supply one chunk of text with one role, and the other with another. However, there simply wasn’t an appropriate extra role to use, and we were unclear as to the wisdom of adding a user defined role. In the end, we simply replaced the list model with a table model, with the two strings occupying the same row but different columns in the model. The view (or more precisely, the delegate) could then request both columns from the model when drawing a single row.1
So, now that we had an easy way to get the two chunks of text, we had to implement their painting to the screen. As mentioned above, we overrode the delegate’s paint method and reimplemented the drawing of the entire item via the passed in painter method. We had to take some care in making sure that we extracted stylisable details like the font from the passed in QStyleOptionViewItem, and that we highlighted the item when it was selected, but this was otherwise fairly straightforward.
Coming up next…
Tomorrow, I explain how we went about pulling information from the internet and parsing XML.
1. On reflection, a better way to do it might have been to return a QVariant containing the entire data object for the message, but this didn’t initially seem practical, and even now I suspect it might have been overkill.
Introduction
Over the course of three weeks earlier this year, Maurizio Scotti and I attempted to develop a Universal Inbox application using Qt on S60 here at Symsource, as a way of getting to grips with the framework and assessing whether it was ready for use in production quality applications.
Brainstorming with the rest of our team, we decided that a good learning project would be to write a simple aggregator that would gather together all of a users SMS, email, Twitter and Facebook messages in a single application. The main view of the application would be a date ordered summary list of messages, with additional views to support the viewing of message details and the composition of new messages.
This series of blog posts is a summary of what we did, how we used Qt to do it, and what we thought of Qt as a platform. By its very nature, it’s a selective look at Qt, as I can only write about the pieces of the platform we actually used. It’s also deliberately not a guide to the practicalities of getting Qt installed and running, this being admirably dealt with in the Qt Quick Start page on the Symbian wiki.
Qt basics from the perspective of a Symbian programmer
Before we dive into the specifics of what we encountered, it’s worthwhile spending a bit of time going through some of the basic ideas behind Qt.
Qt was originally designed as a cross-platform widget and UI library for use on Linux and Windows. It’s since spread to more platforms and offers a standard API that supports a lot more than just GUI programming. There’s a decent selection of network interfaces, for example, and a fairly comprehensive set of container classes.
The basic components of the the UI will be familiar to anyone who’s done any modern UI programming: widgets, views, dialogs, menus and commands. The novel thing is how they fit together - rather than having a system of callbacks and interfaces or providing a simple loosely typed message passsing mechanism, Qt uses signals and slots.
Together, these make up a generalised publish and subscribe service. Signals correspond to events being published, slots act as sinks for the events, and the connect function is used to subscribe a slot to a signal. It’s also possible to chain signals to other signals, again using the connect function. Both signals and slots are declared as methods on a class, but only slots have definitions. Signals are emitted using the special Qt emit keyword, and received by the slots as though they were conventional function calls.1
All the necessary scaffolding needed for a class to handle signals and slots is wrapped up inside the Qt base class, QObject, and the Q_OBJECT declaration macro. Simply derive a class from QObject and include the Q_OBJECT declaration at the beginning of the class declaration and you can create signals and slots at will.
Doing this doesn’t just get you signal handling. Also included as part of QObject are: child object cleanup; low level event interception for things like key presses; support for Qt’s timer mechanism; and a degree of runtime reflection and dynamism. As a consequence of some of these features, objects inherited from QObject have to be treated as though each one has a unique identity, which means that naive copying of QObjects is a bad idea. To prevent problems with this, the QObject assignment operators and copy constructors are automatically disabled.
This identity/value issue, plus the overhead required for the rest of the functionality means that not all Qt classes inherit from QObject. In particular, the string-like classes and containers are all outside the QObject hierarchy. Instead, these all have copy-on-write value semantics, meaning that they can be passed around and returned as though they were simple, small objects, like TInt or TReal, without fear of incurring huge copying costs.
The standard Qt string class, QString, is a full fledged unicode string class, which should be used whenever you are manipulating text internally. It has all the expected string manipulation, searching and conversion operations, and grows and shrinks dynamically as you add and remove data from it.
To handle arrays of bytes, Qt provides a QByteArray with much of the same functionality. The relationship between the two is much like that between TDes16 and TDes8 - one is for text, the other for binary data (that may just happen to be encoded text).
There are several different templated container classes available:
QLinkedList - a linked list
QVector - a resizable array
QList - a hybrid list/array (somewhat like C++’s std::deque)
QMap - a sorted associative array (with binary tree performance)
QHash - a hashmap
There are also assorted subtypes, including a stack, queue and multimap. In general, they are easier to use than the Symbian equivalents, particularly the QHash, which has built in hash functions for a much greater variety of types than Symbian’s equivalent. The only obvious thing missing is a set or hashed set class.
The split between two different types of classes, and especially the difference in their copy semantics is reminiscent of Symbian’s division of its classes into C, T and R classes. However, beware of falling into the trap of assuming that the distinctions are equivalent: they are not.
Qt uses error codes (plus the standard C++ bad_alloc exception) rather than leaves, so there is no need for a cleanup stack, or anything approximating to it, and no explicit two stage construction. This in turn means that there isn’t the same need for a clear correlation between the type of an object and whether it’s created on the stack or not. Instead, everything can equally be created on the stack or heap. If created on the stack, it gets cleaned up normally when the stack is unwound (either normally, or when an exception is thrown); if created on the heap, it either has to be managed manually, or the ownership can be assigned to a parent object derived from QObject, with the object then being automatically deleted when its parent is. This ownership pattern tends to lead to a tree of ownership, with the QApplication object at the root.2
One side effect of this lack of type specialisation on storage type is that there is no automatic zeroing of memory for any Qt-derived object. So you have to make sure that you manually zero anything you expect to be zeroed as part of the object’s construction.
Coming up next…
Tomorrow, I dive into the details of how we used Qt in our application, starting right at the beginning with the implementation of the main function, followed by a look at the way Qt uses the MVC pattern.
1. The signals and slots are supposed to be typesafe, with the arguments of the signal checked against those of the slot. But on a couple of occasions I successfully compiled what appeared to be mismatched signals and slots.
2. This has some implications for the stack/heap balance in applications. Symbian code is generally written with the assumption that the available stack is (by non-embedded standards) extremely small, so very little gets created on the stack. This pattern can be followed without too much difficulty if you are writing all your Qt code from scratch, but when porting code, or using third party libraries, this is not necessarily possible. The current documentation is a bit vague on how this should be handled, though it may be interesting to note that the Qt friendly MMP file generated by Carbide automatically contains this line:
EPOCSTACKSIZE 0×14000
Which corresponds to 80K - ten times that used by Series60v3 application by default.
In a recent project we were creating a funky new Twitter client for Sony-Ericsson MIDP handsets. Sounds dull, but there were some cool features of this product which made it much more interesting than it sounds (no I can’t elaborate :-).
Part of this was upload of pictures to Twitpic. “Great”, we thought, “Twitpic - simple URL-based REST API. Should be easy enough”. As it turned out, not as simple as originally anticipated.
The problem is a mis-match between the behaviour of the Sony-Ericsson MIDP HTTP stack and the needs of the Twitpic server.
The Twitpic server requires a Content-Length header to be included, or else your picture post is rejected. The Sony-Ericsson HTTP stack, however, adopts HTTP/1.1 chunked encoding for any payload over a few kilobytes…. and HTTP/1.1 chunked encoding doesn’t supply a Content-Length header. Argh!
All attempts to explicitly set the Content-Length header in MIDP (via HTTPConnection::setRequestProperty("Content-Length", length) were soundly ignored by the Sony-Ericsson devices.
Double Argh!
The solution? Painful, but for Twitpic posts we use a socket connection and mock-up the HTTP communication, headers etc. including the Content-Length header. This meant more work, but in addition meant the application needed to be signed (i.e. Java Verified) in order for Twitpic to work - socket connections to port 80 require a trusted third party domain.
So, problem solved - in the end without any schedule impact - but with some consequences for the product deployment.
You could be forgiven for thinking Apple’s iPhone is the only platform with downloadable applications. That’s the impression many consumers have, and although other platforms have had apps and games for years, it has taken Apple to show them how it’s done, with great execution and a large investment in marketing creating a truly viable market for third-party downloads.
But while BlackBerry App World and Android Market are slowly gaining traction, only Nokia is putting its money where its mouth is with a TV, print and poster campaign that has already seen Ovi Store downloads increase 70% month on month, even before the campaign has really got going.
In this week’s Mobile Today, we suggest that now may be the perfect time to get your apps and content ready for Ovi Store. Read the full story at http://bit.ly/4w5YlB
Out of the box, the Android Eclipse development environment fails when attempting to debug on the T-Mobile Pulse with the following error:
java.io.IOException: device (????????????) request rejected: device not found
Launch canceled!
However, merely adding the vendor ID 0×12d1 to ~/.android/adb_usb.ini solves the problem, as detailed here: http://osdir.com/ml/Android-Beginners/2009-11/msg00529.html
Symsource is 5 years old today, so we just wanted to take this opportunity to thank all of our staff, partners, suppliers and of course customers past and present for making it possible for us to work in what is still the most exciting field in software, the fast growing, ever changing, never boring and arguably world changing field that is the mobile phone industry.
When we started out, there were no App Stores, no 3G, no GPS, no Wifi in our phones. We hoped that the market for innovative applications and services for the mobile phone would take off and we could ride that wave, and from humble beginnings with projects on Symbian and J2ME, we can now boast a track record across Windows Mobile, BlackBerry, Android and iPhone as well.
Here’s to another 5 years - you’ll be using your mobile to control your home, send voice commands to your car, track your pets location, stream HD (or 3D!) movies to your TV and maybe even figure out some reason for all that social data you’re collecting long before then. And at Symsource we hope we’re the ones who get to build it!
|
|