N'étant pas infographiste, soyez indulgent pour la qualité visuelle. Avant de me lancer dans des explications de code, je tiens à préciser plusieurs choses :
- J'ai utilisé la dernière version du sdk Qt, à savoir la version 1.1.4, avec certains des composants Qt Quick pour Symbian.
- J'ai validé cette application sur un Nokia E7 upgradé avec Symbian Anna. La config de compilation Qt utilisée est donc "Qt 4.7.4 pour Symbian Anna".
- L'application ne permet pas d'envoyer un email avec le simulateur
- Il vous faut créer au moins un compte email sur votre téléphone Symbian
- J'ai fait le choix de gérer la partie purement graphique en QML et la partie logique métier en C++
J'ai créé une classe "helper" dénommée "EmailHelper" comme suit :
class EmailHelper : public QObject
{ Q_OBJECTpublic: explicit EmailHelper(QObject *parent = 0); ~EmailHelper(); Q_INVOKABLE bool sendEmail(QString accountName, QString recipient, QString subject, QString body); Q_INVOKABLE QStringList getEmailAccountList();signals: void stateMsg(const QString &statemsg); void errorMsg(const QString &errormsg);private slots: void messageStateChanged(QMessageService::State s); private: QMessageService iMessageService; QMessageManager iManager; QMessageService::State state; QMap<QString, QMessageAccountId> accountList;};Comme vous le constatez cette classe me permet de récupérer la liste des comptes email configurés sur le téléphone (méthode getEmailAccountList) et d'envoyer un email depuis un compte donné (méthode sendEmail). Notez la signature particulière de ces deux méthodes : j'ai utilisé la macro "Q_INVOKABLE" afin de les rendre accessible depuis le contexte d'execution déclaratif QML. Enfin pour gérer l'envoi d'email, je m'appuie sur le module "Messaging" de Qt Mobility via l'usage des deux classes QMessageService et QMessageManager.
Coté implémentation, rien de bien compliqué :
- Un constructeur où je m'abonne au signal "stateChanged" émis par le service de messagerie:
QObject(parent){ state = QMessageService::InactiveState; connect(&iMessageService, SIGNAL(stateChanged(QMessageService::State)), this, SLOT(messageStateChanged(QMessageService::State)));}- La méthode de récupération des comptes emails :
{ QStringList accountNameList;#ifdef SENDEMAIL_ENABLED // Récupération de la liste des comptes email et ajout dans la liste foreach (const QMessageAccountId &id, iManager.queryAccounts()) { QMessageAccount account(id); if (account.messageTypes() & QMessage::Email) { QString name(account.name()); accountList.insert(name, account.id()); accountNameList.append(name); } }#endif return accountNameList;}Note : le service de messagerie étant commun aux SMS, MMS et Email, je mets un filtre en place sur les emails. Globalement un compte email est composé d'un libellé et d'un identifiant ; je stocke ces informations dans une propriété de type QMap afin de ré-utiliser la correspondance Libellé/Identifiant utlérieurement pour l'envoi d'un email. Et je retourne uniquement la liste de libellés de comptes à destination de l'affichage géré en QML
- La méthode d'envoi d'un email :
bool EmailHelper::sendEmail(QString accountName, QString recipient, QString subject, QString body)
{#ifdef SENDEMAIL_ENABLED if (!QMessageAccount::defaultAccount(QMessage::Email).isValid()) { emit errorMsg("Aucun compte configuré pour envoyer un email."); return false; } if (state == QMessageService::InactiveState || state == QMessageService::FinishedState) { QMessage message; QMessageAccountId accountId = accountList[accountName]; message.setType(QMessage::Email); message.setParentAccountId(accountId); // Email de destination QMessageAddress::Type addrType(QMessageAddress::Email); QMessageAddressList toList; toList.append(QMessageAddress(addrType, recipient)); message.setTo(toList); // Objet du mail if (subject.isEmpty()) { emit errorMsg("Aucun objet spécifié"); return false; } else message.setSubject(subject); // Corps du mail if (body.isEmpty()) { emit errorMsg("Aucun contenu de message spécifié"); return false; } else message.setBody(body); // Envoi de l'email bool result = iMessageService.send(message); return result; } else { return false; }}Cette méthode initialise un objet QMessage à partir du compte email séléctionné par l'utilisateur avec :
1. Une adresse de destination
2. Un objet
3. Un corps de mail
Note : pour aller plus loin dans la prise en main de l'api de messaging, je vous invite à ajouter une pièce jointe.
Afin de rendre ma classe visible et instantiable dans le contexte déclaratif QML, j'ai ajouté la ligne suivante dans la fonction "main" du programme :
qmlRegisterType<EmailHelper>("EmailHelper",1,0,"EmailHelper");
Ainsi, comme nous le verrons plutard, je pourrai instantier mon objet depuis QML via une simple déclaration comme cela :
EmailHelper {
}
}
- Pour finir j'ai implémenté le slot privé "messageStateChange" dont le rôle est de relayer le signal public "stateMsg" à la partie QML afin d'en informer de manière asynchrone du statut de l'envoi d'email :
void EmailHelper::messageStateChanged(QMessageService::State s){
Dernier point : il faut que l'application soit signée avec un certificat "Express Signed" ; le mode "auto-signé" ne fonctionne pas car nous avons besoin de la permission "ReadDeviceData". Donc soit vous utiliserez un certificat fourni par PublishToOVI via votre compte Nokia Publisher, soit vous utiliserez un certificat SymbianSign.
Ayant utilisé le template de projet Qt Quick pour Symbian avec utilisation des composants Qt Quick, je me retrouve donc avec deux fichiers :
1. main.qml
2. MainPage.qml
J'ai mis tout le code dans le fichier MainPage.qml ; pour un vrai projet il est recommendé de modulariser ses composants via une arborescence de fichiers bien organisés.
Commençons par les inclusions de module :
Rectangle {
Je définis ensuite une petite boîte de dialogue servant à afficher les messages d'erreur et de succés :
CommonDialog {
Puis j'instancie mon élément "EmailHelper" :
EmailHelper {
Comme vous pouvez le constater j'y défini les slots pour capturer les signaux "errorMsg" et "stateMsg" ; j'y rattache également la liste des noms de comptes emails via la variable "accountNameList" qui sera initialisée au chargement de la page QML comme le montre le code suivant :
Component.onCompleted: {
Par conséquent, la liste des comptes mails est récupérée au chargement de la page ; si cette liste est vide ou indisponible, j'affiche un popup invitant l'utilisateur à créer un compte email sur son téléphone.
Enfin j'affiche le formulaire de saisie permettant de contruire l'email à envoyer ; ce formulaire possède un header avec un bouton d'envoi :
// Fixed Header
La partie formulaire sera positionnée dans une zone scrollable (Flickable) :
Flickable {
// Sélection du compte email
Ensuite il s'agit d'ajouter les zones de saisies et le tour est joué. Une fois les données saisies, l'utilisateur n'a plus qu'à valider le bouton situé dans le header du formulaire et l'email est envoyé.
// Destinataire du message
state = s;
if (s == QMessageService::InactiveState) { emit stateMsg("InactiveState"); } else if (s == QMessageService::ActiveState) { emit stateMsg("ActiveState"); } else if (s == QMessageService::CanceledState) { emit stateMsg("CanceledState"); } else if (s == QMessageService::FinishedState) { emit stateMsg("FinishedState"); } else { emit stateMsg(QString("QMessageService::%1").arg(s)); }}Et voilà pour la partie C++
Partie Environnement :
Afin de pouvoir compiler et envoyer un email de manière effective, il est INDISPENSABLE de modifier son fichier projet (*.pro) comme suit :
- Ajouter les deux lignes suivantes pour l'utilisation du module "Messaging" de Qt Mobility :
CONFIG += mobility
MOBILITY += messaging
- Ajouter les permissions suivantes (notamment pour accéder aux comptes email de l'utilisateur) :
symbian:TARGET.CAPABILITY += NetworkServices \
LocalServices \
ReadUserData \
WriteUserData \
UserEnvironment \
ReadDeviceData \
WriteDeviceData- Ajouter la dépendance aux composants Qt Quick
Dernier point : il faut que l'application soit signée avec un certificat "Express Signed" ; le mode "auto-signé" ne fonctionne pas car nous avons besoin de la permission "ReadDeviceData". Donc soit vous utiliserez un certificat fourni par PublishToOVI via votre compte Nokia Publisher, soit vous utiliserez un certificat SymbianSign.
Partie UI en QML :
Ayant utilisé le template de projet Qt Quick pour Symbian avec utilisation des composants Qt Quick, je me retrouve donc avec deux fichiers :
1. main.qml
2. MainPage.qml
J'ai mis tout le code dans le fichier MainPage.qml ; pour un vrai projet il est recommendé de modulariser ses composants via une arborescence de fichiers bien organisés.
Commençons par les inclusions de module :
- import com.nokia.symbian 1.1 : pour utiliser les composants Qt Quick
- import Qt.labs.components 1.1 : pour utiliser le regroupement exclusif des boutons radios
- import EmailHelper 1.0 : notre classe "helper" pour envoyer des emails
Rectangle {
id: rootWindow anchors.fill: parent width: 360 height: 640 gradient: Gradient { GradientStop { position: 0 color: "#ccc094" } GradientStop { position: 0.8 color: "#777057" } GradientStop { position: 1 color: "#000000" } }Je définis ensuite une petite boîte de dialogue servant à afficher les messages d'erreur et de succés :
CommonDialog {
id: dialog titleText: "Information" buttonTexts: ["OK"] content: Rectangle { width: 300 height: 200 anchors.horizontalCenter: parent.horizontalCenter color: "transparent" Text { id: dialogMessage anchors.centerIn: parent textFormat: Text.StyledText width:290 text:"" wrapMode: Text.Wrap color: "white" } } onButtonClicked: { if(mainPage.quitRequested == true) Qt.quit() }}Puis j'instancie mon élément "EmailHelper" :
EmailHelper {
id: helper property int accountNumber; property variant accountNameList: [] onStateMsg: { console.log(statemsg) if(statemsg == "FinishedState") { indicator.visible = false indicator.running = false dialogMessage.text = qsTr("Email envoyé avec succès") dialog.open() } } onErrorMsg: { console.log(errormsg) indicator.visible = false indicator.running = false dialogMessage.text = errormsg dialog.open() }}Comme vous pouvez le constater j'y défini les slots pour capturer les signaux "errorMsg" et "stateMsg" ; j'y rattache également la liste des noms de comptes emails via la variable "accountNameList" qui sera initialisée au chargement de la page QML comme le montre le code suivant :
Component.onCompleted: {
console.log("Component.onCompleted") helper.accountNameList = helper.getEmailAccountList() helper.accountNumber = helper.accountNameList.length if( helper.accountNumber == 0) { mainPage.quitRequested = true dialogMessage.text = qsTr("Désolé vous n'avez aucun compte mail configuré sur ce téléphone") dialog.open() } else console.log("Vous avez "+ helper.accountNumber +" comptes"); for(var i=0;i<helper.accountNumber;i++){ console.log("Compte "+i+" : "+helper.accountNameList[i]) } }Par conséquent, la liste des comptes mails est récupérée au chargement de la page ; si cette liste est vide ou indisponible, j'affiche un popup invitant l'utilisateur à créer un compte email sur son téléphone.
Enfin j'affiche le formulaire de saisie permettant de contruire l'email à envoyer ; ce formulaire possède un header avec un bouton d'envoi :
// Fixed Header
Item { id: header width: rootWindow.width height: 60 z:3 Rectangle { color: "black" anchors.fill: parent BusyIndicator { id: indicator anchors.leftMargin: 5 running: false visible: false } Text { id: title text: qsTr("Custom Mail Client") color: "white" anchors.centerIn: parent font.bold: true font.pixelSize:20 } Button { id: send iconSource: "send.png" x:rootWindow.width-send.width anchors.rightMargin: 5 width:header.height-5 height:header.height-5 onClicked: { if(mailAccount.account.length>0 && mailRecipient.recipient.length>0 && mailSubject.subject.length>0 && mailBody.body.length>0) { var requestResult = helper.sendEmail(mailAccount.account, mailRecipient.recipient, mailSubject.subject, mailBody.body); if(requestResult) { indicator.visible = true indicator.running = true } else { dialogMessage.text = qsTr("Echec de la demande d'envoi d'email !"); dialog.open() } } else if(mailRecipient.recipient.length == 0) { dialogMessage.text = qsTr("Veuillez saisir une addresse de destination") dialog.open() } else if(mailSubject.subject.length == 0) { dialogMessage.text = qsTr("Veuillez renseigner un objet") dialog.open() } else if(mailBody.body.length == 0) { dialogMessage.text = qsTr("Veuillez renseigner le corps du mail") dialog.open() } } } }}La partie formulaire sera positionnée dans une zone scrollable (Flickable) :
Flickable {
id:flickable
width: parent.width
height:parent.height-header.height
anchors.top: header.bottom
contentWidth: flickable.width;
contentHeight: mailAccount.height+flickable.height*3/2
flickableDirection: Flickable.VerticalFlick
La partie bouton radio du formulaire pour sélectionner le compte email :
// Sélection du compte email
Item { property string account: helper.accountNameList[0] id: mailAccount width: rootWindow.width-20 height: helper.accountNumber*50 anchors.top: parent.top anchors.topMargin: 10 anchors.left: parent.left anchors.leftMargin: 10 anchors.rightMargin: 10 Text { id: mailAccountLabel text: qsTr("Compte Mail :") font.bold: true font.pixelSize:20 } CheckableGroup { id: group } Column { id: column anchors.top: mailAccountLabel.bottom anchors.topMargin: 5 spacing: platformStyle.paddingMedium Repeater { model: helper.accountNumber RadioButton { text: helper.accountNameList[index] platformExclusiveGroup: group onClicked: { mailAccount.account = text } } } } }Ensuite il s'agit d'ajouter les zones de saisies et le tour est joué. Une fois les données saisies, l'utilisateur n'a plus qu'à valider le bouton situé dans le header du formulaire et l'email est envoyé.
// Destinataire du message
Item { property alias recipient: recipientRect.text id: mailRecipient width: rootWindow.width-20 height: 50 anchors.top: mailAccount.bottom anchors.topMargin: 25 anchors.left: parent.left anchors.leftMargin: 10 anchors.rightMargin: 10 Text { id: mailLabel text: qsTr("Email de destination :") font.bold: true font.pixelSize:20 } TextField { id: recipientRect anchors.top: mailLabel.bottom anchors.topMargin: 5 width: parent.width }} // Objet du mailItem { property alias subject: subjectRect.text id: mailSubject width: rootWindow.width-20 height: 50 anchors.top: mailRecipient.bottom anchors.topMargin: 25 anchors.left: parent.left anchors.leftMargin: 10 anchors.rightMargin: 10 Text { id: subjectLabel text: qsTr("Objet :") font.bold: true font.pixelSize: 20 } TextField { id: subjectRect anchors.top: subjectLabel.bottom anchors.topMargin: 5 width: parent.width }} // Corps du mailItem { property alias body: bodyInput.text id: mailBody width: rootWindow.width-20 height: 150 anchors.top: mailSubject.bottom anchors.topMargin: 25 anchors.left: parent.left anchors.leftMargin: 10 anchors.rightMargin: 10 Text { id: bodyLabel text: qsTr("Corps du message :") font.bold: true font.pixelSize: 20 } TextArea { id: bodyInput anchors.top: bodyLabel.bottom anchors.topMargin: 5 width: parent.width height : 120 }}Le code source complet du projet est téléchargeable ici Bonne lecture. 

Aucun commentaire:
Enregistrer un commentaire