Tuesday, July 26, 2011

Changing and accessing profiles programmatically in Harmattan

Qt's mobility APIs have currently quite restricted support for any profile based operations. For example, you can not change the current active profile. Luckily you can do so by using D-Bus, as Harmattan uses as a backend MeeGo's profile daemon.

In this posting I'll show the code to do the following:

  • Iterating all available profiles
  • Setting the used active profile
  • Catching a notification when the active profile changes

A little background information first. Harmattan's profiles that you can see (Silent, Beep, Ringing), are not really profiles. Instead they match existing profiles (silent, meeting, general) like this:

Harmattan profile type Real profile name
Silent silent
Beep meeting
Ringing general

The profile backend specifies all profiles, and each profile contains properties as key/value pairs. The Harmattan profile types are defined in each profile type under a string key of "ringing.alert.type". So when selecting "Beep" profile from N9/N950, in the background really the "Silent" profile is selected because that profile has as a property "ringing.alert.type=Beep". What this means in practice is best shown with code.

For easier comprehension, these code samples are minimal and lack any error checking (ie. they assume the D-Bus queries always succeed).

Common defines

Here's some common defines used in the code samples. They are for using the D-Bus invocations
#define PROFILED_SERVICE "com.nokia.profiled"
#define PROFILED_PATH "/com/nokia/profiled"
#define PROFILED_INTERFACE "com.nokia.profiled"
#define PROFILED_GET_PROFILES "get_profiles"
#define PROFILED_SET_PROFILE "set_profile"
#define PROFILED_GET_VALUE "get_value"
#define PROFILED_PROFILE_CHANGED "profile_changed"
// The key for accessing Harmattan's profile type under profile
#define PROFILED_TYPE_VALUE "ringing.alert.type"

Iterating all known profiles

This piece of code can be used to iterate all known profiles. These are not the Harmattan profile types that you want to show if targetting N9/N950, but it is something that is anyway needed when constructing the Harmattan profile types list:
QStringList
ProfileClient::profiles() const
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    QDBusReply<QStringList> reply = 
        dbus_iface.call(PROFILED_GET_PROFILES);
    return reply.value();
}
Here's the function that should be used with Harmattan if you want to show the same values as in Harmattan UI. This function basically iterates through the profiles and takes from each profile the property that defines the Harmattan profile type (Silent, Beep or Ringing):
QStringList
ProfileClient::profileTypes() const
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    QStringList profileTypes;
    QString profile;
    foreach (profile, profiles())
    {
        QDBusReply<QString> reply = 
            dbus_iface.call(PROFILED_GET_VALUE, profile, 
                            PROFILED_TYPE_VALUE);
        profileTypes.append(reply.value());
    }

    // In Harmattan at least, profile type Ringing is 
    // attached to  "general" and "outdoors" profiles. 
    // The "general" profile is the one that's used for
    // "ringing", profile "outdoors" should not be used
    // when setting a profile.
    profileTypes.removeDuplicates();
    return profileTypes;
}

Setting the used active profile

Here's the code to set the active profile to the named one. Note that this function requires the real profile (silent, meeting, general). In Harmattan you probably want to set the profile by Harmattan's profile type. Since it's an easy mapping from Harmattan profile type to the real profile name, I'll leave that detail as a fun little exercise for anyone interested.
bool
ProfileClient::setProfile(QString profileName)
{
    QDBusInterface dbus_iface(PROFILED_SERVICE, PROFILED_PATH,
                              PROFILED_INTERFACE);

    // Returns true if success
    QDBusReply<bool> reply = 
        dbus_iface.call(PROFILED_SET_PROFILE, profileName);
    return reply.value();
}

Catching a notification when the active profile changes

If there's a need to do something when active profile changes, you can attach a signal handler to using the following code sample. This catches changes done either by your program or by anything else that uses the profile backend (like changing it from N9/N950 menu)
ProfileClient::ProfileClient(QObject *parent) : QObject(parent)
{
   QDBusConnection::sessionBus().connect("", "",
       PROFILED_INTERFACE, 
       PROFILED_PROFILE_CHANGED,
       this, 
       SLOT(profileChanged(bool, bool, QString))))
}

void
ProfileClient::profileChanged(bool changed, bool active, QString profile)
{
    if (changed && active) {
        qDebug("Profile changed to %s", qPrintable(profile));
    }
}

Further reading

That's the basics for getting started, but if you want more look for hints here:

Profile backend's configuration files

See in the device (or in QEMU emulator) the files stored under /etc/profiled. These are the configuration files, and there you can see under each profile the properties that they define and that you can access like I did above with the Harmattan profile type.

Profile backend's D-Bus API

MeeGo's profile backend that Harmattan uses is open source (luckily, or I would not have been able to figure out these things), and you can see other D-Bus functions that are available for use here: MeeGo profile_dbus.h

1 comment:

  1. but we need an application to change/create a new profile
    like old nokia phones
    every profile can be changed with:
    kind of ringtone/sms/chat/mail
    volume range/beep/silent
    system and keyboard volume range
    vibro fuction able/disable
    kind of connection (2G or 3g or DUAL)
    power saver able/disable
    and NAME

    like the old nokia's phones

    ReplyDelete