Add UI backend based on tizen notifications 96/32196/13
authorAdam Malinowski <a.malinowsk2@partner.samsung.com>
Wed, 10 Dec 2014 09:17:06 +0000 (10:17 +0100)
committerAdam Malinowski <a.malinowsk2@partner.samsung.com>
Tue, 20 Jan 2015 11:59:33 +0000 (12:59 +0100)
Change-Id: Iec59517b8d5a55f54ca831394a12793c2ef2ae4c

packaging/askuser.spec
src/agent/CMakeLists.txt
src/agent/main/Agent.cpp
src/agent/ui/AskUIInterface.h
src/agent/ui/AskUINotificationBackend.cpp [new file with mode: 0644]
src/agent/ui/AskUINotificationBackend.h [new file with mode: 0644]

index 2653053..b044e24 100644 (file)
@@ -16,6 +16,7 @@ BuildRequires: pkgconfig(cynara-agent)
 BuildRequires: pkgconfig(cynara-plugin)
 BuildRequires: pkgconfig(libsystemd-daemon)
 BuildRequires: pkgconfig(libsystemd-journal)
+BuildRequires: pkgconfig(notification)
 BuildRequires: zip
 %{?systemd_requires}
 
index 0f57e63..a9b5c13 100644 (file)
@@ -20,6 +20,7 @@ PKG_CHECK_MODULES(AGENT_DEP
     REQUIRED
     cynara-agent
     cynara-plugin
+    notification
     )
 
 SET(ASKUSER_AGENT_PATH ${ASKUSER_PATH}/agent)
