--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#ifndef __UI_APP_AMBIENT__
+#define __UI_APP_AMBIENT__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdbool.h>
+
+#include <bundle.h>
+
+/**
+ * @brief Enumeration for period.
+ * @remark This function only for internal applications
+ */
+typedef enum {
+ UI_APP_AMBIENT_UPDATE_NONE,
+ UI_APP_AMBIENT_UPDATE_MINUTE,
+ UI_APP_AMBIENT_UPDATE_FIVE_MINUTES,
+ UI_APP_AMBIENT_UPDATE_FIFTEEN_MINUTES,
+ UI_APP_AMBIENT_UPDATE_THIRTY_MINUTES
+} ui_app_ambient_update_period_e;
+
+/**
+ * @brief Enumeration for event.
+ * @remark This function only for internal applications
+ */
+typedef enum {
+ UI_APP_AMBIENT_EVENT_PREPARE,
+ UI_APP_AMBIENT_EVENT_READY,
+ UI_APP_AMBIENT_EVENT_CHANGED,
+} ui_app_ambient_event_e;
+
+/**
+ * @brief Called with the period set with ui_app_ambient_set_update_period() if the device is in the ambient mode.
+ * @remark This function only for internal applications
+ * @param[in] user_data The user data to be passed to the callback functions
+ * @see ui_app_ambient_set_lifecycle()
+ * @see ui_app_ambient_set_update_period()
+ */
+typedef void (*ui_app_ambient_update_frame_cb) (void *user_data);
+
+/**
+ * @brief Called when the device enters or exits the ambient mode.
+ * @remark This function only for internal applications
+ * @param[in] ambient_mode Whether ambient mode is true or not
+ * @param[in] data The extra bundle data from ambient viewer
+ * @param[in] user_data The user data to be passed to the callback functions
+ * @see ui_app_ambient_set_lifecycle()
+ */
+typedef void (*ui_app_ambient_changed_cb) (bool ambient_mode, bundle *data, void *user_data);
+
+/**
+ * @brief The structure type containing the set of callback functions for application events handle.
+ * @remark This function only for internal applications
+ * @see ui_app_ambient_set_lifecycle()
+ */
+typedef struct {
+ ui_app_ambient_update_frame_cb frame_updated;
+ ui_app_ambient_changed_cb ambient_changed;
+} ui_app_ambient_lifecycle_callback_s;
+
+/**
+ * @brief Sets lifecycle for UI ambient lifecycle.
+ * @remark This function only for internal applications
+ * @param[in] lifecycle The set of callback functions to handle application lifecycle events
+ * @param[in] user_data The user data to be passed to the callback functions
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @see ui_app_ambient_unset_lifecycle()
+ */
+int ui_app_ambient_set_lifecycle(ui_app_ambient_lifecycle_callback_s *lifecycle, void *user_data);
+
+/**
+ * @brief Unsets lifecycle for UI ambient lifecycle.
+ * @remark This function only for internal applications
+ * @see ui_app_ambient_set_lifecycle()
+ */
+void ui_app_ambient_unset_lifecycle(void);
+
+/**
+ * @brief Sets the period of ui_app_ambient_update_frame_cb() calls.
+ * @remark This function only for internal applications
+ * @param[in] period period type
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @see ui_app_ambient_get_update_period()
+ */
+int ui_app_ambient_set_update_period(ui_app_ambient_update_period_e period);
+
+/**
+ * @brief Gets the period of ui_app_ambient_update_frame_cb() calls.
+ * @remark This function only for internal applications
+ * @param[out] period period type
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ * @see ui_app_ambient_set_update_period()
+ */
+int ui_app_ambient_get_update_period(ui_app_ambient_update_period_e *period);
+
+/**
+ * @brief Sends event with bundle data to the @a receiver.
+ * @remark This function only for internal applications
+ * @param[in] receiver application id of receiver
+ * @param[in] event ambient event type
+ * @param[in] extra extra bundle data
+ * @return @c 0 on success,
+ * otherwise a negative error value
+ */
+int ui_app_ambient_send_event(const char *receiver, ui_app_ambient_event_e event, bundle *extra);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __UI_APP_AMBIENT__ */
\ No newline at end of file
--- /dev/null
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * 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.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include <dlog.h>
+#include <aul.h>
+#include <aul_app_com.h>
+#include <app_common_internal.h>
+#include <alarm.h>
+
+#include "ui_app_ambient.h"
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "CAPI_UI_APP_AMBIENT"
+
+#define APPID_BUFFER_MAX 512
+
+#define SECOND 1
+#define ONE_MINUTE_IN_SEC 60
+
+#define MILLION 1000000 /* for calculating micro seconds */
+
+typedef struct
+{
+ int alarm_id;
+ int period_type;
+ ui_app_ambient_update_frame_cb frame_updated;
+ ui_app_ambient_changed_cb ambient_changed;
+ void *user_data;
+} ambient_lifecycle_s;
+
+static ambient_lifecycle_s ambient_lifecycle = {
+ .alarm_id = 0,
+ .period_type = UI_APP_AMBIENT_UPDATE_NONE,
+ .frame_updated = NULL,
+ .ambient_changed = NULL,
+ .user_data = NULL
+};
+
+static struct ambient_tick_type_info {
+ int interval;
+ int minute_base;
+ int hour_base;
+} ambient_tick_type_infos[] = {
+ {0, 0, 0},
+ {ONE_MINUTE_IN_SEC, 1, 0},
+ {ONE_MINUTE_IN_SEC * 5, 5, 0},
+ {ONE_MINUTE_IN_SEC * 15, 15, 0},
+ {ONE_MINUTE_IN_SEC * 30, 30, 0}
+};
+
+static aul_app_com_connection_h __conn;
+static bool __initialized = false;
+
+static void __alarm_init(void)
+{
+ int r = 0;
+ int pid = getpid();
+ char appid[APPID_BUFFER_MAX] = {0,};
+
+ r = aul_app_get_appid_bypid(pid, appid, APPID_BUFFER_MAX);
+ if (r != AUL_R_OK) {
+ LOGE("fail to get the appid from the pid : %d", pid);
+ return;
+ }
+
+ r = alarmmgr_init(appid);
+ if (r != ALARMMGR_RESULT_SUCCESS) {
+ LOGE("fail to alarmmgr_init : error_code : %d", r);
+ }
+}
+
+static int __alarm_cb(alarm_id_t id, void *data)
+{
+ LOGD("Ambient Tick callback");
+
+ if (id == ambient_lifecycle.alarm_id)
+ ambient_lifecycle.frame_updated(data);
+
+ return 0;
+}
+
+static int __get_ambient_tick_offset(ui_app_ambient_update_period_e period)
+{
+ struct timespec current_time;
+ struct tm cur_tm;
+ int offset = 0;
+ int remain_min;
+ int minute_base;
+
+ clock_gettime(CLOCK_REALTIME, ¤t_time);
+
+ tzset();
+ localtime_r(¤t_time.tv_sec, &cur_tm);
+
+ switch(period) {
+ case UI_APP_AMBIENT_UPDATE_MINUTE:
+ offset = ONE_MINUTE_IN_SEC - cur_tm.tm_sec;
+ break;
+ case UI_APP_AMBIENT_UPDATE_FIVE_MINUTES:
+ case UI_APP_AMBIENT_UPDATE_FIFTEEN_MINUTES:
+ case UI_APP_AMBIENT_UPDATE_THIRTY_MINUTES:
+ /* To make a gap minutes */
+ minute_base = ambient_tick_type_infos[period].minute_base;
+ remain_min = minute_base - 1 - (cur_tm.tm_min % minute_base);
+ offset = (remain_min * ONE_MINUTE_IN_SEC) +
+ (ONE_MINUTE_IN_SEC - cur_tm.tm_sec);
+ break;
+ default:
+ break;
+ }
+
+ return offset;
+}
+
+static int __set_ambient_tick_cb()
+{
+ int r;
+ int offset_sec = 0;
+
+ if (ambient_lifecycle.period_type == UI_APP_AMBIENT_UPDATE_NONE &&
+ ambient_lifecycle.alarm_id == 0) {
+ LOGI("DEFAULT period is set [FIFTEEN_MINUTES]");
+ ambient_lifecycle.period_type = UI_APP_AMBIENT_UPDATE_FIFTEEN_MINUTES;
+ }
+
+ offset_sec = __get_ambient_tick_offset(ambient_lifecycle.period_type);
+ LOGD("next time offset: %d[type:%d]", offset_sec, ambient_lifecycle.period_type);
+
+ __alarm_init();
+ r = alarmmgr_add_alarm_withcb(ALARM_TYPE_VOLATILE, offset_sec,
+ ambient_tick_type_infos[ambient_lifecycle.period_type].interval,
+ __alarm_cb, ambient_lifecycle.user_data, &ambient_lifecycle.alarm_id);
+ if (r < 0)
+ LOGE("fail to alarmmgr_add_alarm_withcb : error_code : %d", r);
+
+ ambient_lifecycle.frame_updated(ambient_lifecycle.user_data);
+
+ return r;
+}
+
+int __on_change_signal(const char *endpoint, aul_app_com_result_e e,
+ bundle *envelope, void *user_data)
+{
+ LOGE("__on_change_signal");
+
+ bundle *b = NULL;
+ char *extra;
+ char *mode;
+
+ bundle_get_str(envelope, "__AMBIENT_MODE__", &mode);
+ bundle_get_str(envelope, "__AMBIENT_EXTRA__", &extra);
+ b = bundle_decode((bundle_raw *)extra, strlen(extra));
+
+ if (ambient_lifecycle.ambient_changed)
+ ambient_lifecycle.ambient_changed((bool)atoi(mode), b, user_data);
+
+ if (b)
+ bundle_free(b);
+
+ return 0;
+}
+
+static int __set_ambient_changed_cb()
+{
+ int r;
+
+ if (__conn) {
+ LOGD("Already set ambient changed cb");
+ return 0;
+ }
+
+ LOGD("Set ambient changed cb");
+
+ r = aul_app_com_create("ui.ambientchange", NULL, __on_change_signal,
+ ambient_lifecycle.user_data, &__conn);
+ if (r != AUL_R_OK) {
+ LOGE("Failed to listen 'ui.ambientchange' signal");
+ }
+
+ return r;
+}
+
+int ui_app_ambient_set_lifecycle(ui_app_ambient_lifecycle_callback_s *lifecycle,
+ void *user_data)
+{
+ int ret = -1;
+
+ if (!lifecycle) {
+ LOGE("Invalid parameter");
+ return ret;
+ }
+
+ if (__initialized) {
+ LOGW("Lifecycle is already set");
+ return 0;
+ }
+
+ ambient_lifecycle.frame_updated = lifecycle->frame_updated;
+ ambient_lifecycle.ambient_changed = lifecycle->ambient_changed;
+ ambient_lifecycle.user_data = user_data;
+
+ ret = __set_ambient_tick_cb();
+ if (ret != 0) {
+ LOGE("Failed to set ambient tick cb : %d", ret);
+ return ret;
+ }
+
+ ret = __set_ambient_changed_cb();
+ if (ret != 0) {
+ LOGE("Failed to set ambient changed cb : %d", ret);
+ ui_app_ambient_unset_lifecycle();
+ return ret;
+ }
+
+ __initialized = true;
+ return 0;
+}
+
+void ui_app_ambient_unset_lifecycle()
+{
+ if (__conn) {
+ if (aul_app_com_leave(__conn) < 0)
+ LOGE("failed to leave app com disable");
+
+ __conn = NULL;
+ }
+
+ if (ambient_lifecycle.alarm_id) {
+ alarmmgr_remove_alarm(ambient_lifecycle.alarm_id);
+ ambient_lifecycle.alarm_id = 0;
+ }
+
+ if (ambient_lifecycle.ambient_changed)
+ ambient_lifecycle.ambient_changed = NULL;
+
+ if (ambient_lifecycle.frame_updated)
+ ambient_lifecycle.frame_updated = NULL;
+
+ if (ambient_lifecycle.user_data)
+ ambient_lifecycle.user_data = NULL;
+
+ __initialized = false;
+}
+
+int ui_app_ambient_set_update_period(ui_app_ambient_update_period_e period)
+{
+ int ret = 0;
+ LOGD("set update period : %d", period);
+ ambient_lifecycle.period_type = period;
+
+ if (ambient_lifecycle.alarm_id) {
+ alarmmgr_remove_alarm(ambient_lifecycle.alarm_id);
+ ambient_lifecycle.alarm_id = 0;
+
+ if (ambient_lifecycle.period_type != UI_APP_AMBIENT_UPDATE_NONE)
+ ret = __set_ambient_tick_cb();
+ }
+
+ return ret;
+}
+
+int ui_app_ambient_get_update_period(ui_app_ambient_update_period_e *period)
+{
+ if (!period) {
+ LOGE("Invalid parameter");
+ return -1;
+ }
+
+ *period = ambient_lifecycle.period_type;
+ return 0;
+}
+
+int ui_app_ambient_send_event(const char *receiver,
+ ui_app_ambient_event_e event, bundle *extra)
+{
+ char buf[32];
+ char endpoint[APPID_BUFFER_MAX];
+ char appid_buf[APPID_BUFFER_MAX] = {0, };
+ int ret;
+ bundle *envelope;
+
+ if (!receiver) {
+ LOGE("receiver should not be null");
+ return -1;
+ }
+
+ if (extra == NULL) {
+ LOGE("extra data is null");
+ envelope = bundle_create();
+ } else {
+ envelope = bundle_dup(extra);
+ }
+
+ if (envelope == NULL) {
+ LOGE("out of memory");
+ return -1;
+ }
+
+ snprintf(buf, sizeof(buf), "%d", event);
+ ret = bundle_add_str(envelope, "__APP_AMBIENT_EVENT__", buf);
+ if (ret != BUNDLE_ERROR_NONE) {
+ LOGE("bundle_add_str returns false");
+ bundle_free(envelope);
+ return -1;
+ }
+
+ if (aul_app_get_appid_bypid(
+ getpid(), appid_buf, sizeof(appid_buf)) != AUL_R_OK) {
+ LOGE("Failed to get appid (%d)", getpid());
+ bundle_free(envelope);
+ return -1;
+ }
+
+ ret = bundle_add_str(envelope, "__APP_AMBIENT_SENDER__", appid_buf);
+ if (ret != BUNDLE_ERROR_NONE) {
+ LOGE("bundle_add_str returns false");
+ bundle_free(envelope);
+ return -1;
+ }
+
+ snprintf(endpoint, sizeof(endpoint), "send_extra_data:%s", receiver);
+
+ ret = aul_app_com_send(endpoint, envelope);
+ if (ret != AUL_R_OK) {
+ LOGE("Failed aul_app_com_send");
+ bundle_free(envelope);
+ return -1;
+ }
+
+ bundle_free(envelope);
+ return 0;
+}
\ No newline at end of file
License: Apache-2.0
Source0: %{name}-%{version}.tar.gz
Source1001: capi-appfw-application.manifest
+Source1002: appcore-ui-app-ambient.manifest
BuildRequires: cmake
BuildRequires: pkgconfig(dlog)
BuildRequires: pkgconfig(bundle)
BuildRequires: pkgconfig(appcore-common)
BuildRequires: pkgconfig(appcore-efl)
BuildRequires: pkgconfig(aul)
+BuildRequires: pkgconfig(alarm-service)
BuildRequires: pkgconfig(appsvc)
BuildRequires: pkgconfig(elementary)
BuildRequires: pkgconfig(capi-base-common)
%prep
%setup -q
cp %{SOURCE1001} .
+cp %{SOURCE1002} .
%build
%if 0%{?gcov:1}
%post -p /sbin/ldconfig
%postun -p /sbin/ldconfig
+################################################
+# libappcore-ui-app-ambient
+%package -n appcore-ui-app-ambient
+Summary: APIs to set lifecycle ui application ambient
+Version: 0.0.1
+License: Apache-2.0
+Group: Applications/Core Applications
+
+%description -n appcore-ui-app-ambient
+A set of APIs to set lifecycle ui application ambient
+
+%package -n appcore-ui-app-ambient-devel
+Summary: APIs to set lifecycle ui application ambient
+Group: Development/Libraries
+Requires: watch-holder
+
+%description -n appcore-ui-app-ambient-devel
+Header & package configuration of appcore-ui-app-ambient
+
+%post -n appcore-ui-app-ambient -p /sbin/ldconfig
+%postun -n appcore-ui-app-ambient -p /sbin/ldconfig
+
%files
%manifest %{name}.manifest
%{_libdir}/libcapi-appfw-application.so.*
%{_libdir}/pkgconfig/capi-appfw-application.pc
%{_libdir}/libcapi-appfw-application.so
+%files -n appcore-ui-app-ambient
+%manifest appcore-ui-app-ambient.manifest
+%license LICENSE
+%{_libdir}/libappcore-ui-app-ambient.so.*
+
+%files -n appcore-ui-app-ambient-devel
+%{_includedir}/appcore-ui-app-ambient/*.h
+%{_libdir}/pkgconfig/appcore-ui-app-ambient.pc
+%{_libdir}/libappcore-ui-app-ambient.so
+
%if 0%{?gcov:1}
%files gcov
%{_datadir}/gcov/obj/*