From a73acb263853b6ff6b4dbd89a4b54f647e5b26be Mon Sep 17 00:00:00 2001 From: brianjjones Date: Fri, 7 Mar 2014 14:57:02 -0800 Subject: [PATCH 1/1] Initial checkin of the wrt-plugins-ivi-hfp for the HTML5 UI Change-Id: Id4241ae56a4ed897518953a55d6ce63f27c09efa --- CMakeLists.txt | 152 ++++++ config.dtd | 32 ++ packaging/wrt-plugins-ivi-phone.changes | 3 + packaging/wrt-plugins-ivi-phone.spec | 60 +++ pkgconfigs/wrt-plugins-tizen-phone.pc.in | 13 + src/JSPhone.cpp | 734 +++++++++++++++++++++++++ src/JSPhone.h | 477 +++++++++++++++++ src/Phone.cpp | 894 +++++++++++++++++++++++++++++++ src/Phone.h | 232 ++++++++ src/Phone.idl | 172 ++++++ src/config.xml | 16 + src/plugin_initializer.cpp | 77 +++ wrt-plugins-ivi-phone.manifest | 6 + 13 files changed, 2868 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 config.dtd create mode 100644 packaging/wrt-plugins-ivi-phone.changes create mode 100644 packaging/wrt-plugins-ivi-phone.spec create mode 100644 pkgconfigs/wrt-plugins-tizen-phone.pc.in create mode 100644 src/JSPhone.cpp create mode 100644 src/JSPhone.h create mode 100644 src/Phone.cpp create mode 100644 src/Phone.h create mode 100644 src/Phone.idl create mode 100644 src/config.xml create mode 100644 src/plugin_initializer.cpp create mode 100644 wrt-plugins-ivi-phone.manifest diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..4fb84eb --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,152 @@ +CMAKE_MINIMUM_REQUIRED(VERSION 2.8) +PROJECT(wrt-plugin-ivi) + +SET(CMAKE_INSTALL_PREFIX "/usr") + +# ----------------------------------------------------------------------------- +# Required platform modules +# ----------------------------------------------------------------------------- +INCLUDE(FindPkgConfig) + +PKG_CHECK_MODULES(dpl REQUIRED dpl-efl) +PKG_CHECK_MODULES(wrt-plugins-types REQUIRED wrt-plugins-types) +PKG_CHECK_MODULES(wrt-deviceapis-commons-javascript REQUIRED wrt-plugins-commons-javascript) +PKG_CHECK_MODULES(wrt-plugins-tizen-common REQUIRED wrt-plugins-tizen-common) +PKG_CHECK_MODULES(json-glib REQUIRED json-glib-1.0) + +INCLUDE_DIRECTORIES( + ${dpl_INCLUDE_DIRS} + ${wrt-plugins-types_INCLUDE_DIRS} + ${wrt-deviceapis-commons-javascript_INCLUDE_DIRS} + ${wrt-plugins-tizen-common_INCLUDE_DIRS} + ${json-glib_INCLUDE_DIRS} +) + +# ----------------------------------------------------------------------------- +# Determine the log option +# ----------------------------------------------------------------------------- + +OPTION(DPL_LOG "DPL logs status" ON) + +IF(DPL_LOG) + MESSAGE(STATUS "Logging enabled for DPL") + ADD_DEFINITIONS("-DDPL_LOGS_ENABLED") +ELSE(DPL_LOG) + MESSAGE(STATUS "Logging disabled for DPL") +ENDIF(DPL_LOG) + +# ----------------------------------------------------------------------------- +# Determine the time tracing option +# ----------------------------------------------------------------------------- + +OPTION(ENABLE_TIME_TRACER "TIME TRACING" OFF) + +IF(ENABLE_TIME_TRACER) + MESSAGE(STATUS "Time tracer enabled") + ADD_DEFINITIONS("-DENABLE_TIME_TRACER") +ELSE(ENABLE_TIME_TRACER) + MESSAGE(STATUS "Time tracer disabled") +ENDIF(ENABLE_TIME_TRACER) + +# ----------------------------------------------------------------------------- +# Set build type (Release by default) +# ----------------------------------------------------------------------------- +IF("${CMAKE_BUILD_TYPE}" STREQUAL "") + SET(CMAKE_BUILD_TYPE Release) +ENDIF("${CMAKE_BUILD_TYPE}" STREQUAL "") + +MESSAGE("Build type: ${CMAKE_BUILD_TYPE}") + +# ----------------------------------------------------------------------------- +# CFlags +# ----------------------------------------------------------------------------- +SET(CMAKE_C_FLAGS_PROFILING "-O0 -g -pg") +SET(CMAKE_CXX_FLAGS_PROFILING "-O0 -std=c++0x -g -pg") +SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") +SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -std=c++0x -g") +SET(CMAKE_C_FLAGS_RELEASE "-O2 -g") +SET(CMAKE_CXX_FLAGS_RELEASE "-O2 -std=c++0x -g") +ADD_DEFINITIONS("-DCLIENT_IPC_THREAD") +ADD_DEFINITIONS("-DEXPORT_API=") +ADD_DEFINITIONS("-Wall") +ADD_DEFINITIONS("-D_FILE_OFFSET_BITS=64") +#ADD_DEFINITIONS("-Werror") +#ADD_DEFINITIONS("-Wextra") +ADD_DEFINITIONS("-DAPPLICATION_API_BACKWARD_COMPATIBILITY") +ADD_DEFINITIONS("-std=c++11") + + +# ----------------------------------------------------------------------------- +# Schema of plugin's configuration file +# ----------------------------------------------------------------------------- +SET(COMMON_CONFIG_DTD ${CMAKE_CURRENT_SOURCE_DIR}/config.dtd) +SET(COMMON_CONFIG_DTD_DST /usr/etc/tizen-apis) +INSTALL(FILES ${COMMON_CONFIG_DTD} DESTINATION ${COMMON_CONFIG_DTD_DST}) + +# ----------------------------------------------------------------------------- +# Global variables +# ----------------------------------------------------------------------------- + +SET(DESTINATION_LIB_PREFIX lib/wrt-plugins) + +SET(DESTINATION_HEADER_PREFIX include/${PROJECT_NAME}) + +SET(LIBS_WIDGETDB ${wrt-plugins-widgetdb_LIBRARIES}) + +# ----------------------------------------------------------------------------- +# Macros for pkgconfig +# ----------------------------------------------------------------------------- +SET(PKGCONFIG_DIR ${CMAKE_SOURCE_DIR}/pkgconfigs) +#SET(PKGCONFIG_DIR ${CMAKE_SOURCE_DIR}) + +MACRO(configure_and_install_pkg PKG_FILE) + CONFIGURE_FILE(${PKGCONFIG_DIR}/${PKG_FILE}.in + ${PKGCONFIG_DIR}/${PKG_FILE} @ONLY) + INSTALL(FILES ${PKGCONFIG_DIR}/${PKG_FILE} DESTINATION lib/pkgconfig) +ENDMACRO(configure_and_install_pkg) + +SET(TARGET_NAME wrt-plugins-tizen-phone) +SET(DESTINATION_NAME tizen-phone) +SET(TARGET_IMPL_NAME wrt-plugins-tizen-phone-impl) + +configure_and_install_pkg(wrt-plugins-tizen-phone.pc) + +SET(CMAKE_INSTALL_RPATH + ${CMAKE_INSTALL_RPATH} + ${CMAKE_INSTALL_PREFIX}/${DESTINATION_LIB_PREFIX}/${tizen_dest} + ${CMAKE_INSTALL_PREFIX}/${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME} +) + +SET(SRCS_IMPL + src/Phone.cpp + src/JSPhone.cpp +) + +ADD_LIBRARY(${TARGET_IMPL_NAME} SHARED ${SRCS_IMPL}) + +TARGET_LINK_LIBRARIES(${TARGET_IMPL_NAME} + ${dpl_LDFLAGS} + ${dpl-event_LDFLAGS} + ${wrt-deviceapis-commons-javascript_LDFLAGS} + ${wrt-plugins-tizen-common_LDFLAGS} + ${json-glib_LDFLAGS} + -L/usr/lib/wrt-plugins/tizen-tizen/ +) + +SET(SRCS + src/plugin_initializer.cpp +) + +ADD_LIBRARY(${TARGET_NAME} SHARED ${SRCS}) + +TARGET_LINK_LIBRARIES(${TARGET_NAME} + ${TARGET_IMPL_NAME} +) + +INSTALL(TARGETS ${TARGET_NAME} ${TARGET_IMPL_NAME} LIBRARY DESTINATION ${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME}) +INSTALL(FILES ${CMAKE_CURRENT_SOURCE_DIR}/src/config.xml DESTINATION ${DESTINATION_LIB_PREFIX}/${DESTINATION_NAME}) +INSTALL( + DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/ DESTINATION ${DESTINATION_HEADER_PREFIX}/phone + FILES_MATCHING PATTERN "*.h" PATTERN "CMakeFiles" EXCLUDE +) + diff --git a/config.dtd b/config.dtd new file mode 100644 index 0000000..f1a1de4 --- /dev/null +++ b/config.dtd @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packaging/wrt-plugins-ivi-phone.changes b/packaging/wrt-plugins-ivi-phone.changes new file mode 100644 index 0000000..ea708c6 --- /dev/null +++ b/packaging/wrt-plugins-ivi-phone.changes @@ -0,0 +1,3 @@ +* Fri Mar 07 2014 brianjjones 54b2161 +- Initial checkin of the wrt-plugins-ivi-hfp for the HTML5 UI + diff --git a/packaging/wrt-plugins-ivi-phone.spec b/packaging/wrt-plugins-ivi-phone.spec new file mode 100644 index 0000000..e322feb --- /dev/null +++ b/packaging/wrt-plugins-ivi-phone.spec @@ -0,0 +1,60 @@ +Name: wrt-plugins-ivi-phone +Summary: JavaScript plugin to access Phone for WebRuntime +Version: 0.8.5 +Release: 1 +Group: Development/Libraries +License: Apache-2.0 +Source0: %{name}-%{version}.tar.gz + +BuildRequires: pkgconfig(ewebkit2) +BuildRequires: pkgconfig(dpl-efl) +BuildRequires: pkgconfig(dpl-event-efl) +BuildRequires: pkgconfig(wrt-plugins-commons) +BuildRequires: pkgconfig(wrt-plugins-commons-javascript) +BuildRequires: wrt-plugins-tizen-devel +BuildRequires: expat-devel +BuildRequires: cmake +BuildRequires: gettext-devel +BuildRequires: pkgconfig(json-glib-1.0) + +%description +JavaScript plugin to access Phone for WebRuntime + +%package devel +Summary: Wrt-plugins-ivi-phone development headers +Group: Development/Libraries +Requires: %{name} = %{version} + +%description devel +Wrt-plugins-ivi-phone development headers + +%prep +%setup -q + +%build + +%define PREFIX "%{_libdir}/wrt-plugins" + +export LDFLAGS+="-Wl,--rpath=%{PREFIX} -Wl,--as-needed" + +cmake . -DCMAKE_INSTALL_PREFIX=%{_prefix} -DDPL_LOG="ON" -DENABLE_TIME_TRACER="OFF" + +make %{?jobs:-j%jobs} VERBOSE=1 + +%install +rm -rf %{buildroot} +%make_install + +%post +wrt-installer -p + +%postun + +%files +%manifest wrt-plugins-ivi-phone.manifest +%{_libdir}/wrt-plugins/* +/usr/etc/tizen-apis/* + +%files devel +%{_includedir}/* +%{_libdir}/pkgconfig/* diff --git a/pkgconfigs/wrt-plugins-tizen-phone.pc.in b/pkgconfigs/wrt-plugins-tizen-phone.pc.in new file mode 100644 index 0000000..285d15d --- /dev/null +++ b/pkgconfigs/wrt-plugins-tizen-phone.pc.in @@ -0,0 +1,13 @@ +prefix=/usr +project_name=@CMAKE_PROJECT_NAME@ +module_name=phone +exec_prefix=${prefix} +libdir=${prefix}/lib/wrt-plugins/tizen-${module_name} +includedir=${prefix}/include/${project_name} + +Name: wrt-plugins-tizen-${module_name} +Description: wrt-plugins-tizen-${module_name} +Version: @CMAKE_PROJECT_VERSION@ +Requires: icu-i18n icu-io icu-le icu-lx icu-uc +Libs: -L${libdir} -lwrt-plugins-tizen-${module_name}-impl +Cflags: -I${includedir}/${module_name} diff --git a/src/JSPhone.cpp b/src/JSPhone.cpp new file mode 100644 index 0000000..515846f --- /dev/null +++ b/src/JSPhone.cpp @@ -0,0 +1,734 @@ +#include "JSPhone.h" +#include "Phone.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace DeviceAPI { +namespace Phone { + +using namespace DPL; +using namespace DeviceAPI::Common; +using namespace WrtDeviceApis::Commons; +using namespace WrtDeviceApis::CommonsJavaScript; + +#define PHONE_ACTIVECALL_PROP "activeCall" + +JSClassDefinition JSPhone::m_classInfo = { + 0, + kJSClassAttributeNone, + "Phone", + 0, + m_property, + m_function, + initialize, + finalize, + NULL, //HasProperty, + NULL, //GetProperty, + NULL, //SetProperty, + NULL, //DeleteProperty, + NULL, //GetPropertyNames, + NULL, //CallAsFunction, + NULL, //CallAsConstructor, + hasInstance, + NULL, //ConvertToType +}; + +JSStaticFunction JSPhone::m_function[] = { + { "invokeCall", JSPhone::invokeCall, kJSPropertyAttributeNone }, + { "answerCall", JSPhone::answerCall, kJSPropertyAttributeNone }, + { "hangupCall", JSPhone::hangupCall, kJSPropertyAttributeNone }, + { "muteCall", JSPhone::muteCall, kJSPropertyAttributeNone }, + { "selectRemoteDevice", JSPhone::selectRemoteDevice, kJSPropertyAttributeNone }, + { "unselectRemoteDevice", JSPhone::unselectRemoteDevice, kJSPropertyAttributeNone }, + { "addRemoteDeviceSelectedListener", JSPhone::addRemoteDeviceSelectedListener, kJSPropertyAttributeNone }, + { "removeRemoteDeviceSelectedListener", JSPhone::removeRemoteDeviceSelectedListener, kJSPropertyAttributeNone }, + { "getSelectedRemoteDevice", JSPhone::getSelectedRemoteDevice, kJSPropertyAttributeNone }, + { "getContacts", JSPhone::getContacts, kJSPropertyAttributeNone }, + { "addContactsChangedListener", JSPhone::addContactsChangedListener, kJSPropertyAttributeNone }, + { "removeContactsChangedListener", JSPhone::removeContactsChangedListener, kJSPropertyAttributeNone }, + { "getCallHistory", JSPhone::getCallHistory, kJSPropertyAttributeNone }, + { "addCallHistoryChangedListener", JSPhone::addCallHistoryChangedListener, kJSPropertyAttributeNone }, + { "removeCallHistoryChangedListener", JSPhone::removeCallHistoryChangedListener, kJSPropertyAttributeNone }, + { "addCallChangedListener", JSPhone::addCallChangedListener, kJSPropertyAttributeNone }, + { "removeCallChangedListener", JSPhone::removeCallChangedListener, kJSPropertyAttributeNone }, + { "addCallHistoryEntryAddedListener", JSPhone::addCallHistoryEntryAddedListener, kJSPropertyAttributeNone }, + { "removeCallHistoryEntryAddedListener", JSPhone::removeCallHistoryEntryAddedListener, kJSPropertyAttributeNone }, + { 0, 0, 0 } +}; + +JSStaticValue JSPhone::m_property[] = { + {PHONE_ACTIVECALL_PROP, getProperty, NULL, kJSPropertyAttributeNone}, + { 0, 0, 0, 0 } +}; + +const JSClassRef JSPhone::getClassRef() +{ + if (!m_jsClassRef) + { + m_jsClassRef = JSClassCreate(&m_classInfo); + } + return m_jsClassRef; +} + +const JSClassDefinition* JSPhone::getClassInfo() +{ + return &m_classInfo; +} + +JSValueRef JSPhone::getProperty(JSContextRef context, JSObjectRef object, JSStringRef propertyName, JSValueRef* exception) +{ + LoggerD("Enter"); + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(object)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + Try { + if (JSStringIsEqualToUTF8CString(propertyName, PHONE_ACTIVECALL_PROP)) { + PhonePtr phone(privateObject->getObject()); + JSStringRef state = JSStringCreateWithUTF8CString(phone->activeCall().c_str()); + JSValueRef result = JSValueMakeFromJSONString(context, state); + return result; + } + } catch (...) { + return JSValueMakeUndefined(context); + } + + return JSValueMakeUndefined(context); +} + +JSClassRef JSPhone::m_jsClassRef = JSClassCreate(JSPhone::getClassInfo()); + +void JSPhone::initialize(JSContextRef context, JSObjectRef object) +{ + LoggerD("Entered"); + + PhonePrivObject* priv = static_cast(JSObjectGetPrivate(object)); + if (!priv) + { + PhonePtr phone(new PhoneMaster()); + priv = new PhonePrivObject( context, phone); + if(!JSObjectSetPrivate(object, static_cast(priv))) + { + LoggerE("Object can't store private data."); + delete priv; + } + } + + LoggerD("JSPhone::initialize "); +} + +void JSPhone::finalize(JSObjectRef object) +{ + LoggerD("Entered"); + + PhonePrivObject* priv = static_cast(JSObjectGetPrivate(object)); + JSObjectSetPrivate(object, NULL); + LoggerD("Deleting timeutil"); + delete priv; +} + +bool JSPhone::hasInstance(JSContextRef context, + JSObjectRef constructor, + JSValueRef possibleInstance, + JSValueRef* exception) +{ + return JSValueIsObjectOfClass(context, possibleInstance, getClassRef()); +} + +JSValueRef JSPhone::invokeCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + std::string phoneNumber = validator.toString(0); + LoggerD("Dialing phone number: " << phoneNumber); + + JSObjectRef errorCallback = validator.toFunction(1, false); + + JSValueProtect(context, errorCallback); + + phone->invokeCall(phoneNumber, errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::hangupCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef errorCallback = validator.toFunction(0, false); + + JSValueProtect(context, errorCallback); + + phone->hangupCall(errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::answerCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef errorCallback = validator.toFunction(0, false); + + JSValueProtect(context, errorCallback); + + phone->answerCall(errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::muteCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + bool mute = validator.toBool(0); + JSObjectRef errorCallback = validator.toFunction(1, false); + JSValueProtect(context, errorCallback); + + phone->muteCall(mute, errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::getContacts(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + unsigned long count = validator.toULong(0); + LoggerD("Retrieving contacts - the latest " << count << " contacts"); + JSObjectRef successCallback = validator.toFunction(1, false); + JSObjectRef errorCallback = validator.toFunction(2, false); + + JSValueProtect(context, successCallback); + JSValueProtect(context, errorCallback); + + phone->getContacts(count, successCallback, errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::getCallHistory(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + unsigned long count = validator.toULong(0); + LoggerD("Retrieving call history - the latest " << count << " calls"); + JSObjectRef successCallback = validator.toFunction(1, false); + JSObjectRef errorCallback = validator.toFunction(2, false); + + JSValueProtect(context, successCallback); + JSValueProtect(context, errorCallback); + + phone->getCallHistory(count, successCallback, errorCallback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::addCallChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef callback = validator.toFunction(0, false); + + JSValueProtect(gContext, callback); + + int id = phone->addCallChangedListener(callback, gContext); + if(id>0) + return JSValueMakeNumber(context, id); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::selectRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + ArgumentValidator validator(context, argumentCount, arguments); + std::string btAddress = validator.toString(0); + LoggerD("Selecting device: " << btAddress); + + phone->selectRemoteDevice(btAddress); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::unselectRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + LoggerD("Un-selecting selected device"); + + phone->unselectRemoteDevice(); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::addRemoteDeviceSelectedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + JSObjectRef callback = validator.toFunction(0, false); + JSValueProtect(gContext, callback); + + int id = phone->addRemoteDeviceSelectedListener(callback, gContext); + if(id>0) + return JSValueMakeNumber(context, id); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::removeRemoteDeviceSelectedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + int id = validator.toNumber(0); + + phone->removeRemoteDeviceSelectedListener(id, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::getSelectedRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + JSContextRef gContext = privateObject->getContext(); + ArgumentValidator validator(context, argumentCount, arguments); + JSObjectRef callback = validator.toFunction(0, false); + JSValueProtect(gContext, callback); + phone->getSelectedRemoteDevice(callback, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::removeCallChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + int id = validator.toNumber(0); + + phone->removeCallChangedListener(id, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::addCallHistoryEntryAddedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef callback = validator.toFunction(0, false); + + JSValueProtect(gContext, callback); + + int id = phone->addCallHistoryEntryAddedListener(callback, gContext); + if(id>0) + return JSValueMakeNumber(context, id); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::removeCallHistoryEntryAddedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + int id = validator.toNumber(0); + + phone->removeCallHistoryEntryAddedListener(id, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::addContactsChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef callback = validator.toFunction(0, false); + + JSValueProtect(gContext, callback); + + int id = phone->addContactsChangedListener(callback, gContext); + if(id>0) + return JSValueMakeNumber(context, id); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::removeContactsChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + int id = validator.toNumber(0); + + phone->removeContactsChangedListener(id, gContext); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::addCallHistoryChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + JSObjectRef callback = validator.toFunction(0, false); + + JSValueProtect(gContext, callback); + + int id = phone->addCallHistoryChangedListener(callback, gContext); + if(id>0) + return JSValueMakeNumber(context, id); + + return JSValueMakeUndefined(context); +} + +JSValueRef JSPhone::removeCallHistoryChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception) +{ + LoggerD("Entered"); + + PhonePrivObject* privateObject = static_cast(JSObjectGetPrivate(thisObject)); + if (NULL == privateObject) + { + LoggerE("private object is null"); + return JSValueMakeUndefined(context); + } + + PhonePtr phone(privateObject->getObject()); + + JSContextRef gContext = privateObject->getContext(); + + ArgumentValidator validator(context, argumentCount, arguments); + + int id = validator.toNumber(0); + + phone->removeCallHistoryChangedListener(id, gContext); + + return JSValueMakeUndefined(context); +} + +} // Phone +} // DeviceAPI + diff --git a/src/JSPhone.h b/src/JSPhone.h new file mode 100644 index 0000000..1edd394 --- /dev/null +++ b/src/JSPhone.h @@ -0,0 +1,477 @@ +#ifndef JSPHONE_H +#define JSPHONE_H + +#include "Phone.h" +#include +#include +#include +#include + +namespace DeviceAPI { +namespace Phone { + +typedef WrtDeviceApis::CommonsJavaScript::PrivateObject PhonePrivObject; + +/** + * @defgroup wrt-plugins-ivi-phone wrt-plugins-ivi-phone shared library + * \brief WebAPI to provide access to phone's functionality. + * + * WebAPI to provide access to phone's functionality, such as invoke phone call, accept/decline incoming call, hangup active call, retrieve a list of contacts stored in internal memory of a phone device, as well as its call history. + * @{ + */ + +/*! \class DeviceAPI::Phone::JSPhone + * \brief A Class which implements \b tizen.phone object to access phone's functionality from within Web app. + * + * A Class which implements \b tizen.phone object to access phone's functionality from within Web app. + * + * It defines the following methods on \b tizen.phone: + *
    +
  • \b selectRemoteDevice ( \a \b address ) Selects remote BT device for phone's operations. Once remote device gets selected, a listener registered by addRemoteDeviceSelectedListener() is called, passing a MAC address of selected remote device to indicate the success of the method call.
  • +
      +
    • \a \b address A MAC address of remote device to be selected.
    • +
    + +
  • \b unselectRemoteDevice () Unselects remote BT device selected by selectRemoteDevice(). Once remote device gets unselected, a listener registered by addRemoteDeviceSelectedListener() is called, passing \b "" to indicate that there is no currently selected remote device for phone's operations.
  • + +
  • \b getSelectedRemoteDevice ( \a \b callback ) Method to get currently selected remote device.
  • +
      +
    • \a \b callback ( \a \b address ) A callback to invoke when the remote device is retrieved.
    • +
        +
      • \a \b address A MAC address of selected remote device. If there is no currently selected remote device, \b "" is returned.
      • +
      +
    + +
  • \b addRemoteDeviceSelectedListener ( \a \b listener ) Adds a listener to get notifified when selected remote device has changed.
  • +
      +
    • \a \b listener ( \a \b address ) A listener to call when the remote device has changed.
    • +
        +
      • \a \b address A MAC address of selected remote device. Empty string \b "" means no currently selected remote device.
      • +
      +
    • \a \b returns Id of registered listener.
    • +
    + +
  • \b removeRemoteDeviceSelectedListener ( \a \b id ) Removes a listener previously registered by addRemoteDeviceSelectedListener().
  • +
      +
    • \a \b id An id of registered listener to be removed.
    • +
    + +
  • \b invokeCall ( \a \b number, \a \b errorCallback ) Dials a given phone number.
  • +
      +
    • \a \b number A phone number to dial.
    • +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when dialing has failed. +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b answerCall ( \a \b errorCallback ) Answers incoming phone call.
  • +
      +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when answering the call has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b hangupCall ( \a \b errorCallback ) Declines incoming phone call, or hangs-up active one.
  • +
      +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when hanging-up the call has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b muteCall ( \a \b mute, \a \b errorCallback ) Mutes/unmutes the active phone call.
  • +
      +
    • \a \b mute Whether to mute, or unmute the active phone call.
    • +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when muting the call has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b activeCall () Returns active phone call.
  • +
      +
    • \a \b returns Returns active phone call. The active call object contains attributes \b line_id specifying phone number of caller, or calling person respectively, \b state which specify the state of the call and \b contact which matches the \b line_id. If there is no active call, \b line_id is set to \b "" and \b state is set to \b "disconnected".
    • +
    + +
  • \b addCallChangedListener ( \a \b listener ) Adds a listener to get notifified when there is a change on active call, ie. to get notified about incoming call and all other states that the call may go through, up to \b "disconnected" state.
  • +
      +
    • \a \b listener ( \a \b call ) A listener to call when there is a change on the active call.
    • +
        +
      • \a \b call The call object contains attributes \b line_id specifying phone number of caller, or calling person respectively, \b state which specify the state of the call and \b contact which matches the \b line_id. If there is no active call, \b line_id is set to \b "" and \b state is set to \b "disconnected".
      • +
      +
    • \a \b returns Id of registered listener.
    • +
    + +
  • \b removeCallChangedListener ( \a \b id ) Removes a listener previously registered by addCallChangedListener().
  • +
      +
    • \a \b id An id of registered listener to be removed.
    • +
    + +
  • \b getContacts ( \a \b count, \a \b successCallback, \a \b errorCallback ) Retrieves an array of \a \b count synchronized contacts in JSON \b tizen.Contact format.
  • +
      +
    • \a \b count Specifies \a \b count the frist contacts to retrieve. \b 0 means to retrieve all contacts.
    • +
    • \a \b successCallback ( \a \b contacts ) A callback to call when contacts are successfuly retrieved.
    • +
        +
      • \a \b contacts An array of retrieved contacts in \b tizen.Contact JSON format.
      • +
      +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when retrieving the contacts has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b getCallHistory ( \a \b count, \a \b successCallback, \a \b errorCallback ) Retrieves an array of \a \b count synchronized call history entries in JSON \b tizen.CallHistoryEntry format.
  • +
      +
    • \a \b count Specifies \a \b count the frist call history entries to retrieve. \b 0 means to retrieve all calls.
    • +
    • \a \b successCallback ( \a \b contacts ) A callback to call when contacts are successfuly retrieved.
    • +
        +
      • \a \b contacts An array of retrieved call history entries in \b tizen.CallHistoryEntry JSON format.
      • +
      +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when retrieving the call history entries has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b addCallHistoryEntryAddedListener ( \a \b listener ) Adds a listener to get notifified when a call entry has been added to the history list, due to incoming, or dialed call.
  • +
      +
    • \a \b listener ( \a \b call ) A listener to call when the call history entry was added to the list.
    • +
        +
      • \a \b call Call history entry added to the history list.
      • +
      +
    • \a \b returns Id of registered listener.
    • +
    + +
  • \b removeCallHistoryEntryAddedListener ( \a \b id ) Removes a listener previously registered by addCallHistoryEntryAddedListener().
  • +
      +
    • \a \b id An id of registered listener to be removed.
    • +
    + +
  • \b addContactsChangedListener ( \a \b listener ) Adds a listener to get notifified when contacts list has changed, ie. when a new contact was added, or if the list was cleared.
  • +
      +
    • \a \b listener () A listener to call when the contacts list has changed.
    • +
    • \a \b returns Id of registered listener.
    • +
    + +
  • \b removeContactsChangedListener ( \a \b id ) Removes a listener previously registered by addContactsChangedListener().
  • +
      +
    • \a \b id An id of registered listener to be removed.
    • +
    + +
  • \b addCallHistoryChangedListener ( \a \b listener ) Adds a listener to get notifified when call history list has changed, ie. when a new call history entry was added, or if the list was cleared.
  • +
      +
    • \a \b listener () A listener to call when the call history list has changed.
    • +
    • \a \b returns Id of registered listener.
    • +
    + +
  • \b removeCallHistoryChangedListener ( \a \b id ) Removes a listener previously registered by addCallHistoryChangedListener().
  • +
      +
    • \a \b id An id of registered listener to be removed.
    • +
    + +
  • \b createBonding ( \a \b address, \a \b successCallback, \a \b errorCallback ) Initiates a pairing sequence with the remote BT device, specified by it's MAC address.
  • +
      +
    • \a \b address A MAC address of remote device to pair with.
    • +
    • \a \b successCallback () A callback to invoke when the pairing has successfuly finished.
    • +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when pairing with remote BT address has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + +
  • \b destroyBonding ( \a \b address, \a \b successCallback, \a \b errorCallback ) Unpairs with the remote BT device, specified by it's MAC address.
  • +
      +
    • \a \b address A MAC address of remote device to unpair with.
    • +
    • \a \b successCallback () A callback to invoke when the pairing has successfuly finished.
    • +
    • \a \b errorCallback ( \a \b error ) A callback to invoke when unpairing with remote BT address has failed.
    • +
        +
      • \a \b error A JSON object describing the reason of failure. It contains a \a \b message attribute holding a string representation of failure.
      • +
      +
    + *