@@ -28,6 +29,7 @@ SET(ASKUSER_SOURCES
     ${ASKUSER_AGENT_PATH}/main/Agent.cpp
     ${ASKUSER_AGENT_PATH}/main/CynaraTalker.cpp
     ${ASKUSER_AGENT_PATH}/main/main.cpp
+    ${ASKUSER_AGENT_PATH}/ui/AskUINotificationBackend.cpp
     )
 
 INCLUDE_DIRECTORIES(
index 89aaa04..e8aaba9 100644 (file)
@@ -31,6 +31,8 @@
 #include <types/AgentErrorMsg.h>
 #include <types/SupportedTypes.h>
 
+#include <ui/AskUINotificationBackend.h>
+
 #include "Agent.h"
 
 namespace AskUser {
@@ -182,7 +184,7 @@ void Agent::processUIResponse(const Response &response) {
 
 bool Agent::startUIForRequest(Request *request) {
     auto data = Translator::Agent::dataToRequest(request->data());
-    AskUIInterfacePtr ui; // TODO: create pointer to backend
+    AskUIInterfacePtr ui(new AskUINotificationBackend());
 
     auto handler = [&](RequestId requestId, UIResponseType resultType) -> void {
                        UIResponseHandler(requestId, resultType);
@@ -191,6 +193,7 @@ bool Agent::startUIForRequest(Request *request) {
     if (ret) {
         m_UIs.insert(std::make_pair(request->id(), std::move(ui)));
     }
+
     return ret;
 }
 
index b78c35a..e00ba53 100644 (file)
@@ -49,7 +49,7 @@ public:
                        const std::string &privilege, RequestId requestId, UIResponseCallback) = 0;
     virtual bool setOutdated() = 0;
     virtual bool dismiss() = 0;
-    virtual bool isDismissing() = 0;
+    virtual bool isDismissing() const = 0;
 };
 
 typedef std::unique_ptr<AskUIInterface> AskUIInterfacePtr;
diff --git a/src/agent/ui/AskUINotificationBackend.cpp b/src/agent/ui/AskUINotificationBackend.cpp
new file mode 100644 (file)
index 0000000..746081d
--- /dev/null
@@ -0,0 +1,246 @@
+/*
+ *  Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License
+ */
+/**
+ * @file        AskUINotificationBackend.cpp
+ * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
+ * @brief       This file implements class for ask user window
+ */
+
+#include <bundle.h>
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <cstring>
+#include <libintl.h>
+#include <privilegemgr/privilege_info.h>
+
+#include <attributes/attributes.h>
+#include <log/log.h>
+
+#include "AskUINotificationBackend.h"
+
+namespace {
+
+const char *errorToString(notification_error_e error) {
+    if (error == NOTIFICATION_ERROR_INVALID_DATA)
+        return "NOTIFICATION_ERROR_INVALID_DATA";
+    if (error == NOTIFICATION_ERROR_NO_MEMORY)
+        return "NOTIFICATION_ERROR_NO_MEMORY";
+    if (error == NOTIFICATION_ERROR_FROM_DB)
+        return "NOTIFICATION_ERROR_FROM_DB";
+    if (error == NOTIFICATION_ERROR_ALREADY_EXIST_ID)
+        return "NOTIFICATION_ERROR_ALREADY_EXIST_ID";
+    if (error == NOTIFICATION_ERROR_FROM_DBUS)
+        return "NOTIFICATION_ERROR_FROM_DBUS";
+    if (error == NOTIFICATION_ERROR_NOT_EXIST_ID)
+        return "NOTIFICATION_ERROR_NOT_EXIST_ID";
+    if (error == NOTIFICATION_ERROR_IO)
+        return "NOTIFICATION_ERROR_IO";
+    if (error == NOTIFICATION_ERROR_SERVICE_NOT_READY)
+        return "NOTIFICATION_ERROR_SERVICE_NOT_READY";
+    if (error == NOTIFICATION_ERROR_NONE)
+        return "NOTIFICATION_ERROR_NONE";
+
+    return "UNHANDLED ERROR";
+}
+
+}
+
+namespace AskUser {
+
+namespace Agent {
+
+AskUINotificationBackend::AskUINotificationBackend() : m_notification(nullptr),
+                                                       m_dismissing(false) {
+    m_future = m_threadFinished.get_future();
+}
+
+AskUINotificationBackend::~AskUINotificationBackend() {
+    notification_free(m_notification);
+}
+
+bool AskUINotificationBackend::start(const std::string &client, const std::string &user,
+                                     const std::string &privilege, RequestId requestId,
+                                     UIResponseCallback responseCallback) {
+    if (!responseCallback) {
+        LOGE("Empty response callback is not allowed");
+        return false;
+    }
+
+    if (!createUI(client, user, privilege)) {
+        LOGE("UI window for request could not be created!");
+        return false;
+    }
+
+    m_requestId = requestId;
+    m_responseCallback = responseCallback;
+    m_thread = std::thread(&AskUINotificationBackend::run, this);
+    return true;
+}
+
+bool AskUINotificationBackend::createUI(const std::string &client, const std::string &user,
+                                        const std::string &privilege) {
+    notification_error_e err;
+
+    m_notification = notification_new(NOTIFICATION_TYPE_NOTI, NOTIFICATION_GROUP_ID_NONE,
+                                      NOTIFICATION_PRIV_ID_NONE);
+    if (m_notification == nullptr) {
+        LOGE("Failed to create notification.");
+        return false;
+    }
+
+    err = notification_set_pkgname(m_notification, "cynara-askuser");
+    if (err != NOTIFICATION_ERROR_NONE) {
+        LOGE("Unable to set notification pkgname: <" << errorToString(err) << ">");
+        return false;
+    }
+
+    char *dialogTitle = dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_TITLE");
+    err = notification_set_text(m_notification, NOTIFICATION_TEXT_TYPE_TITLE, dialogTitle, nullptr,
+                                NOTIFICATION_VARIABLE_TYPE_NONE);
+    if (err != NOTIFICATION_ERROR_NONE) {
+        LOGE("Unable to set notification title: <" << errorToString(err) << ">");
+        return false;
+    }
+
+    char *messageFormat = dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_MESSAGE");
+    char *privilegeDisplayName;
+    int ret = privilege_info_get_privilege_display_name(privilege.c_str(), &privilegeDisplayName);
+    if (ret != PRVMGR_ERR_NONE) {
+        LOGE("Unable to get privilege display name, err: [" << ret << "]");
+        privilegeDisplayName = strdup(privilege.c_str());
+    }
+    LOGD("privilege_info_get_privilege_display_name: [" << ret << "],"
+         " <" << privilegeDisplayName << ">");
+
+    char tmpBuffer[BUFSIZ];
+    ret = std::snprintf(tmpBuffer, sizeof(tmpBuffer), messageFormat, client.c_str(), user.c_str(),
+                   privilegeDisplayName);
+    free(privilegeDisplayName);
+    if (ret < 0) {
+        int erryes = errno;
+        LOGE("sprintf failed with error: <" << strerror(erryes) << ">");
+        return false;
+    }
+
+    err = notification_set_text(m_notification, NOTIFICATION_TEXT_TYPE_CONTENT, tmpBuffer, nullptr,
+                                NOTIFICATION_VARIABLE_TYPE_NONE);
+    if (err != NOTIFICATION_ERROR_NONE) {
+        LOGE("Unable to set notification content: <" << errorToString(err) << ">");
+        return false;
+    }
+
+    ret = std::snprintf(tmpBuffer, sizeof(tmpBuffer), "%s,%s,%s",
+                   dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_NO"),
+                   dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES"),
+                   dgettext(PROJECT_NAME, "SID_PRIVILEGE_REQUEST_DIALOG_BUTTON_YES_FOR_SESSION"));
+    if (ret < 0) {
+        int erryes = errno;
+        LOGE("sprintf failed with error: <" << strerror(erryes) << ">");
+        return false;
+    }
+
+    bundle *b = bundle_create();
+    if (!b) {
+        int erryes = errno;
+        LOGE("Unable to create bundle: <" << strerror(erryes) << ">");
+        return false;
+    }
+
+    if (bundle_add(b, "buttons", tmpBuffer)) {
+        int erryes = errno;
+        LOGE("Unable to add button to bundle: <" << strerror(erryes) << ">");
+        bundle_free(b);
+        return false;
+    }
+
+    err = notification_set_execute_option(m_notification, NOTIFICATION_EXECUTE_TYPE_RESPONDING,
+                                          nullptr, nullptr, b);
+    if (err != NOTIFICATION_ERROR_NONE) {
+        LOGE("Unable to set execute option: <" << errorToString(err) << ">");
+        bundle_free(b);
+        return false;
+    }
+
+    bundle_free(b);
+
+    err = notification_insert(m_notification, nullptr);
+    if (err != NOTIFICATION_ERROR_NONE) {
+        LOGE("Unable to insert notification: <" << errorToString(err) << ">");
+        return false;
+    }
+
+    return true;
+}
+
+bool AskUINotificationBackend::setOutdated() {
+    // There is no possibility to update window using notifications framework - at least for now
+    return true;
+}
+
+bool AskUINotificationBackend::dismiss() {
+    // There is no possibility to dismiss window using notifications framework
+    // We can only try to get rid of thread
+    m_dismissing = true;
+    auto status = m_future.wait_for(std::chrono::milliseconds(10));
+    if (status == std::future_status::ready) {
+        LOGD("UI thread, for request: [" << m_requestId << "], finished and ready to join.");
+        m_thread.join();
+        return true;
+    }
+
+    LOGD("UI thread, for request: [" << m_requestId << "], not finished.");
+    return false;
+}
+
+void AskUINotificationBackend::run() {
+    try {
+        int buttonClicked = 0;
+        notification_error_e ret = notification_wait_response(m_notification, m_responseTimeout,
+                                                              &buttonClicked, nullptr);
+        LOGD("notification_wait_response finished with ret code: [" << ret << "]");
+
+        UIResponseType response = URT_ERROR;
+        if (ret == NOTIFICATION_ERROR_NONE) {
+            if (buttonClicked) {
+                static UIResponseType responseType[] = {URT_NO, URT_YES, URT_SESSION};
+                LOGD("Got response from user: [" << buttonClicked << "]");
+                if (static_cast<unsigned int>(buttonClicked) >
+                                             sizeof(responseType) / sizeof(responseType[0])) {
+                    LOGE("Wrong code of response: [" << buttonClicked << "]");
+                    response = URT_NO;
+                } else {
+                    response = responseType[buttonClicked - 1];
+                }
+            } else {
+                LOGD("notification_wait_response, for request ID: [" << m_requestId <<
+                     "] timeouted");
+                response = URT_TIMEOUT;
+            }
+        }
+        m_responseCallback(m_requestId, response);
+        LOGD("UI thread for request ID: [" << m_requestId << "] stopped execution");
+    } catch (const std::exception &e) {
+        LOGC("Unexpected exception: <" << e.what() << ">");
+    } catch (...) {
+        LOGE("Unexpected unknown exception caught!");
+    }
+    m_threadFinished.set_value(true);
+}
+
+} // namespace Agent
+
+} // namespace AskUser
diff --git a/src/agent/ui/AskUINotificationBackend.h b/src/agent/ui/AskUINotificationBackend.h
new file mode 100644 (file)
index 0000000..1f77bd0
--- /dev/null
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+/**
+ * @file        AskUINotificationBackend.h
+ * @author      Adam Malinowski <a.malinowsk2@partner.samsung.com>
+ * @brief       This file declares class for ask user window
+ */
+
+#pragma once
+
+#include <atomic>
+#include <notification.h>
+#include <future>
+#include <thread>
+
+#include <ui/AskUIInterface.h>
+
+namespace AskUser {
+
+namespace Agent {
+
+class AskUINotificationBackend : public AskUIInterface {
+public:
+    AskUINotificationBackend();
+    virtual ~AskUINotificationBackend();
+
+    virtual bool start(const std::string &client, const std::string &user,
+                       const std::string &privilege, RequestId requestId,
+                       UIResponseCallback responseCallback);
+    virtual bool setOutdated();
+    virtual bool dismiss();
+    virtual bool isDismissing() const {
+        return m_dismissing;
+    }
+
+private:
+    notification_h m_notification;
+    std::thread m_thread;
+    RequestId m_requestId;
+    UIResponseCallback m_responseCallback;
+    static const int m_responseTimeout = 60; // seconds
+    std::promise<bool> m_threadFinished;
+    std::future<bool> m_future;
+    std::atomic<bool> m_dismissing;
+
+    void run();
+    bool createUI(const std::string &client, const std::string &user, const std::string &privilege);
+};
+
+} // namespace Agent
+
+} // namespace AskUser