+ */ +class JSPhone +{ +public: + /** + * Gets the Call Definition. + * @see getClassRef() + * @return JS class definition. + */ + static const JSClassDefinition* getClassInfo(); + + /** + * Method to create (if it's not yet created) and return this class reference. Used by the plugin loader to load this plugin. + * @see getClassInfo() + * @return JS class reference. + */ + static const JSClassRef getClassRef(); + +private: + /** + * The callback invoked when an object is first created. + */ + static void initialize(JSContextRef context, + JSObjectRef object); + + /** + * The callback invoked when an object is finalized. + */ + static void finalize(JSObjectRef object); + + /** + * Getters for properties + */ + static JSValueRef getProperty(JSContextRef context, + JSObjectRef object, + JSStringRef propertyName, + JSValueRef* exception); + + /** + * The callback invoked when an object is used as the target of an 'instanceof' expression. + */ + static bool hasInstance(JSContextRef ctx, + JSObjectRef constructor, + JSValueRef possibleInstance, + JSValueRef* exception); + + /** + * Invoke phone call on BT paired device + */ + static JSValueRef invokeCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Hangup (active/incoming, dialing) phone call. + */ + static JSValueRef hangupCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Answer (incoming) phone call. + */ + static JSValueRef answerCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Mute/UnMute phone call. + */ + static JSValueRef muteCall(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Selects a remote BT device for Phone operations + * btAddress: address of paired and trusted BT device + * the client app should subscribe for RemoteDeviceSelected signal, ie. should call addRemoteDeviceSelectedListener() + * see addRemoteDeviceSelectedListener for more information + */ + static JSValueRef selectRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Un-selects selected remote BT device for Phone operations + * the client app should subscribe for RemoteDeviceSelected signal, ie. should call addRemoteDeviceSelectedListener() + * see addRemoteDeviceSelectedListener for more information + */ + static JSValueRef unselectRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Adds listener to get notified about the result of selecting remote device + * parameter to the callback defines result of operation: + * if result contains 'error' atribute, it contains error meessage of the failure + * if resutl contains 'value' and if value is "" - it holds a MAC address of selected device + * if resutl contains 'value' and if value is "" - it indicates that the remote device is unselected + * services OFono/Obex are up and running + * returns id of registered listener, to be used for removing + */ + static JSValueRef addRemoteDeviceSelectedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Removes listener for notification about selected remote device. + * parameter is id of listener registered via addRemoteDeviceSelectedListener() + */ + static JSValueRef removeRemoteDeviceSelectedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Get selected remote BT device + * returns: MAC address of selected remote BT device + */ + static JSValueRef getSelectedRemoteDevice(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Retrieve contacts from BT paired device: the latest 'count' entries + */ + static JSValueRef getContacts(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Retrieve call history: the latest 'count' entries + */ + static JSValueRef getCallHistory(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Adds listener for property changed on active call + * returns id of registered listener, to be used for removing + */ + static JSValueRef addCallChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Removes listener for property changed on active call. + * parameter is id of listener registered via addCallCangedListener() + */ + static JSValueRef removeCallChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Adds listener to get notified about an Call entry being added to the Call History + * returns id of registered listener, to be used for removing + */ + static JSValueRef addCallHistoryEntryAddedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Removes listener for notification about call entry being added to the Call History + * parameter is id of listener registered via addCallHistoryEntryAddedListener() + */ + static JSValueRef removeCallHistoryEntryAddedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Adds listener to get notified about an Contacts being changed - as a result of selectRemoteDevice + * returns id of registered listener, to be used for removing + */ + static JSValueRef addContactsChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Removes listener for notification about contacts being changed (as a result of selectRemoteDevice) + * parameter is id of listener registered via addContactsChangedListener() + */ + static JSValueRef removeContactsChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Adds listener to get notified about an CallHistory being changed - as a result of selectRemoteDevice + * returns id of registered listener, to be used for removing + */ + static JSValueRef addCallHistoryChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * Removes listener for notification about contacts being changed (as a result of selectRemoteDevice) + * parameter is id of listener registered via addCallHistoryChangedListener() + */ + static JSValueRef removeCallHistoryChangedListener(JSContextRef context, + JSObjectRef object, + JSObjectRef thisObject, + size_t argumentCount, + const JSValueRef arguments[], + JSValueRef* exception); + + /** + * This structure contains properties and callbacks that define a type of object. + */ + static JSClassDefinition m_classInfo; + + /** + * This structure describes a statically declared function. + */ + static JSStaticFunction m_function[]; + + /** + * This member variable contains the initialization values for the + * properties of this class. The values are given according to the + * data structure JSPropertySpec. + */ + static JSStaticValue m_property[]; + + static JSClassRef m_jsClassRef; +}; + +} // Phone +} // DeviceAPI + +#endif // JSPHONE_H + +/** @} */ + diff --git a/src/Phone.cpp b/src/Phone.cpp new file mode 100644 index 0000000..e53eb40 --- /dev/null +++ b/src/Phone.cpp @@ -0,0 +1,894 @@ +#include "Phone.h" +#include +#include +#include + +#include +#include +#include + +#define TIZEN_PREFIX "org.tizen" +#define PHONE_SERVICE TIZEN_PREFIX ".phone" +#define PHONE_IFACE TIZEN_PREFIX ".Phone" +#define PHONE_OBJ_PATH "/" + +#define DEFAULT_PINCODE "123456" +#define DEFAULT_PASSKEY 123456 + +namespace DeviceAPI +{ +namespace Phone +{ + +using namespace WrtDeviceApis::Commons; +using namespace WrtDeviceApis::CommonsJavaScript; + +PhoneMaster::PhoneMaster() +{ + LoggerD("entered"); +} + +PhoneMaster::~PhoneMaster() +{ + LoggerD("entered"); +} + +void PhoneMaster::handleSignal( GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data) +{ + LoggerD("entered"); + //LoggerD("\tsender_name: " << sender_name); + //LoggerD("\tobject_path: " << object_path); + //LoggerD("\tinterface_name: " << interface_name); + //LoggerD("\tsignal_name: " << signal_name); + + PhoneMaster* ctx = static_cast(user_data); + if(!ctx) { + LoggerE("Failed to cast to PhoneMaster ... invalid context"); + return; + } + + if(!strcmp(signal_name, "RemoteDeviceSelected")) { + const char *result; + g_variant_get(parameters, "(s)", &result); + if(result) { + if(ctx->mRemoteDeviceSelectedListenersMap.size() > 0) { + for(auto it=ctx->mRemoteDeviceSelectedListenersMap.begin(); it!=ctx->mRemoteDeviceSelectedListenersMap.end(); ++it) { + PhoneSubscriptionData *data = (*it).second; + JSStringRef jsonString = JSStringCreateWithUTF8CString(result); + JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + break; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL); + } + } + } + } + else if(!strcmp(signal_name, "CallChanged")) { + GVariantIter *iter; + g_variant_get(parameters, "(a{sv})", &iter); + const char *key = NULL, *state = NULL, *line_id = NULL, *contact = NULL; + GVariant *value; + while(g_variant_iter_next(iter, "{sv}", &key, &value)) { + if(!strcmp(key, "state")) { + state = g_variant_get_string(value, NULL); + LoggerD("\t- state: " << state); + } + else if(!strcmp(key, "line_id")) { + line_id = g_variant_get_string(value, NULL); + LoggerD("\t- line_id: " << line_id); + } + else if(!strcmp(key, "contact")) { + contact = g_variant_get_string(value, NULL); + LoggerD("\t- contact: " << contact); + } + } + + if(ctx->mCallChangedListenersMap.size() > 0) { + LoggerD("subscribed for 'CallChanged' ... calling callback"); + std::string json; + json += "{\"line_id\" : \""; + json += line_id; + json += "\", \"state\" : \""; + json += state; + json += "\", \"contact\" : "; + json += contact; + json += "}"; + + // call all subscribed listeners + std::map::iterator iter = ctx->mCallChangedListenersMap.begin(); + while(iter != ctx->mCallChangedListenersMap.end()) { + PhoneSubscriptionData *data = (*iter).second; + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + break; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL); + iter++; + } + } + } + else if(!strcmp(signal_name, "ContactsChanged")) { + if(ctx->mContactsChangedListenersMap.size() > 0) { + for(auto it=ctx->mContactsChangedListenersMap.begin(); it!=ctx->mContactsChangedListenersMap.end(); ++it) { + PhoneSubscriptionData *data = (*it).second; + JSObjectCallAsFunction(data->context, data->callback, NULL, 0, NULL, NULL); + } + } + } + else if(!strcmp(signal_name, "CallHistoryChanged")) { + if(ctx->mCallHistoryChangedListenersMap.size() > 0) { + for(auto it=ctx->mCallHistoryChangedListenersMap.begin(); it!=ctx->mCallHistoryChangedListenersMap.end(); ++it) { + PhoneSubscriptionData *data = (*it).second; + JSObjectCallAsFunction(data->context, data->callback, NULL, 0, NULL, NULL); + } + } + } + else if(!strcmp(signal_name, "CallHistoryEntryAdded")) { + const char *call; + g_variant_get(parameters, "(s)", &call); + if(call) { + if(ctx->mCallHistoryEntryAddedListenersMap.size() > 0) { + for(auto it=ctx->mCallHistoryEntryAddedListenersMap.begin(); it!=ctx->mCallHistoryEntryAddedListenersMap.end(); ++it) { + PhoneSubscriptionData *data = (*it).second; + JSStringRef jsonString = JSStringCreateWithUTF8CString(call); + JSValueRef value = JSValueMakeFromJSONString(data->context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + break; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL); + } + } + } + } + else { + LoggerD("Unhandled signal: " << signal_name); + } +} + +void PhoneMaster::invokeCall(std::string phoneNumber, JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "Dial", + g_variant_new("(s)", phoneNumber.c_str()), // floating variants are consumed + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if(error) { + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + //json += error->code; + json += "1"; + json += "\", \"message\" : \"DBUS: "; + json += error->message; + json += "\"}"; + + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } +} + +void PhoneMaster::hangupCall(JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "Hangup", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if(error) { + LoggerE("Failed to hangup call: " << error->message); + + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + //json += error->code; + json += "1"; + json += "\", \"message\" : \"DBUS: "; + json += error->message; + json += "\"}"; + + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } +} + +void PhoneMaster::answerCall(JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "Answer", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if(error) { + LoggerE("Failed to answer call: " << error->message); + + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + //json += error->code; + json += "1"; + json += "\", \"message\" : \"DBUS: "; + json += error->message; + json += "\"}"; + + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } +} + +void PhoneMaster::muteCall(bool mute, JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "Mute", + g_variant_new("(b)", mute), // floating variants are consumed + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + if(error) { + LoggerE("Failed to mute call: " << error->message); + + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + //json += error->code; + json += "1"; + json += "\", \"message\" : \"DBUS: "; + json += error->message; + json += "\"}"; + + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } +} + +void PhoneMaster::selectRemoteDevice(std::string btAddress) { + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "SelectRemoteDevice", + g_variant_new("(s)", btAddress.c_str()), // floating variants are consumed + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if(error) { + LoggerE("Failed to call 'SelectremoteDevice': " << error->message); + g_error_free(error); + } +} + +void PhoneMaster::unselectRemoteDevice() { + GError *error = NULL; + g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "UnselectRemoteDevice", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + if(error) { + LoggerE("Failed to call 'UnselectremoteDevice': " << error->message); + g_error_free(error); + } +} + +void PhoneMaster::getContacts(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + GVariant *reply = NULL; + reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "GetContacts", + g_variant_new("(u)", count), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + /* for async DBUS call + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + data->context = context; + data->callback = successCallback; + data->errorCallback = errorCallback; + */ + + if(error || !reply) { + if(error) + LoggerE("Failed to get contacts: " << error->message); + if(!reply) + LoggerE("reply is null"); + + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + json += error ? 1 : 2; + json += "\", \"message\" : \"DBUS: "; + json += error ? "Failed to 'GetContacts'" : "Reply from 'GetContacts' is null"; + json += "\"}"; + + if(error) + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } + + char *contacts = NULL; + g_variant_get(reply, "(s)", &contacts); + + JSStringRef str = JSStringCreateWithUTF8CString(contacts); + JSValueRef value = JSValueMakeFromJSONString(context, str); + if(!value) + { + std::string json; + json += "{\"code\" : \""; + json += 2; + json += "\", \"message\" : \""; + json += "Unable to create response value."; + json += "\"}"; + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("Failed to make value from json"); + // !!! it's bad !!! no callback is called !!! + // TODO: should we call error callback with 'undefined' value instead? + g_variant_unref(reply); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, successCallback, NULL, 1, arguments, NULL); + g_variant_unref(reply); +} + +void PhoneMaster::getCallHistory(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context) +{ + LoggerD("entered"); + + GError *error = NULL; + GVariant *reply = NULL; + reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "GetCallHistory", + g_variant_new("(u)", count), + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + /* for async DBUS call + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + data->context = context; + data->callback = successCallback; + data->errorCallback = errorCallback; + */ + + if(error || !reply) { + if(error) + LoggerE("Failed to get call history: " << error->message); + if(!reply) + LoggerE("reply is null"); + + // call the error callback to notify client about the error condition + std::string json; + json += "{\"code\" : \""; + json += error ? 1 : 2; + json += "\", \"message\" : \"DBUS: "; + json += error ? "Failed to 'GetCallHistory'" : "Reply from 'GetCallHistory' is null"; + json += "\"}"; + + if(error) + g_error_free(error); + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("failed to make value from json"); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } + + char *calls = NULL; + g_variant_get(reply, "(s)", &calls); + + JSStringRef str = JSStringCreateWithUTF8CString(calls); + JSValueRef value = JSValueMakeFromJSONString(context, str); + if(!value) + { + std::string json; + json += "{\"code\" : \""; + json += 2; + json += "\", \"message\" : \""; + json += "Unable to create response value."; + json += "\"}"; + + JSStringRef jsonString = JSStringCreateWithUTF8CString(json.c_str()); + JSValueRef value = JSValueMakeFromJSONString(context, jsonString); + if(!value) + { + LoggerE("Failed to make value from json"); + // !!! it's bad !!! no callback is called !!! + // TODO: should we call error callback with 'undefined' value instead? + g_variant_unref(reply); + return; + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, errorCallback, NULL, 1, arguments, NULL); + } + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(context, successCallback, NULL, 1, arguments, NULL); + g_variant_unref(reply); +} + +int PhoneMaster::addRemoteDeviceSelectedListener(JSObjectRef callback, JSContextRef context) +{ + LoggerD("entered"); + + guint id = 0; + if(mRemoteDeviceSelectedListenersMap.size() == 0) { + // we are not yet subscribed to the signal + id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_IFACE, + "RemoteDeviceSelected", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + handleSignal, + this, + NULL); + if(id <= 0) { + LoggerE("Failed to subscribe for 'RemoteDeviceSelected': " << id); + return -1; + } + } + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + if(!data) { + // unsubscribe the signal, othewise we will have 'unhandled' signal + LoggerE("Unable to create subscription data ... removing subscription with id: " << id); + g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id); + return -2; + } + data->context = context; + data->callback = callback; + + mRemoteDeviceSelectedListenersMap[id] = data; + + return id; +} + +void PhoneMaster::removeRemoteDeviceSelectedListener(int id, JSContextRef context) +{ + LoggerD("removing listener id=" << id); + + mRemoteDeviceSelectedListenersMap.erase(id); +} + +int PhoneMaster::addCallChangedListener(JSObjectRef callback, JSContextRef context) +{ + LoggerD("adding listener for CallChanged"); + + // we are not yet subscribed to ofono's CallChanged signal + guint id = 0; + if(mCallChangedListenersMap.size() == 0) { + id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_IFACE, + "CallChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + handleSignal, + this, + NULL); + if(id <= 0) { + LoggerE("Failed to subscribe for 'CallChanged': " << id); + return -1; + } + } + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + if(!data) { + // unsubscribe the signal, othewise we will have 'unhandled' signal + LoggerE("Unable to create subscription data ... removing subscription with id: " << id); + g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id); + return -2; + } + data->context = context; + data->callback = callback; + + mCallChangedListenersMap[id] = data; + + return id; +} + +void PhoneMaster::removeCallChangedListener(int id, JSContextRef context) +{ + LoggerD("removing listener id=" << id); + + mCallChangedListenersMap.erase(id); +} + +int PhoneMaster::addCallHistoryEntryAddedListener(JSObjectRef callback, JSContextRef context) +{ + LoggerD("adding listener for CallHistoryEntryAdded"); + + guint id = 0; + if(mCallHistoryEntryAddedListenersMap.size() == 0) { + // we are not yet subscribed to the signal + id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_IFACE, + "CallHistoryEntryAdded", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + handleSignal, + this, + NULL); + if(id <= 0) { + LoggerE("Failed to subscribe for 'CallHistoryEntryAdded': " << id); + return -1; + } + } + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + if(!data) { + // unsubscribe the signal, othewise we will have 'unhandled' signal + LoggerE("Unable to create subscription data ... removing subscription with id: " << id); + g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id); + return -2; + } + data->context = context; + data->callback = callback; + + mCallHistoryEntryAddedListenersMap[id] = data; + + return id; +} + +void PhoneMaster::removeCallHistoryEntryAddedListener(int id, JSContextRef context) +{ + LoggerD("removing listener id=" << id); + + mCallHistoryEntryAddedListenersMap.erase(id); +} + +int PhoneMaster::addContactsChangedListener(JSObjectRef callback, JSContextRef context) +{ + LoggerD("entered"); + + guint id = 0; + if(mContactsChangedListenersMap.size() == 0) { + // we are not yet subscribed to the signal + id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_IFACE, + "ContactsChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + handleSignal, + this, + NULL); + if(id <= 0) { + LoggerE("Failed to subscribe for 'ContactsChanged': " << id); + return -1; + } + } + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + if(!data) { + // unsubscribe the signal, othewise we will have 'unhandled' signal + LoggerE("Unable to create subscription data ... removing subscription with id: " << id); + g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id); + return -2; + } + data->context = context; + data->callback = callback; + + mContactsChangedListenersMap[id] = data; + + return id; +} + +void PhoneMaster::removeContactsChangedListener(int id, JSContextRef context) +{ + LoggerD("removing listener id=" << id); + + mContactsChangedListenersMap.erase(id); +} + +int PhoneMaster::addCallHistoryChangedListener(JSObjectRef callback, JSContextRef context) +{ + LoggerD("entered"); + + guint id = 0; + if(mCallHistoryChangedListenersMap.size() == 0) { + // we are not yet subscribed to the signal + id = g_dbus_connection_signal_subscribe( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), + PHONE_SERVICE, + PHONE_IFACE, + "CallHistoryChanged", + NULL, + NULL, + G_DBUS_SIGNAL_FLAGS_NONE, + handleSignal, + this, + NULL); + if(id <= 0) { + LoggerE("Failed to subscribe for 'CallHistoryChanged': " << id); + return -1; + } + } + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + if(!data) { + // unsubscribe the signal, othewise we will have 'unhandled' signal + LoggerE("Unable to create subscription data ... removing subscription with id: " << id); + g_dbus_connection_signal_unsubscribe(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL,NULL), id); + return -2; + } + data->context = context; + data->callback = callback; + + mCallHistoryChangedListenersMap[id] = data; + + return id; +} + +void PhoneMaster::removeCallHistoryChangedListener(int id, JSContextRef context) +{ + LoggerD("removing listener id=" << id); + + mCallHistoryChangedListenersMap.erase(id); +} + +std::string PhoneMaster::activeCall(void) { + LoggerD("entered"); + + GError *error = NULL; + GVariant *reply = NULL; + reply = g_dbus_connection_call_sync( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "ActiveCall", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + &error); + + std::string json; + if(error || !reply) { + if(error) { + LoggerE("Failed to get 'ActiveCall': " << error->message); + g_error_free(error); + } + else if(!reply) + LoggerE("reply is null"); + + json += "{\"line_id\" : \""; + json += ""; + json += "\", \"state\" : \""; + json += "disconnected"; + json += "\", \"contact\" : \""; + json += "{}"; + json += "\"}"; + } + else { + GVariantIter *iter; + g_variant_get(reply, "(a{sv})", &iter); + const char *key = NULL, *state = NULL, *line_id = NULL, *contact = NULL; + GVariant *value; + while(g_variant_iter_next(iter, "{sv}", &key, &value)) { + if(!strcmp(key, "state")) { + state = g_variant_get_string(value, NULL); + LoggerD("\t- state: " << state); + } + else if(!strcmp(key, "line_id")) { + line_id = g_variant_get_string(value, NULL); + LoggerD("\t- line_id: " << line_id); + } + else if(!strcmp(key, "contact")) { + contact = g_variant_get_string(value, NULL); + LoggerD("\t- contact: " << contact); + } + } + + json += "{\"line_id\" : \""; + json += line_id; + json += "\", \"state\" : \""; + json += state; + json += "\", \"contact\" : "; + json += contact; + json += "}"; + + g_variant_unref(reply); + } + + return json; +} + +void PhoneMaster::getSelectedRemoteDevice(JSObjectRef callback, JSContextRef context) { + LoggerD("entered"); + + PhoneMaster::PhoneSubscriptionData *data = new PhoneMaster::PhoneSubscriptionData(); + data->context = context; + data->callback = callback; + data->errorCallback = NULL; + + g_dbus_connection_call( g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), + PHONE_SERVICE, + PHONE_OBJ_PATH, + PHONE_IFACE, + "GetSelectedRemoteDevice", + NULL, + NULL, + G_DBUS_CALL_FLAGS_NONE, + -1, + NULL, + PhoneMaster::asyncGetSelectedRemoteDeviceCB, + data); +} + +void PhoneMaster::asyncGetSelectedRemoteDeviceCB(GObject *source, GAsyncResult *result, gpointer user_data) { + PhoneMaster::PhoneSubscriptionData *data = static_cast(user_data); + if(!data) { + LoggerE("Failed to cast object: PhoneSubscriptionData"); + return; + } + + GError *err = NULL; + GVariant *reply; + reply = g_dbus_connection_call_finish(g_bus_get_sync(G_BUS_TYPE_SESSION, NULL, NULL), result, &err); + JSStringRef strRef = JSStringCreateWithUTF8CString(""); + if(err || !reply) { + if(err) { + LoggerE("Failed to \"GetSelectedRemoteDevice\": " << err->message); + g_error_free(err); + } + else if(!reply) + LoggerE("reply is null"); + } + else { + const char *str; + g_variant_get(reply, "(s)", &str); + if(str) + strRef = JSStringCreateWithUTF8CString(str); + } + + JSValueRef value = JSValueMakeString(data->context, strRef); + const JSValueRef arguments[1] = { value }; + JSObjectCallAsFunction(data->context, data->callback, NULL, 1, arguments, NULL); + delete data; + if(reply) + g_variant_unref(reply); +} + +} // Phone +} // DeviceAPI + diff --git a/src/Phone.h b/src/Phone.h new file mode 100644 index 0000000..6b688ec --- /dev/null +++ b/src/Phone.h @@ -0,0 +1,232 @@ +#ifndef PHONE_H +#define PHONE_H + +#include +#include +#include +#include +#include +#include + +class AbstractPropertyType; + +namespace DeviceAPI { +namespace Phone { + +/** + * @addtogroup wrt-plugins-ivi-phone + * + * @{ + */ + +/*! \class DeviceAPI::Phone::PhoneMaster + * \brief Class which is utilizing Phone D-Bus service. + * + * A Class which is utilizing Phone D-Bus service. It connects to \b org.tizen.phone D-Bus service, subscribes to it's signals and implements methods to allow JSPhone to access phone's functionality. + */ +class PhoneMaster +{ + /** + * A helper class to store context/callback for asynchronous operations. + */ + class PhoneSubscriptionData + { + public: + JSContextRef context; /*!< A variable to store context. */ + JSObjectRef callback; /*!< A variable to store callback. */ + JSObjectRef errorCallback; /*!< A variable to store error callback. */ + }; +public: + + /** + * A default constructor. Constructs the object. + */ + PhoneMaster(); + + /** + * A destructor. Destructs the object. + */ + ~PhoneMaster(); + + /** + * Selects remote BT device for phone's operations. + * @param[in] btAddress A MAC address of the remote device to get selected. Will call a listener registered by addRemoteDeviceSelectedListener(). + */ + void selectRemoteDevice(std::string btAddress); + + /** + * Unselects remote BT device selected by selectRemoteDevice(). Will call a listener registered by addRemoteDeviceSelectedListener(). + */ + void unselectRemoteDevice(); + + /** + * Method to get currently selected remote device. + * @param[in] callback A callback to invoke when the remote device is retrieved, passing a MAC address of selected remote device, or \b "" if there is no currently selected remote device. + * @param[in] context A context on which the method is invoked. + */ + void getSelectedRemoteDevice(JSObjectRef callback, JSContextRef context); + + /** + * Adds a listener to get notifified when selected remote device has changed. + * @param[in] callback A listener to call when the remote device has changed. + * @param[in] context A context on which the method is invoked. + * @return Id of registered listener. + */ + int addRemoteDeviceSelectedListener(JSObjectRef callback, JSContextRef context); + + /** + * Removes a listener previously registered by addRemoteDeviceSelectedListener(). + * @param[in] id An id of registered listener to be removed. + * @param[in] context A context on which the method is invoked. + */ + void removeRemoteDeviceSelectedListener(int id, JSContextRef context); + + /** + * Dials a given phone number. + * @param[in] phoneNumber A phone number to dial. + * @param[in] errorCallback A callback to invoke when dialing has failed. + * @param[in] context A context on which the method is invoked. + */ + void invokeCall(std::string phoneNumber, JSObjectRef errorCallback, JSContextRef context); + + /** + * Answers incoming phone call. + * @param[in] errorCallback A callback to invoke when answering the call has failed. + * @param[in] context A context on which the method is invoked. + */ + void answerCall(JSObjectRef errorCallback, JSContextRef context); + + /** + * Declines incoming phone call, or hangs-up active one. + * @param[in] errorCallback A callback to invoke when hanging-up the call has failed. + * @param[in] context A context on which the method is invoked. + */ + void hangupCall(JSObjectRef errorCallback, JSContextRef context); + + /** + * Mutes/unmutes the active phone call. + * @param[in] mute Whether to mute, or unmute the active phone call. + * @param[in] errorCallback A callback to invoke when muting the call has failed. + * @param[in] context A context on which the method is invoked. + */ + void muteCall(bool mute, JSObjectRef errorCallback, JSContextRef context); + + /** + * Returns active phone call. + * @return Active phone call. + */ + std::string activeCall(void); + + /** + * Adds a listener to get notifified when there is a change on active call, ie. to get notified about incoming call and all other states that the call may go through, up to \b "disconnected" state. + * @param[in] callback A listener to call when there is a change on active call. + * @param[in] context A context on which the method is invoked. + * @return Id of registered listener. + */ + int addCallChangedListener(JSObjectRef callback, JSContextRef context); // returns id of registered listener + + /** + * Removes a listener previously registered by addCallChangedListener(). + * @param[in] id An id of registered listener to be removed. + * @param[in] context A context on which the method is invoked. + */ + void removeCallChangedListener(int id, JSContextRef context); + + // Obex specific + + /** + * Retrieves an array of \a \b count synchronized contacts in JSON \b tizen.Contact format. + * @param[in] count Specifies \a \b count the frist contacts to retrieve. \b 0 means to retrieve all contacts. + * @param[in] successCallback A callback to invoke when contacts are successfuly retrieved. + * @param[in] errorCallback A callback to invoke when retrieving the contacts has failed. + * @param[in] context A context on which the method is invoked. + */ + void getContacts(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context); + + /** + * Retrieves an array of \a \b count synchronized call history entries in JSON \b tizen.CallHistoryEntry format. + * @param[in] count Specifies \a \b count the frist call history entries to retrieve. + * @param[in] successCallback A callback to invoke when call history entries are successfuly retrieved. + * @param[in] errorCallback A callback to invoke when retrieving the call history entries has failed. + * @param[in] context A context on which the method is invoked. + */ + void getCallHistory(unsigned long count, JSObjectRef successCallback, JSObjectRef errorCallback, JSContextRef context); + + /** + * Adds a listener to get notifified when a call entry has been added to the history list, due to incoming, or dialed call. + * @param[in] callback A listener to call when the call history entry was added to the list. + * @param[in] context A context on which the method is invoked. + * @return Id of registered listener. + */ + int addCallHistoryEntryAddedListener(JSObjectRef callback, JSContextRef context); + + /** + * Removes a listener previously registered by addCallHistoryEntryAddedListener(). + * @param[in] id An id of registered listener to be removed. + * @param[in] context A context on which the method is invoked. + */ + void removeCallHistoryEntryAddedListener(int id, JSContextRef context); + + /** + * Adds a listener to get notifified when contacts list has changed, ie. when a new contact was added, or the list was cleared. + * @param[in] callback A listener to call when the contacts list has changed. + * @param[in] context A context on which the method is invoked. + * @return Id of registered listener. + */ + int addContactsChangedListener(JSObjectRef callback, JSContextRef context); + + /** + * Removes a listener previously registered by addContactsChangedListener(). + * @param[in] id An id of registered listener to be removed. + * @param[in] context A context on which the method is invoked. + */ + void removeContactsChangedListener(int id, JSContextRef context); + + /** + * Adds a listener to get notifified when call history list has changed, ie. when a new call history entry was added, or the list was cleared. + * @param[in] callback A listener to call when the call history list has changed. + * @param[in] context A context on which the method is invoked. + * @return Id of registered listener. + */ + int addCallHistoryChangedListener(JSObjectRef callback, JSContextRef context); + + /** + * Removes a listener previously registered by addCallHistoryChangedListener(). + * @param[in] id An id of registered listener to be removed. + * @param[in] context A context on which the method is invoked. + */ + void removeCallHistoryChangedListener(int id, JSContextRef context); + +private: + // OFono specific + static void handleSignal( GDBusConnection *connection, + const gchar *sender_name, + const gchar *object_path, + const gchar *interface_name, + const gchar *signal_name, + GVariant *parameters, + gpointer user_data); + + static void asyncGetSelectedRemoteDeviceCB(GObject *source, GAsyncResult *result, gpointer user_data); + +private: + std::map mCallChangedListenersMap; + std::map mCallHistoryEntryAddedListenersMap; + std::map mContactsChangedListenersMap; + std::map mCallHistoryChangedListenersMap; + std::map mRemoteDeviceSelectedListenersMap; +}; + +/** + * A typedef for shared pointer to PhoneMaster. + */ +typedef DPL::SharedPtr PhonePtr; + + +} // Phone +} // DeviceAPI + +#endif // PHONE_H + +/** @} */ + diff --git a/src/Phone.idl b/src/Phone.idl new file mode 100644 index 0000000..ac9d366 --- /dev/null +++ b/src/Phone.idl @@ -0,0 +1,172 @@ +/** +Details. +\brief Allows access to the phone API +*/ + +module Phone { + +[NoInterfaceObject] +interface PhoneError { + /** code + * MUST return error code. + **/ + attribute unsigned short code; + + /** message + * MUST return error message + **/ + attribute DOMString message; +}; + +typedef DOMString PersonId; +typedef DOMString ContactId; +typedef DOMString ContactGroupId; + +[NoInterfaceObject] +interface ContactName { + attribute DOMString? prefix; + attribute DOMString? suffix; + attribute DOMString? firstName; + attribute DOMString? middleName; + attribute DOMString? lastName; + attribute DOMString[] nicknames; + attribute DOMString? phoneticFirstName; + attribute DOMString? phoneticLastName; + readonly attribute DOMString? displayName; +}; + +[NoInterfaceObject] +interface ContactAddress { + attribute DOMString? country; + attribute DOMString? region; + attribute DOMString? city; + attribute DOMString? streetAddress; + attribute DOMString? additionalInformation; + attribute DOMString? postalCode; + attribute boolean isDefault; + attribute DOMString[] types; +}; + +[NoInterfaceObject] +interface ContactPhoneNumber { + attribute DOMString number; + attribute boolean isDefault; + attribute DOMString[] types; +}; + +[NoInterfaceObject] +interface ContactEmailAddress { + attribute DOMString email; + attribute boolean isDefault; + attribute DOMString[] types; +}; + +[NoInterfaceObject] +interface ContactAnniversary { + attribute Date date; + attribute DOMString? label; +}; + +[NoInterfaceObject] +interface ContactOrganization { + attribute DOMString? name; + attribute DOMString? department; + attribute DOMString? title; + attribute DOMString? role; + attribute DOMString? logoURI; +}; + +[NoInterfaceObject] +interface ContactWebSite { + attribute DOMString url; + attribute DOMString type; +}; + +[NoInterfaceObject] +interface Contact { + readonly attribute ContactId? id; + readonly attribute PersonId? personId; + readonly attribute AddressBookId? addressBookId; + readonly attribute Date? lastUpdated; + readonly attribute boolean isFavorite; + attribute DOMString? photoURI; + attribute ContactPhoneNumber[] phoneNumbers; + attribute ContactEmailAddress[] emails; + attribute Date? birthday; + attribute ContactAnniversary[] anniversaries; + attribute ContactOrganization[] organizations; + attribute DOMString[] notes; + attribute ContactWebSite[] urls; + attribute DOMString? ringtoneURI; + attribute ContactGroupId[] groupIds; + //DOMString convertToString(optional ContactTextFormat? format); + //Contact clone(); +}; + +[NoInterfaceObject] +interface RemoteParty { + readonly attribute DOMString? remoteParty; + readonly attribute PersonId? personId; +}; + +[NoInterfaceObject] +interface CallHistoryEntry { + readonly attribute DOMString uid; + readonly attribute DOMString type; + readonly attribute DOMString[]? features; + readonly attribute RemoteParty[] remoteParties; + readonly attribute Date startTime; + readonly attribute unsigned long duration; + readonly attribute DOMString direction; +}; + +callback PhoneErrorCallback = void (PhoneError error); +callback GetContactsCallback = void (Contact[] contacts); +callback GetCallHistoryCallback = void (CallHistoryEntry[] entries); + +[NoInterfaceObject] +interface Phone { + + /** + * \brief Invoke phone call on BT paired device + * \arg DOMString phoneNumber Phone number to invoke call to + * \arg PhoneErrorCallback errorCallback. Callback if an error has occurred + **/ + invokeCall(DOMString phoneNumber, PhoneErrorCallback errorCallback); + + /** + * \brief Answer incoming phone call. + * \arg PhoneErrorCallback errorCallback. Callback if an error has occurred + **/ + answerCall(PhoneErrorCallback errorCallback); + + /** + * \brief Hangup/End incoming/active phone call + * \arg PhoneErrorCallback errorCallback. Callback if an error has occurred + **/ + hangupCall(PhoneErrorCallback errorCallback); + + /** + * \brief Mute/UnMute active phone call on BT paired device + * \arg boolean mute Specifies whether the phone call should be muted, or unmuted. + **/ + muteCall(boolean mute); + + /** + * \brief Retrieve contacts from BT paired device + * \arg GetContactsCallback successCallback. Callback with the result of the method call + * \arg PhoneErrorCallback errorCallback. Callback if an error has occurred + **/ + getContacts(GetContactsCallback successCallback, PhoneErrorCallback errorCallback); + + /** + * \brief Get call history of given direction (DIALED/MISSED/RECEIVED) + * \arg DOMString direction An attribute to indicate the type of CallHistory to retrieve ("DIALED", "MISSED", "RECEIVED") + * \arg GetCallHistoryCallback successCallback. Callback with the result of the method call + * \arg PhoneErrorCallback errorCallback. Callback if an error has occurred + **/ + getCallHistory(DOMString direction, GetCallHistoryCallback successCallback, PhoneErrorCallback errorCallback); +}; + +}; + diff --git a/src/config.xml b/src/config.xml new file mode 100644 index 0000000..d561d4e --- /dev/null +++ b/src/config.xml @@ -0,0 +1,16 @@ + + + + libwrt-plugins-tizen-phone.so + phone.install.uri + INTEL plugin group + INTEL certificate authority + AAAABBBBCCCCDDDEEEE0000 + + + http://tizen.org/privilege/phone + phone + + + + diff --git a/src/plugin_initializer.cpp b/src/plugin_initializer.cpp new file mode 100644 index 0000000..1dc3189 --- /dev/null +++ b/src/plugin_initializer.cpp @@ -0,0 +1,77 @@ +// +// Tizen Web Device API +// Copyright (c) 2013 Intel Corporation +// +// 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 +#include +#include "JSPhone.h" +#include + +namespace DeviceAPI { +namespace Phone { + +using namespace WrtDeviceApis; +using namespace WrtDeviceApis::Commons; + +class_definition_options_t ConstructorClassOptions = +{ + JS_INTERFACE, + CREATE_INSTANCE, + NONE_NOTICE, + USE_OVERLAYED, + NULL, + NULL, + NULL +}; + +void on_widget_start_callback(int widgetId) { + LoggerD("[TIZEN\\Phone] on_widget_start_callback ("< + + + + + -- 2.7.4