diff options
author | Guilherme Lepsch <lepsch@expertisesolutions.com.br> | 2015-07-02 16:50:57 -0300 |
---|---|---|
committer | Felipe Magno de Almeida <felipe@expertisesolutions.com.br> | 2015-10-22 16:23:59 -0200 |
commit | b030e01b2f03b5ef3218dcbf9ecea3d958ac56ea (patch) | |
tree | 5d83c37df5b2618bb1ca479655549886cacf7d01 | |
parent | b1600b2daa0d5de62b5f738d7845fd512aa6bfba (diff) | |
download | efl-b030e01b2f03b5ef3218dcbf9ecea3d958ac56ea.tar.gz |
Ecordova eolification and tizen implementation
152 files changed, 17607 insertions, 1 deletions
diff --git a/cmakeconfig/EcordovaConfig.cmake.in b/cmakeconfig/EcordovaConfig.cmake.in new file mode 100644 index 0000000000..9ca947c004 --- /dev/null +++ b/cmakeconfig/EcordovaConfig.cmake.in @@ -0,0 +1,32 @@ +# - Try to find ecordova +# Once done this will define +# ECORDOVA_FOUND - System has ecordova +# ECORDOVA_INCLUDE_DIRS - The ecordova include directories +# ECORDOVA_LIBRARIES - The libraries needed to use ecordova +# ECORDOVA_DEFINITIONS - Compiler switches required for using ecordova + +set(MY_PKG ecordova) + +find_package(PkgConfig) +if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER "2.8.1") + # "QUIET" was introduced in 2.8.2 + set(_QUIET QUIET) +endif () +pkg_check_modules(PC_LIBECORDOVA ${_QUIET} ${MY_PKG}) + +find_library(ECORDOVA_LIBRARY + NAMES ${PC_LIBECORDOVA_LIBRARIES} + HINTS ${PC_LIBECORDOVA_LIBDIR} ${PC_LIBECORDOVA_LIBRARY_DIRS} ) + +set(ECORDOVA_DEFINITIONS ${PC_LIBECORDOVA_CFLAGS_OTHER}) +set(ECORDOVA_LIBRARIES ${ECORDOVA_LIBRARY}) +set(ECORDOVA_INCLUDE_DIRS ${PC_LIBECORDOVA_INCLUDE_DIRS}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set ECORDOVA_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(${MY_PKG} DEFAULT_MSG + ECORDOVA_LIBRARIES ECORDOVA_INCLUDE_DIRS) + +mark_as_advanced(ECORDOVA_INCLUDE_DIRS ECORDOVA_LIBRARY ECORDOVA_LIBRARIES ECORDOVA_DEFINITIONS) + diff --git a/cmakeconfig/EcordovaCxxConfig.cmake.in b/cmakeconfig/EcordovaCxxConfig.cmake.in new file mode 100644 index 0000000000..ebd8414fb4 --- /dev/null +++ b/cmakeconfig/EcordovaCxxConfig.cmake.in @@ -0,0 +1,30 @@ +# - Try to find ecordova +# Once done this will define +# ECORDOVA_CXX_FOUND - System has ecordova +# ECORDOVA_CXX_INCLUDE_DIRS - The ecordova include directories +# ECORDOVA_CXX_LIBRARIES - The libraries needed to use ecordova +# ECORDOVA_CXX_DEFINITIONS - Compiler switches required for using ecordova + +set(MY_PKG ecordova_cxx) + +find_package(PkgConfig) +if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_GREATER "2.8.1") + # "QUIET" was introduced in 2.8.2 + set(_QUIET QUIET) +endif () +pkg_check_modules(PC_ECORDOVA_CXX ${_QUIET} ${MY_PKG}) +find_library(ECORDOVA_CXX_LIBRARY + NAMES ${PC_ECORDOVA_CXX_LIBRARIES} + HINTS ${PC_ECORDOVA_CXX_LIBDIR} ${PC_ECORDOVA_CXX_LIBRARY_DIRS} ) + +set(ECORDOVA_CXX_DEFINITIONS ${PC_ECORDOVA_CXX_CFLAGS_OTHER}) +set(ECORDOVA_CXX_LIBRARIES ${ECORDOVA_CXX_LIBRARY}) +set(ECORDOVA_CXX_INCLUDE_DIRS ${PC_ECORDOVA_CXX_INCLUDE_DIRS}) + +include(FindPackageHandleStandardArgs) +# handle the QUIETLY and REQUIRED arguments and set ECORDOVA_CXX_FOUND to TRUE +# if all listed variables are TRUE +find_package_handle_standard_args(${MY_PKG} DEFAULT_MSG + ECORDOVA_CXX_LIBRARIES ECORDOVA_CXX_INCLUDE_DIRS) + +mark_as_advanced(ECORDOVA_CXX_INCLUDE_DIRS ECORDOVA_CXX_LIBRARY ECORDOVA_CXX_LIBRARIES ECORDOVA_CXX_DEFINITIONS) diff --git a/configure.ac b/configure.ac index 929a088783..c6f0fbaabd 100644 --- a/configure.ac +++ b/configure.ac @@ -3911,7 +3911,7 @@ EFL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [emile]) ECORE_EVAS_MODULE([extn], [${want_ecore_evas_extn}]) ECORE_EVAS_MODULE([ews], [yes]) ECORE_EVAS_MODULE([fb], [${want_fb}]) -ECORE_EVAS_MODULE([drm], [${want_drm}], +ECORE_EVAS_MODULE([drm], [${want_drm}], [EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_drm}], [ecore-drm])]) ECORE_EVAS_MODULE([gl-drm], [${want_gl_drm}], [EFL_OPTIONAL_INTERNAL_DEPEND_PKG([ECORE_EVAS], [${want_gl_drm}], [ecore-drm])]) @@ -4581,6 +4581,93 @@ EFL_ADD_LIBS([ELOCATION], [-lm]) EFL_LIB_END([Elocation]) #### End of Elocation + + +#### Ecordova +AC_ARG_WITH([ecordova], + [AS_HELP_STRING([--with-ecordova=tizen|none],[ecordova implementation: tizen or none. @<:@default=none@:>@])], + [build_ecordova=${withval}], + [build_ecordova="none"]) + +want_ecordova_tizen="no" +want_ecordova_none="no" +want_ecordova="no" +case "${build_ecordova}" in + tizen) + want_ecordova_tizen="yes" + want_ecordova="yes" + ;; + none) + want_ecordova_none="yes" + ;; + *) + AC_MSG_ERROR([Unknown build ecordova --with-ecordova=${build_ecordova}]) + ;; +esac + +AM_CONDITIONAL([HAVE_ECORDOVA], [test "x${want_ecordova}" = "xyes"]) + +EFL_LIB_START_OPTIONAL([Ecordova], [test "x${want_ecordova}" = "xyes"]) + +### Additional options to configure + +### Default values + +### Checks for programs + +### Checks for libraries + +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [eina]) +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [eo]) +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [efl]) +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [ecore]) +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [ecore-evas]) +EFL_INTERNAL_DEPEND_PKG([ECORDOVA], [ecore-file]) + +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_VCONF], [vconf]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_CONTACTS_SERVICE], [contacts-service2]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_INFO], [capi-system-info]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_SENSOR], [capi-system-sensor]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_LOCATION_MANAGER], [capi-location-manager]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_HAPTIC], [capi-system-haptic]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_NETWORK_CONNECTION], [capi-network-connection]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_MEDIA_METADATA_EXTRACTOR], [capi-media-metadata-extractor]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_MEDIA_PLAYER], [capi-media-player]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_MEDIA_RECORDER], [capi-media-recorder]) +EFL_OPTIONAL_DEPEND_PKG([ECORDOVA], [${want_ecordova_tizen}], [TIZEN_BASE_UTILS_I18N], [capi-base-utils-i18n]) + +EFL_ADD_LIBS([ECORDOVA], [-lm]) + +EFL_EVAL_PKGS([ECORDOVA]) + +### Checks for header files + +### Checks for types + +### Checks for structures + +### Checks for compiler characteristics + +### Checks for linker characteristics + +### Checks for library functions + +EFL_LIB_END_OPTIONAL([Ecordova]) + +#### End of Ecordova + + +#### Ecordova CXX +EFL_LIB_START_OPTIONAL([Ecordova_Cxx], [test "x${want_ecordova}" = "xyes"]) + +EFL_EVAL_PKGS([ECORDOVA_CXX]) + +EFL_LIB_END_OPTIONAL([Ecordova_Cxx]) +#### End of Ecordova CXX + + + + ### Add Wayland server library if test is enabled if test "x${want_tests}" = "xyes" -a "x${want_wayland}" = "xyes"; then EFL_DEPEND_PKG([ECORE_WAYLAND_SRV], [WAYLAND], [wayland-server >= 1.8.0]) @@ -4769,6 +4856,8 @@ pc/ethumb.pc pc/ethumb_client.pc pc/elocation.pc pc/elua.pc +pc/ecordova.pc +pc/ecordova-cxx.pc dbus-services/org.enlightenment.Ethumb.service systemd-services/ethumb.service $po_makefile_in @@ -4818,6 +4907,10 @@ cmakeconfig/EluaConfig.cmake cmakeconfig/EluaConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in cmakeconfig/EmileConfig.cmake cmakeconfig/EmileConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in +cmakeconfig/EcordovaConfig.cmake +cmakeconfig/EcordovaConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in +cmakeconfig/EcordovaCxxConfig.cmake +cmakeconfig/EcordovaCxxConfigVersion.cmake:cmakeconfig/EFLConfigVersion.cmake.in ]) AC_OUTPUT @@ -4932,6 +5025,7 @@ echo "Emotion.........: yes (${features_emotion})" echo "Ethumb..........: yes" echo "Ethumb_Client...: yes" echo "Elua............: $have_elua" +echo "Ecordova........: ${efl_lib_optional_ecordova} (${COLOR_OTHER}${build_ecordova}${COLOR_RESET})" if test "${build_tests}" = "none"; then echo "Tests...........: no" elif test "${build_tests}" = "auto"; then diff --git a/pc/ecordova-cxx.pc.in b/pc/ecordova-cxx.pc.in new file mode 100644 index 0000000000..8cfae25b2d --- /dev/null +++ b/pc/ecordova-cxx.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Ecordova C++ +Description: A C++ binding for the Ecordova library +Requires.private: @requirements_pc_ecordova@ @requirements_pc_eo@ +Version: @VERSION@ +Libs: -L${libdir} -lecordova @requirements_public_libs_ecordova@ @requirements_public_libs_eo@ +Libs.private: @requirements_libs_ecordova@ @requirements_libs_eo@ +Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/eo-@VMAJ@ -I${includedir}/ecordova-@VMAJ@ -I${includedir}/ecordova-cxx-@VMAJ@ diff --git a/pc/ecordova.pc.in b/pc/ecordova.pc.in new file mode 100644 index 0000000000..87c091c404 --- /dev/null +++ b/pc/ecordova.pc.in @@ -0,0 +1,12 @@ +prefix=@prefix@ +exec_prefix=@exec_prefix@ +libdir=@libdir@ +includedir=@includedir@ + +Name: Ecordova +Description: Cordova API eolification +Requires.private: @requirements_pc_ecordova@ +Version: @VERSION@ +Libs: -L${libdir} -lecordova @requirements_public_libs_ecordova@ +Libs.private: @requirements_libs_ecordova@ +Cflags: -I${includedir}/efl-@VMAJ@ -I${includedir}/ecordova-@VMAJ@ diff --git a/src/Makefile.am b/src/Makefile.am index dccc538af2..28e8e83fbb 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -74,6 +74,8 @@ include Makefile_Eio_Cxx.am include Makefile_Elua.am include Makefile_Elocation.am +include Makefile_Ecordova.am +include Makefile_Ecordova_Cxx.am .PHONY: benchmark examples diff --git a/src/Makefile_Ecordova.am b/src/Makefile_Ecordova.am new file mode 100644 index 0000000000..1315925e53 --- /dev/null +++ b/src/Makefile_Ecordova.am @@ -0,0 +1,189 @@ +if HAVE_ECORDOVA +### Library + +ecordova_eolian_files = \ +lib/ecordova/ecordova_systeminfo.eo \ +lib/ecordova/ecordova_batterystatus.eo \ +lib/ecordova/ecordova_console.eo \ +lib/ecordova/ecordova_contacts.eo \ +lib/ecordova/ecordova_contact.eo \ +lib/ecordova/ecordova_contactaddress.eo \ +lib/ecordova/ecordova_contactfield.eo \ +lib/ecordova/ecordova_contactname.eo \ +lib/ecordova/ecordova_contactorganization.eo \ +lib/ecordova/ecordova_device.eo \ +lib/ecordova/ecordova_devicemotion.eo \ +lib/ecordova/ecordova_deviceorientation.eo \ +lib/ecordova/ecordova_dialogs.eo \ +lib/ecordova/ecordova_geolocation.eo \ +lib/ecordova/ecordova_globalization.eo \ +lib/ecordova/ecordova_inappbrowser.eo \ +lib/ecordova/ecordova_media.eo \ +lib/ecordova/ecordova_networkinformation.eo \ +lib/ecordova/ecordova_splashscreen.eo \ +lib/ecordova/ecordova_vibration.eo \ +lib/ecordova/ecordova_file.eo \ +lib/ecordova/ecordova_filewriter.eo \ +lib/ecordova/ecordova_filereader.eo \ +lib/ecordova/ecordova_filesystem.eo \ +lib/ecordova/ecordova_entry.eo \ +lib/ecordova/ecordova_directoryentry.eo \ +lib/ecordova/ecordova_directoryreader.eo \ +lib/ecordova/ecordova_fileentry.eo \ +lib/ecordova/ecordova_mediafile.eo \ +lib/ecordova/ecordova_filetransfer.eo \ +lib/ecordova/ecordova_capture.eo + +ecordova_eolian_c = $(ecordova_eolian_files:%.eo=%.eo.c) +ecordova_eolian_h = $(ecordova_eolian_files:%.eo=%.eo.h) + +BUILT_SOURCES += \ + $(ecordova_eolian_c) \ + $(ecordova_eolian_h) + +CLEANFILES += \ + $(ecordova_eolian_c) \ + $(ecordova_eolian_h) + +ecordovaeolianfilesdir = $(datadir)/eolian/include/ecordova-@VMAJ@ +ecordovaeolianfiles_DATA = \ + $(ecordova_eolian_files) + +EXTRA_DIST += \ + ${ecordovaeolianfiles_DATA} + +lib_LTLIBRARIES += lib/ecordova/libecordova.la + +installed_ecordovamainheadersdir = $(includedir)/ecordova-@VMAJ@ +dist_installed_ecordovamainheaders_DATA = \ +lib/ecordova/Ecordova.h \ +lib/ecordova/Ecordova_Common.h \ +lib/ecordova/Ecordova_Eo.h + +nodist_installed_ecordovamainheaders_DATA = \ + $(ecordova_eolian_h) + +lib_ecordova_libecordova_la_SOURCES = \ +lib/ecordova/ecordova_main.c \ +lib/ecordova/ecordova_private.h \ +lib/ecordova/ecordova_systeminfo.c \ +lib/ecordova/ecordova_batterystatus.c \ +lib/ecordova/ecordova_console.c \ +lib/ecordova/ecordova_contacts.c \ +lib/ecordova/ecordova_contact.c \ +lib/ecordova/ecordova_contactaddress.c \ +lib/ecordova/ecordova_contactfield.c \ +lib/ecordova/ecordova_contactname.c \ +lib/ecordova/ecordova_contactorganization.c \ +lib/ecordova/ecordova_contacts_record_utils.c \ +lib/ecordova/ecordova_device.c \ +lib/ecordova/ecordova_devicemotion.c \ +lib/ecordova/ecordova_deviceorientation.c \ +lib/ecordova/ecordova_dialogs.c \ +lib/ecordova/ecordova_geolocation.c \ +lib/ecordova/ecordova_globalization.c \ +lib/ecordova/ecordova_inappbrowser.c \ +lib/ecordova/ecordova_media.c \ +lib/ecordova/ecordova_networkinformation.c \ +lib/ecordova/ecordova_splashscreen.c \ +lib/ecordova/ecordova_vibration.c \ +lib/ecordova/ecordova_file.c \ +lib/ecordova/ecordova_filewriter.c \ +lib/ecordova/ecordova_filereader.c \ +lib/ecordova/ecordova_filesystem.c \ +lib/ecordova/ecordova_entry.c \ +lib/ecordova/ecordova_directoryentry.c \ +lib/ecordova/ecordova_directoryreader.c \ +lib/ecordova/ecordova_fileentry.c \ +lib/ecordova/ecordova_mediafile.c \ +lib/ecordova/ecordova_filetransfer.c \ +lib/ecordova/ecordova_capture.c + +lib_ecordova_libecordova_la_CPPFLAGS = -I$(top_builddir)/src/lib/efl @ECORDOVA_CFLAGS@ @EFL_CFLAGS@ +lib_ecordova_libecordova_la_LIBADD = @ECORDOVA_LIBS@ @EFL_LIBS@ +lib_ecordova_libecordova_la_DEPENDENCIES = @ECORDOVA_INTERNAL_LIBS@ @EFL_INTERNAL_LIBS@ +lib_ecordova_libecordova_la_LDFLAGS = @EFL_LTLIBRARY_FLAGS@ + +### Unit tests + +if EFL_ENABLE_TESTS + +check_PROGRAMS += tests/ecordova/ecordova_suite +TESTS += tests/ecordova/ecordova_suite + +tests_ecordova_ecordova_suite_SOURCES = \ +tests/ecordova/ecordova_suite.c \ +tests/ecordova/ecordova_suite.h \ +tests/ecordova/ecordova_contacts_test.c \ +tests/ecordova/ecordova_contacts_test.h \ +tests/ecordova/ecordova_device_test.c \ +tests/ecordova/ecordova_device_test.h \ +tests/ecordova/ecordova_devicemotion_test.c \ +tests/ecordova/ecordova_devicemotion_test.h \ +tests/ecordova/ecordova_deviceorientation_test.c \ +tests/ecordova/ecordova_deviceorientation_test.h \ +tests/ecordova/ecordova_geolocation_test.c \ +tests/ecordova/ecordova_geolocation_test.h \ +tests/ecordova/ecordova_batterystatus_test.c \ +tests/ecordova/ecordova_batterystatus_test.h \ +tests/ecordova/ecordova_console_test.c \ +tests/ecordova/ecordova_console_test.h \ +tests/ecordova/ecordova_filetransfer_test.c \ +tests/ecordova/ecordova_filetransfer_test.h \ +tests/ecordova/ecordova_media_test.c \ +tests/ecordova/ecordova_media_test.h \ +tests/ecordova/ecordova_networkinformation_test.c \ +tests/ecordova/ecordova_networkinformation_test.h \ +tests/ecordova/ecordova_vibration_test.c \ +tests/ecordova/ecordova_vibration_test.h \ +tests/ecordova/ecordova_directoryreader_test.c \ +tests/ecordova/ecordova_directoryreader_test.h \ +tests/ecordova/ecordova_directoryentry_test.c \ +tests/ecordova/ecordova_directoryentry_test.h \ +tests/ecordova/ecordova_entry_test.c \ +tests/ecordova/ecordova_entry_test.h \ +tests/ecordova/ecordova_file_test.c \ +tests/ecordova/ecordova_file_test.h \ +tests/ecordova/ecordova_fileentry_test.c \ +tests/ecordova/ecordova_fileentry_test.h \ +tests/ecordova/ecordova_filereader_test.c \ +tests/ecordova/ecordova_filereader_test.h \ +tests/ecordova/ecordova_filewriter_test.c \ +tests/ecordova/ecordova_filewriter_test.h \ +tests/ecordova/ecordova_mediafile_test.c \ +tests/ecordova/ecordova_mediafile_test.h \ +tests/ecordova/ecordova_globalization_test.c \ +tests/ecordova/ecordova_globalization_test.h + +tests_ecordova_ecordova_suite_CPPFLAGS = -I$(top_builddir)/src/lib/efl \ +-DTESTS_SRC_DIR=\"$(abs_top_srcdir)/src/tests/ecordova\" \ +-DTESTS_BUILD_DIR=\"$(abs_top_builddir)/src/tests/ecordova\" \ +@CHECK_CFLAGS@ \ +@ECORDOVA_CFLAGS@ @EFL_CFLAGS@ + +tests_ecordova_ecordova_suite_LDADD = \ +@CHECK_LIBS@ \ +@USE_ECORDOVA_LIBS@ \ +@USE_EFL_LIBS@ + +tests_ecordova_ecordova_suite_DEPENDENCIES = \ +@USE_ECORDOVA_INTERNAL_LIBS@ + +endif + +EXTRA_DIST += $(ECORDOVA_DATA_FILES) + +if HAVE_ELUA + +ecordova_eolian_lua = $(ecordova_eolian_files:%.eo=%.eo.lua) + +generated_ecordova_lua_all = $(ecordova_eolian_lua) + +CLEANFILES += $(generated_ecordova_lua_all) + +installed_ecordovaluadir = $(datadir)/elua/modules/ecordova +nodist_installed_ecordovalua_DATA = $(generated_ecordova_lua_all) + +endif + +endif diff --git a/src/Makefile_Ecordova_Cxx.am b/src/Makefile_Ecordova_Cxx.am new file mode 100644 index 0000000000..764a55f5fa --- /dev/null +++ b/src/Makefile_Ecordova_Cxx.am @@ -0,0 +1,22 @@ +if HAVE_CXX11 + +### Generated headers + +generated_ecordova_cxx_bindings = $(ecordova_eolian_files:%.eo=%.eo.hh) + +lib/ecordova/Ecordova.hh: $(generated_ecordova_cxx_bindings) + @echo @ECHO_E@ "#ifndef EFL_CXX_ECORDOVA_HH\n#define EFL_CXX_ECORDOVA_HH\n" > $(top_builddir)/src/lib/ecordova/Ecordova.hh + @echo @ECHO_E@ "#ifdef EFL_BETA_API_SUPPORT" >> $(top_builddir)/src/lib/ecordova/Ecordova.hh + @for i in $(generated_ecordova_cxx_bindings); do echo "#include <$$(basename $$i)>" >> $(top_builddir)/src/lib/ecordova/Ecordova.hh; done + @echo @ECHO_E@ "#endif\n\n#endif\n" >> $(top_builddir)/src/lib/ecordova/Ecordova.hh + +generated_ecordova_cxx_all = \ + $(generated_ecordova_cxx_bindings) \ + lib/ecordova/Ecordova.hh + +CLEANFILES += $(generated_ecordova_cxx_all) + +installed_ecordovacxxmainheadersdir = $(includedir)/ecordova-cxx-@VMAJ@/ +nodist_installed_ecordovacxxmainheaders_DATA = $(generated_ecordova_cxx_all) + +endif diff --git a/src/lib/ecordova/Ecordova.h b/src/lib/ecordova/Ecordova.h new file mode 100644 index 0000000000..46396e02af --- /dev/null +++ b/src/lib/ecordova/Ecordova.h @@ -0,0 +1,50 @@ +#ifndef _ECORDOVA_H +#define _ECORDOVA_H + +#include <Ecore.h> +#include <Efl.h> +#include <Efl_Config.h> + +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORDOVA_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORDOVA_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "Ecordova_Common.h" +#ifdef EFL_EO_API_SUPPORT +#include "Ecordova_Eo.h" +#endif + +#ifdef __cplusplus +} +#endif + +#undef EAPI +#define EAPI + +#endif diff --git a/src/lib/ecordova/Ecordova_Common.h b/src/lib/ecordova/Ecordova_Common.h new file mode 100644 index 0000000000..b1e45cf1a9 --- /dev/null +++ b/src/lib/ecordova/Ecordova_Common.h @@ -0,0 +1,13 @@ +/** + * @brief Initialize ecordova. + * + * @return 1 or greater on success, 0 otherwise + */ +EAPI int ecordova_init(void); + +/** + * @brief Shutdown ecordova. + * + * @return 0 if ecordova shuts down, greater than 0 otherwise. + */ +EAPI int ecordova_shutdown(void); diff --git a/src/lib/ecordova/Ecordova_Eo.h b/src/lib/ecordova/Ecordova_Eo.h new file mode 100644 index 0000000000..d1da074fd3 --- /dev/null +++ b/src/lib/ecordova/Ecordova_Eo.h @@ -0,0 +1,34 @@ +#include <Efl.h> + +typedef Eo Ecordova_DirectoryReader; + +#include <ecordova_batterystatus.eo.h> +#include <ecordova_console.eo.h> +#include <ecordova_contactaddress.eo.h> +#include <ecordova_contactfield.eo.h> +#include <ecordova_contactname.eo.h> +#include <ecordova_contactorganization.eo.h> +#include <ecordova_contact.eo.h> +#include <ecordova_contacts.eo.h> +#include <ecordova_device.eo.h> +#include <ecordova_devicemotion.eo.h> +#include <ecordova_deviceorientation.eo.h> +#include <ecordova_dialogs.eo.h> +#include <ecordova_geolocation.eo.h> +#include <ecordova_globalization.eo.h> +#include <ecordova_inappbrowser.eo.h> +#include <ecordova_media.eo.h> +#include <ecordova_networkinformation.eo.h> +#include <ecordova_splashscreen.eo.h> +#include <ecordova_vibration.eo.h> +#include <ecordova_file.eo.h> +#include <ecordova_filewriter.eo.h> +#include <ecordova_filereader.eo.h> +#include <ecordova_filesystem.eo.h> +#include <ecordova_entry.eo.h> +#include <ecordova_directoryentry.eo.h> +#include <ecordova_directoryreader.eo.h> +#include <ecordova_fileentry.eo.h> +#include <ecordova_mediafile.eo.h> +#include <ecordova_filetransfer.eo.h> +#include <ecordova_capture.eo.h> diff --git a/src/lib/ecordova/ecordova_batterystatus.c b/src/lib/ecordova/ecordova_batterystatus.c new file mode 100644 index 0000000000..a864e733df --- /dev/null +++ b/src/lib/ecordova/ecordova_batterystatus.c @@ -0,0 +1,196 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_batterystatus_private.h" +#include "ecordova_systeminfo.eo.h" + +#include <vconf.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_BATTERYSTATUS_CLASS +#define MY_CLASS_NAME "Ecordova_BatteryStatus" + +#define STATUS_CRITICAL 5 +#define STATUS_LOW 20 + +static Eina_Bool _add_cb(void *, Eo *, const Eo_Event_Description *, void *); +static Eina_Bool _del_cb(void *, Eo *, const Eo_Event_Description *, void *); +static void _start(Ecordova_BatteryStatus_Data *); +static void _stop(Ecordova_BatteryStatus_Data *); +static Eina_Bool _on_battery_changed(void *, Eo *, const Eo_Event_Description *, void *); +static bool _fetch_level(int *); +static bool _fetch_charging_is(Eina_Bool *); + +static Eo_Base * +_ecordova_batterystatus_eo_base_constructor(Eo *obj, + Ecordova_BatteryStatus_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_batterystatus_constructor(Eo *obj, + Ecordova_BatteryStatus_Data *pd) +{ + DBG("(%p)", obj); + + eo_do(obj, eo_event_callback_add(EO_EV_CALLBACK_ADD, _add_cb, pd)); + eo_do(obj, eo_event_callback_add(EO_EV_CALLBACK_DEL, _del_cb, pd)); +} + +static void +_ecordova_batterystatus_eo_base_destructor(Eo *obj, + Ecordova_BatteryStatus_Data *pd) +{ + DBG("(%p)", obj); + + if (pd->callback_ref_count) + _stop(pd); + + eo_do(obj, eo_event_callback_del(EO_EV_CALLBACK_ADD, _add_cb, pd)); + eo_do(obj, eo_event_callback_del(EO_EV_CALLBACK_DEL, _del_cb, pd)); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Eina_Bool +_add_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc, + void *event_info) +{ + Ecordova_BatteryStatus_Data *pd = (Ecordova_BatteryStatus_Data*)data; + const Eo_Callback_Array_Item *array = (const Eo_Callback_Array_Item*)event_info; + + for (size_t i = 0; (desc = array[i].desc); ++i) + { + if (ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_STATUS == desc || + ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_CRITICAL == desc || + ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_LOW == desc) + { + ++pd->callback_ref_count; + if (1 == pd->callback_ref_count) + _start(pd); + } + } + + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_del_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc, + void *event_info) +{ + Ecordova_BatteryStatus_Data *pd = (Ecordova_BatteryStatus_Data*)data; + const Eo_Callback_Array_Item *array = (const Eo_Callback_Array_Item*)event_info; + + for (size_t i = 0; (desc = array[i].desc); ++i) + { + if (ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_STATUS == desc || + ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_CRITICAL == desc || + ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_LOW == desc) + --pd->callback_ref_count; + } + + if (0 == pd->callback_ref_count) + _stop(pd); + + return EO_CALLBACK_CONTINUE; +} + +static void +_start(Ecordova_BatteryStatus_Data *pd) +{ + eo_do(_ecordova_systeminfo, + eo_event_callback_add(ECORDOVA_SYSTEMINFO_EVENT_BATTERY_CHANGED, + _on_battery_changed, + pd)); +} + +static void +_stop(Ecordova_BatteryStatus_Data *pd) +{ + free(pd->info); + pd->info = NULL; + + eo_do(_ecordova_systeminfo, + eo_event_callback_del(ECORDOVA_SYSTEMINFO_EVENT_BATTERY_CHANGED, + _on_battery_changed, + pd)); +} + +static bool +_fetch_level(int *level) +{ + int value = 0; + bool ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_CAPACITY, &value) == 0; + if (ret) + ERR("%s", "Failed to get battery capacity"); + + *level = value; + return ret; +} + +static bool +_fetch_charging_is(Eina_Bool *charging_is) +{ + int value = 0; + int ret = vconf_get_int(VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, &value) == 0; + if (!ret) + ERR("%s", "Failed to get battery change"); + + *charging_is = (0 != value) ? EINA_TRUE : EINA_FALSE; + return ret; +} + +static Eina_Bool +_on_battery_changed(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Ecordova_BatteryStatus_Data *pd = (Ecordova_BatteryStatus_Data*)data; + + Ecordova_BatteryStatus_EventInfo info; + if (!_fetch_level(&info.level) || + !_fetch_charging_is(&info.plugged_is)) + return EO_CALLBACK_CONTINUE; + + if (!pd->info || + pd->info->level != info.level || + pd->info->plugged_is != info.plugged_is) + { + eo_do(pd->obj, eo_event_callback_call + (ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_STATUS, &info)); + + if (!info.plugged_is) + { + if ((!pd->info || pd->info->level > STATUS_CRITICAL) && + info.level <= STATUS_CRITICAL) + eo_do(pd->obj, eo_event_callback_call + (ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_CRITICAL, &info)); + else + if ((!pd->info || pd->info->level > STATUS_LOW) && + info.level <= STATUS_LOW) + eo_do(pd->obj, eo_event_callback_call + (ECORDOVA_BATTERYSTATUS_EVENT_BATTERY_LOW, &info)); + } + + if (!pd->info) + pd->info = malloc(sizeof(Ecordova_BatteryStatus_EventInfo)); + *pd->info = info; + } + return EO_CALLBACK_CONTINUE; +} + +#include "ecordova_batterystatus.eo.c" diff --git a/src/lib/ecordova/ecordova_batterystatus.eo b/src/lib/ecordova/ecordova_batterystatus.eo new file mode 100644 index 0000000000..2dcb511c73 --- /dev/null +++ b/src/lib/ecordova/ecordova_batterystatus.eo @@ -0,0 +1,51 @@ +struct Ecordova.BatteryStatus.EventInfo { + [[The battery status event info is passed an object that contains two + properties. + ]] + + level: int; + [[The percentage of battery charge (0-100).]] + + plugged_is: bool; + [[A boolean that indicates whether the device is plugged in.]] +} + +class Ecordova.BatteryStatus (Eo.Base) { + [[Ecordova Battery Plugin + Plugin ID: org.apache.cordova.battery-status + http://plugins.cordova.io/#/package/org.apache.cordova.battery-status + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_BatteryStatus constructor. + @.constructor + + @since 2.3 + ]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + battery,status: const(Ecordova.BatteryStatus.EventInfo)*; + [[This event fires when the percentage of battery charge changes by at + least 1 percent, or if the device is plugged in or unplugged. + ]] + + battery,critical: const(Ecordova.BatteryStatus.EventInfo)*; + [[This event fires when the percentage of battery charge has reached + the critical battery threshold. The value is device-specific. + ]] + + battery,low: const(Ecordova.BatteryStatus.EventInfo)*; + [[This event fires when the percentage of battery charge has reached + the low battery threshold, device-specific value. + ]] + } +} diff --git a/src/lib/ecordova/ecordova_batterystatus_private.h b/src/lib/ecordova/ecordova_batterystatus_private.h new file mode 100644 index 0000000000..f9ced32f04 --- /dev/null +++ b/src/lib/ecordova/ecordova_batterystatus_private.h @@ -0,0 +1,19 @@ +#ifndef _ECORDOVA_BATTERYSTATUS_PRIVATE_H +#define _ECORDOVA_BATTERYSTATUS_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_batterystatus.eo.h" + +typedef struct _Ecordova_BatteryStatus_Data Ecordova_BatteryStatus_Data; + +/** + * Ecordova.BatteryStatus private data + */ +struct _Ecordova_BatteryStatus_Data +{ + Eo *obj; + int callback_ref_count; + Ecordova_BatteryStatus_EventInfo *info; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_capture.c b/src/lib/ecordova/ecordova_capture.c new file mode 100644 index 0000000000..c17b1dad12 --- /dev/null +++ b/src/lib/ecordova/ecordova_capture.c @@ -0,0 +1,60 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_capture_private.h" + +#define MY_CLASS ECORDOVA_CAPTURE_CLASS +#define MY_CLASS_NAME "Ecordova_Capture" + +static Eo_Base * +_ecordova_capture_eo_base_constructor(Eo *obj, Ecordova_Capture_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_capture_constructor(Eo *obj EINA_UNUSED, + Ecordova_Capture_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_capture_eo_base_destructor(Eo *obj, + Ecordova_Capture_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_capture_audio_capture(Eo *obj EINA_UNUSED, + Ecordova_Capture_Data *pd EINA_UNUSED, + const Ecordova_Capture_AudioOptions *options EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_capture_image_capture(Eo *obj EINA_UNUSED, + Ecordova_Capture_Data *pd EINA_UNUSED, + const Ecordova_Capture_ImageOptions *options EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_capture_video_capture(Eo *obj EINA_UNUSED, + Ecordova_Capture_Data *pd EINA_UNUSED, + const Ecordova_Capture_VideoOptions *options EINA_UNUSED) +{ + ERR("Not implemented."); +} + +#include "ecordova_capture.eo.c" diff --git a/src/lib/ecordova/ecordova_capture.eo b/src/lib/ecordova/ecordova_capture.eo new file mode 100644 index 0000000000..cef42940f0 --- /dev/null +++ b/src/lib/ecordova/ecordova_capture.eo @@ -0,0 +1,178 @@ +enum Ecordova.Capture.ErrorCode { + CAPTURE_INTERNAL_ERR, + [[The camera or microphone failed to capture image or sound.]] + + CAPTURE_APPLICATION_BUSY, + [[The camera or audio capture application is currently serving another capture request.]] + + CAPTURE_INVALID_ARGUMENT, + [[Invalid use of the API (e.g., the value of limit is less than one).]] + + CAPTURE_NO_MEDIA_FILES, + [[The user exits the camera or audio capture application before capturing anything.]] + + CAPTURE_NOT_SUPPORTED + [[The requested capture operation is not supported.]] + +} + +struct Ecordova.Capture.Error { + code: Ecordova.Capture.ErrorCode; [[One of the pre-defined error codes]] +} + +struct Ecordova.Capture.AudioOptions { + [[Encapsulates audio capture configuration options.]] + + limit: int; + [[The maximum number of audio clips the device user can record in a single + capture operation. The value must be greater than or equal to 1 (defaults to 1).]] + + duration: int; + [[The maximum duration of an audio sound clip, in seconds.]] +} + +struct Ecordova.Capture.ImageOptions { + [[Encapsulates image capture configuration options.]] + + limit: int; + [[The maximum number of images the user can capture in a single capture + operation. The value must be greater than or equal to 1 (defaults to 1).]] +} + +struct Ecordova.Capture.VideoOptions { + [[Encapsulates video capture configuration options.]] + + limit: int; + [[The maximum number of video clips the device's user can capture in a + single capture operation. The value must be greater than or equal to 1 (defaults to 1).]] + + duration: int; + [[The maximum duration of a video clip, in seconds.]] +} + +class Ecordova.Capture (Eo.Base) { + [[Ecordova Media-Capture Plugin + Plugin ID: org.apache.cordova.media-capture + http://plugins.cordova.io/#/package/org.apache.cordova.media-capture + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Capture constructor. + @.constructor + + @since 2.3 + ]] + } + audio_capture { + [[Start the audio recorder application and return information + about captured audio clip files. + + Starts an asynchronous operation to capture audio recordings + using the device's default audio recording application. The + operation allows the device user to capture multiple recordings + in a single session. + + The capture operation ends when either the user exits the audio + recording application, or the maximum number of recordings + specified by CaptureAudioOptions.limit is reached. If no limit + parameter value is specified, it defaults to one (1), and the + capture operation terminates after the user records a single + audio clip. + + When the capture operation finishes, the CaptureCallback + executes with an array of MediaFile objects describing each + captured audio clip file. If the user terminates the operation + before an audio clip is captured, the CaptureErrorCallback + executes with a CaptureError object, featuring the + CaptureError.CAPTURE_NO_MEDIA_FILES error code. + ]] + params { + options: const(Ecordova.Capture.AudioOptions)*; + } + } + image_capture { + [[Start the camera application and return information about + captured image files. + + Starts an asynchronous operation to capture images using the + device's camera application. The operation allows users to + capture more than one image in a single session. + + The capture operation ends either when the user closes the + camera application, or the maximum number of recordings + specified by CaptureAudioOptions.limit is reached. If no limit + value is specified, it defaults to one (1), and the capture + operation terminates after the user captures a single image. + + When the capture operation finishes, it invokes the CaptureCB + callback with an array of MediaFile objects describing each + captured image file. If the user terminates the operation before + capturing an image, the CaptureErrorCB callback executes with a + CaptureError object featuring a + CaptureError.CAPTURE_NO_MEDIA_FILES error code. + ]] + params { + options: const(Ecordova.Capture.ImageOptions)*; + } + } + video_capture { + [[Start the video recorder application and return information + about captured video clip files. + + Starts an asynchronous operation to capture video recordings + using the device's video recording application. The operation + allows the user to capture more than one recordings in a single + session. + + The capture operation ends when either the user exits the video + recording application, or the maximum number of recordings + specified by CaptureVideoOptions.limit is reached. If no limit + parameter value is specified, it defaults to one (1), and the + capture operation terminates after the user records a single + video clip. + + When the capture operation finishes, it the CaptureCB callback + executes with an array of MediaFile objects describing each + captured video clip file. If the user terminates the operation + before capturing a video clip, the CaptureErrorCB callback + executes with a CaptureError object featuring a + CaptureError.CAPTURE_NO_MEDIA_FILES error code. + ]] + params { + options: const(Ecordova.Capture.VideoOptions)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + success: const(array<MediaFile*>)*; + [[Invoked upon a successful media capture operation. + + This function executes after a successful capture operation + completes. At this point a media file has been captured, and either + the user has exited the media capture application, or the capture + limit has been reached. + + Each MediaFile object describes a captured media file. + ]] + + error: const(Ecordova.Capture.Error)*; + [[Invoked if an error occurs during a media capture operation. + + This function executes if an error occurs when trying to launch a + media capture operation. Failure scenarios include when the capture + application is busy, a capture operation is already taking place, or + the user cancels the operation before any media files are captured. + + This function executes with a CaptureError object containing an + appropriate error code. + ]] + } +} diff --git a/src/lib/ecordova/ecordova_capture_private.h b/src/lib/ecordova/ecordova_capture_private.h new file mode 100644 index 0000000000..bea0c4207a --- /dev/null +++ b/src/lib/ecordova/ecordova_capture_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_CAPTURE_PRIVATE_H +#define _ECORDOVA_CAPTURE_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Capture_Data Ecordova_Capture_Data; + +/** + * Ecordova.Capture private data + */ +struct _Ecordova_Capture_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_console.c b/src/lib/ecordova/ecordova_console.c new file mode 100644 index 0000000000..fd3c5ff408 --- /dev/null +++ b/src/lib/ecordova/ecordova_console.c @@ -0,0 +1,175 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_console_private.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONSOLE_CLASS +#define MY_CLASS_NAME "Ecordova_Console" + +static void _ecordova_console_level_log(Ecordova_Console_Data *, Ecordova_Console_LoggerLevel, const char *); + +static Eo_Base * +_ecordova_console_eo_base_constructor(Eo *obj, Ecordova_Console_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->logger_use = EINA_TRUE; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_console_constructor(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_console_eo_base_destructor(Eo *obj, + Ecordova_Console_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_Console_LoggerLevel +_ecordova_console_level_get(Eo *obj EINA_UNUSED, Ecordova_Console_Data *pd) +{ + return pd->level; +} + +static void +_ecordova_console_level_set(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + Ecordova_Console_LoggerLevel value) +{ + pd->level = value; +} + +static Eina_Bool +_ecordova_console_console_use_get(Eo *obj EINA_UNUSED, Ecordova_Console_Data *pd) +{ + return pd->console_use; +} + +static void +_ecordova_console_console_use_set(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + Eina_Bool value) +{ + pd->console_use = value; +} + +static Eina_Bool +_ecordova_console_logger_use_get(Eo *obj EINA_UNUSED, Ecordova_Console_Data *pd) +{ + return pd->logger_use; +} + +static void +_ecordova_console_logger_use_set(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + Eina_Bool value) +{ + pd->logger_use = value; +} + +static void +_ecordova_console_log(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + const char *message) +{ + _ecordova_console_level_log(pd, ECORDOVA_CONSOLE_LOGGERLEVEL_LOG, message); +} + +static void +_ecordova_console_error(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + const char *message) +{ + _ecordova_console_level_log(pd, ECORDOVA_CONSOLE_LOGGERLEVEL_ERROR, message); +} + +static void +_ecordova_console_warn(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + const char *message) +{ + _ecordova_console_level_log(pd, ECORDOVA_CONSOLE_LOGGERLEVEL_WARN, message); +} + +static void +_ecordova_console_info(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + const char *message) +{ + _ecordova_console_level_log(pd, ECORDOVA_CONSOLE_LOGGERLEVEL_INFO, message); +} + +static void +_ecordova_console_debug(Eo *obj EINA_UNUSED, + Ecordova_Console_Data *pd, + const char *message) +{ + _ecordova_console_level_log(pd, ECORDOVA_CONSOLE_LOGGERLEVEL_DEBUG, message); +} + +static void +_ecordova_console_level_log(Ecordova_Console_Data *pd, + Ecordova_Console_LoggerLevel level, + const char *message) +{ + EINA_SAFETY_ON_NULL_RETURN(message); + + if (level < 0 || pd->level >= ECORDOVA_CONSOLE_LOGGERLEVEL_LAST) + { + ERR("Invalid logging level: %d", level); + return; + } + + if (level > pd->level) return; + + if (pd->logger_use) + { + switch (level) + { + case ECORDOVA_CONSOLE_LOGGERLEVEL_LOG: + CRI("%s", message); + break; + case ECORDOVA_CONSOLE_LOGGERLEVEL_ERROR: + ERR("%s", message); + break; + case ECORDOVA_CONSOLE_LOGGERLEVEL_WARN: + WRN("%s", message); + break; + case ECORDOVA_CONSOLE_LOGGERLEVEL_INFO: + INF("%s", message); + break; + case ECORDOVA_CONSOLE_LOGGERLEVEL_DEBUG: + DBG("%s", message); + break; + default: break; // removes warning + } + } + + const char *level_str[ECORDOVA_CONSOLE_LOGGERLEVEL_LAST] = { + [ECORDOVA_CONSOLE_LOGGERLEVEL_LOG] = "", + [ECORDOVA_CONSOLE_LOGGERLEVEL_ERROR] = "ERROR: ", + [ECORDOVA_CONSOLE_LOGGERLEVEL_WARN] = "WARN: ", + [ECORDOVA_CONSOLE_LOGGERLEVEL_INFO] = "INFO: ", + [ECORDOVA_CONSOLE_LOGGERLEVEL_DEBUG] = "DEBUG: ", + }; + + if (pd->console_use) + printf("%s%s\n", level_str[level], message); +} + +#include "ecordova_console.eo.c" diff --git a/src/lib/ecordova/ecordova_console.eo b/src/lib/ecordova/ecordova_console.eo new file mode 100644 index 0000000000..208f3f961c --- /dev/null +++ b/src/lib/ecordova/ecordova_console.eo @@ -0,0 +1,106 @@ +enum Ecordova.Console.LoggerLevel { + [[Logging levels]] + LOG, + ERROR, + WARN, + INFO, + DEBUG, + LAST +} + +class Ecordova.Console (Eo.Base) { + [[Ecordova Console Plugin + Plugin ID: org.apache.cordova.console + http://plugins.cordova.io/#/package/org.apache.cordova.console + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Console constructor. + @.constructor + + @since 2.3 + ]] + } + log { + [[Logs a message at the LOG level.]] + params { + message: const(char)*; [[The log message]] + } + } + error { + [[Logs a message at the ERROR level.]] + params { + message: const(char)*; [[The log message]] + } + } + warn { + [[Logs a message at the WARN level.]] + params { + message: const(char)*; [[The log message]] + } + } + info { + [[Logs a message at the INFO level.]] + params { + message: const(char)*; [[The log message]] + } + } + debug { + [[Logs a message at the DEBUG level.]] + params { + message: const(char)*; [[The log message]] + } + } + @property level { + [[Getter/Setter for the logging level + + Returns the current logging level. + + When a value is passed, sets the logging level to that value. + The values should be one of the following constants: + CONSOLE_LOGGER_LEVEL_LOG + CONSOLE_LOGGER_LEVEL_ERROR + CONSOLE_LOGGER_LEVEL_WARN + CONSOLE_LOGGER_LEVEL_INFO + CONSOLE_LOGGER_LEVEL_DEBUG + + The value used determines which messages get printed. The logging + values above are in order, and only messages logged at the logging + level or above will actually be displayed to the user. E.g., the + default level is WARN, so only messages logged with LOG, ERROR, or + WARN will be displayed; INFO and DEBUG messages will be ignored. + ]] + values { + value: Ecordova.Console.LoggerLevel; [[A valid logger level]] + } + } + @property console_use { + [[Getter/Setter for the console_use functionality + + When console_use is true, the logger will log via the + browser 'console' object. + ]] + values { + value: bool; [[$EINA_TRUE to enable console output]] + } + } + @property logger_use { + [[Getter/Setter for the logger_use functionality + + When logger_use is true, the logger will log via the + native Logger plugin. + ]] + values { + value: bool; [[$EINA_TRUE to enable logger output]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_console_private.h b/src/lib/ecordova/ecordova_console_private.h new file mode 100644 index 0000000000..178e60b966 --- /dev/null +++ b/src/lib/ecordova/ecordova_console_private.h @@ -0,0 +1,20 @@ +#ifndef _ECORDOVA_CONSOLE_PRIVATE_H +#define _ECORDOVA_CONSOLE_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_console.eo.h" + +typedef struct _Ecordova_Console_Data Ecordova_Console_Data; + +/** + * Ecordova.Console private data + */ +struct _Ecordova_Console_Data +{ + Eo *obj; + Ecordova_Console_LoggerLevel level; + Eina_Bool console_use; + Eina_Bool logger_use; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_contact.c b/src/lib/ecordova/ecordova_contact.c new file mode 100644 index 0000000000..9b986c09e5 --- /dev/null +++ b/src/lib/ecordova/ecordova_contact.c @@ -0,0 +1,1117 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contact_private.h" + +#include "ecordova_contactname_private.h" +#include "ecordova_contactfield_private.h" +#include "ecordova_contacts_record_utils.h" +#include "ecordova_contactaddress_private.h" +#include "ecordova_contactorganization_private.h" + +#include <contacts.h> + +#include <assert.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACT_CLASS +#define MY_CLASS_NAME "Ecordova_Contact" + +static Ecordova_ContactName *_contactname_copy(Ecordova_ContactName *); +static Eina_Array *_contactfield_array_copy(Eina_Array *); +static Eina_Array *_contactaddress_array_copy(Eina_Array *); +static Eina_Array *_contactorganization_array_copy(Eina_Array *); +static void _contactfield_array_free(Eina_Array *); +static void _contactaddress_array_free(Eina_Array *); +static void _contactorganization_array_free(Eina_Array *); +static bool _ecordova_contactfields_import(Eina_Array *, contacts_record_h, const Ecordova_ContactField_Metadata); +static bool _ecordova_contactfields_export(Eina_Array *, contacts_record_h, const Ecordova_ContactField_Metadata); +static bool _ecordova_contactaddresses_import(Eina_Array *, contacts_record_h); +static bool _ecordova_contactaddresses_export(Eina_Array *, contacts_record_h); +static bool _ecordova_contactorganizations_import(Eina_Array *, contacts_record_h); +static bool _ecordova_contactorganizations_export(Eina_Array *, contacts_record_h); +static void _contactfield_array_clear_id(Eina_Array *); +static void _contactaddress_array_clear_id(Eina_Array *); +static void _contactorganization_array_clear_id(Eina_Array *); + +static Eo_Base * +_ecordova_contact_eo_base_constructor(Eo *obj, Ecordova_Contact_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->record = NULL; + int ret = contacts_record_create(_contacts_contact._uri, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + pd->name = eo_add(ECORDOVA_CONTACTNAME_CLASS, NULL); + pd->phone_numbers = eina_array_new(1); + pd->emails = eina_array_new(1); + pd->addresses = eina_array_new(1); + pd->ims = eina_array_new(1); + pd->organizations = eina_array_new(1); + pd->photos = eina_array_new(1); + pd->categories = eina_array_new(1); + pd->urls = eina_array_new(1); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contact_eo_base_destructor(Eo *obj, Ecordova_Contact_Data *pd) +{ + DBG("(%p)", obj); + + eo_unref(pd->name); + _contactfield_array_free(pd->phone_numbers); + _contactfield_array_free(pd->emails); + _contactaddress_array_free(pd->addresses); + _contactfield_array_free(pd->ims); + _contactorganization_array_free(pd->organizations); + _contactfield_array_free(pd->photos); + _contactfield_array_free(pd->categories); + _contactfield_array_free(pd->urls); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_contact_remove(Eo *obj, Ecordova_Contact_Data *pd) +{ + if (!pd->id) + { + ERR("%s", "No id"); + goto on_error; + } + + int ret = contacts_db_delete_record(_contacts_contact._uri, pd->id); + if (CONTACTS_ERROR_NONE != ret) + { + ERR("Error deleting record id: %d", pd->id); + goto on_error; + } + + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACT_EVENT_REMOVE_SUCCESS, NULL)); + return; + +on_error: + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACT_EVENT_ERROR, NULL)); +} + +static Ecordova_Contact * +_ecordova_contact_clone(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + Ecordova_Contact *cloned = eo_add(ECORDOVA_CONTACTNAME_CLASS, NULL); + EINA_SAFETY_ON_NULL_RETURN_VAL(cloned, NULL); + + if (!ecordova_contact_export(obj, pd->record)) + goto on_error; + + if (!ecordova_contact_import(cloned, pd->record)) + goto on_error; + + Ecordova_Contact_Data *cloned_pd = eo_data_scope_get(cloned, ECORDOVA_CONTACT_CLASS); + EINA_SAFETY_ON_NULL_GOTO(cloned_pd, on_error); + + cloned_pd->id = 0; + _contactfield_array_clear_id(pd->phone_numbers); + _contactfield_array_clear_id(pd->emails); + _contactaddress_array_clear_id(pd->addresses); + _contactfield_array_clear_id(pd->ims); + _contactorganization_array_clear_id(pd->organizations); + _contactfield_array_clear_id(pd->photos); + _contactfield_array_clear_id(pd->categories); + _contactfield_array_clear_id(pd->urls); + + return cloned; + +on_error: + eo_unref(cloned); + return NULL; +} + +static void +_ecordova_contact_save(Eo *obj, Ecordova_Contact_Data *pd) +{ + int ret; + + // TODO: export records in a background thread + if (!ecordova_contact_export(obj, pd->record)) + { + ERR("%s", "Exporting record"); + goto on_error; + } + + if (pd->id) + { + ret = contacts_db_update_record(pd->record); + + if (CONTACTS_ERROR_NONE != ret) + { + ERR("Error updating record: %d", ret); + goto on_error; + } + } + else + { + ret = contacts_db_insert_record(pd->record, &pd->id); + if (CONTACTS_ERROR_NONE != ret) + { + ERR("Error inserting record: %d", ret); + goto on_error; + } + + // must get the inserted record so we can properly update it further + contacts_record_h contacts_record = NULL; + ret = contacts_db_get_record(_contacts_contact._uri, pd->id, &contacts_record); + if (CONTACTS_ERROR_NONE != ret) + { + ERR("Error getting record: %d", ret); + goto on_error; + } + + ret = contacts_record_destroy(pd->record, true); + if (CONTACTS_ERROR_NONE != ret) + { + ERR("Error destroying record: %d", ret); + goto on_error; + } + + pd->record = contacts_record; + } + + // TODO: Check if it's necessary to update children records + + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACT_EVENT_SAVE_SUCCESS, NULL)); + return; + +on_error: + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACT_EVENT_ERROR, NULL)); +} + +static int +_ecordova_contact_id_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->id; +} + +static const char * +_ecordova_contact_display_name_get(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_contact.display_name, &value); + return value; +} + +static void +_ecordova_contact_display_name_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_contact.display_name, value); +} + +static Ecordova_ContactName * +_ecordova_contact_name_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->name; +} + +static void +_ecordova_contact_name_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Ecordova_ContactName *name) +{ + if (pd->name) eo_unref(pd->name); + pd->name = _contactname_copy(name); +} + +static const char * +_ecordova_contact_nickname_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(pd->record, + _contacts_contact.nickname, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + if (0 == count) return NULL; + + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(pd->record, + _contacts_contact.nickname, + 0, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + const char *value = NULL; + get_str_p(child_record, _contacts_nickname.name, &value); + return value; +} + +static void +_ecordova_contact_nickname_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + const char *value) +{ + int ret; + contacts_record_h child_record = NULL; + ret = contacts_record_create(_contacts_nickname._uri, &child_record); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + if (!set_str(child_record, _contacts_nickname.name, value)) + goto on_error; + + if (!clear_all_contact_record(pd->record, _contacts_contact.nickname)) + goto on_error; + + ret = contacts_record_add_child_record(pd->record, + _contacts_contact.nickname, + child_record); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error); + return; + +on_error: + ret = contacts_record_destroy(child_record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); +} + +static Eina_Array * +_ecordova_contact_phone_numbers_get(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd) +{ + return pd->phone_numbers; +} + +static void +_ecordova_contact_phone_numbers_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *phone_numbers) +{ + _contactfield_array_free(pd->phone_numbers); + pd->phone_numbers = _contactfield_array_copy(phone_numbers); +} + +static Eina_Array * +_ecordova_contact_emails_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->emails; +} + +static void +_ecordova_contact_emails_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *emails) +{ + _contactfield_array_free(pd->emails); + pd->emails = _contactfield_array_copy(emails); +} + +static Eina_Array * +_ecordova_contact_addresses_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->addresses; +} + +static void +_ecordova_contact_addresses_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *addresses) +{ + _contactaddress_array_free(pd->addresses); + pd->addresses = _contactaddress_array_copy(addresses); +} + +static Eina_Array * +_ecordova_contact_ims_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->ims; +} + +static void +_ecordova_contact_ims_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *ims) +{ + _contactfield_array_free(pd->ims); + pd->ims = _contactfield_array_copy(ims); +} + +static Eina_Array * +_ecordova_contact_organizations_get(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd) +{ + return pd->organizations; +} + +static void +_ecordova_contact_organizations_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *organizations) +{ + _contactorganization_array_free(pd->organizations); + pd->organizations = _contactorganization_array_copy(organizations); +} + +static time_t +_ecordova_contact_birthday_get(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd EINA_UNUSED) +{ + // TODO: get birthday + return 0; +} + +static void +_ecordova_contact_birthday_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd EINA_UNUSED, + time_t birthday EINA_UNUSED) +{ + // TODO: set birthday +} + +static const char * +_ecordova_contact_note_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(pd->record, + _contacts_contact.note, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + if (0 == count) + return NULL; + + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(pd->record, + _contacts_contact.note, + 0, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + const char *value = NULL; + get_str_p(child_record, _contacts_note.note, &value); + return value; +} + +static void +_ecordova_contact_note_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + const char *value) +{ + int ret; + contacts_record_h child_record = NULL; + ret = contacts_record_create(_contacts_note._uri, &child_record); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + if (!set_str(child_record, _contacts_note.note, value)) + goto on_error; + + if (!clear_all_contact_record(pd->record, _contacts_contact.note)) + goto on_error; + + ret = contacts_record_add_child_record(pd->record, + _contacts_contact.note, + child_record); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error); + return; + +on_error: + ret = contacts_record_destroy(child_record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); +} + +static Eina_Array * +_ecordova_contact_photos_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->photos; +} + +static void +_ecordova_contact_photos_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *photos) +{ + _contactfield_array_free(pd->photos); + pd->photos = _contactfield_array_copy(photos); +} + +static Eina_Array * +_ecordova_contact_categories_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->categories; +} + +static void +_ecordova_contact_categories_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *categories) +{ + _contactfield_array_free(pd->categories); + pd->categories = _contactfield_array_copy(categories); +} + +static Eina_Array * +_ecordova_contact_urls_get(Eo *obj EINA_UNUSED, Ecordova_Contact_Data *pd) +{ + return pd->urls; +} + +static void +_ecordova_contact_urls_set(Eo *obj EINA_UNUSED, + Ecordova_Contact_Data *pd, + Eina_Array *urls) +{ + _contactfield_array_free(pd->urls); + pd->urls = _contactfield_array_copy(urls); +} + +static Ecordova_ContactName * +_contactname_copy(Ecordova_ContactName *name) +{ + if (!name) + return eo_add(ECORDOVA_CONTACTNAME_CLASS, NULL); + + return ecordova_contactname_clone(name); +} + +static Ecordova_ContactAddress * +_contactaddress_copy(Ecordova_ContactAddress *address) +{ + if (!address) + return NULL; + + Eina_Bool pref = EINA_FALSE; + const char *type = NULL; + const char *formatted = NULL; + const char *street_address = NULL; + const char *locality = NULL; + const char *region = NULL; + const char *postal_code = NULL; + const char *country = NULL; + eo_do(address, + pref = ecordova_contactaddress_pref_get(), + type = ecordova_contactaddress_type_get(), + formatted = ecordova_contactaddress_formatted_get(), + street_address = ecordova_contactaddress_street_address_get(), + locality = ecordova_contactaddress_locality_get(), + region = ecordova_contactaddress_region_get(), + postal_code = ecordova_contactaddress_postal_code_get(), + country = ecordova_contactaddress_country_get()); + + return eo_add(ECORDOVA_CONTACTADDRESS_CLASS, + NULL, + ecordova_contactaddress_constructor(pref, + type, + formatted, + street_address, + locality, + region, + postal_code, + country)); +} + +static Ecordova_ContactField * +_contactfield_copy(Ecordova_ContactField *field) +{ + if (!field) + return NULL; + + const char *type = NULL; + const char *value = NULL; + Eina_Bool pref = EINA_FALSE; + eo_do(field, + type = ecordova_contactfield_type_get(), + value = ecordova_contactfield_value_get(), + pref = ecordova_contactfield_pref_get()); + + return eo_add(ECORDOVA_CONTACTFIELD_CLASS, NULL, + ecordova_contactfield_constructor(type, value, pref)); +} + +static Ecordova_ContactOrganization * +_contactorganization_copy(Ecordova_ContactOrganization *organization) +{ + if (!organization) + return NULL; + + Eina_Bool pref = EINA_FALSE; + const char *type = NULL; + const char *name = NULL; + const char *dept = NULL; + const char *title = NULL; + eo_do(organization, + pref = ecordova_contactorganization_pref_get(), + type = ecordova_contactorganization_type_get(), + name = ecordova_contactorganization_name_get(), + dept = ecordova_contactorganization_dept_get(), + title = ecordova_contactorganization_title_get()); + + return eo_add(ECORDOVA_CONTACTORGANIZATION_CLASS, + NULL, + ecordova_contactorganization_constructor(pref, + type, + name, + dept, + title)); +} + +static Eina_Array * +_contactfield_array_copy(Eina_Array *fields) +{ + Eina_Array *result = eina_array_new(1); + if (!fields) + return result; + + size_t i; + Ecordova_ContactField *field; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(fields, i, field, it) + eina_array_push(result, _contactfield_copy(field)); + return result; +} + +static Eina_Array * +_contactaddress_array_copy(Eina_Array *addresses) +{ + Eina_Array *result = eina_array_new(1); + if (!addresses) + return result; + + size_t i; + Ecordova_ContactAddress *address; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(addresses, i, address, it) + eina_array_push(result, _contactaddress_copy(address)); + return result; +} + +static Eina_Array * +_contactorganization_array_copy(Eina_Array *organizations) +{ + Eina_Array *result = eina_array_new(1); + if (!organizations) + return result; + + size_t i; + Ecordova_ContactOrganization *organization; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(organizations, i, organization, it) + eina_array_push(result, _contactorganization_copy(organization)); + return result; +} + +static void +_contactfield_array_free(Eina_Array *fields) +{ + if (!fields) + return; + + size_t i; + Ecordova_ContactField *field; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(fields, i, field, it) + eo_unref(field); + eina_array_free(fields); +} + +static void +_contactaddress_array_free(Eina_Array *addresses) +{ + if (!addresses) + return; + + size_t i; + Ecordova_ContactAddress *address; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(addresses, i, address, it) + eo_unref(address); + eina_array_free(addresses); +} + +static void +_contactorganization_array_free(Eina_Array *organizations) +{ + if (!organizations) + return; + + size_t i; + Ecordova_ContactOrganization *organization; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(organizations, i, organization, it) + eo_unref(organization); + eina_array_free(organizations); +} + +static void +_contactfield_array_clear_id(Eina_Array *fields) +{ + if (!fields) + return; + + size_t i; + Ecordova_ContactField *field; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(fields, i, field, it) + { + Ecordova_ContactField_Data *pd = + eo_data_scope_get(field, ECORDOVA_CONTACTFIELD_CLASS); + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->id = 0; + } +} + +static void +_contactaddress_array_clear_id(Eina_Array *addresses) +{ + if (!addresses) + return; + + size_t i; + Ecordova_ContactAddress *address; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(addresses, i, address, it) + { + Ecordova_ContactAddress_Data *pd = + eo_data_scope_get(address, ECORDOVA_CONTACTADDRESS_CLASS); + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->id = 0; + } +} + +static void +_contactorganization_array_clear_id(Eina_Array *organizations) +{ + if (!organizations) + return; + + size_t i; + Ecordova_ContactOrganization *organization; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(organizations, i, organization, it) + { + Ecordova_ContactOrganization_Data *pd = + eo_data_scope_get(organization, ECORDOVA_CONTACTORGANIZATION_CLASS); + EINA_SAFETY_ON_NULL_RETURN(pd); + + pd->id = 0; + } +} + +static const Ecordova_ContactField_Metadata +_contact_number_metadata = { + .uri = &_contacts_number._uri, + .ids = { + [ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID] = &_contacts_contact.number, + [ECORDOVA_CONTACTFIELD_PROPERTY_ID] = &_contacts_number.id, + [ECORDOVA_CONTACTFIELD_PROPERTY_TYPE] = &_contacts_number.type, + [ECORDOVA_CONTACTFIELD_PROPERTY_LABEL] = &_contacts_number.label, + [ECORDOVA_CONTACTFIELD_PROPERTY_VALUE] = &_contacts_number.number, + [ECORDOVA_CONTACTFIELD_PROPERTY_PREF] = &_contacts_number.is_default}, + .type2label = ecordova_contactnumber_type2label, + .label2type = ecordova_contactnumber_label2type +}; + +static const Ecordova_ContactField_Metadata +_contact_email_metadata = { + .uri = &_contacts_email._uri, + .ids = { + [ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID] = &_contacts_contact.email, + [ECORDOVA_CONTACTFIELD_PROPERTY_ID] = &_contacts_email.id, + [ECORDOVA_CONTACTFIELD_PROPERTY_TYPE] = &_contacts_email.type, + [ECORDOVA_CONTACTFIELD_PROPERTY_LABEL] = &_contacts_email.label, + [ECORDOVA_CONTACTFIELD_PROPERTY_VALUE] = &_contacts_email.email, + [ECORDOVA_CONTACTFIELD_PROPERTY_PREF] = &_contacts_email.is_default}, + .type2label = ecordova_contactemail_type2label, + .label2type = ecordova_contactemail_label2type +}; + +static const Ecordova_ContactField_Metadata +_contact_messenger_metadata = { + .uri = &_contacts_messenger._uri, + .ids = { + [ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID] = &_contacts_contact.messenger, + [ECORDOVA_CONTACTFIELD_PROPERTY_ID] = &_contacts_messenger.id, + [ECORDOVA_CONTACTFIELD_PROPERTY_TYPE] = &_contacts_messenger.type, + [ECORDOVA_CONTACTFIELD_PROPERTY_LABEL] = &_contacts_messenger.label, + [ECORDOVA_CONTACTFIELD_PROPERTY_VALUE] = &_contacts_messenger.im_id, + [ECORDOVA_CONTACTFIELD_PROPERTY_PREF] = NULL}, + .type2label = ecordova_contactmessenger_type2label, + .label2type = ecordova_contactmessenger_label2type +}; + +static const Ecordova_ContactField_Metadata +_contact_image_metadata = { + .uri = &_contacts_image._uri, + .ids = { + [ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID] = &_contacts_contact.image, + [ECORDOVA_CONTACTFIELD_PROPERTY_ID] = &_contacts_image.id, + [ECORDOVA_CONTACTFIELD_PROPERTY_TYPE] = &_contacts_image.type, + [ECORDOVA_CONTACTFIELD_PROPERTY_LABEL] = &_contacts_image.label, + [ECORDOVA_CONTACTFIELD_PROPERTY_VALUE] = &_contacts_image.path, + [ECORDOVA_CONTACTFIELD_PROPERTY_PREF] = &_contacts_image.is_default}, + .type2label = ecordova_contactimage_type2label, + .label2type = ecordova_contactimage_label2type +}; + +static const Ecordova_ContactField_Metadata +_contact_url_metadata = { + .uri = &_contacts_url._uri, + .ids = { + [ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID] = &_contacts_contact.url, + [ECORDOVA_CONTACTFIELD_PROPERTY_ID] = &_contacts_url.id, + [ECORDOVA_CONTACTFIELD_PROPERTY_TYPE] = &_contacts_url.type, + [ECORDOVA_CONTACTFIELD_PROPERTY_LABEL] = &_contacts_url.label, + [ECORDOVA_CONTACTFIELD_PROPERTY_VALUE] = &_contacts_url.url, + [ECORDOVA_CONTACTFIELD_PROPERTY_PREF] = NULL}, + .type2label = ecordova_contacturl_type2label, + .label2type = ecordova_contacturl_label2type +}; + +bool +ecordova_contact_import(Ecordova_Contact *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_Contact_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACT_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int ret; + + ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + ret = contacts_record_clone(contacts_record, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_get_int(pd->record, _contacts_contact.id, &pd->id); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + // name + DBG("%s", "Importing name"); + if (!ecordova_contactname_import(pd->name, pd->record)) + return false; + + // phone_numbers + DBG("%s", "Importing phone_numbers"); + if (!_ecordova_contactfields_import(pd->phone_numbers, + pd->record, + _contact_number_metadata)) + return false; + + // emails + DBG("%s", "Importing emails"); + if (!_ecordova_contactfields_import(pd->emails, + pd->record, + _contact_email_metadata)) + return false; + + // addresses + DBG("%s", "Importing addresses"); + if (!_ecordova_contactaddresses_import(pd->addresses, pd->record)) + return false; + + // ims + DBG("%s", "Importing ims"); + if (!_ecordova_contactfields_import(pd->ims, + pd->record, + _contact_messenger_metadata)) + return false; + + // organizations + DBG("%s", "Importing organizations"); + if (!_ecordova_contactorganizations_import(pd->organizations, pd->record)) + return false; + + // photos + DBG("%s", "Importing photos"); + if (!_ecordova_contactfields_import(pd->photos, + pd->record, + _contact_image_metadata)) + return false; + + // TODO: categories + + // urls + DBG("%s", "Importing urls"); + if (!_ecordova_contactfields_import(pd->urls, + pd->record, + _contact_url_metadata)) + return false; + + return true; +} + +bool +ecordova_contact_export(Ecordova_Contact *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_Contact_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACT_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + // name + if (!clear_all_contact_record(contacts_record, _contacts_contact.name) || + !ecordova_contactname_export(pd->name, contacts_record)) + return false; + + // phone_numbers + if (!_ecordova_contactfields_export(pd->phone_numbers, + contacts_record, + _contact_number_metadata)) + return false; + + // emails + if (!_ecordova_contactfields_export(pd->emails, + contacts_record, + _contact_email_metadata)) + return false; + + // addresses + if (!_ecordova_contactaddresses_export(pd->addresses, contacts_record)) + return false; + + // ims + if (!_ecordova_contactfields_export(pd->ims, + contacts_record, + _contact_messenger_metadata)) + return false; + + // organizations + if (!_ecordova_contactorganizations_export(pd->organizations, contacts_record)) + return false; + + // photos + if (!_ecordova_contactfields_export(pd->photos, + contacts_record, + _contact_image_metadata)) + return false; + + // TODO: categories + + // urls + if (!_ecordova_contactfields_export(pd->urls, + contacts_record, + _contact_url_metadata)) + return false; + + return true; +} + +static bool +_ecordova_contactfields_import(Eina_Array *fields, + contacts_record_h contacts_record, + const Ecordova_ContactField_Metadata metadata) +{ + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(contacts_record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID], + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + DBG("Importing %d children", count); + for (int index = 0; index < count; ++index) + { + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(contacts_record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID], + index, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + Ecordova_ContactField *field = eo_add(ECORDOVA_CONTACTFIELD_CLASS, NULL); + if (!ecordova_contactfield_import(field, child_record, metadata)) + { + eo_unref(field); + return false; + } + + Eina_Bool ret = eina_array_push(fields, field); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false); + } + + return true; +} + +static bool +_ecordova_contactfields_export(Eina_Array *fields, + contacts_record_h contacts_record, + const Ecordova_ContactField_Metadata metadata) +{ + if (!clear_all_contact_record(contacts_record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID])) + return false; + + Ecordova_ContactField *field; + Eina_Array_Iterator iterator; + unsigned int i; + EINA_ARRAY_ITER_NEXT(fields, i, field, iterator) + { + contacts_record_h record = NULL; + int ret = contacts_record_create(*metadata.uri, &record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + if (!ecordova_contactfield_export(field, record, metadata)) + { + ret = contacts_record_destroy(record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + return false; + } + + ret = contacts_record_add_child_record(contacts_record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID], + record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + } + + return true; +} + +static bool +_ecordova_contactaddresses_import(Eina_Array *fields, + contacts_record_h contacts_record) +{ + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(contacts_record, + _contacts_contact.address, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + DBG("Importing %d addresses", count); + for (int index = 0; index < count; ++index) + { + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(contacts_record, + _contacts_contact.address, + index, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + Ecordova_ContactAddress *address = eo_add(ECORDOVA_CONTACTADDRESS_CLASS, NULL); + if (!ecordova_contactaddress_import(address, child_record)) + { + eo_unref(address); + return false; + } + + Eina_Bool ret = eina_array_push(fields, address); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false); + } + + return true; +} + +static bool +_ecordova_contactaddresses_export(Eina_Array *addresses, + contacts_record_h contacts_record) +{ + if (!clear_all_contact_record(contacts_record, _contacts_contact.address)) + return false; + + Ecordova_ContactAddress *address; + Eina_Array_Iterator iterator; + unsigned int i; + EINA_ARRAY_ITER_NEXT(addresses, i, address, iterator) + { + contacts_record_h record = NULL; + int ret = contacts_record_create(_contacts_address._uri, &record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + if (!ecordova_contactaddress_export(address, record)) + { + ret = contacts_record_destroy(record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + return false; + } + + ret = contacts_record_add_child_record(contacts_record, + _contacts_contact.address, + record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + } + + return true; +} + +static bool +_ecordova_contactorganizations_import(Eina_Array *organizations, + contacts_record_h contacts_record) +{ + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(contacts_record, + _contacts_contact.company, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + DBG("Importing %d organizations", count); + for (int index = 0; index < count; ++index) + { + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(contacts_record, + _contacts_contact.company, + index, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + Ecordova_ContactOrganization *organization = eo_add(ECORDOVA_CONTACTORGANIZATION_CLASS, NULL); + if (!ecordova_contactorganization_import(organization, child_record)) + { + eo_unref(organization); + return false; + } + + Eina_Bool ret = eina_array_push(organizations, organization); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret, false); + } + + return true; +} + +static bool +_ecordova_contactorganizations_export(Eina_Array *organizations, + contacts_record_h contacts_record) +{ + if (!clear_all_contact_record(contacts_record, _contacts_contact.company)) + return false; + + Ecordova_ContactOrganization *organization; + Eina_Array_Iterator iterator; + unsigned int i; + EINA_ARRAY_ITER_NEXT(organizations, i, organization, iterator) + { + contacts_record_h record = NULL; + int ret = contacts_record_create(_contacts_company._uri, &record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + if (!ecordova_contactaddress_export(organization, record)) + { + ret = contacts_record_destroy(record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + return false; + } + + ret = contacts_record_add_child_record(contacts_record, + _contacts_contact.company, + record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + } + + return true; +} + +#include "ecordova_contact.eo.c" diff --git a/src/lib/ecordova/ecordova_contact.eo b/src/lib/ecordova/ecordova_contact.eo new file mode 100644 index 0000000000..88afe5fe34 --- /dev/null +++ b/src/lib/ecordova/ecordova_contact.eo @@ -0,0 +1,98 @@ +class Ecordova.Contact (Eo.Base) { + [[Contains information about a single contact.]] + legacy_prefix: null; + methods { + remove { + [[Removes contact from device storage.]] + } + clone { + [[Creates a deep copy of this Contact. With the contact ID set to + null. + ]] + return: Ecordova.Contact*; [[copy of this Contact]] + } + save { + [[Persists contact to device storage.]] + } + @property id { + get {} + values { + id: int ; + } + } + @property display_name { + values { + display_name: const(char)*; + } + } + @property name { + values { + name: Ecordova.ContactName*; + } + } + @property nickname { + values { + nickname: const(char)*; + } + } + @property phone_numbers { + values { + phone_numbers: array<Ecordova.ContactField*>*; + } + } + @property emails { + values { + emails: array<Ecordova.ContactField*>*; + } + } + @property addresses { + values { + addresses: array<Ecordova.ContactAddress*>*; + } + } + @property ims { + values { + ims: array<Ecordova.ContactField*>*; + } + } + @property organizations { + values { + organizations: array<Ecordova.ContactOrganization*>*; + } + } + @property birthday { + values { + birthday: time ; + } + } + @property note { + values { + note: const(char)*; + } + } + @property photos { + values { + photos: array<Ecordova.ContactField*>*; + } + } + @property categories { + values { + categories: array<Ecordova.ContactField*>*; + } + } + @property urls { + values { + urls: array<Ecordova.ContactField*>*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + events { + save,success; + remove,success; + error; + } +} diff --git a/src/lib/ecordova/ecordova_contact_private.h b/src/lib/ecordova/ecordova_contact_private.h new file mode 100644 index 0000000000..4a1ddc8deb --- /dev/null +++ b/src/lib/ecordova/ecordova_contact_private.h @@ -0,0 +1,38 @@ +#ifndef _ECORDOVA_CONTACT_PRIVATE_H +#define _ECORDOVA_CONTACT_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_contactaddress.eo.h" +#include "ecordova_contactfield.eo.h" +#include "ecordova_contactname.eo.h" +#include "ecordova_contactorganization.eo.h" +#include "ecordova_contact.eo.h" + +#include <contacts.h> +#include <stdbool.h> + +typedef struct _Ecordova_Contact_Data Ecordova_Contact_Data; + +/** + * Ecordova.Contact private data + */ +struct _Ecordova_Contact_Data +{ + Eo *obj; + contacts_record_h record; + int id; + Ecordova_ContactName *name; + Eina_Array *phone_numbers; + Eina_Array *emails; + Eina_Array *addresses; + Eina_Array *ims; + Eina_Array *organizations; + Eina_Array *photos; + Eina_Array *categories; + Eina_Array *urls; +}; + +bool ecordova_contact_import(Ecordova_Contact *, contacts_record_h); +bool ecordova_contact_export(Ecordova_Contact *, contacts_record_h); + +#endif diff --git a/src/lib/ecordova/ecordova_contactaddress.c b/src/lib/ecordova/ecordova_contactaddress.c new file mode 100644 index 0000000000..23918f2432 --- /dev/null +++ b/src/lib/ecordova/ecordova_contactaddress.c @@ -0,0 +1,312 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contactaddress_private.h" +#include "ecordova_contacts_record_utils.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACTADDRESS_CLASS +#define MY_CLASS_NAME "Ecordova_ContactAddress" + +static Eo_Base * +_ecordova_contactaddress_eo_base_constructor(Eo *obj, + Ecordova_ContactAddress_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->id = 0; + pd->record = NULL; + int ret = contacts_record_create(_contacts_address._uri, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contactaddress_constructor(Eo *obj, + Ecordova_ContactAddress_Data *pd EINA_UNUSED, + Eina_Bool pref, + const char *type, + const char *formatted, + const char *street_address, + const char *locality, + const char *region, + const char *postal_code, + const char *country) +{ + DBG("(%p)", obj); + eo_do(obj, + ecordova_contactaddress_pref_set(pref), + ecordova_contactaddress_type_set(type), + ecordova_contactaddress_formatted_set(formatted), + ecordova_contactaddress_street_address_set(street_address), + ecordova_contactaddress_locality_set(locality), + ecordova_contactaddress_region_set(region), + ecordova_contactaddress_postal_code_set(postal_code), + ecordova_contactaddress_country_set(country)); +} + +static void +_ecordova_contactaddress_eo_base_destructor(Eo *obj, + Ecordova_ContactAddress_Data *pd) +{ + DBG("(%p)", obj); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static int +_ecordova_contactaddress_id_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + return pd->id; +} + +static Eina_Bool +_ecordova_contactaddress_pref_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + Eina_Bool value = EINA_FALSE; + get_bool(pd->record, _contacts_address.is_default, &value); + return value; +} + +static void +_ecordova_contactaddress_pref_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + Eina_Bool value) +{ + set_bool(pd->record, _contacts_address.is_default, value); +} + +static const char * +_ecordova_contactaddress_type_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + int type; + if (!get_int(pd->record, _contacts_address.type, &type)) + return NULL; + + switch (type) + { + case CONTACTS_ADDRESS_TYPE_HOME: + return "Home"; + case CONTACTS_ADDRESS_TYPE_WORK: + return "Work"; + case CONTACTS_ADDRESS_TYPE_DOM: + return "Dom"; + case CONTACTS_ADDRESS_TYPE_INTL: + return "Intl"; + case CONTACTS_ADDRESS_TYPE_POSTAL: + return "Postal"; + case CONTACTS_ADDRESS_TYPE_PARCEL: + return "Parcel"; + case CONTACTS_ADDRESS_TYPE_OTHER: + case CONTACTS_ADDRESS_TYPE_CUSTOM: + default: + { + const char *custom = NULL; + get_str_p(pd->record, _contacts_address.label, &custom); + return custom; + } + } +} + +static void +_ecordova_contactaddress_type_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + int type = CONTACTS_ADDRESS_TYPE_OTHER; + if (!value || strlen(value) == 0) + type = CONTACTS_ADDRESS_TYPE_OTHER; + else + if (strcasecmp(value, "Home")) + type = CONTACTS_ADDRESS_TYPE_HOME; + else if (strcasecmp(value, "Work")) + type = CONTACTS_ADDRESS_TYPE_WORK; + else if (strcasecmp(value, "Dom")) + type = CONTACTS_ADDRESS_TYPE_DOM; + else if (strcasecmp(value, "Intl")) + type = CONTACTS_ADDRESS_TYPE_INTL; + else if (strcasecmp(value, "Postal")) + type = CONTACTS_ADDRESS_TYPE_POSTAL; + else if (strcasecmp(value, "Parcel")) + type = CONTACTS_ADDRESS_TYPE_PARCEL; + else + type = CONTACTS_ADDRESS_TYPE_CUSTOM; + + set_int(pd->record, _contacts_address.type, type); + if (strlen(value) != 0) + set_str(pd->record, _contacts_address.label, value); +} + +static const char * +_ecordova_contactaddress_formatted_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd EINA_UNUSED) +{ + // TODO: mount the formatted address + ERR("%s", "Not implemented"); + return NULL; +} + +static void +_ecordova_contactaddress_formatted_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd EINA_UNUSED, + const char *formatted EINA_UNUSED) +{ + ERR("%s", "Not implemented"); +} + +static const char * +_ecordova_contactaddress_street_address_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_address.street, &value); + return value; +} + +static void +_ecordova_contactaddress_street_address_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_address.street, value); +} + +static const char * +_ecordova_contactaddress_locality_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_address.locality, &value); + return value; +} + +static void +_ecordova_contactaddress_locality_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_address.locality, value); +} + +static const char * +_ecordova_contactaddress_region_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_address.region, &value); + return value; +} + +static void +_ecordova_contactaddress_region_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_address.region, value); +} + +static const char * +_ecordova_contactaddress_postal_code_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_address.postal_code, &value); + return value; +} + +static void +_ecordova_contactaddress_postal_code_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_address.postal_code, value); +} + +static const char * +_ecordova_contactaddress_country_get(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_address.country, &value); + return value; +} + +static void +_ecordova_contactaddress_country_set(Eo *obj EINA_UNUSED, + Ecordova_ContactAddress_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_address.country, value); +} + +bool +ecordova_contactaddress_import(Ecordova_ContactAddress *obj, + contacts_record_h child_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_record, false); + + Ecordova_ContactAddress_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTADDRESS_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + ret = contacts_record_clone(child_record, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return get_int(child_record, _contacts_address.id, &pd->id); +} + +bool +ecordova_contactaddress_export(Ecordova_ContactAddress *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_ContactAddress_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTADDRESS_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + contacts_record_h child_record = NULL; + int ret = contacts_record_clone(pd->record, &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_add_child_record(contacts_record, + _contacts_contact.address, + child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +Ecordova_ContactAddress * +ecordova_contactaddress_clone(Ecordova_ContactAddress *other) +{ + Ecordova_ContactAddress *cloned = eo_add(ECORDOVA_CONTACTADDRESS_CLASS, NULL); + + Ecordova_ContactAddress_Data *cloned_pd = eo_data_scope_get(cloned, ECORDOVA_CONTACTADDRESS_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(cloned_pd, NULL); + Ecordova_ContactAddress_Data *other_pd = eo_data_scope_get(other, ECORDOVA_CONTACTADDRESS_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(other_pd, NULL); + + int ret = contacts_record_destroy(cloned_pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + ret = contacts_record_clone(other_pd->record, &cloned_pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return cloned; +} + +#include "ecordova_contactaddress.eo.c" diff --git a/src/lib/ecordova/ecordova_contactaddress.eo b/src/lib/ecordova/ecordova_contactaddress.eo new file mode 100644 index 0000000000..ee5a9efef9 --- /dev/null +++ b/src/lib/ecordova/ecordova_contactaddress.eo @@ -0,0 +1,93 @@ +class Ecordova.ContactAddress (Eo.Base) { + [[Contains information about a single contact.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_ContactAddress constructor. + @.constructor + + @since 2.3 + ]] + params { + pref: bool; + [[user's preferred value.]] + + type: const(char)*; + [[A string indicating what type of field this is, home for + example. + ]] + + formatted: const(char)*; + [[The full address formatted for display.]] + + street_address: const(char)*; + [[The full street address.]] + + locality: const(char)*; + [[The city or locality.]] + + region: const(char)*; + [[The state or region.]] + + postal_code: const(char)*; + [[The zip code or postal code.]] + + country: const(char)*; + [[The country name.]] + } + } + @property id { + get {} + values { + id: int; + } + } + @property pref { + values { + preferred: bool; + } + } + @property type { + values { + type: const(char)*; + } + } + @property formatted { + values { + formatted: const(char)*; + } + } + @property street_address { + values { + street_address: const(char)*; + } + } + @property locality { + values { + locality: const(char)*; + } + } + @property region { + values { + region: const(char)*; + } + } + @property postal_code { + values { + postal_code: const(char)*; + } + } + @property country { + values { + country: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_contactaddress_private.h b/src/lib/ecordova/ecordova_contactaddress_private.h new file mode 100644 index 0000000000..56715d34f1 --- /dev/null +++ b/src/lib/ecordova/ecordova_contactaddress_private.h @@ -0,0 +1,25 @@ +#ifndef _ECORDOVA_CONTACTADDRESS_PRIVATE_H +#define _ECORDOVA_CONTACTADDRESS_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_contactaddress.eo.h" + +#include <contacts.h> + +typedef struct _Ecordova_ContactAddress_Data Ecordova_ContactAddress_Data; + +/** + * Ecordova.ContactAddress private data + */ +struct _Ecordova_ContactAddress_Data +{ + Eo *obj; + int id; + contacts_record_h record; +}; + +bool ecordova_contactaddress_import(Ecordova_ContactAddress *, contacts_record_h); +bool ecordova_contactaddress_export(Ecordova_ContactAddress *, contacts_record_h); +Ecordova_ContactAddress *ecordova_contactaddress_clone(Ecordova_ContactAddress *); + +#endif diff --git a/src/lib/ecordova/ecordova_contactfield.c b/src/lib/ecordova/ecordova_contactfield.c new file mode 100644 index 0000000000..8397a1944d --- /dev/null +++ b/src/lib/ecordova/ecordova_contactfield.c @@ -0,0 +1,415 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contactfield_private.h" +#include "ecordova_contacts_record_utils.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACTFIELD_CLASS +#define MY_CLASS_NAME "Ecordova_ContactField" + +static Eo_Base * +_ecordova_contactfield_eo_base_constructor(Eo *obj, + Ecordova_ContactField_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contactfield_constructor(Eo *obj, + Ecordova_ContactField_Data *pd, + const char *type, + const char *value, + Eina_Bool pref) +{ + DBG("(%p)", obj); + pd->id = 0; + pd->type = type ? strdup(type) : NULL; + pd->value = value ? strdup(value) : NULL; + pd->pref = pref; +} + +static void +_ecordova_contactfield_eo_base_destructor(Eo *obj, + Ecordova_ContactField_Data *pd) +{ + DBG("(%p)", obj); + + free(pd->type); + free(pd->value); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static int +_ecordova_contactfield_id_get(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd) +{ + return pd->id; +} + +static const char * +_ecordova_contactfield_type_get(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd) +{ + return pd->type; +} + +static void +_ecordova_contactfield_type_set(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd, + const char *type) +{ + free(pd->type); + pd->type = type ? strdup(type) : NULL; +} + +static const char * +_ecordova_contactfield_value_get(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd) +{ + return pd->value; +} + +static void +_ecordova_contactfield_value_set(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd, + const char *value) +{ + free(pd->value); + pd->value = value ? strdup(value) : NULL; +} + +static Eina_Bool +_ecordova_contactfield_pref_get(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd) +{ + return pd->pref; +} + +static void +_ecordova_contactfield_pref_set(Eo *obj EINA_UNUSED, + Ecordova_ContactField_Data *pd, + Eina_Bool pref) +{ + pd->pref = pref; +} + +bool +ecordova_contactfield_import(Ecordova_ContactField *obj, + contacts_record_h record, + const Ecordova_ContactField_Metadata metadata) +{ + DBG("%p", obj); + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + Ecordova_ContactField_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTFIELD_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int type = 0; + const char* label = NULL; + if (!get_int(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_TYPE], + &type) || + !get_str_p(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_LABEL], + &label)) + return false; + + pd->type = metadata.type2label(type, label); + + if (!get_int(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_ID], + &pd->id) || + !get_str(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_VALUE], + &pd->value)) + return false; + + if (metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_PREF] && + !get_bool(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_PREF], + &pd->pref)) + return false; + + return true; +} + +bool +ecordova_contactfield_export(Ecordova_ContactField *obj, + contacts_record_h record, + const Ecordova_ContactField_Metadata metadata) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + Ecordova_ContactField_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTFIELD_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int type = metadata.label2type(pd->type); + if (!set_int(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_TYPE], + type)) + return false; + + if (pd->type && + !set_str(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_LABEL], + pd->type)) + return false; + + if (!set_str(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_VALUE], + pd->value)) + return false; + + if (metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_PREF] && + !set_bool(record, + *metadata.ids[ECORDOVA_CONTACTFIELD_PROPERTY_PREF], + pd->pref)) + return false; + + return true; +} + +char * +ecordova_contactnumber_type2label(int type, const char *custom) +{ + switch (type) + { + case CONTACTS_NUMBER_TYPE_HOME: + return strdup("Home"); + case CONTACTS_NUMBER_TYPE_WORK: + return strdup("Work"); + case CONTACTS_NUMBER_TYPE_VOICE: + return strdup("Voice"); + case CONTACTS_NUMBER_TYPE_FAX: + return strdup("Fax"); + case CONTACTS_NUMBER_TYPE_MSG: + return strdup("Msg"); + case CONTACTS_NUMBER_TYPE_CELL: + return strdup("Cell"); + case CONTACTS_NUMBER_TYPE_PAGER: + return strdup("Pager"); + case CONTACTS_NUMBER_TYPE_BBS: + return strdup("BBS"); + case CONTACTS_NUMBER_TYPE_MODEM: + return strdup("Modem"); + case CONTACTS_NUMBER_TYPE_CAR: + return strdup("Car"); + case CONTACTS_NUMBER_TYPE_ISDN: + return strdup("ISDN"); + case CONTACTS_NUMBER_TYPE_VIDEO: + return strdup("Video"); + case CONTACTS_NUMBER_TYPE_PCS: + return strdup("Pcs"); + case CONTACTS_NUMBER_TYPE_COMPANY_MAIN: + return strdup("Company_Main"); + case CONTACTS_NUMBER_TYPE_RADIO: + return strdup("Radio"); + case CONTACTS_NUMBER_TYPE_MAIN: + return strdup("Main"); + case CONTACTS_NUMBER_TYPE_ASSISTANT: + return strdup("Assistant"); + case CONTACTS_NUMBER_TYPE_OTHER: + case CONTACTS_NUMBER_TYPE_CUSTOM: + default: + if (custom) + return strdup(custom); + return NULL; + } +} + +int +ecordova_contactnumber_label2type(const char *label) +{ + if (!label) + return CONTACTS_NUMBER_TYPE_OTHER; + if (strcasecmp(label, "Home") == 0) + return CONTACTS_NUMBER_TYPE_HOME; + if (strcasecmp(label, "Work") == 0) + return CONTACTS_NUMBER_TYPE_WORK; + if (strcasecmp(label, "Voice") == 0) + return CONTACTS_NUMBER_TYPE_VOICE; + if (strcasecmp(label, "Fax") == 0) + return CONTACTS_NUMBER_TYPE_FAX; + if (strcasecmp(label, "Msg") == 0) + return CONTACTS_NUMBER_TYPE_MSG; + if (strcasecmp(label, "Cell") == 0) + return CONTACTS_NUMBER_TYPE_CELL; + if (strcasecmp(label, "Pager") == 0) + return CONTACTS_NUMBER_TYPE_PAGER; + if (strcasecmp(label, "BBS") == 0) + return CONTACTS_NUMBER_TYPE_BBS; + if (strcasecmp(label, "Modem") == 0) + return CONTACTS_NUMBER_TYPE_MODEM; + if (strcasecmp(label, "Car") == 0) + return CONTACTS_NUMBER_TYPE_CAR; + if (strcasecmp(label, "ISDN") == 0) + return CONTACTS_NUMBER_TYPE_ISDN; + if (strcasecmp(label, "Video") == 0) + return CONTACTS_NUMBER_TYPE_VIDEO; + if (strcasecmp(label, "Pcs") == 0) + return CONTACTS_NUMBER_TYPE_PCS; + if (strcasecmp(label, "Company_Main") == 0) + return CONTACTS_NUMBER_TYPE_COMPANY_MAIN; + if (strcasecmp(label, "Radio") == 0) + return CONTACTS_NUMBER_TYPE_RADIO; + if (strcasecmp(label, "Main") == 0) + return CONTACTS_NUMBER_TYPE_MAIN; + if (strcasecmp(label, "Assistant") == 0) + return CONTACTS_NUMBER_TYPE_ASSISTANT; + + return CONTACTS_NUMBER_TYPE_CUSTOM; +} + +char * +ecordova_contactemail_type2label(int type, const char *custom) +{ + switch (type) + { + case CONTACTS_EMAIL_TYPE_HOME: + return strdup("Home"); + case CONTACTS_EMAIL_TYPE_WORK: + return strdup("Work"); + case CONTACTS_EMAIL_TYPE_MOBILE: + return strdup("Mobile"); + case CONTACTS_EMAIL_TYPE_OTHER: + case CONTACTS_EMAIL_TYPE_CUSTOM: + default: + if (custom) + return strdup(custom); + return NULL; + } +} + +int +ecordova_contactemail_label2type(const char *label) +{ + if (!label) + return CONTACTS_EMAIL_TYPE_OTHER; + if (strcasecmp(label, "Home") == 0) + return CONTACTS_EMAIL_TYPE_HOME; + if (strcasecmp(label, "Work") == 0) + return CONTACTS_EMAIL_TYPE_WORK; + if (strcasecmp(label, "Mobile") == 0) + return CONTACTS_EMAIL_TYPE_MOBILE; + + return CONTACTS_EMAIL_TYPE_CUSTOM; +} + +char * +ecordova_contactmessenger_type2label(int type, const char *custom) +{ + switch (type) + { + case CONTACTS_MESSENGER_TYPE_GOOGLE: + return strdup("Google"); + case CONTACTS_MESSENGER_TYPE_WLM: + return strdup("Wlm"); + case CONTACTS_MESSENGER_TYPE_YAHOO: + return strdup("Yahoo"); + case CONTACTS_MESSENGER_TYPE_FACEBOOK: + return strdup("Facebook"); + case CONTACTS_MESSENGER_TYPE_ICQ: + return strdup("Icq"); + case CONTACTS_MESSENGER_TYPE_AIM: + return strdup("Aim"); + case CONTACTS_MESSENGER_TYPE_QQ: + return strdup("Qq"); + case CONTACTS_MESSENGER_TYPE_JABBER: + return strdup("Jabber"); + case CONTACTS_MESSENGER_TYPE_SKYPE: + return strdup("Skype"); + case CONTACTS_MESSENGER_TYPE_IRC: + return strdup("IRC"); + case CONTACTS_MESSENGER_TYPE_OTHER: + case CONTACTS_MESSENGER_TYPE_CUSTOM: + default: + if (custom) + return strdup(custom); + return NULL; + } +} + +int +ecordova_contactmessenger_label2type(const char *label) +{ + if (!label) + return CONTACTS_MESSENGER_TYPE_OTHER; + if (strcasecmp(label, "Google") == 0) + return CONTACTS_MESSENGER_TYPE_GOOGLE; + if (strcasecmp(label, "Wlm") == 0) + return CONTACTS_MESSENGER_TYPE_WLM; + if (strcasecmp(label, "Yahoo") == 0) + return CONTACTS_MESSENGER_TYPE_YAHOO; + if (strcasecmp(label, "Facebook") == 0) + return CONTACTS_MESSENGER_TYPE_FACEBOOK; + if (strcasecmp(label, "Icq") == 0) + return CONTACTS_MESSENGER_TYPE_ICQ; + if (strcasecmp(label, "Aim") == 0) + return CONTACTS_MESSENGER_TYPE_AIM; + if (strcasecmp(label, "Qq") == 0) + return CONTACTS_MESSENGER_TYPE_QQ; + if (strcasecmp(label, "Jabber") == 0) + return CONTACTS_MESSENGER_TYPE_JABBER; + if (strcasecmp(label, "Skype") == 0) + return CONTACTS_MESSENGER_TYPE_SKYPE; + if (strcasecmp(label, "IRC") == 0) + return CONTACTS_MESSENGER_TYPE_IRC; + + return CONTACTS_MESSENGER_TYPE_CUSTOM; +} + +char * +ecordova_contactimage_type2label(int type EINA_UNUSED, const char *custom) +{ + return custom ? strdup(custom) : NULL; +} + +int +ecordova_contactimage_label2type(const char *label EINA_UNUSED) +{ + return 0; +} + +char * +ecordova_contacturl_type2label(int type, const char *custom) +{ + switch (type) + { + case CONTACTS_URL_TYPE_HOME: + return strdup("Home"); + case CONTACTS_URL_TYPE_WORK: + return strdup("Work"); + case CONTACTS_URL_TYPE_OTHER: + case CONTACTS_URL_TYPE_CUSTOM: + default: + if (custom) + return strdup(custom); + return NULL; + } +} + +int +ecordova_contacturl_label2type(const char *label) +{ + if (!label) + return CONTACTS_URL_TYPE_OTHER; + if (strcasecmp(label, "Home") == 0) + return CONTACTS_URL_TYPE_HOME; + if (strcasecmp(label, "Work") == 0) + return CONTACTS_URL_TYPE_WORK; + return CONTACTS_URL_TYPE_CUSTOM; +} + +#include "ecordova_contactfield.eo.c" diff --git a/src/lib/ecordova/ecordova_contactfield.eo b/src/lib/ecordova/ecordova_contactfield.eo new file mode 100644 index 0000000000..b61930547f --- /dev/null +++ b/src/lib/ecordova/ecordova_contactfield.eo @@ -0,0 +1,57 @@ +class Ecordova.ContactField (Eo.Base) { + [[Contains information about a single contact.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_ContactField constructor. + @.constructor + + @since 2.3 + ]] + params { + type: const(char)*; + [[A string that indicates what type of field this is, home for + example. + ]] + + value: const(char)*; + [[The value of the field, such as a phone number or email + address. + ]] + + pref: bool; + [[Set to true if this ContactField contains the user's preferred + value. + ]] + } + } + @property id { + get {} + values { + id: int; + } + } + @property type { + values { + type: const(char)*; + } + } + @property value { + values { + value: const(char)*; + } + } + @property pref { + values { + preferred: bool; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_contactfield_private.h b/src/lib/ecordova/ecordova_contactfield_private.h new file mode 100644 index 0000000000..408a40addc --- /dev/null +++ b/src/lib/ecordova/ecordova_contactfield_private.h @@ -0,0 +1,58 @@ +#ifndef _ECORDOVA_CONTACTFIELD_PRIVATE_H +#define _ECORDOVA_CONTACTFIELD_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_contactfield.eo.h" + +#include <contacts.h> +#include <stdbool.h> + +typedef enum _Ecordova_ContactField_Property +{ + ECORDOVA_CONTACTFIELD_PARENT_PROPERTY_ID, + ECORDOVA_CONTACTFIELD_PROPERTY_ID, + ECORDOVA_CONTACTFIELD_PROPERTY_TYPE, + ECORDOVA_CONTACTFIELD_PROPERTY_LABEL, + ECORDOVA_CONTACTFIELD_PROPERTY_VALUE, + ECORDOVA_CONTACTFIELD_PROPERTY_PREF, + ECORDOVA_CONTACTFIELD_PROPERTY_COUNT +} Ecordova_ContactField_Property; + +typedef struct _Ecordova_ContactField_Metadata +{ + const char * const *uri; + const unsigned int *ids[ECORDOVA_CONTACTFIELD_PROPERTY_COUNT]; + char *(*type2label)(int type, const char *custom); + int(*label2type)(const char *label); +} Ecordova_ContactField_Metadata; + +typedef struct _Ecordova_ContactField_Data Ecordova_ContactField_Data; + +/** + * Ecordova.ContactField private data + */ +struct _Ecordova_ContactField_Data +{ + Eo *obj; + int id; + char *type; + char *value; + Eina_Bool pref; + Ecordova_ContactField_Metadata metadata; +}; + +bool ecordova_contactfield_import(Ecordova_ContactField *, contacts_record_h, const Ecordova_ContactField_Metadata); +bool ecordova_contactfield_export(Ecordova_ContactField *, contacts_record_h, const Ecordova_ContactField_Metadata); + +char *ecordova_contactnumber_type2label(int, const char *); +int ecordova_contactnumber_label2type(const char *); +char *ecordova_contactemail_type2label(int, const char *); +int ecordova_contactemail_label2type(const char *); +char *ecordova_contactmessenger_type2label(int, const char *); +int ecordova_contactmessenger_label2type(const char *); +char *ecordova_contactimage_type2label(int, const char *); +int ecordova_contactimage_label2type(const char *); +char *ecordova_contacturl_type2label(int, const char *); +int ecordova_contacturl_label2type(const char *); + +#endif diff --git a/src/lib/ecordova/ecordova_contactname.c b/src/lib/ecordova/ecordova_contactname.c new file mode 100644 index 0000000000..87cdcbfaaf --- /dev/null +++ b/src/lib/ecordova/ecordova_contactname.c @@ -0,0 +1,257 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contactname_private.h" +#include "ecordova_contact_private.h" +#include "ecordova_contacts_record_utils.h" +#include "ecordova_contactname.eo.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACTNAME_CLASS +#define MY_CLASS_NAME "Ecordova_ContactName" + +static Eo_Base * +_ecordova_contactname_eo_base_constructor(Eo *obj, + Ecordova_ContactName_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->record = NULL; + int ret = contacts_record_create(_contacts_name._uri, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contactname_constructor(Eo *obj, + Ecordova_ContactName_Data *pd EINA_UNUSED, + const char *formatted, + const char *family_name, + const char *given_name, + const char *middle, + const char *prefix, + const char *suffix) +{ + DBG("(%p)", obj); + eo_do(obj, + ecordova_contactname_formatted_set(formatted), + ecordova_contactname_family_name_set(family_name), + ecordova_contactname_given_name_set(given_name), + ecordova_contactname_middle_set(middle), + ecordova_contactname_prefix_set(prefix), + ecordova_contactname_suffix_set(suffix)); +} + +static void +_ecordova_contactname_eo_base_destructor(Eo *obj, Ecordova_ContactName_Data *pd) +{ + DBG("(%p)", obj); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static const char * +_ecordova_contactname_formatted_get(Eo *obj, + Ecordova_ContactName_Data *pd EINA_UNUSED) +{ + Eo *parent = NULL; + eo_do(obj, parent = eo_parent_get()); + EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL); + + Ecordova_Contact_Data *parent_pd = eo_data_scope_get(parent, ECORDOVA_CONTACT_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(parent_pd, NULL); + + const char *value = NULL; + get_str_p(parent_pd->record, _contacts_contact.display_name, &value); + return value; +} + +static void +_ecordova_contactname_formatted_set(Eo *obj, + Ecordova_ContactName_Data *pd EINA_UNUSED, + const char *value) +{ + Eo *parent = NULL; + eo_do(obj, parent = eo_parent_get()); + EINA_SAFETY_ON_NULL_RETURN(parent); + + Ecordova_Contact_Data *parent_pd = eo_data_scope_get(parent, ECORDOVA_CONTACT_CLASS); + EINA_SAFETY_ON_NULL_RETURN(parent_pd); + + set_str(parent_pd->record, _contacts_contact.display_name, value); +} + +static const char * +_ecordova_contactname_family_name_get(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_name.last, &value); + return value; +} + +static void +_ecordova_contactname_family_name_set(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_name.last, value); +} + +static const char * +_ecordova_contactname_given_name_get(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_name.first, &value); + return value; +} + +static void +_ecordova_contactname_given_name_set(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_name.first, value); +} + +static const char * +_ecordova_contactname_middle_get(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_name.addition, &value); + return value; +} + +static void +_ecordova_contactname_middle_set(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_name.addition, value); +} + +static const char * +_ecordova_contactname_prefix_get(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_name.prefix, &value); + return value; +} + +static void +_ecordova_contactname_prefix_set(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_name.prefix, value); +} + +static const char * +_ecordova_contactname_suffix_get(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_name.suffix, &value); + return value; +} + +static void +_ecordova_contactname_suffix_set(Eo *obj EINA_UNUSED, + Ecordova_ContactName_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_name.suffix, value); +} + +bool +ecordova_contactname_import(Ecordova_ContactName *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_ContactName_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTNAME_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int ret; + + int count = 0; + ret = contacts_record_get_child_record_count(contacts_record, + _contacts_contact.name, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + EINA_SAFETY_ON_FALSE_RETURN_VAL(count == 1, false); + + contacts_record_h contactname_record = NULL; + ret = contacts_record_get_child_record_at_p(contacts_record, + _contacts_contact.name, + 0, + &contactname_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + ret = contacts_record_clone(contactname_record, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + // TODO: check display name + + return true; +} + +bool +ecordova_contactname_export(Ecordova_ContactName *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_ContactName_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTNAME_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + contacts_record_h contactname_record = NULL; + int ret = contacts_record_clone(pd->record, &contactname_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_add_child_record(contacts_record, + _contacts_contact.name, + contactname_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + // TODO: check display name + + return true; +} + +Ecordova_ContactName * +ecordova_contactname_clone(Ecordova_ContactName *other) +{ + Ecordova_ContactName *cloned = eo_add(ECORDOVA_CONTACTNAME_CLASS, NULL); + + Ecordova_ContactName_Data *cloned_pd = eo_data_scope_get(cloned, ECORDOVA_CONTACTNAME_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(cloned_pd, NULL); + Ecordova_ContactName_Data *other_pd = eo_data_scope_get(other, ECORDOVA_CONTACTNAME_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(other_pd, NULL); + + int ret = contacts_record_destroy(cloned_pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + ret = contacts_record_clone(other_pd->record, &cloned_pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return cloned; +} + + +#include "ecordova_contactname.eo.c" diff --git a/src/lib/ecordova/ecordova_contactname.eo b/src/lib/ecordova/ecordova_contactname.eo new file mode 100644 index 0000000000..435d714dbb --- /dev/null +++ b/src/lib/ecordova/ecordova_contactname.eo @@ -0,0 +1,58 @@ +class Ecordova.ContactName (Eo.Base) { + [[Contact name.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_ContactName constructor. + @.constructor + + @since 2.3 + ]] + params { + formatted: const(char)*; [[The complete name of the contact.]] + family_name: const(char)*; [[The contact's family name.]] + given_name: const(char)*; [[The contact's given name.]] + middle: const(char)*; [[The contact's middle name.]] + prefix: const(char)*; [[The contact's prefix (example Mr. or Dr.)]] + suffix: const(char)*; [[The contact's suffix (example Esq.).]] + } + } + @property formatted { + values { + formatted: const(char)*; + } + } + @property family_name { + values { + family_name: const(char)*; + } + } + @property given_name { + values { + given_name: const(char)*; + } + } + @property middle { + values { + middle: const(char)*; + } + } + @property prefix { + values { + prefix: const(char)*; + } + } + @property suffix { + values { + suffix: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_contactname_private.h b/src/lib/ecordova/ecordova_contactname_private.h new file mode 100644 index 0000000000..ccdcd61cb8 --- /dev/null +++ b/src/lib/ecordova/ecordova_contactname_private.h @@ -0,0 +1,25 @@ +#ifndef _ECORDOVA_CONTACTNAME_PRIVATE_H +#define _ECORDOVA_CONTACTNAME_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_contactname.eo.h" + +#include <contacts.h> +#include <stdbool.h> + +typedef struct _Ecordova_ContactName_Data Ecordova_ContactName_Data; + +/** + * Ecordova.ContactName private data + */ +struct _Ecordova_ContactName_Data +{ + Eo *obj; + contacts_record_h record; +}; + +bool ecordova_contactname_import(Ecordova_ContactName *, contacts_record_h); +bool ecordova_contactname_export(Ecordova_ContactName *, contacts_record_h); +Ecordova_ContactName *ecordova_contactname_clone(Ecordova_ContactName *); + +#endif diff --git a/src/lib/ecordova/ecordova_contactorganization.c b/src/lib/ecordova/ecordova_contactorganization.c new file mode 100644 index 0000000000..50cbde09cf --- /dev/null +++ b/src/lib/ecordova/ecordova_contactorganization.c @@ -0,0 +1,232 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contactorganization_private.h" +#include "ecordova_contacts_record_utils.h" + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACTORGANIZATION_CLASS +#define MY_CLASS_NAME "Ecordova_ContactOrganization" + +static Eo_Base * +_ecordova_contactorganization_eo_base_constructor(Eo *obj, + Ecordova_ContactOrganization_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->id = 0; + pd->record = NULL; + int ret = contacts_record_create(_contacts_company._uri, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contactorganization_constructor(Eo *obj, + Ecordova_ContactOrganization_Data *pd EINA_UNUSED, + Eina_Bool pref EINA_UNUSED, + const char *type, + const char *name, + const char *dept, + const char *title) +{ + DBG("(%p)", obj); + eo_do(obj, + ecordova_contactorganization_type_set(type), + ecordova_contactorganization_name_set(name), + ecordova_contactorganization_dept_set(dept), + ecordova_contactorganization_title_set(title)); +} + +static void +_ecordova_contactorganization_eo_base_destructor(Eo *obj, + Ecordova_ContactOrganization_Data *pd) +{ + DBG("(%p)", obj); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static int +_ecordova_contactorganization_id_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd) +{ + return pd->id; +} + +static Eina_Bool +_ecordova_contactorganization_pref_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd EINA_UNUSED) +{ + ERR("%s", "Not implemented"); + return EINA_FALSE; +} + +static void +_ecordova_contactorganization_pref_set(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd EINA_UNUSED, + Eina_Bool pref EINA_UNUSED) +{ + ERR("%s", "Not implemented"); +} + +static const char * +_ecordova_contactorganization_type_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd) +{ + int type; + if (!get_int(pd->record, _contacts_company.type, &type)) + return NULL; + + switch (type) + { + case CONTACTS_COMPANY_TYPE_WORK: + return "Work"; + case CONTACTS_COMPANY_TYPE_OTHER: + case CONTACTS_COMPANY_TYPE_CUSTOM: + default: + { + const char *custom = NULL; + get_str_p(pd->record, _contacts_company.label, &custom); + return custom; + } + } +} + +static void +_ecordova_contactorganization_type_set(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd, + const char *value) +{ + int type = CONTACTS_COMPANY_TYPE_OTHER; + if (!value || strlen(value) == 0) + type = CONTACTS_COMPANY_TYPE_OTHER; + else if (strcasecmp(value, "Work")) + type = CONTACTS_COMPANY_TYPE_WORK; + else + type = CONTACTS_COMPANY_TYPE_CUSTOM; + + set_int(pd->record, _contacts_company.type, type); + if (strlen(value) != 0) + set_str(pd->record, _contacts_company.label, value); +} + +static const char * +_ecordova_contactorganization_name_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_company.name, &value); + return value; +} + +static void +_ecordova_contactorganization_name_set(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_company.name, value); +} + +static const char * +_ecordova_contactorganization_dept_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_company.department, &value); + return value; +} + +static void +_ecordova_contactorganization_dept_set(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_company.department, value); +} + +static const char * +_ecordova_contactorganization_title_get(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd) +{ + const char *value = NULL; + get_str_p(pd->record, _contacts_company.job_title, &value); + return value; +} + +static void +_ecordova_contactorganization_title_set(Eo *obj EINA_UNUSED, + Ecordova_ContactOrganization_Data *pd, + const char *value) +{ + set_str(pd->record, _contacts_company.job_title, value); +} + +bool +ecordova_contactorganization_import(Ecordova_ContactOrganization *obj, + contacts_record_h child_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(child_record, false); + + Ecordova_ContactOrganization_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTORGANIZATION_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + int ret = contacts_record_destroy(pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + ret = contacts_record_clone(child_record, &pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return get_int(child_record, _contacts_company.id, &pd->id); +} + +bool +ecordova_contactorganization_export(Ecordova_ContactOrganization *obj, + contacts_record_h contacts_record) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(obj, false); + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + Ecordova_ContactOrganization_Data *pd = eo_data_scope_get(obj, ECORDOVA_CONTACTORGANIZATION_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd, false); + + contacts_record_h child_record = NULL; + int ret = contacts_record_clone(pd->record, &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_add_child_record(contacts_record, + _contacts_contact.company, + child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +Ecordova_ContactOrganization * +ecordova_contactorganization_clone(Ecordova_ContactOrganization *other) +{ + Ecordova_ContactOrganization *cloned = eo_add(ECORDOVA_CONTACTORGANIZATION_CLASS, NULL); + + Ecordova_ContactOrganization_Data *cloned_pd = eo_data_scope_get(cloned, ECORDOVA_CONTACTORGANIZATION_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(cloned_pd, NULL); + Ecordova_ContactOrganization_Data *other_pd = eo_data_scope_get(other, ECORDOVA_CONTACTORGANIZATION_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(other_pd, NULL); + + int ret = contacts_record_destroy(cloned_pd->record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + ret = contacts_record_clone(other_pd->record, &cloned_pd->record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + + return cloned; +} + +#include "ecordova_contactorganization.eo.c" diff --git a/src/lib/ecordova/ecordova_contactorganization.eo b/src/lib/ecordova/ecordova_contactorganization.eo new file mode 100644 index 0000000000..baf6b275cd --- /dev/null +++ b/src/lib/ecordova/ecordova_contactorganization.eo @@ -0,0 +1,72 @@ +class Ecordova.ContactOrganization (Eo.Base) { + [[Contact name.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_ContactOrganization constructor. + @.constructor + + @since 2.3 + ]] + params { + pref: bool; + [[Set to true if this ContactOrganization contains the user's + preferred value. + ]] + + type: const(char)*; + [[A string that indicates what type of field this is, home for + example. + ]] + + name: const(char)*; + [[The name of the organization.]] + + dept: const(char)*; + [[The department the contract works for.]] + + title: const(char)*; + [[The contact's title at the organization.]] + + } + } + @property id { + get {} + values { + id: int; + } + } + @property pref { + values { + pref: bool; + } + } + @property type { + values { + type: const(char)*; + } + } + @property name { + values { + name: const(char)*; + } + } + @property dept { + values { + dept: const(char)*; + } + } + @property title { + values { + title: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_contactorganization_private.h b/src/lib/ecordova/ecordova_contactorganization_private.h new file mode 100644 index 0000000000..643b11792d --- /dev/null +++ b/src/lib/ecordova/ecordova_contactorganization_private.h @@ -0,0 +1,25 @@ +#ifndef _ECORDOVA_CONTACTORGANIZATION_PRIVATE_H +#define _ECORDOVA_CONTACTORGANIZATION_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_contactorganization.eo.h" + +#include <contacts.h> + +typedef struct _Ecordova_ContactOrganization_Data Ecordova_ContactOrganization_Data; + +/** + * Ecordova.ContactOrganization private data + */ +struct _Ecordova_ContactOrganization_Data +{ + Eo *obj; + int id; + contacts_record_h record; +}; + +bool ecordova_contactorganization_import(Ecordova_ContactOrganization *, contacts_record_h); +bool ecordova_contactorganization_export(Ecordova_ContactOrganization *, contacts_record_h); +Ecordova_ContactOrganization *ecordova_contactorganization_clone(Ecordova_ContactOrganization *); + +#endif diff --git a/src/lib/ecordova/ecordova_contacts.c b/src/lib/ecordova/ecordova_contacts.c new file mode 100644 index 0000000000..c914ec08fb --- /dev/null +++ b/src/lib/ecordova/ecordova_contacts.c @@ -0,0 +1,519 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contacts_private.h" +#include "ecordova_contact_private.h" +#include "ecordova_contacts_record_utils.h" + +#include <contacts.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_CONTACTS_CLASS +#define MY_CLASS_NAME "Ecordova_Contacts" + +typedef enum Contacts_Search_Id { + SEARCH_CONTACT, + SEARCH_ADDRESS, + SEARCH_EVENT, + SEARCH_COMPANY, + SEARCH_EMAIL, + SEARCH_NAME, + SEARCH_NICKNAME, + SEARCH_MESSENGER, + SEARCH_NOTE, + SEARCH_NUMBER, + SEARCH_URL, + SEARCH_IMAGE, + SEARCH_ID_COUNT +} Contacts_Search_Id; + +static contacts_filter_h _filter_get(contacts_filter_h *, const char *); +static contacts_list_h _get_records(const Eina_List *, const Ecordova_Contacts_FindOptions *); +static bool _search_records(contacts_filter_h, const char *, contacts_list_h *, Eina_Bool); +static bool _populate_list(contacts_list_h, Eina_Hash *, contacts_filter_h, const char *, int, Eina_Bool); + +static Eo_Base * +_ecordova_contacts_eo_base_constructor(Eo *obj, Ecordova_Contacts_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_contacts_constructor(Eo *obj EINA_UNUSED, + Ecordova_Contacts_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_contacts_eo_base_destructor(Eo *obj, + Ecordova_Contacts_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_contacts_find(Eo *obj, + Ecordova_Contacts_Data *pd EINA_UNUSED, + const Eina_List *fields, + const Ecordova_Contacts_FindOptions *options) +{ + DBG("(%p)", obj); + if (!fields) + { + Ecordova_Contacts_Error error = ECORDOVA_CONTACTS_ERROR_INVALID_ARGUMENT_ERROR; + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_ERROR, &error)); + return; + } + + // TODO: load all records in a background thread + + contacts_list_h list = NULL; + + const char *field = eina_list_data_get(fields); + if (strcmp("*", field) == 0) + { + int ret = contacts_db_get_all_records(_contacts_contact._uri, 0, 0, &list); + if (CONTACTS_ERROR_NONE != ret) + { + ERR("%s returned %d", "contacts_db_get_all_records", ret); + Ecordova_Contacts_Error error = ECORDOVA_CONTACTS_ERROR_UNKNOWN_ERROR; + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_ERROR, &error)); + return; + } + } + else + { + if (!options) + { + ERR("%s", "options cannot be NULL"); + Ecordova_Contacts_Error error = ECORDOVA_CONTACTS_ERROR_INVALID_ARGUMENT_ERROR; + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_ERROR, &error)); + return; + } + + if (!options->filter) + { + ERR("%s", "options.filter cannot be NULL"); + Ecordova_Contacts_Error error = ECORDOVA_CONTACTS_ERROR_INVALID_ARGUMENT_ERROR; + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_ERROR, &error)); + return; + } + + list = _get_records(fields, options); + if (!list) + { + Ecordova_Contacts_Error error = ECORDOVA_CONTACTS_ERROR_INVALID_ARGUMENT_ERROR; + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_ERROR, &error)); + return; + } + } + + Eina_Array *contacts = eina_array_new(1); + do + { + contacts_record_h record = NULL; + int ret = contacts_list_get_current_record_p(list, &record); + if (CONTACTS_ERROR_NONE != ret || !record) + { + WRN("contacts_list_get_current_record_p returned %d", ret); + break; + } + + Ecordova_Contact *contact = eo_add(ECORDOVA_CONTACT_CLASS, NULL); + if (!ecordova_contact_import(contact, record)) + { + WRN("%s", "Error importing contact record"); + eo_unref(contact); + continue; + } + + eina_array_push(contacts, contact); + + } while (contacts_list_next(list) == CONTACTS_ERROR_NONE); + + eo_do(obj, eo_event_callback_call(ECORDOVA_CONTACTS_EVENT_FIND_SUCCESS, contacts)); + + int ret; + size_t i; + Ecordova_Contact *contact; + Eina_Array_Iterator it; + + EINA_ARRAY_ITER_NEXT(contacts, i, contact, it) + eo_unref(contact); + eina_array_free(contacts); + + ret = contacts_list_destroy(list, true); + EINA_SAFETY_ON_FALSE_RETURN(CONTACTS_ERROR_NONE == ret); +} + +static void +_ecordova_contacts_contact_pick(Eo *obj EINA_UNUSED, + Ecordova_Contacts_Data *pd EINA_UNUSED) +{ + // TODO: pick_contact UI +} + +static Ecordova_Contact * +_ecordova_contacts_create(Eo *obj EINA_UNUSED, + Ecordova_Contacts_Data *pd EINA_UNUSED, + Ecordova_Contact *other EINA_UNUSED) +{ + // TODO: clone 'other' if it's not NULL + return eo_add(ECORDOVA_CONTACT_CLASS, NULL); +} + +static contacts_filter_h +_filter_get(contacts_filter_h *filter, const char *uri) +{ + if (NULL == *filter) + { + int ret = contacts_filter_create(uri, filter); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + } + else + { + int ret = contacts_filter_add_operator(*filter, CONTACTS_FILTER_OPERATOR_OR); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, NULL); + } + + return *filter; +} + +static contacts_list_h +_get_records(const Eina_List *fields, + const Ecordova_Contacts_FindOptions *options) +{ + contacts_list_h list = NULL; + + typedef struct + { + const char *field; + unsigned int property_id; + enum {INT_FIELD, STRING_FIELD, BIRTHDAY_FIELD, ADDRESSES_FIELD} type; + Contacts_Search_Id search_id; + } Search_Metadata; + + Search_Metadata search_metadata[] = { + {"id", _contacts_contact.id, INT_FIELD, SEARCH_CONTACT}, + {"display_name", _contacts_contact.display_name, STRING_FIELD, SEARCH_CONTACT}, + {"formatted", _contacts_contact.display_name, STRING_FIELD, SEARCH_CONTACT}, + {"name", _contacts_contact.display_name, STRING_FIELD, SEARCH_CONTACT}, + {"given_name", _contacts_name.first, STRING_FIELD, SEARCH_NAME}, + {"family_name", _contacts_name.last, STRING_FIELD, SEARCH_NAME}, + {"honorific_prefix", _contacts_name.prefix, STRING_FIELD, SEARCH_NAME}, + {"honorific_suffix", _contacts_name.suffix, STRING_FIELD, SEARCH_NAME}, + {"middle_name", _contacts_name.addition, STRING_FIELD, SEARCH_NAME}, + {"country", _contacts_address.country, STRING_FIELD, SEARCH_ADDRESS}, + {"locality", _contacts_address.locality, STRING_FIELD, SEARCH_ADDRESS}, + {"postal_code", _contacts_address.postal_code, STRING_FIELD, SEARCH_ADDRESS}, + {"region", _contacts_address.region, STRING_FIELD, SEARCH_ADDRESS}, + {"street_address", _contacts_address.street, STRING_FIELD, SEARCH_ADDRESS}, + {"phone_numbers", _contacts_number.number, STRING_FIELD, SEARCH_NUMBER}, + {"emails", _contacts_email.email, STRING_FIELD, SEARCH_EMAIL}, + {"urls", _contacts_url.url, STRING_FIELD, SEARCH_URL}, + {"note", _contacts_note.note, STRING_FIELD, SEARCH_NOTE}, + {"ims", _contacts_messenger.im_id, STRING_FIELD, SEARCH_MESSENGER}, + {"nickname", _contacts_nickname.name, STRING_FIELD, SEARCH_NICKNAME}, + {"department", _contacts_company.department, STRING_FIELD, SEARCH_COMPANY}, + {"organizations", _contacts_company.name, STRING_FIELD, SEARCH_COMPANY}, + {"title", _contacts_company.job_title, STRING_FIELD, SEARCH_COMPANY}, + {"photos", _contacts_image.path, STRING_FIELD, SEARCH_IMAGE}, + {"birthday", _contacts_event.date, BIRTHDAY_FIELD, SEARCH_EVENT}, + {"addresses", 0, ADDRESSES_FIELD, SEARCH_ADDRESS}, + // TODO: categories: "categories", ? + {0} + }; + + const char *uri[SEARCH_ID_COUNT] = { + [SEARCH_CONTACT] = _contacts_contact._uri, + [SEARCH_ADDRESS] = _contacts_address._uri, + [SEARCH_EVENT] = _contacts_event._uri, + [SEARCH_COMPANY] = _contacts_company._uri, + [SEARCH_EMAIL] = _contacts_email._uri, + [SEARCH_NAME] = _contacts_name._uri, + [SEARCH_NICKNAME] = _contacts_nickname._uri, + [SEARCH_MESSENGER] = _contacts_messenger._uri, + [SEARCH_NOTE] = _contacts_note._uri, + [SEARCH_NUMBER] = _contacts_number._uri, + [SEARCH_URL] = _contacts_url._uri, + [SEARCH_IMAGE] = _contacts_image._uri + }; + + contacts_filter_h filters[SEARCH_ID_COUNT] = {NULL}; + int ret; + Eina_List *it; + const char *field; + EINA_LIST_FOREACH((Eina_List*)fields, it, field) + { + contacts_filter_h filter = NULL; + + int i = 0; + Search_Metadata *metadata = NULL; + while ((metadata = &search_metadata[i++]) && metadata->field) + { + if (strcmp(metadata->field, field) != 0) + continue; + + switch (metadata->type) + { + case INT_FIELD: + { + int value = 0; + if ((sscanf(options->filter, "%d", &value) != 1) && + (sscanf(options->filter, "%x", &value) != 1) && + (sscanf(options->filter, "%o", &value) != 1)) + { + ERR("Cannot convert options.filter to int to match the contact %s", field); + goto on_error_1; + } + + filter = _filter_get(&filters[metadata->search_id], uri[metadata->search_id]); + if (!filter) goto on_error_1; + + ret = contacts_filter_add_int(filter, metadata->property_id, CONTACTS_MATCH_EQUAL, value); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + break; + } + case STRING_FIELD: + { + filter = _filter_get(&filters[metadata->search_id], uri[metadata->search_id]); + if (!filter) goto on_error_1; + + ret = contacts_filter_add_str(filter, metadata->property_id, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + break; + } + case BIRTHDAY_FIELD: + { + int value = 0; + + // TODO: Convert options->filter as date string to int + + filter = _filter_get(&filters[metadata->search_id], uri[metadata->search_id]); + if (!filter) goto on_error_1; + + ret = contacts_filter_add_int(filter, metadata->property_id, CONTACTS_MATCH_EQUAL, value); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + int ret = contacts_filter_add_operator(filter, CONTACTS_FILTER_OPERATOR_AND); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_int(filter, _contacts_event.type, CONTACTS_MATCH_EQUAL, CONTACTS_EVENT_TYPE_BIRTH); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + break; + } + case ADDRESSES_FIELD: + { + filter = _filter_get(&filters[metadata->search_id], uri[metadata->search_id]); + if (!filter) goto on_error_1; + + ret = contacts_filter_add_str(filter, _contacts_address.country, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_operator(filter, CONTACTS_FILTER_OPERATOR_OR); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_str(filter, _contacts_address.region, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_operator(filter, CONTACTS_FILTER_OPERATOR_OR); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_str(filter, _contacts_address.locality, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_operator(filter, CONTACTS_FILTER_OPERATOR_OR); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_str(filter, _contacts_address.street, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_operator(filter, CONTACTS_FILTER_OPERATOR_OR); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_filter_add_str(filter, _contacts_address.postal_code, CONTACTS_MATCH_CONTAINS, options->filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + break; + } + } + } + } + + Eina_Hash *contact_ids = eina_hash_int32_new(NULL); + + if (filters[SEARCH_CONTACT]) + { + if (!_search_records(filters[SEARCH_CONTACT], uri[SEARCH_CONTACT], &list, options->multiple)) + goto on_error_2; + + do + { + contacts_record_h record = NULL; + ret = contacts_list_get_current_record_p(list, &record); + if (CONTACTS_ERROR_NONE != ret || !record) + { + WRN("contacts_list_get_current_record_p returned %d", ret); + break; + } + + int id; + bool ok = get_int(record, _contacts_contact.id, &id); + EINA_SAFETY_ON_FALSE_GOTO(ok, on_error_2); + + ok = eina_hash_add(contact_ids, &id, &id) == EINA_TRUE; + EINA_SAFETY_ON_FALSE_GOTO(ok, on_error_2); + + } while (contacts_list_next(list) == CONTACTS_ERROR_NONE); + + contacts_list_first(list); + } + else + { + ret = contacts_list_create(&list); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_2); + } + + + for (int search_id = 0; search_id < SEARCH_ID_COUNT; ++search_id) + { + if (SEARCH_CONTACT == search_id || !filters[search_id]) + continue; + + if (!options->multiple) + { + int count = 0; + ret = contacts_list_get_count(list, &count); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_2); + if (0 != count) break; + } + + if (!_populate_list(list, contact_ids, filters[search_id], uri[search_id], search_id, options->multiple)) + continue; + } + +on_error_2: + eina_hash_free(contact_ids); + +on_error_1: + for (int id = 0; id < SEARCH_ID_COUNT; ++id) + { + if (filters[id]) + contacts_filter_destroy(filters[id]); + } + + return list; +} + +static bool +_search_records(contacts_filter_h filter, + const char *uri, + contacts_list_h *list, + Eina_Bool multiple) +{ + int ret; + contacts_query_h query = NULL; + + ret = contacts_query_create(uri, &query); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_query_set_filter(query, filter); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_db_get_records_with_query(query, 0, multiple ? 0 : 1, list); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_1); + + ret = contacts_query_destroy(query); + EINA_SAFETY_ON_FALSE_GOTO(CONTACTS_ERROR_NONE == ret, on_error_2); + return true; + +on_error_2: + ret = contacts_list_destroy(*list, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + *list = NULL; + +on_error_1: + ret = contacts_query_destroy(query); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + return false; +} + +static bool +_populate_list(contacts_list_h list, + Eina_Hash *contact_ids, + contacts_filter_h filter, + const char *uri, + int search_id, + Eina_Bool multiple) +{ + contacts_list_h sub_list = NULL; + if (!_search_records(filter, uri, &sub_list, multiple)) + return false; + + unsigned int contact_id[SEARCH_ID_COUNT] = { + [SEARCH_CONTACT] = _contacts_contact.id, + [SEARCH_ADDRESS] = _contacts_address.contact_id, + [SEARCH_EVENT] = _contacts_event.contact_id, + [SEARCH_COMPANY] = _contacts_company.contact_id, + [SEARCH_EMAIL] = _contacts_email.contact_id, + [SEARCH_NAME] = _contacts_name.contact_id, + [SEARCH_NICKNAME] = _contacts_nickname.contact_id, + [SEARCH_MESSENGER] = _contacts_messenger.contact_id, + [SEARCH_NOTE] = _contacts_note.contact_id, + [SEARCH_NUMBER] = _contacts_number.contact_id, + [SEARCH_URL] = _contacts_url.contact_id, + [SEARCH_IMAGE] = _contacts_image.contact_id + }; + + int ret; + bool result = true; + do + { + contacts_record_h record = NULL; + ret = contacts_list_get_current_record_p(sub_list, &record); + if (CONTACTS_ERROR_NONE != ret || !record) + { + WRN("contacts_list_get_current_record_p returned %d", ret); + break; + } + + int id; + result = get_int(record, contact_id[search_id], &id); + EINA_SAFETY_ON_FALSE_GOTO(result, on_error); + + if (eina_hash_find(contact_ids, &id)) + continue; + + contacts_record_h contact_record = NULL; + result = contacts_db_get_record(uri, id, &contact_record) == CONTACTS_ERROR_NONE; + EINA_SAFETY_ON_FALSE_GOTO(result, on_error); + + if (!contact_record) continue; + + result = contacts_list_add(list, contact_record) == CONTACTS_ERROR_NONE; + EINA_SAFETY_ON_FALSE_GOTO(result, on_error); + + result = eina_hash_add(contact_ids, &id, &id) == EINA_TRUE; + EINA_SAFETY_ON_FALSE_GOTO(result, on_error); + + } while (multiple && contacts_list_next(sub_list) == CONTACTS_ERROR_NONE); + +on_error: + ret = contacts_list_destroy(sub_list, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + return result; +} + +#include "ecordova_contacts.eo.c" diff --git a/src/lib/ecordova/ecordova_contacts.eo b/src/lib/ecordova/ecordova_contacts.eo new file mode 100644 index 0000000000..0c968006ab --- /dev/null +++ b/src/lib/ecordova/ecordova_contacts.eo @@ -0,0 +1,76 @@ +struct Ecordova.Contacts.FindOptions { + [[Contact find options]] + filter: const(char)*; + [[The search string used to find contacts.]] + + multiple: bool; + [[used to determine if more than one contact should be returned]] + + desiredFields: list<char*>*; + [[Contact fields to be returned back. If specified, the resulting Contact + object only features values for these fields. (optional)]] +} + +enum Ecordova.Contacts.Error { + UNKNOWN_ERROR = 0, + INVALID_ARGUMENT_ERROR = 1, + TIMEOUT_ERROR = 2, + PENDING_OPERATION_ERROR = 3, + IO_ERROR = 4, + NOT_SUPPORTED_ERROR = 5, + PERMISSION_DENIED_ERROR = 20 +} + +class Ecordova.Contacts (Eo.Base) { + [[Ecordova Contacts Plugin + Plugin ID: org.apache.cordova.contacts + http://plugins.cordova.io/#/package/org.apache.cordova.contacts + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Contacts constructor. + @.constructor + + @since 2.3 + ]] + } + find { + [[Returns an array of Contacts matching the search criteria.]] + params { + fields: const(list<char*>)*; + [[fields that should be searched]] + + options: const(Ecordova.Contacts.FindOptions)*; + [[options that can be applied to contact searching]] + } + } + contact_pick { + [[This function picks contact from phone using contact picker UI]] + } + create { + [[This function creates a new contact, but it does not persist the contact + to device storage. To persist the contact to device storage, invoke + contact.save(). + ]] + params { + other: Ecordova.Contact*; + [[an object whose properties will be examined to create a new Contact]] + } + return: Ecordova.Contact*; + [[new Contact object]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + find,success: const(array<Ecordova.Contact*>)*; + pick,success: const(array<Ecordova.Contact*>)*; + error: Ecordova.Contacts.Error*; + } +} diff --git a/src/lib/ecordova/ecordova_contacts_private.h b/src/lib/ecordova/ecordova_contacts_private.h new file mode 100644 index 0000000000..7a60b2328b --- /dev/null +++ b/src/lib/ecordova/ecordova_contacts_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_CONTACTS_PRIVATE_H +#define _ECORDOVA_CONTACTS_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Contacts_Data Ecordova_Contacts_Data; + +/** + * Ecordova.Contacts private data + */ +struct _Ecordova_Contacts_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_contacts_record_utils.c b/src/lib/ecordova/ecordova_contacts_record_utils.c new file mode 100644 index 0000000000..09cab89d4f --- /dev/null +++ b/src/lib/ecordova/ecordova_contacts_record_utils.c @@ -0,0 +1,121 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contacts_record_utils.h" + +#include <Eina.h> + +bool +get_str(contacts_record_h record, unsigned int property_id, char **value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_get_str(record, property_id, value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +get_str_p(contacts_record_h record, unsigned int property_id, const char **value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_get_str_p(record, property_id, (char**)value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +set_str(contacts_record_h record, unsigned int property_id, const char *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_set_str(record, property_id, value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +get_int(contacts_record_h record, unsigned int property_id, int *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_get_int(record, property_id, value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +set_int(contacts_record_h record, unsigned int property_id, int value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_set_int(record, property_id, value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +get_bool(contacts_record_h record, unsigned int property_id, Eina_Bool *value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + bool raw_value; + int ret = contacts_record_get_bool(record, property_id, &raw_value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + *value = raw_value ? EINA_TRUE : EINA_FALSE; + return true; +} + +bool +set_bool(contacts_record_h record, unsigned int property_id, Eina_Bool value) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(record, false); + + int ret = contacts_record_set_bool(record, property_id, (bool)value); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + return true; +} + +bool +clear_all_contact_record(contacts_record_h contacts_record, + unsigned int property_id) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(contacts_record, false); + + int ret; + int count = 0; + ret = contacts_record_get_child_record_count(contacts_record, + property_id, + &count); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret || + CONTACTS_ERROR_NO_DATA == ret, false); + + for (int index = count - 1; index >= 0; --index) + { + contacts_record_h child_record = NULL; + ret = contacts_record_get_child_record_at_p(contacts_record, + property_id, + index, + &child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_remove_child_record(contacts_record, + property_id, + child_record); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + + ret = contacts_record_destroy(child_record, true); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONTACTS_ERROR_NONE == ret, false); + } + + return true; +} diff --git a/src/lib/ecordova/ecordova_contacts_record_utils.h b/src/lib/ecordova/ecordova_contacts_record_utils.h new file mode 100644 index 0000000000..9e9aa2d55e --- /dev/null +++ b/src/lib/ecordova/ecordova_contacts_record_utils.h @@ -0,0 +1,20 @@ +#ifndef _ECORDOVA_CONTACTS_RECORD_UTILS_H +#define _ECORDOVA_CONTACTS_RECORD_UTILS_H + +#include <Eina.h> +#include <contacts.h> +#include <stdbool.h> + +bool get_str(contacts_record_h, unsigned int, char **); +bool get_str_p(contacts_record_h, unsigned int, const char **); +bool set_str(contacts_record_h, unsigned int, const char *); + +bool get_int(contacts_record_h, unsigned int, int *); +bool set_int(contacts_record_h, unsigned int, int); + +bool get_bool(contacts_record_h, unsigned int, Eina_Bool *); +bool set_bool(contacts_record_h, unsigned int, Eina_Bool); + +bool clear_all_contact_record(contacts_record_h, unsigned int); + +#endif diff --git a/src/lib/ecordova/ecordova_device.c b/src/lib/ecordova/ecordova_device.c new file mode 100644 index 0000000000..0971292111 --- /dev/null +++ b/src/lib/ecordova/ecordova_device.c @@ -0,0 +1,107 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_device_private.h" + +#include <system/system_info.h> + +#define MY_CLASS ECORDOVA_DEVICE_CLASS +#define MY_CLASS_NAME "Ecordova_Device" + +static Eo_Base * +_ecordova_device_eo_base_constructor(Eo *obj, Ecordova_Device_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->uuid = NULL; + pd->version = NULL; + pd->model = NULL; + pd->platform = NULL; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_device_constructor(Eo *obj EINA_UNUSED, + Ecordova_Device_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_device_eo_base_destructor(Eo *obj, Ecordova_Device_Data *pd) +{ + DBG("(%p)", obj); + + free(pd->uuid); + free(pd->version); + free(pd->model); + free(pd->platform); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static const char * +_ecordova_device_ecordova_get(Eo *obj EINA_UNUSED, + Ecordova_Device_Data *pd EINA_UNUSED) +{ + ERR("Not implemented."); + return NULL; +} + +static const char * +_ecordova_device_model_get(Eo *obj EINA_UNUSED, Ecordova_Device_Data *pd) +{ + if (!pd->model) + { + int ret = system_info_get_platform_string("tizen.org/system/model_name", + &pd->model); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == SYSTEM_INFO_ERROR_NONE, NULL); + } + + return pd->model; +} + +static const char * +_ecordova_device_platform_get(Eo *obj EINA_UNUSED, Ecordova_Device_Data *pd) +{ + if (!pd->model) + { + int ret = system_info_get_platform_string("tizen.org/system/platform.name", + &pd->model); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == SYSTEM_INFO_ERROR_NONE, NULL); + } + + return pd->model; +} + +static const char * +_ecordova_device_uuid_get(Eo *obj EINA_UNUSED, Ecordova_Device_Data *pd) +{ + if (!pd->uuid) + { + int ret = system_info_get_platform_string("tizen.org/system/tizenid", + &pd->uuid); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == SYSTEM_INFO_ERROR_NONE, NULL); + } + + return pd->uuid; +} + +static const char * +_ecordova_device_version_get(Eo *obj EINA_UNUSED, Ecordova_Device_Data *pd) +{ + if (!pd->version) + { + // requires http://tizen.org/privilege/system (and not "systeminfo") privileges to be added in config.xml + int ret = system_info_get_platform_string("tizen.org/feature/platform.version", + &pd->version); + EINA_SAFETY_ON_FALSE_RETURN_VAL(ret == SYSTEM_INFO_ERROR_NONE, NULL); + } + + return pd->version; +} + +#include "ecordova_device.eo.c" diff --git a/src/lib/ecordova/ecordova_device.eo b/src/lib/ecordova/ecordova_device.eo new file mode 100644 index 0000000000..8221850c97 --- /dev/null +++ b/src/lib/ecordova/ecordova_device.eo @@ -0,0 +1,60 @@ +class Ecordova.Device (Eo.Base) { + [[Ecordova Device Plugin + Plugin ID: org.apache.cordova.device + http://plugins.cordova.io/#/package/org.apache.cordova.device + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Device constructor. + @.constructor + + @since 2.3 + ]] + } + @property ecordova { + [[Get the version of Ecordova running on the device.]] + get {} + values { + value: const(char)*; + } + } + @property model { + [[Returns the name of the device's model or product. The value is + set by the device manufacturer and may be different across + versions of the same product.]] + get {} + values { + value: const(char)*; + } + } + @property platform { + [[Get the device's operating system name.]] + get {} + values { + value: const(char)*; + } + } + @property uuid { + [[Get the device's Universally Unique Identifier (UUID).]] + get {} + values { + value: const(char)*; + } + } + @property version { + [[Get the operating system version.]] + get {} + values { + value: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_device_private.h b/src/lib/ecordova/ecordova_device_private.h new file mode 100644 index 0000000000..7bed6e5486 --- /dev/null +++ b/src/lib/ecordova/ecordova_device_private.h @@ -0,0 +1,20 @@ +#ifndef _ECORDOVA_DEVICE_PRIVATE_H +#define _ECORDOVA_DEVICE_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Device_Data Ecordova_Device_Data; + +/** + * Ecordova.Device private data + */ +struct _Ecordova_Device_Data +{ + Eo *obj; + char *uuid; + char *version; + char *model; + char *platform; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_devicemotion.c b/src/lib/ecordova/ecordova_devicemotion.c new file mode 100644 index 0000000000..3b6543cb42 --- /dev/null +++ b/src/lib/ecordova/ecordova_devicemotion.c @@ -0,0 +1,176 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_devicemotion_private.h" + +#define MY_CLASS ECORDOVA_DEVICEMOTION_CLASS +#define MY_CLASS_NAME "Ecordova_DeviceMotion" + +static void _on_sensor_event(sensor_h, sensor_event_s *, void *); +static void _hash_data_free(sensor_listener_h); +static sensor_listener_h _create_listener(Ecordova_DeviceMotion_Data *); + +static Eo_Base * +_ecordova_devicemotion_eo_base_constructor(Eo *obj, + Ecordova_DeviceMotion_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->sensor = NULL; + pd->listeners = eina_hash_int32_new(EINA_FREE_CB(_hash_data_free)); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_devicemotion_constructor(Eo *obj EINA_UNUSED, + Ecordova_DeviceMotion_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_devicemotion_eo_base_destructor(Eo *obj, + Ecordova_DeviceMotion_Data *pd) +{ + DBG("(%p)", obj); + + eina_hash_free(pd->listeners); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_devicemotion_current_acceleration_get(Eo *obj, + Ecordova_DeviceMotion_Data *pd) +{ + sensor_listener_h listener = _create_listener(pd); + if (!listener) + { + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEMOTION_EVENT_ERROR, + NULL)); + return; + } + + int ret = sensor_listener_start(listener); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + sensor_event_s event; + ret = sensor_listener_read_data(listener, &event); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + Ecordova_DeviceMotion_Acceleration acceleration = { + .x = event.values[0], + .y = event.values[1], + .z = event.values[2], + .timestamp = event.timestamp + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEMOTION_EVENT_CURRENT_SUCCESS, + &acceleration)); + +on_error: + ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN(SENSOR_ERROR_NONE == ret); +} + +static Ecordova_DeviceMotion_WatchID +_ecordova_devicemotion_acceleration_watch(Eo *obj, + Ecordova_DeviceMotion_Data *pd, + const Ecordova_DeviceMotion_AccelerometerOptions *options) +{ + sensor_listener_h listener = _create_listener(pd); + if (!listener) + { + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEMOTION_EVENT_ERROR, + NULL)); + return 0; + } + + const Ecordova_DeviceMotion_AccelerometerOptions default_options = { + .frequency = 10000 + }; + if (!options) + options = &default_options; + + int ret = sensor_listener_set_event_cb(listener, + options->frequency, + _on_sensor_event, + pd->obj); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + ret = sensor_listener_start(listener); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + static Ecordova_DeviceMotion_WatchID id = 0; + ++id; + eina_hash_add(pd->listeners, &id, listener); + return id; + +on_error: + ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, 0); + return 0; +} + +static void +_ecordova_devicemotion_watch_clear(Eo *obj, + Ecordova_DeviceMotion_Data *pd, + Ecordova_DeviceMotion_WatchID watch_id) +{ + sensor_listener_h listener = eina_hash_find(pd->listeners, &watch_id); + if (!listener) + { + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEMOTION_EVENT_ERROR, + NULL)); + return; + } + + Eina_Bool ret = eina_hash_del(pd->listeners, &watch_id, NULL); + EINA_SAFETY_ON_FALSE_RETURN(ret); +} + +static sensor_listener_h +_create_listener(Ecordova_DeviceMotion_Data *pd) +{ + int ret = SENSOR_ERROR_NONE; + + if (!pd->sensor) + { + ret = sensor_get_default_sensor(SENSOR_ACCELEROMETER, &pd->sensor); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, NULL); + } + + sensor_listener_h listener = NULL; + ret = sensor_create_listener(pd->sensor, &listener); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, NULL); + + return listener; +} + +static void +_on_sensor_event(sensor_h sensor EINA_UNUSED, + sensor_event_s *event, + void *user_data) +{ + Eo *obj = user_data; + + Ecordova_DeviceMotion_Acceleration acceleration = { + .x = event->values[0], + .y = event->values[1], + .z = event->values[2], + .timestamp = event->timestamp + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEMOTION_EVENT_WATCH_SUCCESS, + &acceleration)); +} + +static void +_hash_data_free(sensor_listener_h listener) +{ + int ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN(SENSOR_ERROR_NONE == ret); +} + +#include "ecordova_devicemotion.eo.c" diff --git a/src/lib/ecordova/ecordova_devicemotion.eo b/src/lib/ecordova/ecordova_devicemotion.eo new file mode 100644 index 0000000000..20640d3351 --- /dev/null +++ b/src/lib/ecordova/ecordova_devicemotion.eo @@ -0,0 +1,70 @@ +type Ecordova.DeviceMotion.WatchID: int; + +struct Ecordova.DeviceMotion.Acceleration { + x: double; [[Amount of acceleration on the x-axis. (in m/s^2)]] + y: double; [[Amount of acceleration on the y-axis. (in m/s^2)]] + z: double; [[Amount of acceleration on the z-axis. (in m/s^2)]] + timestamp: time; [[Creation timestamp in milliseconds.]] +} + +struct Ecordova.DeviceMotion.AccelerometerOptions { + frequency: int; + [[requested period of calls to accelerometerSuccess with acceleration data + in Milliseconds. (Default: 10000)]] +} + +class Ecordova.DeviceMotion (Eo.Base) { + [[Ecordova Device-Motion Plugin + Plugin ID: org.apache.cordova.device-motion + http://plugins.cordova.io/#/package/org.apache.cordova.device-motion + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_DeviceMotion constructor. + @.constructor + + @since 2.3 + ]] + params { + } + } + current_acceleration_get { + [[Get the current acceleration]] + } + acceleration_watch { + [[Retrieves the device's current Acceleration at a regular + interval, executing the accelerometerSuccess callback function + each time. Specify the interval in milliseconds via the + acceleratorOptions object's frequency parameter. + ]] + params { + options: const(Ecordova.DeviceMotion.AccelerometerOptions)*; + } + return: Ecordova.DeviceMotion.WatchID; + [[references the accelerometer's watch interval, and can be used + with navigator.accelerometer.watch_clear to stop watching the + accelerometer. + ]] + } + watch_clear { + [[Stop watching the Acceleration referenced by the watchID parameter.]] + params { + watch_id: Ecordova.DeviceMotion.WatchID; + [[The ID returned by navigator.accelerometer.acceleration_watch.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + current,success: Ecordova.DeviceMotion.Acceleration*; + watch,success: Ecordova.DeviceMotion.Acceleration*; + error; + } +} diff --git a/src/lib/ecordova/ecordova_devicemotion_private.h b/src/lib/ecordova/ecordova_devicemotion_private.h new file mode 100644 index 0000000000..fb75f4d810 --- /dev/null +++ b/src/lib/ecordova/ecordova_devicemotion_private.h @@ -0,0 +1,20 @@ +#ifndef _ECORDOVA_DEVICEMOTION_PRIVATE_H +#define _ECORDOVA_DEVICEMOTION_PRIVATE_H + +#include "ecordova_private.h" + +#include <sensor.h> + +typedef struct _Ecordova_DeviceMotion_Data Ecordova_DeviceMotion_Data; + +/** + * Ecordova.DeviceMotion private data + */ +struct _Ecordova_DeviceMotion_Data +{ + Eo *obj; + sensor_h sensor; + Eina_Hash *listeners; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_deviceorientation.c b/src/lib/ecordova/ecordova_deviceorientation.c new file mode 100644 index 0000000000..87a3d65dbe --- /dev/null +++ b/src/lib/ecordova/ecordova_deviceorientation.c @@ -0,0 +1,180 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_deviceorientation_private.h" + +#define MY_CLASS ECORDOVA_DEVICEORIENTATION_CLASS +#define MY_CLASS_NAME "Ecordova_DeviceOrientation" + +static void _on_sensor_event(sensor_h, sensor_event_s *, void *); +static void _hash_data_free(sensor_listener_h); +static sensor_listener_h _create_listener(Ecordova_DeviceOrientation_Data *); + +static Eo_Base * +_ecordova_deviceorientation_eo_base_constructor(Eo *obj, + Ecordova_DeviceOrientation_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->sensor = NULL; + pd->listeners = eina_hash_int32_new(EINA_FREE_CB(_hash_data_free)); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_deviceorientation_constructor(Eo *obj EINA_UNUSED, + Ecordova_DeviceOrientation_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_deviceorientation_eo_base_destructor(Eo *obj, + Ecordova_DeviceOrientation_Data *pd) +{ + DBG("(%p)", obj); + + eina_hash_free(pd->listeners); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_deviceorientation_current_heading_get(Eo *obj, + Ecordova_DeviceOrientation_Data *pd) +{ + sensor_listener_h listener = _create_listener(pd); + if (!listener) + { + Ecordova_DeviceOrientation_Error error = ECORDOVA_DEVICEORIENTATION_ERROR_COMPASS_INTERNAL_ERR; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_ERROR, + &error)); + return; + } + + int ret = sensor_listener_start(listener); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + sensor_event_s event; + ret = sensor_listener_read_data(listener, &event); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + // TODO: check the correct values + Ecordova_DeviceOrientation_Heading orientation = { + .magnetic_heading = event.values[0], + .true_heading = event.values[0], + .heading_accuracy = 0, + .timestamp = event.timestamp + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_CURRENT_SUCCESS, + &orientation)); + +on_error: + ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN(SENSOR_ERROR_NONE == ret); +} + +static Ecordova_DeviceOrientation_WatchID +_ecordova_deviceorientation_heading_watch(Eo *obj, + Ecordova_DeviceOrientation_Data *pd, + const Ecordova_DeviceOrientation_OrientationOptions *options) +{ + sensor_listener_h listener = _create_listener(pd); + if (!listener) + { + Ecordova_DeviceOrientation_Error error = ECORDOVA_DEVICEORIENTATION_ERROR_COMPASS_INTERNAL_ERR; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_ERROR, + &error)); + return 0; + } + + const Ecordova_DeviceOrientation_OrientationOptions default_options = { + .frequency = 100 + }; + if (!options) + options = &default_options; + + int ret = sensor_listener_set_event_cb(listener, + options->frequency, + _on_sensor_event, + pd->obj); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + ret = sensor_listener_start(listener); + EINA_SAFETY_ON_FALSE_GOTO(SENSOR_ERROR_NONE == ret, on_error); + + static Ecordova_DeviceOrientation_WatchID id = 0; + ++id; + eina_hash_add(pd->listeners, &id, listener); + return id; + +on_error: + ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, 0); + return 0; +} + +static void +_ecordova_deviceorientation_watch_clear(Eo *obj, + Ecordova_DeviceOrientation_Data *pd, + Ecordova_DeviceOrientation_WatchID watch_id) +{ + sensor_listener_h listener = eina_hash_find(pd->listeners, &watch_id); + if (!listener) + { + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_ERROR, + NULL)); + return; + } + + Eina_Bool ret = eina_hash_del(pd->listeners, &watch_id, NULL); + EINA_SAFETY_ON_FALSE_RETURN(ret); +} + +static sensor_listener_h +_create_listener(Ecordova_DeviceOrientation_Data *pd) +{ + int ret = SENSOR_ERROR_NONE; + + if (!pd->sensor) + { + ret = sensor_get_default_sensor(SENSOR_ORIENTATION, &pd->sensor); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, NULL); + } + + sensor_listener_h listener = NULL; + ret = sensor_create_listener(pd->sensor, &listener); + EINA_SAFETY_ON_FALSE_RETURN_VAL(SENSOR_ERROR_NONE == ret, NULL); + + return listener; +} + +static void +_on_sensor_event(sensor_h sensor EINA_UNUSED, + sensor_event_s *event, + void *user_data) +{ + Eo *obj = user_data; + + // TODO: check the correct values + Ecordova_DeviceOrientation_Heading orientation = { + .magnetic_heading = event->values[0], + .true_heading = event->values[0], + .heading_accuracy = 0, + .timestamp = event->timestamp + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_WATCH_SUCCESS, + &orientation)); +} + +static void +_hash_data_free(sensor_listener_h listener) +{ + int ret = sensor_destroy_listener(listener); + EINA_SAFETY_ON_FALSE_RETURN(SENSOR_ERROR_NONE == ret); +} + +#include "ecordova_deviceorientation.eo.c" diff --git a/src/lib/ecordova/ecordova_deviceorientation.eo b/src/lib/ecordova/ecordova_deviceorientation.eo new file mode 100644 index 0000000000..edf3b4a6c2 --- /dev/null +++ b/src/lib/ecordova/ecordova_deviceorientation.eo @@ -0,0 +1,87 @@ +type Ecordova.DeviceOrientation.WatchID: int; + +struct Ecordova.DeviceOrientation.Heading { + magnetic_heading: double; + [[The heading in degrees from 0-359.99 at a single moment in time.]] + + true_heading: double; + [[The heading relative to the geographic North Pole in degrees 0-359.99 at a + single moment in time. A negative value indicates that the true heading + can't be determined.]] + + heading_accuracy: double; + [[The deviation in degrees between the reported heading and the true heading.]] + + timestamp: time; + [[The time at which this heading was determined. (milliseconds)]] +} + +struct Ecordova.DeviceOrientation.OrientationOptions { + frequency: int; + [[How often to retrieve the compass heading in milliseconds. (Default: 100)]] + + filter: double; + [[The change in degrees required to initiate a heading_watch success + callback. When this value is set, frequency is ignored.]] +} + +enum Ecordova.DeviceOrientation.Error { + COMPASS_INTERNAL_ERR = 0, + COMPASS_NOT_SUPPORTED = 20 +} + +class Ecordova.DeviceOrientation (Eo.Base) { + [[Ecordova Device-Orientation Plugin + Plugin ID: org.apache.cordova.device-orientation + http://plugins.cordova.io/#/package/org.apache.cordova.device-orientation + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_DeviceOrientation constructor. + @.constructor + + @since 2.3 + ]] + } + current_heading_get { + [[Get the current compass heading. The compass heading is returned + via a CompassHeading object using the compassSuccess callback + function. + ]] + } + heading_watch { + [[Gets the device's current heading at a regular interval. Each + time the heading is retrieved, the headingSuccess callback + function is executed. + ]] + params { + options: const(Ecordova.DeviceOrientation.OrientationOptions)*; + } + return: Ecordova.DeviceOrientation.WatchID; + [[references the compass watch interval. The watch ID can be used + with navigator.compass.watch_clear to stop watching the + navigator.compass. + ]] + } + watch_clear { + [[Stop watching the compass referenced by the watch ID parameter.]] + params { + watch_id: Ecordova.DeviceOrientation.WatchID; + [[The ID returned by navigator.compass.heading_watch.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + current,success: Ecordova.DeviceOrientation.Heading*; + watch,success: Ecordova.DeviceOrientation.Heading*; + error: Ecordova.DeviceOrientation.Error; + } +} diff --git a/src/lib/ecordova/ecordova_deviceorientation_private.h b/src/lib/ecordova/ecordova_deviceorientation_private.h new file mode 100644 index 0000000000..a2f1c6dfea --- /dev/null +++ b/src/lib/ecordova/ecordova_deviceorientation_private.h @@ -0,0 +1,20 @@ +#ifndef _ECORDOVA_DEVICEORIENTATION_PRIVATE_H +#define _ECORDOVA_DEVICEORIENTATION_PRIVATE_H + +#include "ecordova_private.h" + +#include <sensor.h> + +typedef struct _Ecordova_DeviceOrientation_Data Ecordova_DeviceOrientation_Data; + +/** + * Ecordova.DeviceOrientation private data + */ +struct _Ecordova_DeviceOrientation_Data +{ + Eo *obj; + sensor_h sensor; + Eina_Hash *listeners; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_dialogs.c b/src/lib/ecordova/ecordova_dialogs.c new file mode 100644 index 0000000000..96b660ad9d --- /dev/null +++ b/src/lib/ecordova/ecordova_dialogs.c @@ -0,0 +1,75 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_dialogs_private.h" + +#define MY_CLASS ECORDOVA_DIALOGS_CLASS +#define MY_CLASS_NAME "Ecordova_Dialogs" + +static Eo_Base * +_ecordova_dialogs_eo_base_constructor(Eo *obj, Ecordova_Dialogs_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_dialogs_constructor(Eo *obj EINA_UNUSED, + Ecordova_Dialogs_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_dialogs_eo_base_destructor(Eo *obj, + Ecordova_Dialogs_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_dialogs_alert(Eo *obj EINA_UNUSED, + Ecordova_Dialogs_Data *pd EINA_UNUSED, + const char *message EINA_UNUSED, + const char *title EINA_UNUSED, + Eina_List *buttons EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_dialogs_confirm(Eo *obj EINA_UNUSED, + Ecordova_Dialogs_Data *pd EINA_UNUSED, + const char *message EINA_UNUSED, + const char *title EINA_UNUSED, + Eina_List *buttons EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_dialogs_prompt(Eo *obj EINA_UNUSED, + Ecordova_Dialogs_Data *pd EINA_UNUSED, + const char *message EINA_UNUSED, + const char *title EINA_UNUSED, + Eina_List *buttons EINA_UNUSED, + const char *default_text EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_dialogs_beep(Eo *obj EINA_UNUSED, + Ecordova_Dialogs_Data *pd EINA_UNUSED, + int times EINA_UNUSED) +{ + ERR("Not implemented."); +} + +#include "ecordova_dialogs.eo.c" diff --git a/src/lib/ecordova/ecordova_dialogs.eo b/src/lib/ecordova/ecordova_dialogs.eo new file mode 100644 index 0000000000..4f51b20b46 --- /dev/null +++ b/src/lib/ecordova/ecordova_dialogs.eo @@ -0,0 +1,87 @@ +struct Ecordova_Dialogs_PromptCallback { + button_index: int; + [[The index of the pressed button. (Number) Note that the index uses + one-based indexing, so the value is 1, 2, 3, etc.]] + + input1: const(char)*; + [[The text entered in the prompt dialog box.]] +} + +class Ecordova.Dialogs (Eo.Base) { + [[Ecordova Dialogs Plugin + Plugin ID: org.apache.cordova.dialogs + http://plugins.cordova.io/#/package/org.apache.cordova.dialogs + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Dialogs constructor. + @.constructor + + @since 2.3 + ]] + } + alert { + params { + message: const(char)*; + [[Dialog message.]] + + title: const(char)*; + [[Dialog title (Optional, defaults to 'Alert').]] + + buttons: list<const(char)*>*; + [[Button names (Optional, defaults to OK)]] + } + } + confirm { + params { + message: const(char)*; + [[Dialog message.]] + + title: const(char)*; + [[Dialog title (Optional, defaults to 'Confirm').]] + + buttons: list<const(char)*>*; + [[Button names (Optional, defaults to [OK,Cancel])]] + } + } + prompt { + params { + message: const(char)*; + [[Dialog message.]] + + title: const(char)*; + [[Dialog title (Optional, defaults to 'Prompt').]] + + buttons: list<const(char)*>*; + [[Button names (Optional, defaults to [OK,Cancel])]] + + default_text: const(char)*; + [[Default textbox input value (Optional, Default: empty string)]] + } + } + beep { + [[The device plays a beep sound.]] + params { + times: int; [[The number of times to repeat the beep.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + alert; + [[Callback to invoke when alert dialog is dismissed.]] + + confirm: int; + [[Callback to invoke with index of button pressed (1, 2, or 3) or when + the dialog is dismissed without a button press (0).]] + + prompt: Ecordova_Dialogs_PromptCallback; + } +} diff --git a/src/lib/ecordova/ecordova_dialogs_private.h b/src/lib/ecordova/ecordova_dialogs_private.h new file mode 100644 index 0000000000..3bc664ee45 --- /dev/null +++ b/src/lib/ecordova/ecordova_dialogs_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_DIALOGS_PRIVATE_H +#define _ECORDOVA_DIALOGS_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Dialogs_Data Ecordova_Dialogs_Data; + +/** + * Ecordova.Dialogs private data + */ +struct _Ecordova_Dialogs_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_directoryentry.c b/src/lib/ecordova/ecordova_directoryentry.c new file mode 100644 index 0000000000..44d6de7e60 --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryentry.c @@ -0,0 +1,324 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_directoryentry_private.h" +#include "ecordova_entry_private.h" + +#include <Ecore_File.h> + +#include <stdio.h> + +#define MY_CLASS ECORDOVA_DIRECTORYENTRY_CLASS +#define MY_CLASS_NAME "Ecordova_DirectoryEntry" + +static void _remove_notify(Eo *, void *); +static bool _is_absolute(const char *); +static void _directory_get(Eo *, void *); +static void _file_get(Eo *, void *); +static void _eio_directory_get_cb(void *, Eio_File *, const Eina_Stat *); +static void _eio_file_get_cb(void *, Eio_File *, const Eina_Stat *); +static void _eio_create_directory_cb(void *, Eio_File *, int); +static void _eio_fail_if_path_exists_cb(void *, Eio_File *, const Eina_Stat *); +static void _eio_mkdir_cb(void *, Eio_File *); +static void _set_data_path_name_native(Eio_Operation_Data *, const char *); +static void _eio_create_file_cb(void *, Eio_File *, int); + +static Eo_Base * +_ecordova_directoryentry_eo_base_constructor(Eo *obj, + Ecordova_DirectoryEntry_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_directoryentry_constructor(Eo *obj, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED, + const char *name, + const char *path, + Ecordova_FileSystem *file_system, + const char *url) +{ + DBG("(%p)", obj); + eo_do_super(obj, MY_CLASS, ecordova_entry_constructor(EINA_FALSE, + EINA_TRUE, + name, + path, + file_system, + url)); +} + +static void +_ecordova_directoryentry_eo_base_destructor(Eo *obj EINA_UNUSED, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_DirectoryReader * +_ecordova_directoryentry_reader_create(Eo *obj, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED) +{ + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(super, NULL); + + return eo_add(ECORDOVA_DIRECTORYREADER_CLASS, NULL, + ecordova_directoryreader_constructor(super->native)); +} + +static void +_ecordova_directoryentry_directory_get(Eo *obj, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED, + const char *path, + Ecordova_FileFlags flags) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(path); + + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(super); + + Eio_Operation_Data *data = _data_new(super, _directory_get, _error_notify); + _set_data_path_name_native(data, path); + + Eio_Stat_Cb eio_stat_cb = _eio_directory_get_cb; + Eio_Error_Cb eio_error_cb = _eio_error_cb; + + if (ECORDOVA_FILEFLAGS_CREATE & flags) + { + eio_error_cb = _eio_create_directory_cb; + if (ECORDOVA_FILEFLAGS_EXCLUSIVE & flags) + eio_stat_cb = _eio_fail_if_path_exists_cb; + } + + super->pending = eina_list_append(super->pending, data); + + data->file = eio_file_direct_stat(data->native, + eio_stat_cb, + eio_error_cb, + data); + EINA_SAFETY_ON_NULL_RETURN(data->file); +} + +static void +_eio_directory_get_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_Stat *stat EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + Ecordova_DirectoryEntry *directory = eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, NULL, + ecordova_directoryentry_constructor(data->name, data->path, NULL, data->native)); // TODO: filesystem? + + data->success_cb(data->pd->obj, directory); + _data_free(data); +} + +static void +_eio_file_get_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_Stat *stat EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + Ecordova_FileEntry *file = eo_add(ECORDOVA_FILEENTRY_CLASS, NULL, + ecordova_fileentry_constructor(data->name, data->path, NULL, data->native)); // TODO: filesystem? + + data->success_cb(data->pd->obj, file); + _data_free(data); +} + +static void +_eio_fail_if_path_exists_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_Stat *stat EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + Ecordova_FileError file_error = ECORDOVA_FILEERROR_PATH_EXISTS_ERR; + eo_do(data->pd->obj, + eo_event_callback_call(ECORDOVA_ENTRY_EVENT_ERROR, &file_error)); + _data_free(data); +} + +static void +_eio_create_directory_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + int error) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + if (ENOENT != error) + { + _error_notify(data->pd->obj, error); + _data_free(data); + return; + } + + data->file = eio_file_mkdir(data->native, + 0777, + _eio_mkdir_cb, + _eio_error_cb, + data); +} + +static void +_eio_create_file_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + int error) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + if (ENOENT != error) + { + _error_notify(data->pd->obj, error); + _data_free(data); + return; + } + + // TODO: Create the file in a background thread + FILE *fd = fopen(data->native, "ab+"); + if (!fd) + { + _error_notify(data->pd->obj, errno); + _data_free(data); + return; + } + fclose(fd); + + Ecordova_FileEntry *file = eo_add(ECORDOVA_FILEENTRY_CLASS, NULL, + ecordova_fileentry_constructor(data->name, data->path, NULL, data->native)); // TODO: filesystem? + data->success_cb(data->pd->obj, file); + _data_free(data); +} + +static void +_eio_mkdir_cb(void *user_data, Eio_File *handler EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + Ecordova_DirectoryEntry *directory = eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, NULL, + ecordova_directoryentry_constructor(data->name, data->path, NULL, data->native)); // TODO: filesystem? + data->success_cb(data->pd->obj, directory); + _data_free(data); +} + +static void +_directory_get(Eo *obj, void *data) +{ + Ecordova_DirectoryEntry *directory = data; + eo_do(obj, + eo_event_callback_call(ECORDOVA_DIRECTORYENTRY_EVENT_DIRECTORY_GET, directory)); + eo_unref(directory); +} + +static void +_file_get(Eo *obj, void *data) +{ + Ecordova_FileEntry *file = data; + eo_do(obj, eo_event_callback_call(ECORDOVA_DIRECTORYENTRY_EVENT_FILE_GET, file)); + eo_unref(file); +} + +static void +_ecordova_directoryentry_recursively_remove(Eo *obj, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(super); + + _entry_remove(super, _remove_notify, _error_notify, true); +} + +static void +_ecordova_directoryentry_file_get(Eo *obj, + Ecordova_DirectoryEntry_Data *pd EINA_UNUSED, + const char *path, + Ecordova_FileFlags flags) +{ + EINA_SAFETY_ON_NULL_RETURN(path); + DBG("(%p) path=%s", obj, path); + + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(super); + + Eio_Operation_Data *data = _data_new(super, _file_get, _error_notify); + _set_data_path_name_native(data, path); + + Eio_Stat_Cb eio_stat_cb = _eio_file_get_cb; + Eio_Error_Cb eio_error_cb = _eio_error_cb; + + if (ECORDOVA_FILEFLAGS_CREATE & flags) + { + eio_error_cb = _eio_create_file_cb; + if (ECORDOVA_FILEFLAGS_EXCLUSIVE & flags) + eio_stat_cb = _eio_fail_if_path_exists_cb; + } + + super->pending = eina_list_append(super->pending, data); + + data->file = eio_file_direct_stat(data->native, + eio_stat_cb, + eio_error_cb, + data); +} + +static void +_remove_notify(Eo *obj, void *data EINA_UNUSED) +{ + eo_do(obj, + eo_event_callback_call(ECORDOVA_DIRECTORYENTRY_EVENT_REMOVE_SUCCESS, NULL)); +} + +static bool +_is_absolute(const char *path) +{ + // TODO: not multiplatform + return path[0] == '/'; +} + +static void +_set_data_path_name_native(Eio_Operation_Data *data, const char *path) +{ + EINA_SAFETY_ON_NULL_RETURN(data); + EINA_SAFETY_ON_NULL_RETURN(path); + split_path(data->pd->native, path, &data->path, &data->name, &data->native); +} + +void +split_path(const char *working_dir, + const char *path, + char **dir, + char **name, + char **url) +{ + EINA_SAFETY_ON_NULL_RETURN(path); + EINA_SAFETY_ON_NULL_RETURN(dir); + EINA_SAFETY_ON_NULL_RETURN(name); + EINA_SAFETY_ON_NULL_RETURN(url); + + if (!working_dir || _is_absolute(path)) + *url = eina_file_path_sanitize(path); + else + { + size_t len = strlen(working_dir) + 1 + strlen(path) + 1; + char buffer[len]; + snprintf(buffer, len, "%s/%s", working_dir, path); // TODO: path separator ? + *url = eina_file_path_sanitize(buffer); + } + + const char *nameonly = ecore_file_file_get(*url); + EINA_SAFETY_ON_NULL_RETURN(nameonly); + *name = strdup(nameonly); + + *dir = ecore_file_dir_get(*url); +} + +#include "ecordova_directoryentry.eo.c" diff --git a/src/lib/ecordova/ecordova_directoryentry.eo b/src/lib/ecordova/ecordova_directoryentry.eo new file mode 100644 index 0000000000..d751d1d3a5 --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryentry.eo @@ -0,0 +1,75 @@ +enum Ecordova_FileFlags { + CREATE = 1, + EXCLUSIVE = 2 +} + +class Ecordova.DirectoryEntry (Ecordova.Entry) { + [[An interface representing a directory on the file system.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_DirectoryEntry constructor. + @.constructor + + @since 2.3 + ]] + params { + name: const(char)*; + [[name of the directory, excluding the path leading to it]] + + path: const(char)*; + [[the absolute full path to the directory]] + + file_system: Ecordova.FileSystem*; + [[filesystem on which the directory resides]] + + url: const(char)*; + [[an alternate URL which can be used by native webview + controls, for example media players.]] + } + } + reader_create { + [[Creates a new DirectoryReader to read entries from this directory]] + return: own(Ecordova.DirectoryReader*); + } + directory_get { + [[Creates or looks up a directory]] + params { + path: const(char)*; + [[either a relative or absolute path from this directory in + which to look up or create a directory]] + + flags: Ecordova_FileFlags; + [[options to create or exclusively create the directory]] + } + } + recursively_remove { + [[Deletes a directory and all of it's contents]] + } + file_get { + [[Creates or looks up a file]] + params { + path: const(char)*; + [[either a relative or absolute path from this directory in + which to look up or create a file]] + + flags: Ecordova_FileFlags; + [[options to create or exclusively create the file]] + + } + } + + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + directory,get: Ecordova.DirectoryEntry*; + remove,success; [[called with no parameters]] + file,get: Ecordova.FileEntry*; + } +} diff --git a/src/lib/ecordova/ecordova_directoryentry_private.h b/src/lib/ecordova/ecordova_directoryentry_private.h new file mode 100644 index 0000000000..2a5ac5472b --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryentry_private.h @@ -0,0 +1,18 @@ +#ifndef _ECORDOVA_DIRECTORYENTRY_PRIVATE_H +#define _ECORDOVA_DIRECTORYENTRY_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_DirectoryEntry_Data Ecordova_DirectoryEntry_Data; + +/** + * Ecordova.DirectoryEntry private data + */ +struct _Ecordova_DirectoryEntry_Data +{ + Eo *obj; +}; + +void split_path(const char *, const char *, char **, char **, char **); + +#endif diff --git a/src/lib/ecordova/ecordova_directoryreader.c b/src/lib/ecordova/ecordova_directoryreader.c new file mode 100644 index 0000000000..b92fcc9f50 --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryreader.c @@ -0,0 +1,161 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_directoryreader_private.h" +#include "ecordova_entry_private.h" + +#define MY_CLASS ECORDOVA_DIRECTORYREADER_CLASS +#define MY_CLASS_NAME "Ecordova_DirectoryReader" + +typedef struct +{ + Ecordova_DirectoryReader_Data *pd; + Eio_File *file; + Eina_List *entries; +} DirectoryReader_Data; + +static Eina_Bool _filter_cb(void *, Eio_File *, const Eina_File_Direct_Info *); +static void _main_cb(void *, Eio_File *, const Eina_File_Direct_Info *); +static void _done_cb(void *, Eio_File *); +static void _error_cb(void *, Eio_File *, int); +static void _directory_data_free(DirectoryReader_Data *); + +static Eo_Base * +_ecordova_directoryreader_eo_base_constructor(Eo *obj, + Ecordova_DirectoryReader_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->native = NULL; + pd->pending = NULL; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_directoryreader_constructor(Eo *obj, + Ecordova_DirectoryReader_Data *pd, + const char *native) +{ + DBG("(%p) url=%s", obj, native); + pd->native = strdup(native); +} + +static void +_ecordova_directoryreader_eo_base_destructor(Eo *obj, + Ecordova_DirectoryReader_Data *pd) +{ + DBG("(%p)", obj); + + free(pd->native); + + DirectoryReader_Data *data; + EINA_LIST_FREE(pd->pending, data) + { + eio_file_cancel(data->file); + _directory_data_free(data); + } + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_directoryreader_entries_read(Eo *obj, + Ecordova_DirectoryReader_Data *pd) +{ + DBG("(%p)", obj); + + DirectoryReader_Data *data = calloc(1, sizeof(DirectoryReader_Data)); + data->pd = pd; + data->file = eio_file_stat_ls(pd->native, + _filter_cb, + _main_cb, + _done_cb, + _error_cb, + data); + + pd->pending = eina_list_append(pd->pending, data); +} + +static Eina_Bool +_filter_cb(void *data EINA_UNUSED, + Eio_File *handler EINA_UNUSED, + const Eina_File_Direct_Info *info EINA_UNUSED) +{ + return EINA_TRUE; +} + +static void +_main_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_File_Direct_Info *info) +{ + DirectoryReader_Data *data = user_data; + Ecordova_Entry *entry = NULL; + + size_t len = info->path_length - info->name_length - 1; + char path[len + 1]; + strncpy(path, info->path, len); + path[len] = '\0'; + + const char *name = &info->path[info->name_start]; + + switch (info->type) + { + case EINA_FILE_DIR: + { + entry = eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, NULL, + ecordova_directoryentry_constructor(name, path, NULL, info->path)); // TODO: filesystem? + break; + } + case EINA_FILE_REG: + { + entry = eo_add(ECORDOVA_FILEENTRY_CLASS, NULL, + ecordova_fileentry_constructor(name, path, NULL, info->path)); // TODO: filesystem? + break; + } + // TODO: case EINA_FILE_LNK ? + default: break; + } + + if (entry) + data->entries = eina_list_append(data->entries, entry); +} + +static void +_done_cb(void *user_data, Eio_File *handler EINA_UNUSED) +{ + DirectoryReader_Data *data = user_data; + data->pd->pending = eina_list_remove(data->pd->pending, data); + + eo_do(data->pd->obj, + eo_event_callback_call(ECORDOVA_DIRECTORYREADER_EVENT_SUCCESS, + data->entries)); + _directory_data_free(data); +} + +static void +_error_cb(void *user_data, Eio_File *handler EINA_UNUSED, int error) +{ + DirectoryReader_Data *data = user_data; + data->pd->pending = eina_list_remove(data->pd->pending, data); + + Ecordova_FileError file_error = _translate_errno(error); + eo_do(data->pd->obj, + eo_event_callback_call(ECORDOVA_DIRECTORYREADER_EVENT_ERROR, + &file_error)); + _directory_data_free(data); +} + +static void +_directory_data_free(DirectoryReader_Data *data) +{ + Ecordova_Entry *entry; + EINA_LIST_FREE(data->entries, entry) + eo_unref(entry); + free(data); +} + +#include "ecordova_directoryreader.eo.c" diff --git a/src/lib/ecordova/ecordova_directoryreader.eo b/src/lib/ecordova/ecordova_directoryreader.eo new file mode 100644 index 0000000000..e8c6428136 --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryreader.eo @@ -0,0 +1,32 @@ +struct ProgressEvent; + +class Ecordova.DirectoryReader (Eo.Base) { + [[An interface that lists the files and directories in a directory.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_DirectoryReader constructor. + @.constructor + + @since 2.3 + ]] + params { + native: const(char)*; + } + } + entries_read { + [[Returns a list of entries from a directory.]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + success: const(list<Ecordova.Entry*>*); [[called with a list of entries]] + error: const(Ecordova_FileError)*; [[called with a FileError]] + } +} diff --git a/src/lib/ecordova/ecordova_directoryreader_private.h b/src/lib/ecordova/ecordova_directoryreader_private.h new file mode 100644 index 0000000000..b12467cbf5 --- /dev/null +++ b/src/lib/ecordova/ecordova_directoryreader_private.h @@ -0,0 +1,18 @@ +#ifndef _ECORDOVA_DIRECTORYREADER_PRIVATE_H +#define _ECORDOVA_DIRECTORYREADER_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_DirectoryReader_Data Ecordova_DirectoryReader_Data; + +/** + * Ecordova.DirectoryReader private data + */ +struct _Ecordova_DirectoryReader_Data +{ + Eo *obj; + char *native; + Eina_List *pending; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_entry.c b/src/lib/ecordova/ecordova_entry.c new file mode 100644 index 0000000000..ed5acb9f50 --- /dev/null +++ b/src/lib/ecordova/ecordova_entry.c @@ -0,0 +1,506 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_entry_private.h" +#include "ecordova_directoryentry_private.h" + +#define MY_CLASS ECORDOVA_ENTRY_CLASS +#define MY_CLASS_NAME "Ecordova_Entry" + +static void _eio_stat_cb(void *, Eio_File *, const Eina_Stat *); +static void _eio_moved_cb(void *, Eio_File *); +static void _eio_copied_cb(void *, Eio_File *); +static void _metadata_notify(Eo *, void *); +static void _move_notify(Eo *, void *); +static void _copy_notify(Eo *, void *); +static void _remove_notify(Eo *, void *); +static Eina_Bool _eio_remove_non_recursively_filter_cb(void *, Eio_File *, const Eina_File_Direct_Info *); +static void _parent_get_cb(void *, Eio_File *, const Eina_Stat *); +static void _parent_get_notify(Eo *, void *); + +static Eo_Base * +_ecordova_entry_eo_base_constructor(Eo *obj, Ecordova_Entry_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->name = NULL; + pd->path = NULL; + pd->file_system = NULL; + pd->native = NULL; + pd->pending = NULL; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_entry_constructor(Eo *obj, + Ecordova_Entry_Data *pd, + Eina_Bool file_is, + Eina_Bool directory_is, + const char *name, + const char *path, + Ecordova_FileSystem *file_system, + const char *url) +{ + EINA_SAFETY_ON_NULL_RETURN(name); + EINA_SAFETY_ON_NULL_RETURN(path); + //EINA_SAFETY_ON_NULL_RETURN(file_system); + EINA_SAFETY_ON_NULL_RETURN(url); + DBG("(%p) name=%s, path=%s, url=%s", obj, name, path, url); + + pd->is_file = file_is; + pd->is_directory = directory_is; + pd->name = strdup(name); + pd->path = strdup(path); + pd->file_system = eo_ref(file_system); + pd->native = strdup(url); +} + +static void +_ecordova_entry_eo_base_destructor(Eo *obj, Ecordova_Entry_Data *pd) +{ + DBG("(%p)", obj); + + free(pd->name); + free(pd->path); + eo_unref(pd->file_system); + free(pd->native); + + Eio_Operation_Data *data; + EINA_LIST_FREE(pd->pending, data) + { + eio_file_cancel(data->file); + _data_free(data); + } + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_entry_metadata_get(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + DBG("(%p)", obj); + _metadata_get(pd, _metadata_notify, _error_notify); +} + +static void +_ecordova_entry_metadata_set(Eo *obj EINA_UNUSED, + Ecordova_Entry_Data *pd EINA_UNUSED, + Ecordova_Metadata *metadata EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_entry_move(Eo *obj EINA_UNUSED, + Ecordova_Entry_Data *pd, + Ecordova_DirectoryEntry *parent, + const char *new_name) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(parent); + if (!new_name) new_name = pd->name; + + Eio_Operation_Data *data = _data_new(pd, _move_notify, _error_notify); + + Ecordova_Entry_Data *dest_dir = eo_data_scope_get(parent, ECORDOVA_ENTRY_CLASS); + + data->name = strdup(new_name); + data->path = strdup(dest_dir->native); + + // TODO: file_system? + + size_t len = strlen(data->path) + 1 + strlen(data->name) + 1; + data->native = malloc(len); + EINA_SAFETY_ON_NULL_GOTO(data->native, on_error); + snprintf(data->native, len, "%s/%s", data->path, new_name); + + if (pd->is_file) + { + DBG("Moving file from %s to %s", pd->native, data->native); + data->file = eio_file_move(pd->native, + data->native, + _eio_progress_cb, + _eio_moved_cb, + _eio_error_cb, + data); + } + else + { + DBG("Moving directory from %s to %s", pd->native, data->native); + data->file = eio_dir_move(pd->native, + data->native, + _eio_filter_cb, + _eio_progress_cb, + _eio_moved_cb, + _eio_error_cb, + data); + } + + pd->pending = eina_list_append(pd->pending, data); + return; + +on_error: + _data_free(data); +} + +static void +_ecordova_entry_copy(Eo *obj EINA_UNUSED, + Ecordova_Entry_Data *pd, + Ecordova_DirectoryEntry *parent, + const char *new_name) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(parent); + if (!new_name) new_name = pd->name; + + Eio_Operation_Data *data = _data_new(pd, _copy_notify, _error_notify); + + Ecordova_Entry_Data *dest_dir = eo_data_scope_get(parent, ECORDOVA_ENTRY_CLASS); + + data->name = strdup(new_name); + data->path = strdup(dest_dir->native); + + // TODO: file_system? + + size_t len = strlen(data->path) + 1 + strlen(data->name) + 1; + data->native = malloc(len); + EINA_SAFETY_ON_NULL_GOTO(data->native, on_error); + snprintf(data->native, len, "%s/%s", data->path, new_name); + + if (pd->is_file) + data->file = eio_file_copy(pd->native, + data->native, + _eio_progress_cb, + _eio_copied_cb, + _eio_error_cb, + data); + else + data->file = eio_dir_copy(pd->native, + data->native, + _eio_filter_cb, + _eio_progress_cb, + _eio_copied_cb, + _eio_error_cb, + data); + + pd->pending = eina_list_append(pd->pending, data); + return; + +on_error: + _data_free(data); +} + +static void +_ecordova_entry_remove(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + DBG("(%p)", obj); + _entry_remove(pd, _remove_notify, _error_notify, false); +} + +void +_entry_remove(Ecordova_Entry_Data *pd, + Ecordova_Entry_Success_Callback success_cb, + Ecordova_Entry_Error_Callback error_cb, + bool recursively) +{ + DBG("(%p)", pd->obj); + + Eio_Operation_Data *data = _data_new(pd, success_cb, error_cb); + + // TODO: file_system? + + if (pd->is_file) + data->file = eio_file_unlink(pd->native, + _eio_removed_cb, + _eio_error_cb, + data); + else + { + Eio_Filter_Direct_Cb filter_cb = + recursively ? _eio_filter_cb + : _eio_remove_non_recursively_filter_cb; + + data->file = eio_dir_unlink(pd->native, + filter_cb, + _eio_progress_cb, + _eio_removed_cb, + _eio_error_cb, + data); + } + + pd->pending = eina_list_append(pd->pending, data); +} + +static void +_ecordova_entry_parent_get(Eo *obj, Ecordova_Entry_Data *pd) +{ + DBG("(%p)", obj); + + Eio_Operation_Data *data = _data_new(pd, _parent_get_notify, _error_notify); + + data->file = eio_file_direct_stat(pd->native, + _parent_get_cb, + _eio_error_cb, + data); + + pd->pending = eina_list_append(pd->pending, data); + } + +static void +_parent_get_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_Stat *stat EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + data->success_cb(data->pd->obj, data->pd); + _data_free(data); +} + +static void +_parent_get_notify(Eo *obj, void *data) +{ + Ecordova_Entry_Data *pd = data; + + char *name, *path, *url; + split_path(NULL, pd->path, &path, &name, &url); + + Ecordova_DirectoryEntry *parent = + eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, + NULL, + ecordova_directoryentry_constructor(name, + path, + NULL, // TODO: filesystem ? + url)); + free(name); + free(path); + free(url); + + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_PARENT_GET, parent)); + eo_unref(parent); +} + +static Eina_Bool +_ecordova_entry_file_is_get(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + return pd->is_file; +} + +static Eina_Bool +_ecordova_entry_directory_is_get(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + return pd->is_directory; +} + +static const char * +_ecordova_entry_name_get(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + return pd->name; +} + +static const char * +_ecordova_entry_path_get(Eo *obj EINA_UNUSED, Ecordova_Entry_Data *pd) +{ + return pd->path; +} + +void +_metadata_get(Ecordova_Entry_Data *pd, + Ecordova_Entry_Success_Callback success_cb, + Ecordova_Entry_Error_Callback error_cb) +{ + DBG("(%p)", pd->obj); + + Eio_Operation_Data *data = _data_new(pd, success_cb, error_cb); + + data->file = eio_file_direct_stat(pd->native, + _eio_stat_cb, + _eio_error_cb, + data); + + pd->pending = eina_list_append(pd->pending, data); +} + +static void +_eio_stat_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_Stat *stat) +{ + Eio_Operation_Data *data = user_data; + Ecordova_Metadata metadata = { + .modification_date = (time_t)stat->mtime, + .size = stat->size + }; + + data->success_cb(data->pd->obj, &metadata); + _data_free(data); +} + +static void +_metadata_notify(Eo *obj, void *data) +{ + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_METADATA_GET, data)); +} + +void +_eio_error_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + int error) +{ + Eio_Operation_Data *data = user_data; + DBG("(%p)", data->pd->obj); + data->error_cb(data->pd->obj, error); + _data_free(data); +} + +void +_eio_progress_cb(void *data EINA_UNUSED, + Eio_File *handler EINA_UNUSED, + const Eio_Progress *info EINA_UNUSED) +{ +} + +static void +_eio_moved_cb(void *user_data, Eio_File *handler EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + free(data->pd->path); + free(data->pd->name); + free(data->pd->native); + data->pd->path = data->path; + data->pd->name = data->name; + data->pd->native = data->native; + data->path = NULL; + data->name = NULL; + data->native = NULL; + + data->success_cb(data->pd->obj, data->pd->obj); + _data_free(data); +} + +static void +_move_notify(Eo *obj, void *data) +{ + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_MOVE_SUCCESS, data)); +} + +static void +_eio_copied_cb(void *user_data, Eio_File *handler EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + Ecordova_Entry *entry = NULL; + if (data->pd->is_file) + entry = eo_add(ECORDOVA_FILEENTRY_CLASS, + NULL, + ecordova_fileentry_constructor(data->name, + data->path, + data->pd->file_system, + data->native)); + else + entry = eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, + NULL, + ecordova_directoryentry_constructor(data->name, + data->path, + data->pd->file_system, + data->native)); + + data->success_cb(data->pd->obj, entry); + + eo_unref(entry); + _data_free(data); +} + +static void +_copy_notify(Eo *obj, void *data) +{ + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_COPY_SUCCESS, data)); +} + +void +_eio_removed_cb(void *user_data, Eio_File *handler EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + data->success_cb(data->pd->obj, NULL); + _data_free(data); +} + +static void +_remove_notify(Eo *obj, void *data) +{ + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_REMOVE_SUCCESS, data)); +} + +Eina_Bool +_eio_filter_cb(void *data EINA_UNUSED, + Eio_File *handler EINA_UNUSED, + const Eina_File_Direct_Info *info EINA_UNUSED) +{ + return EINA_TRUE; +} + +static Eina_Bool +_eio_remove_non_recursively_filter_cb(void *user_data, + Eio_File *handler EINA_UNUSED, + const Eina_File_Direct_Info *info EINA_UNUSED) +{ + Eio_Operation_Data *data = user_data; + DBG("filter_cb: %s", info->path); + if (++data->count != 1) + { + eio_file_cancel(handler); + return EINA_FALSE; + } + + return EINA_TRUE; +} + +Eio_Operation_Data * +_data_new(Ecordova_Entry_Data *pd, + Ecordova_Entry_Success_Callback success_cb, + Ecordova_Entry_Error_Callback error_cb) +{ + Eio_Operation_Data *data = calloc(1, sizeof(Eio_Operation_Data)); + data->pd = pd; + data->success_cb = success_cb; + data->error_cb = error_cb; + return data; +} + +void +_data_free(Eio_Operation_Data *data) +{ + EINA_SAFETY_ON_NULL_RETURN(data); + + data->pd->pending = eina_list_remove(data->pd->pending, data); + free(data->path); + free(data->name); + free(data->native); + free(data); +} + +void +_error_notify(Eo *obj, int error) +{ + Ecordova_FileError file_error = _translate_errno(error); + eo_do(obj, eo_event_callback_call(ECORDOVA_ENTRY_EVENT_ERROR, &file_error)); +} + +Ecordova_FileError +_translate_errno(int error) +{ + // TODO: translate other errors + switch (error) + { + case EPERM: + case EACCES: + return ECORDOVA_FILEERROR_SECURITY_ERR; + case ENOENT: + return ECORDOVA_FILEERROR_NOT_FOUND_ERR; + } + + return -1; +} + +#include "ecordova_entry.eo.c" diff --git a/src/lib/ecordova/ecordova_entry.eo b/src/lib/ecordova/ecordova_entry.eo new file mode 100644 index 0000000000..044c9231a5 --- /dev/null +++ b/src/lib/ecordova/ecordova_entry.eo @@ -0,0 +1,114 @@ +struct Ecordova_Metadata { + modification_date: time; + size: long; +} + +class Ecordova.Entry (Eo.Base) { + [[Represents a file or directory on the local file system.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Entry constructor. + @.constructor + + @since 2.3 + ]] + params { + file_is: bool; + [[true if Entry is a file]] + + directory_is: bool; + [[true if Entry is a directory]] + + name: const(char)*; + [[name of the file or directory, excluding the path leading to it]] + + path: const(char)*; + [[the absolute full path to the file or directory]] + + file_system: Ecordova.FileSystem*; + [[the filesystem on which this entry resides]] + + url: const(char)*; + [[an alternate URL which can be used by native webview controls, + for example media players.]] + } + } + metadata_get { + [[Look up the metadata of the entry.]] + } + metadata_set { + [[Set the metadata of the entry.]] + params { + metadata: Ecordova_Metadata*; [[keys and values to set]] + } + } + move { + [[Move a file or directory to a new location.]] + params { + parent: Eo*/*Ecordova.DirectoryEntry*/; + [[the directory to which to move this entry]] + + new_name: const(char)*; + [[new name of the entry, defaults to the current name]] + } + } + copy { + [[Copy a directory to a different location.]] + params { + parent: Eo*/*Ecordova.DirectoryEntry*/; + [[the directory to which to copy this entry]] + + new_name: const(char)*; + [[new name of the entry, defaults to the current name]] + } + } + remove { + [[Remove a file or directory. It is an error to attempt to delete + a directory that is not empty. It is an error to attempt to + delete a root directory of a file system.]] + } + parent_get { + [[Look up the parent DirectoryEntry of this entry.]] + } + @property file_is { + get {} + values { + value: bool; + } + } + @property directory_is { + get {} + values { + value: bool; + } + } + @property name { + get {} + values { + value: const(char)*; + } + } + @property path { + get {} + values { + value: const(char)*; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + metadata,get: const(Ecordova_Metadata)*; + error: Ecordova_FileError*; + move,success: const(Ecordova.Entry)*; + copy,success: const(Ecordova.Entry)*; + remove,success; + parent,get: const(Eo)/*Ecordova.DirectoryEntry*/*; + } +} diff --git a/src/lib/ecordova/ecordova_entry_private.h b/src/lib/ecordova/ecordova_entry_private.h new file mode 100644 index 0000000000..5bcf00aac4 --- /dev/null +++ b/src/lib/ecordova/ecordova_entry_private.h @@ -0,0 +1,53 @@ +#ifndef _ECORDOVA_ENTRY_PRIVATE_H +#define _ECORDOVA_ENTRY_PRIVATE_H + +#include "ecordova_private.h" + +#include <Eio.h> + +#include <stdbool.h> + +typedef struct _Ecordova_Entry_Data Ecordova_Entry_Data; + +/** + * Ecordova.Entry private data + */ +struct _Ecordova_Entry_Data +{ + Eo *obj; + Eina_Bool is_file; + Eina_Bool is_directory; + char *name; + char *path; + Ecordova_FileSystem *file_system; + char *native; + Eina_List *pending; +}; + +typedef void(*Ecordova_Entry_Success_Callback)(Eo *, void *); +typedef void(*Ecordova_Entry_Error_Callback)(Eo *, int); + +typedef struct +{ + Ecordova_Entry_Data *pd; + Eio_File *file; + char *path; + char *name; + char *native; + Ecordova_Entry_Success_Callback success_cb; + Ecordova_Entry_Error_Callback error_cb; + unsigned long count; +} Eio_Operation_Data; + +Eio_Operation_Data *_data_new(Ecordova_Entry_Data *, Ecordova_Entry_Success_Callback, Ecordova_Entry_Error_Callback); +void _data_free(Eio_Operation_Data *); +void _metadata_get(Ecordova_Entry_Data *, Ecordova_Entry_Success_Callback, Ecordova_Entry_Error_Callback); +void _entry_remove(Ecordova_Entry_Data *, Ecordova_Entry_Success_Callback, Ecordova_Entry_Error_Callback, bool); +void _error_notify(Eo *, int); +void _eio_error_cb(void *, Eio_File *, int); +void _eio_removed_cb(void *, Eio_File *); +Eina_Bool _eio_filter_cb(void *, Eio_File *, const Eina_File_Direct_Info *); +void _eio_progress_cb(void *, Eio_File *, const Eio_Progress *); +Ecordova_FileError _translate_errno(int); + +#endif diff --git a/src/lib/ecordova/ecordova_file.c b/src/lib/ecordova/ecordova_file.c new file mode 100644 index 0000000000..69f5a61eb2 --- /dev/null +++ b/src/lib/ecordova/ecordova_file.c @@ -0,0 +1,128 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_file_private.h" + +#define MY_CLASS ECORDOVA_FILE_CLASS +#define MY_CLASS_NAME "Ecordova_File" + +static Eo_Base * +_ecordova_file_eo_base_constructor(Eo *obj, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_file_constructor(Eo *obj, + Ecordova_File_Data *pd, + const char *name, + const char *url, + const char *type, + time_t last_modified_date, + long size) +{ + DBG("(%p)", obj); + pd->name = name ? strdup(name) : strdup(""); + pd->url = url ? strdup(url) : NULL; + pd->type = type ? strdup(type) : NULL; + pd->modified_date = last_modified_date; + pd->size = size; + + pd->start = 0; + pd->end = pd->size; +} + +static void +_ecordova_file_eo_base_destructor(Eo *obj, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + + free(pd->name); + free(pd->url); + free(pd->type); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_File * +_ecordova_file_slice(Eo *obj EINA_UNUSED, + Ecordova_File_Data *pd, + long start, + long end) +{ + DBG("(%p) start=%ld end=%ld", obj, start, end); + long size = pd->end - pd->start; + long new_start = 0; + long new_end = size; + + if (start < 0) + new_start = MAX(size + start, 0); + else + new_start = MIN(size, start); + + if (end < 0) + new_end = MAX(size + end, 0); + else + new_end = MIN(end, size); + + if (new_start > new_end) + { + long aux = new_start; + new_start = new_end; + new_end = aux; + } + + Ecordova_File *new_file = eo_add(MY_CLASS, NULL, + ecordova_file_constructor(pd->name, + pd->url, + pd->type, + pd->modified_date, + pd->size)); + EINA_SAFETY_ON_NULL_RETURN_VAL(new_file, NULL); + + Ecordova_File_Data *new_file_pd = eo_data_scope_get(new_file, MY_CLASS); + EINA_SAFETY_ON_NULL_GOTO(new_file_pd, on_error); + + new_file_pd->start = pd->start + new_start; + new_file_pd->end = pd->start + new_end; + return new_file; + +on_error: + eo_unref(new_file); + return NULL; +} + +static const char * +_ecordova_file_name_get(Eo *obj EINA_UNUSED, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + return pd->name; +} + +static const char * +_ecordova_file_url_get(Eo *obj EINA_UNUSED, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + return pd->url; +} + +static long +_ecordova_file_start_get(Eo *obj EINA_UNUSED, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + return pd->start; +} + +static long +_ecordova_file_end_get(Eo *obj EINA_UNUSED, Ecordova_File_Data *pd) +{ + DBG("(%p)", obj); + return pd->end; +} + +#include "ecordova_file.eo.c" diff --git a/src/lib/ecordova/ecordova_file.eo b/src/lib/ecordova/ecordova_file.eo new file mode 100644 index 0000000000..68eb529b5b --- /dev/null +++ b/src/lib/ecordova/ecordova_file.eo @@ -0,0 +1,61 @@ +class Ecordova.File (Eo.Base) { + [[Ecordova File Plugin + Plugin ID: org.apache.cordova.file + http://plugins.cordova.io/#/package/org.apache.cordova.file + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_File constructor. + @.constructor + + @since 2.3 + ]] + params { + name: const(char)*; [[name of the file, without path information]] + url: const(char)*; [[the full path of the file, including the name]] + type: const(char)*; [[mime type]] + last_modified_date: time; [[last modified date]] + size: long; [[size of the file in bytes]] + } + } + slice { + params { + start: long; + end: long; + } + return: own(Ecordova.File*); + } + @property name { + get {} + values { + value: const(char)*; + } + } + @property url { + get {} + values { + value: const(char)*; + } + } + @property start { + get {} + values { + value: long; + } + } + @property end { + get {} + values { + value: long; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_file_private.h b/src/lib/ecordova/ecordova_file_private.h new file mode 100644 index 0000000000..9e17c1805b --- /dev/null +++ b/src/lib/ecordova/ecordova_file_private.h @@ -0,0 +1,23 @@ +#ifndef _ECORDOVA_FILE_PRIVATE_H +#define _ECORDOVA_FILE_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_File_Data Ecordova_File_Data; + +/** + * Ecordova.File private data + */ +struct _Ecordova_File_Data +{ + Eo *obj; + char *name; + char *url; + char *type; + time_t modified_date; + long size; + long start; + long end; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_fileentry.c b/src/lib/ecordova/ecordova_fileentry.c new file mode 100644 index 0000000000..337be04c7d --- /dev/null +++ b/src/lib/ecordova/ecordova_fileentry.c @@ -0,0 +1,109 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_fileentry_private.h" +#include "ecordova_entry_private.h" + +#define MY_CLASS ECORDOVA_FILEENTRY_CLASS +#define MY_CLASS_NAME "Ecordova_FileEntry" + +static void _file_notify(Eo *, void *); +static void _writer_create(Eo *, void *); + +static Eo_Base * +_ecordova_fileentry_eo_base_constructor(Eo *obj, Ecordova_FileEntry_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_fileentry_constructor(Eo *obj, + Ecordova_FileEntry_Data *pd EINA_UNUSED, + const char *name, + const char *path, + Ecordova_FileSystem *file_system, + const char *url) +{ + DBG("(%p)", obj); + eo_do_super(obj, MY_CLASS, ecordova_entry_constructor(EINA_TRUE, + EINA_FALSE, + name, + path, + file_system, + url)); +} + +static void +_ecordova_fileentry_eo_base_destructor(Eo *obj, + Ecordova_FileEntry_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_fileentry_writer_create(Eo *obj, + Ecordova_FileEntry_Data *pd EINA_UNUSED) +{ + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(super); + + _metadata_get(super, _writer_create, _error_notify); +} + +static void +_ecordova_fileentry_file(Eo *obj, Ecordova_FileEntry_Data *pd EINA_UNUSED) +{ + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN(super); + + _metadata_get(super, _file_notify, _error_notify); +} + +static Ecordova_File * +_create_file(Eo *obj, const Ecordova_Metadata *metadata) +{ + Ecordova_Entry_Data *super = eo_data_scope_get(obj, ECORDOVA_ENTRY_CLASS); + EINA_SAFETY_ON_NULL_RETURN_VAL(super, NULL); + + return eo_add(ECORDOVA_FILE_CLASS, NULL, + ecordova_file_constructor(super->name, + super->native, + NULL, //< TODO: mime-type? + metadata->modification_date, + metadata->size)); +} + +static void +_file_notify(Eo *obj, void *data) +{ + const Ecordova_Metadata *metadata = data; + + Ecordova_File *file = _create_file(obj, metadata); + + eo_do(obj, eo_event_callback_call(ECORDOVA_FILEENTRY_EVENT_FILE, file)); + eo_unref(file); +} + +static void +_writer_create(Eo *obj, void *data) +{ + const Ecordova_Metadata *metadata = data; + Ecordova_File *file = _create_file(obj, metadata); + Ecordova_FileWriter *writer = eo_add(ECORDOVA_FILEWRITER_CLASS, NULL, + ecordova_filewriter_constructor(file)); + + eo_do(obj, + eo_event_callback_call(ECORDOVA_FILEENTRY_EVENT_CREATE_WRITER, writer)); + + eo_unref(writer); + eo_unref(file); +} + +#include "ecordova_fileentry.eo.c" diff --git a/src/lib/ecordova/ecordova_fileentry.eo b/src/lib/ecordova/ecordova_fileentry.eo new file mode 100644 index 0000000000..c635c8e942 --- /dev/null +++ b/src/lib/ecordova/ecordova_fileentry.eo @@ -0,0 +1,48 @@ +class Ecordova.FileEntry (Ecordova.Entry) { + [[An interface representing a file on the file system.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_FileEntry constructor. + @.constructor + + @since 2.3 + ]] + params { + name: const(char)*; + [[name of the file, excluding the path leading to it]] + + path: const(char)*; + [[the absolute full path to the file]] + + file_system: Ecordova.FileSystem*; + [[filesystem on which the file resides]] + + url: const(char)*; + [[an alternate URL which can be used by native webview + controls, for example media players.]] + } + } + writer_create { + [[Creates a new FileWriter associated with the file that this + FileEntry represents.]] + } + file { + [[Returns a File that represents the current state of the file + that this FileEntry represents.]] + } + + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + create,writer: Ecordova.FileWriter*; + file: Ecordova.File*; + error: Ecordova_FileError*; + } +} diff --git a/src/lib/ecordova/ecordova_fileentry_private.h b/src/lib/ecordova/ecordova_fileentry_private.h new file mode 100644 index 0000000000..832aeb881c --- /dev/null +++ b/src/lib/ecordova/ecordova_fileentry_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_FILEENTRY_PRIVATE_H +#define _ECORDOVA_FILEENTRY_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_FileEntry_Data Ecordova_FileEntry_Data; + +/** + * Ecordova.FileEntry private data + */ +struct _Ecordova_FileEntry_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_filereader.c b/src/lib/ecordova/ecordova_filereader.c new file mode 100644 index 0000000000..e90bde02d5 --- /dev/null +++ b/src/lib/ecordova/ecordova_filereader.c @@ -0,0 +1,272 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filereader_private.h" +#include "ecordova_entry_private.h" + +#include <Eio.h> + +#define MY_CLASS ECORDOVA_FILEREADER_CLASS +#define MY_CLASS_NAME "Ecordova_FileReader" + +static void _read_cb(void *, Ecore_Thread *); +static void _progress_notify(size_t, size_t, Eo *, Ecore_Thread *); +static void _read_progress_cb(void *, Ecore_Thread *, void *); +static void _read_end_cb(void *, Ecore_Thread *); +static void _read_abort_cb(void *, Ecore_Thread *); + +static Eo_Base * +_ecordova_filereader_eo_base_constructor(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->error = 0; + pd->state = ECORDOVA_FILEREADER_STATE_EMPTY; + pd->result = NULL; + pd->result_length = 0; + pd->thread = NULL; + pd->url = NULL; + pd->offset = 0; + pd->length = 0; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_filereader_constructor(Eo *obj EINA_UNUSED, + Ecordova_FileReader_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_filereader_eo_base_destructor(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + + if (pd->thread) ERR("%s", "Destructing without aborting first"); + + free(pd->result); + free(pd->url); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_filereader_abort(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + + if (ECORDOVA_FILEREADER_STATE_DONE == pd->state || + ECORDOVA_FILEREADER_STATE_EMPTY == pd->state) + return; + pd->error = ECORDOVA_FILEERROR_ABORT_ERR; + pd->state = ECORDOVA_FILEREADER_STATE_DONE; + + if (pd->thread) + ecore_thread_cancel(pd->thread); +} + +static Ecordova_FileError +_ecordova_filereader_read(Eo *obj, + Ecordova_FileReader_Data *pd, + Ecordova_File *file) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN_VAL(file, ECORDOVA_FILEERROR_SYNTAX_ERR); + + if (ECORDOVA_FILEREADER_STATE_LOADING == pd->state) + return ECORDOVA_FILEERROR_INVALID_STATE_ERR; + pd->state = ECORDOVA_FILEREADER_STATE_LOADING; + pd->error = 0; + + const char *url; + eo_do(file, + url = ecordova_file_url_get(), + pd->offset = ecordova_file_start_get(), + pd->length = ecordova_file_end_get() - pd->offset); + EINA_SAFETY_ON_NULL_RETURN_VAL(url, ECORDOVA_FILEERROR_SYNTAX_ERR); + pd->url = strdup(url); + + Ecordova_ProgressEvent loadstart = {.type = "loadstart", .target = obj}; + eo_do(obj, eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_LOAD_START, &loadstart)); + + pd->thread = ecore_thread_feedback_run(_read_cb, + _read_progress_cb, + _read_end_cb, + _read_abort_cb, + pd, + EINA_FALSE); + return 0; +} + +static Ecordova_FileError +_ecordova_filereader_error_get(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + return pd->error; +} + +static const char * +_ecordova_filereader_result_get(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + return pd->result; +} + +static size_t +_ecordova_filereader_length_get(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + return pd->result_length; +} + +static Ecordova_FileReader_State +_ecordova_filereader_state_get(Eo *obj, Ecordova_FileReader_Data *pd) +{ + DBG("(%p)", obj); + return pd->state; +} + +static void +_read_cb(void *data, Ecore_Thread *thread) +{ + Ecordova_FileReader_Data *pd = data; + DBG("(%p)", pd->obj); + + if (ecore_thread_check(thread)) + return; + + FILE *stream = fopen(pd->url, "rb"); + if (!stream) + { + pd->error = _translate_errno(errno); + return; + } + + if (pd->offset > 0) + { + int error = fseek(stream, pd->offset, SEEK_SET); + if (error) + { + pd->error = _translate_errno(errno); + goto on_error; + } + } + + _progress_notify(0, pd->length, pd->obj, thread); + if (ecore_thread_check(thread)) + goto on_error; + + pd->result = pd->result ? realloc(pd->result, pd->length * sizeof(char)) + : malloc(pd->length * sizeof(char)); + if (NULL == pd->result) + { + pd->error = _translate_errno(errno); + goto on_error; + } + + pd->result_length = 0; + size_t total = pd->length; + char *buffer = pd->result; + do + { + size_t read = fread(buffer, sizeof(char), total, stream); + if (!read) + { + pd->error = _translate_errno(errno); + goto on_error; + } + + buffer += read; + total -= read; + pd->result_length += read; + + _progress_notify(pd->length - total, pd->length, pd->obj, thread); + if (ecore_thread_check(thread)) + goto on_error; + } + while (total > 0); + +on_error: + fclose(stream); +} + +static void +_progress_notify(size_t read, size_t total, Eo *obj, Ecore_Thread *thread) +{ + DBG("(%p)", obj); + Ecordova_ProgressEvent *progress = malloc(sizeof(Ecordova_ProgressEvent)); + *progress = (Ecordova_ProgressEvent) + { + .type = "progress", + .length_computable = EINA_TRUE, + .loaded = read, + .total = total, + .target = obj + }; + if (!ecore_thread_feedback(thread, progress)) + free(progress); +} + +static void +_read_progress_cb(void *data, + Ecore_Thread *thread EINA_UNUSED, + void *msg_data) +{ + Ecordova_FileReader_Data *pd = data; + DBG("(%p)", pd->obj); + Ecordova_ProgressEvent *event = msg_data; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_PROGRESS, &event)); + free(event); +} + +static void +_read_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileReader_Data *pd = data; + DBG("(%p)", pd->obj); + + // If DONE (cancelled), then don't do anything + if (ECORDOVA_FILEREADER_STATE_DONE == pd->state) + return; + pd->thread = NULL; + pd->state = ECORDOVA_FILEREADER_STATE_DONE; + + if (pd->error) + { + Ecordova_ProgressEvent error = {.type = "error", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_ERROR, &error)); + } + else + { + Ecordova_ProgressEvent load = {.type = "load", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_LOAD, &load)); + } + + Ecordova_ProgressEvent loadend = {.type = "loadend", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, &loadend)); +} + +static void +_read_abort_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileReader_Data *pd = data; + DBG("(%p)", pd->obj); + + pd->thread = NULL; + + Ecordova_ProgressEvent on_abort = {.type = "abort", .target = pd->obj}; + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_ABORT, &on_abort)); + + Ecordova_ProgressEvent loadend = {.type = "loadend", .target = pd->obj}; + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, &loadend)); +} + +#include "ecordova_filereader.eo.c" diff --git a/src/lib/ecordova/ecordova_filereader.eo b/src/lib/ecordova/ecordova_filereader.eo new file mode 100644 index 0000000000..4dcdd7a759 --- /dev/null +++ b/src/lib/ecordova/ecordova_filereader.eo @@ -0,0 +1,92 @@ +enum Ecordova_FileReader_State { + EMPTY = 0, + LOADING = 1, + DONE = 2 +} + +struct Ecordova_ProgressEvent { + type: const(char)*; + bubbles: bool; + cancel_bubble: bool; + cancelable: bool; + length_computable: bool; + loaded: int; + total: int; + target: Eo*; +} + +class Ecordova.FileReader (Eo.Base) { + [[This class reads the device file system.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_FileReader constructor. + @.constructor + + @since 2.3 + ]] + } + abort { + [[Abort reading file.]] + } + read { + [[Read file and return data as a binary data.]] + params { + file: Ecordova.File*; [[File object containing file properties]] + } + return: Ecordova_FileError; + } + @property error { + get {} + values { + value: Ecordova_FileError; + } + } + @property result { + get {} + values { + value: const(char)*; + } + } + @property length { + get {} + values { + value: size; + } + } + @property state { + get {} + values { + state: Ecordova_FileReader_State; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + on,load,start: const(Ecordova_ProgressEvent)*; + [[When read starts]] + + on,progress: const(Ecordova_ProgressEvent)*; + [[While reading (and decoding) file or fileBlob data, and reporting + partial file data (progress.loaded/progress.total)]] + + on,load: const(Ecordova_ProgressEvent)*; + [[When the read has successfully completed.]] + + on,error: const(Ecordova_ProgressEvent)*; + [[When the read has failed (see errors).]] + + on,load,end: const(Ecordova_ProgressEvent)*; + [[When the request has completed (either in success or failure).]] + + on,abort: const(Ecordova_ProgressEvent)*; + [[When the read has been aborted. For instance, by invoking the + abort() method.]] + } +} diff --git a/src/lib/ecordova/ecordova_filereader_private.h b/src/lib/ecordova/ecordova_filereader_private.h new file mode 100644 index 0000000000..bff6857eca --- /dev/null +++ b/src/lib/ecordova/ecordova_filereader_private.h @@ -0,0 +1,26 @@ +#ifndef _ECORDOVA_FILEREADER_PRIVATE_H +#define _ECORDOVA_FILEREADER_PRIVATE_H + +#include "ecordova_private.h" + +#include <Eio.h> + +typedef struct _Ecordova_FileReader_Data Ecordova_FileReader_Data; + +/** + * Ecordova.FileReader private data + */ +struct _Ecordova_FileReader_Data +{ + Eo *obj; + Ecordova_FileError error; + Ecordova_FileReader_State state; + char *result; + size_t result_length; + Ecore_Thread *thread; + char *url; + long offset; + long length; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_filesystem.c b/src/lib/ecordova/ecordova_filesystem.c new file mode 100644 index 0000000000..4086a84b68 --- /dev/null +++ b/src/lib/ecordova/ecordova_filesystem.c @@ -0,0 +1,38 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filesystem_private.h" + +#define MY_CLASS ECORDOVA_FILESYSTEM_CLASS +#define MY_CLASS_NAME "Ecordova_FileSystem" + +static Eo_Base * +_ecordova_filesystem_eo_base_constructor(Eo *obj, Ecordova_FileSystem_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_filesystem_constructor(Eo *obj EINA_UNUSED, + Ecordova_FileSystem_Data *pd EINA_UNUSED, + const char *name EINA_UNUSED, + Ecordova_DirectoryEntry *root EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_filesystem_eo_base_destructor(Eo *obj, + Ecordova_FileSystem_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +#include "ecordova_filesystem.eo.c" diff --git a/src/lib/ecordova/ecordova_filesystem.eo b/src/lib/ecordova/ecordova_filesystem.eo new file mode 100644 index 0000000000..fb201034c7 --- /dev/null +++ b/src/lib/ecordova/ecordova_filesystem.eo @@ -0,0 +1,25 @@ +class Ecordova.FileSystem (Eo.Base) { + [[An interface representing a file system]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_FileSystem constructor. + @.constructor + + @since 2.3 + ]] + params { + name: const(char)*; [[name the unique name of the file system]] + root: Eo*/*Ecordova.DirectoryEntry*/; [[root directory of the file system]] + } + } + + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_filesystem_private.h b/src/lib/ecordova/ecordova_filesystem_private.h new file mode 100644 index 0000000000..ef5756bdb3 --- /dev/null +++ b/src/lib/ecordova/ecordova_filesystem_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_FILESYSTEM_PRIVATE_H +#define _ECORDOVA_FILESYSTEM_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_FileSystem_Data Ecordova_FileSystem_Data; + +/** + * Ecordova.FileSystem private data + */ +struct _Ecordova_FileSystem_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_filetransfer.c b/src/lib/ecordova/ecordova_filetransfer.c new file mode 100644 index 0000000000..c0643e81f7 --- /dev/null +++ b/src/lib/ecordova/ecordova_filetransfer.c @@ -0,0 +1,613 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filetransfer_private.h" +#include "ecordova_directoryentry_private.h" + +#include <Ecore_Con.h> + +#define MY_CLASS ECORDOVA_FILETRANSFER_CLASS +#define MY_CLASS_NAME "Ecordova_FileTransfer" + +struct _Ecordova_FileTransfer_Job +{ + char *source; + char *target; + Ecore_File_Download_Job *download; + Ecore_Thread *upload; + int upload_error; + char *upload_buffer; + long int upload_length; + Ecore_Con_Url *upload_con_url; + Ecore_Event_Handler *progress_event_hanbler; + Ecore_Event_Handler *complete_event_hanbler; + Ecordova_FileTransfer_UploadOptions upload_options; +}; + +static int _download_progress_cb(void *, const char *, long int, long int, long int, long int); +static void _download_completion_cb(void *, const char *, int); +static void _clear(Ecordova_FileTransfer_Data *); +static void _abort_error_notify(Ecordova_FileTransfer_Data *); +static void _status_error_notify(Ecordova_FileTransfer_Data *, int); +static void _file_error_notify(Ecordova_FileTransfer_Data *, int); +static void _connection_error_notify(Ecordova_FileTransfer_Data *); +static void _already_in_progress_error_notify(Ecordova_FileTransfer_Data *, const char *, const char *); +static void _upload_cb(void *, Ecore_Thread *); +static void _upload_progress_notify(size_t, size_t, Eo *, Ecore_Thread *); +static void _upload_progress_cb(void *, Ecore_Thread *, void *); +static void _upload_end_cb(void *, Ecore_Thread *); +static void _upload_abort_cb(void *, Ecore_Thread *); +static void _progress_notify(Ecordova_FileTransfer_Data *, Ecordova_ProgressEvent *); +static Ecordova_FileTransfer_Job *_job_new(const char *, const char *); +static void _job_free(Ecordova_FileTransfer_Job **); +static Eina_Bool _url_progress_cb(void *, int, void *); +static Eina_Bool _url_complete_cb(void *, int, void *); + +static Eo_Base * +_ecordova_filetransfer_eo_base_constructor(Eo *obj, + Ecordova_FileTransfer_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->job = NULL; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_filetransfer_constructor(Eo *obj EINA_UNUSED, + Ecordova_FileTransfer_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_filetransfer_eo_base_destructor(Eo *obj, + Ecordova_FileTransfer_Data *pd) +{ + DBG("(%p)", obj); + + _clear(pd); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_filetransfer_upload(Eo *obj EINA_UNUSED, + Ecordova_FileTransfer_Data *pd EINA_UNUSED, + const char *file_url EINA_UNUSED, + const char *server, + Ecordova_FileTransfer_UploadOptions *options EINA_UNUSED, + Eina_Bool trust_all_hosts EINA_UNUSED) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(file_url); + EINA_SAFETY_ON_NULL_RETURN(server); + + if (pd->job) + { + _already_in_progress_error_notify(pd, file_url, server); + return; + } + + bool is_http = strncmp(server, "http://", 7) == 0; + bool is_https = strncmp(server, "https://", 8) == 0; + + if (!is_http && !is_https) + { + ERR("%s", "Invalid server address"); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_INVALID_URL_ERR, + .source = file_url, + .target = server, + .http_status = 0, + .body = NULL, + .exception = "Invalid server address" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); + return; + } + + pd->job = _job_new(file_url, server); + pd->job->upload = ecore_thread_feedback_run(_upload_cb, + _upload_progress_cb, + _upload_end_cb, + _upload_abort_cb, + pd, + EINA_FALSE); +} + +static void +_ecordova_filetransfer_download(Eo *obj, + Ecordova_FileTransfer_Data *pd, + const char *source, + const char *target, + Eina_Bool trust_all_hosts EINA_UNUSED, + Eina_Hash *options) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(source); + EINA_SAFETY_ON_NULL_RETURN(target); + + if (pd->job) + { + _already_in_progress_error_notify(pd, source, target); + return; + } + + Ecordova_FileTransfer_Job *job = _job_new(source, target); + + Eina_Bool ret; + if (options) + ret = ecore_file_download_full(source, + target, + _download_completion_cb, + _download_progress_cb, + pd, + &job->download, + options); + else + ret = ecore_file_download(source, + target, + _download_completion_cb, + _download_progress_cb, + pd, + &job->download); + + if (!ret) + { + _job_free(&job); + ERR("%s", "An error occurred downloading the file"); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_ABORT_ERR, + .source = source, + .target = target, + .http_status = 0, + .body = NULL, + .exception = "An error occurred downloading the file" + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); + return; + } + + pd->job = job; +} + +static void +_ecordova_filetransfer_abort(Eo *obj, Ecordova_FileTransfer_Data *pd) +{ + DBG("(%p)", obj); + + if (!pd->job) return; + + if (pd->job->download) + ecore_file_download_abort(pd->job->download); + else + ecore_thread_cancel(pd->job->upload); +} + +static int +_download_progress_cb(void *data, + const char *file EINA_UNUSED, + long int dltotal, + long int dlnow, + long int ultotal EINA_UNUSED, + long int ulnow EINA_UNUSED) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + Ecordova_ProgressEvent event = { + .type = "download", + .cancelable = true, + .length_computable = true, + .loaded = dlnow, + .total = dltotal, + .target = pd->obj + }; + _progress_notify(pd, &event); + return ECORE_FILE_PROGRESS_CONTINUE; +} + +static void +_download_completion_cb(void *data, const char *file, int status) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + if (1 == status) + _abort_error_notify(pd); + else if (200 != status) + _status_error_notify(pd, status); + else + { + char *path, *name, *native; + split_path(NULL, file, &path, &name, &native); + + Ecordova_FileEntry *file_entry = eo_add(ECORDOVA_FILEENTRY_CLASS, NULL, + ecordova_fileentry_constructor(name, path, NULL, native)); // TODO: filesystem? + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_DOWNLOAD_SUCCESS, file_entry)); + eo_unref(file_entry); + + free(path); + free(name); + free(native); + } + + _clear(pd); +} + +static void +_clear(Ecordova_FileTransfer_Data *pd) +{ + DBG("(%p)", pd->obj); + _job_free(&pd->job); +} + +static void +_upload_cb(void *data, Ecore_Thread *thread) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + + if (ecore_thread_check(thread)) + { + pd->job->upload_error = 1; + return; + } + + FILE *stream = fopen(pd->job->source, "rb"); + if (!stream) + { + pd->job->upload_error = errno; + return; + } + + int error = fseek(stream, 0L, SEEK_END); + if (error) + { + pd->job->upload_error = errno; + goto on_error; + } + + pd->job->upload_length = ftell(stream); + if (pd->job->upload_length < 0) + { + pd->job->upload_error = errno; + goto on_error; + } + + error = fseek(stream, 0L, SEEK_SET); + { + pd->job->upload_error = errno; + goto on_error; + } + + _upload_progress_notify(0, pd->job->upload_length, pd->obj, thread); + + pd->job->upload_buffer = malloc(pd->job->upload_length); + if (!pd->job->upload_buffer) + { + pd->job->upload_error = errno; + goto on_error; + } + + long int total_read = 0; + while (total_read < pd->job->upload_length) + { + size_t read = fread(&pd->job->upload_buffer[total_read], + sizeof(char), + pd->job->upload_length - total_read, + stream); + total_read += read; + + _upload_progress_notify(0, pd->job->upload_length, pd->obj, thread); + if (ecore_thread_check(thread)) + { + pd->job->upload_error = 1; + break; + } + } + +on_error: + fclose(stream); +} + +static void +_upload_progress_notify(size_t uploaded, + size_t total, + Eo *obj, + Ecore_Thread *thread) +{ + DBG("(%p)", obj); + + Ecordova_ProgressEvent *progress = malloc(sizeof(Ecordova_ProgressEvent)); + *progress = (Ecordova_ProgressEvent) + { + .type = "upload", + .length_computable = EINA_TRUE, + .loaded = uploaded, + .total = total, + .target = obj + }; + if (!ecore_thread_feedback(thread, progress)) + free(progress); +} + +static void +_upload_progress_cb(void *data, + Ecore_Thread *thread EINA_UNUSED, + void *msg_data) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + Ecordova_ProgressEvent *event = msg_data; + _progress_notify(pd, event); + free(event); +} + +static void +_upload_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + + if (1 == pd->job->upload_error) + _abort_error_notify(pd); + else if (pd->job->upload_error) + _file_error_notify(pd, pd->job->upload_error); + else + { + pd->job->upload_con_url = ecore_con_url_custom_new(pd->job->target, + pd->job->upload_options.http_method); + if (!pd->job->upload_con_url) + { + _connection_error_notify(pd); + goto on_error; + } + + pd->job->complete_event_hanbler = ecore_event_handler_add(ECORE_CON_EVENT_URL_COMPLETE, _url_complete_cb, pd); + pd->job->progress_event_hanbler = ecore_event_handler_add(ECORE_CON_EVENT_URL_PROGRESS, _url_progress_cb, pd); + if (!pd->job->complete_event_hanbler || !pd->job->progress_event_hanbler) + { + _connection_error_notify(pd); + goto on_error; + } + + if (pd->job->upload_options.headers) + { + Eina_Iterator *it = eina_hash_iterator_tuple_new(pd->job->upload_options.headers); + Eina_Hash_Tuple *tuple; + EINA_ITERATOR_FOREACH(it, tuple) + ecore_con_url_additional_header_add(pd->job->upload_con_url, tuple->key, tuple->data); + eina_iterator_free(it); + } + + Eina_Bool ret = ecore_con_url_post(pd->job->upload_con_url, + pd->job->upload_buffer, + pd->job->upload_length, + pd->job->upload_options.mime_type); + if (!ret) + { + _connection_error_notify(pd); + goto on_error; + } + + return; + } + +on_error: + _clear(pd); +} + +static void +_upload_abort_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileTransfer_Data *pd = data; + DBG("(%p)", pd->obj); + + _abort_error_notify(pd); + _clear(pd); +} + +static void +_progress_notify(Ecordova_FileTransfer_Data *pd, + Ecordova_ProgressEvent *event) +{ + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ON_PROGRESS, event)); +} + +static Ecordova_FileTransfer_Job * +_job_new(const char *source, const char *target) +{ + Ecordova_FileTransfer_Job *job = calloc(1, sizeof(Ecordova_FileTransfer_Job)); + job->source = strdup(source); + job->target = strdup(target); + job->upload_options = (Ecordova_FileTransfer_UploadOptions){ + .file_key = "file", + .file_name = "image.jpg", + .http_method = "POST", + .mime_type = "image/jpeg", + .chunked_mode = EINA_TRUE + }; + return job; +} + +static void +_job_free(Ecordova_FileTransfer_Job **job) +{ + if (!*job) return; + + if ((*job)->upload_con_url) + { + if ((*job)->progress_event_hanbler) + ecore_event_handler_del((*job)->progress_event_hanbler); + if ((*job)->complete_event_hanbler) + ecore_event_handler_del((*job)->complete_event_hanbler); + ecore_con_url_free((*job)->upload_con_url); + } + free((*job)->upload_buffer); + free((*job)->source); + free((*job)->target); + free(*job); + *job = NULL; +} + +static Eina_Bool +_url_progress_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event_info) +{ + Ecore_Con_Event_Url_Progress *url_progress = event_info; + Ecordova_FileTransfer_Data *pd = data; + + Ecordova_ProgressEvent event = { + .type = "upload", + .cancelable = false, + .length_computable = true, + .loaded = url_progress->up.now, + .total = url_progress->up.total, + .target = pd->obj + }; + _progress_notify(pd, &event); + + return EINA_TRUE; +} + +static Eina_Bool +_url_complete_cb(void *data, int type EINA_UNUSED, void *event_info) +{ + Ecore_Con_Event_Url_Complete *url_complete = event_info; + Ecordova_FileTransfer_Data *pd = data; + + if (200 != url_complete->status) + _status_error_notify(pd, url_complete->status); + else + { + Eina_Hash *headers_hash = NULL; + const Eina_List *headers = ecore_con_url_response_headers_get(url_complete->url_con); + if (eina_list_count(headers)) + { + headers_hash = eina_hash_string_superfast_new(free); + const char *header_line; + const Eina_List *it; + EINA_LIST_FOREACH(headers, it, header_line) + { + const char *separator = strchr(header_line, ':'); + if (!separator) continue; + + size_t key_len = separator - header_line + 1; + char key[key_len]; + strncpy(key, header_line, key_len); + key[key_len - 1] = '\0'; + + if (*(++separator) == ' ') + ++separator; + char *value = strdup(separator); + eina_hash_add(headers_hash, key, value); + } + } + + Ecordova_FileTransfer_UploadResult result = { + .bytes_sent = pd->job->upload_length, + .response_code = url_complete->status, + .headers = headers_hash, + .response = NULL // TODO: Get the HTTP response + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_UPLOAD_SUCCESS, + &result)); + if (headers_hash) + eina_hash_free(headers_hash); + } + + _clear(pd); + return EINA_TRUE; +} + +static void +_abort_error_notify(Ecordova_FileTransfer_Data *pd) +{ + INF("%s", "Aborted"); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_ABORT_ERR, + .source = pd->job->source, + .target = pd->job->target, + .http_status = 0, + .body = NULL, + .exception = "Aborted" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); +} + +static void +_status_error_notify(Ecordova_FileTransfer_Data *pd, int status) +{ + ERR("Error status: %d", status); + Ecordova_FileTransfer_Error error = { + // TODO: translate other errors checking first which protocol it is. + .code = 404 == status ? ECORDOVA_FILETRANSFER_ERRORCODE_FILE_NOT_FOUND_ERR : + ECORDOVA_FILETRANSFER_ERRORCODE_ABORT_ERR, + .source = pd->job->source, + .target = pd->job->target, + .http_status = status, + .body = NULL, + .exception = "Error" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); +} + +static void +_file_error_notify(Ecordova_FileTransfer_Data *pd, int code) +{ + ERR("Error code: %d", code); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_FILE_NOT_FOUND_ERR, + .source = pd->job->source, + .target = pd->job->target, + .http_status = 0, + .body = NULL, + .exception = "Internal error" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); +} + +static void +_connection_error_notify(Ecordova_FileTransfer_Data *pd) +{ + ERR("%s", "Connection error"); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_CONNECTION_ERR, + .source = pd->job->source, + .target = pd->job->target, + .http_status = 0, + .body = NULL, + .exception = "Connection error" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); +} + +static void +_already_in_progress_error_notify(Ecordova_FileTransfer_Data *pd, + const char *source, + const char *target) +{ + ERR("%s", "A job is already in progress"); + Ecordova_FileTransfer_Error error = { + .code = ECORDOVA_FILETRANSFER_ERRORCODE_ABORT_ERR, + .source = source, + .target = target, + .http_status = 0, + .body = NULL, + .exception = "A job is already in progress" + }; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILETRANSFER_EVENT_ERROR, &error)); +} + +#include "ecordova_filetransfer.eo.c" diff --git a/src/lib/ecordova/ecordova_filetransfer.eo b/src/lib/ecordova/ecordova_filetransfer.eo new file mode 100644 index 0000000000..19faa3d298 --- /dev/null +++ b/src/lib/ecordova/ecordova_filetransfer.eo @@ -0,0 +1,155 @@ +struct Ecordova_FileTransfer_UploadOptions { + file_key: const(char)*; + [[The name of the form element. Defaults to file.]] + + file_name: const(char)*; + [[The file name to use when saving the file on the server. Defaults to image.jpg.]] + + http_method: const(char)*; + [[The HTTP method to use - either PUT or POST. Defaults to POST.]] + + mime_type: const(char)*; + [[The mime type of the data to upload. Defaults to image/jpeg.]] + + params: hash<const(char)*, const(char)*>*; + [[A set of optional key/value pairs to pass in the HTTP request.]] + + chunked_mode: bool; + [[Whether to upload the data in chunked streaming mode. Defaults to true.]] + + headers: hash<const(char)*, const(char)*>*; + [[A map of header name/header values. Use an array to specify more than one value.]] +} + +struct Ecordova_FileTransfer_UploadResult { + bytes_sent: long; + [[The number of bytes sent to the server as part of the upload.]] + + response_code: long; + [[The HTTP response code returned by the server.]] + + response: const(char)*; + [[The HTTP response returned by the server.]] + + headers: hash<const(char)*, const(char)*>*; + [[The HTTP response headers by the server.]] +} + +enum Ecordova_FileTransfer_ErrorCode { + FILE_NOT_FOUND_ERR = 1, + INVALID_URL_ERR = 2, + CONNECTION_ERR = 3, + ABORT_ERR = 4, + NOT_MODIFIED_ERR = 5 +} + +struct Ecordova_FileTransfer_Error { + [[A FileTransferError object is passed to an error callback when an error occurs.]] + + code: int; + [[One of the predefined error codes listed above.]] + + source: const(char)*; + [[URL to the source.]] + + target: const(char)*; + [[URL to the target.]] + + http_status: int; + [[HTTP status code. This attribute is only available when a response code is received from the HTTP connection.]] + + body: const(char)*; + [[Response body. This attribute is only available when a response is received from the HTTP connection.]] + + exception: const(char)*; + [[Either e.getMessage or e.toString]] +} + +struct Ecordova.ProgressEvent; + +class Ecordova.FileTransfer (Eo.Base) { + [[Ecordova File-Transfer Plugin + Plugin ID: org.apache.cordova.file-transfer + http://plugins.cordova.io/#/package/org.apache.cordova.file-transfer + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_FileTransfer constructor. + @.constructor + + @since 2.3 + ]] + } + upload { + [[sends a file to a server.]] + params { + file_url: const(char)*; + [[Filesystem URL representing the file on the device. For + backwards compatibility, this can also be the full path of + the file on the device.]] + + server: const(char)*; + [[URL of the server to receive the file, as encoded by + encodeURI().]] + + options: Ecordova_FileTransfer_UploadOptions*; + [[Optional parameters]] + + trust_all_hosts: bool; + [[Optional parameter, defaults to false. If set to true, it + accepts all security certificates. This is useful since + Android rejects self-signed security certificates. Not + recommended for production use.]] + } + } + download { + [[downloads a file from server.]] + params { + source: const(char)*; + [[URL of the server to download the file, as encoded by + encodeURI().]] + + target: const(char)*; + [[Filesystem url representing the file on the device. For + backwards compatibility, this can also be the full path of + the file on the device]] + + trust_all_hosts: bool; + [[Optional parameter, defaults to false. If set to true, it + accepts all security certificates. This is useful since + Android rejects self-signed security certificates. Not + recommended for production use.]] + + options: hash<const(char)*, const(char)*>*; + [[Optional parameters, currently only supports headers (such + as Authorization (Basic Authentication), etc).]] + } + } + abort { + [[Aborts an in-progress transfer.]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + upload,success: const(Ecordova_FileTransfer_UploadResult)*; + [[A callback that is passed a FileUploadResult object.]] + + download,success: Ecordova.FileEntry*; + [[callback that is passed a FileEntry object.]] + + error: Ecordova_FileTransfer_Error*; + [[A callback that executes if an error occurs. Invoked with a + FileTransferError object.]] + + on,progress: const(Ecordova.ProgressEvent)*; + [[Called with a ProgressEvent whenever a new chunk of data is + transferred.]] + } +} diff --git a/src/lib/ecordova/ecordova_filetransfer_private.h b/src/lib/ecordova/ecordova_filetransfer_private.h new file mode 100644 index 0000000000..b17fe85e9a --- /dev/null +++ b/src/lib/ecordova/ecordova_filetransfer_private.h @@ -0,0 +1,21 @@ +#ifndef _ECORDOVA_FILETRANSFER_PRIVATE_H +#define _ECORDOVA_FILETRANSFER_PRIVATE_H + +#include "ecordova_private.h" + +#include <Ecore_File.h> + +typedef struct _Ecordova_FileTransfer_Data Ecordova_FileTransfer_Data; + +typedef struct _Ecordova_FileTransfer_Job Ecordova_FileTransfer_Job; + +/** + * Ecordova.FileTransfer private data + */ +struct _Ecordova_FileTransfer_Data +{ + Eo *obj; + Ecordova_FileTransfer_Job *job; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_filewriter.c b/src/lib/ecordova/ecordova_filewriter.c new file mode 100644 index 0000000000..514e1f4456 --- /dev/null +++ b/src/lib/ecordova/ecordova_filewriter.c @@ -0,0 +1,357 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filewriter_private.h" +#include "ecordova_entry_private.h" + +#include <stdbool.h> +#include <unistd.h> + +#define MY_CLASS ECORDOVA_FILEWRITER_CLASS +#define MY_CLASS_NAME "Ecordova_FileWriter" + +static void _write_cb(void *, Ecore_Thread *); +static void _truncate_cb(void *, Ecore_Thread *); +static bool _stream_init(Ecordova_FileWriter_Data *); +static bool _offset_set(Ecordova_FileWriter_Data *); +static void _write_end_cb(void *, Ecore_Thread *); +static void _write_abort_cb(void *, Ecore_Thread *); +static void _write_progress_cb(void *, Ecore_Thread *, void *); +static void _progress_notify(size_t, size_t, Eo *, Ecore_Thread *); + +static Eo_Base * +_ecordova_filewriter_eo_base_constructor(Eo *obj, + Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->error = 0; + pd->error = ECORDOVA_FILEWRITER_STATE_INIT; + pd->url = NULL; + pd->offset = 0; + pd->length = 0; + pd->stream = NULL; + pd->data = NULL; + pd->data_size = 0; + pd->truncate_size = 0; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_filewriter_constructor(Eo *obj, + Ecordova_FileWriter_Data *pd, + Ecordova_File *file) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(file); + + eo_do(file, + pd->url = strdup(ecordova_file_url_get()), + pd->offset = ecordova_file_start_get(), + pd->length = ecordova_file_end_get() - pd->offset); +} + +static void +_ecordova_filewriter_eo_base_destructor(Eo *obj, + Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + + if (pd->thread) ERR("%s", "Destructing without aborting first"); + + free(pd->url); + if (pd->stream) + fclose(pd->stream); + if (pd->data) + free(pd->data); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_FileError +_ecordova_filewriter_abort(Eo *obj, Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + + if (ECORDOVA_FILEWRITER_STATE_DONE == pd->state || + ECORDOVA_FILEWRITER_STATE_INIT == pd->state) + return ECORDOVA_FILEERROR_INVALID_STATE_ERR; + pd->state = ECORDOVA_FILEWRITER_STATE_DONE; + pd->error = ECORDOVA_FILEERROR_ABORT_ERR; + + if (pd->thread) + ecore_thread_cancel(pd->thread); + + return 0; +} + +static Ecordova_FileError +_ecordova_filewriter_write(Eo *obj, + Ecordova_FileWriter_Data *pd, + const char *data, + long size) +{ + DBG("(%p)", obj); + + if (ECORDOVA_FILEWRITER_STATE_WRITING == pd->state) + return ECORDOVA_FILEERROR_INVALID_STATE_ERR; + pd->state = ECORDOVA_FILEWRITER_STATE_WRITING; + pd->error = 0; + + Ecordova_ProgressEvent writestart = {.type = "writestart", .target = obj}; + eo_do(obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_START, + &writestart)); + + pd->data_size = size ? size : (long)strlen(data); + EINA_SAFETY_ON_FALSE_RETURN_VAL(pd->data_size > 0, ECORDOVA_FILEERROR_SYNTAX_ERR); + + pd->data = pd->data ? realloc(pd->data, pd->data_size) : malloc(pd->data_size); + EINA_SAFETY_ON_NULL_RETURN_VAL(pd->data, ECORDOVA_FILEERROR_SYNTAX_ERR); + + memcpy(pd->data, data, pd->data_size); + + pd->thread = ecore_thread_feedback_run(_write_cb, + _write_progress_cb, + _write_end_cb, + _write_abort_cb, + pd, + EINA_FALSE); + return 0; +} + +static Ecordova_FileError +_ecordova_filewriter_seek(Eo *obj, Ecordova_FileWriter_Data *pd, long offset) +{ + DBG("(%p)", obj); + if (ECORDOVA_FILEWRITER_STATE_WRITING == pd->state) + return ECORDOVA_FILEERROR_INVALID_STATE_ERR; + + if (offset < 0) + pd->offset = MAX(offset + pd->length, 0); + else if (offset > pd->length) + pd->offset = pd->length; + else + pd->offset = offset; + + return 0; +} + +static Ecordova_FileError +_ecordova_filewriter_truncate(Eo *obj, Ecordova_FileWriter_Data *pd, long size) +{ + DBG("(%p)", obj); + + if (ECORDOVA_FILEWRITER_STATE_WRITING == pd->state) + return ECORDOVA_FILEERROR_INVALID_STATE_ERR; + pd->state = ECORDOVA_FILEWRITER_STATE_WRITING; + pd->error = 0; + pd->truncate_size = size; + + pd->thread = ecore_thread_run(_truncate_cb, + _write_end_cb, + _write_abort_cb, + pd); + return 0; +} + +static Ecordova_FileError +_ecordova_filewriter_error_get(Eo *obj, Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + return pd->error; +} + +static long +_ecordova_filewriter_position_get(Eo *obj, Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + return pd->offset; +} + +static Ecordova_FileWriter_State +_ecordova_filewriter_state_get(Eo *obj, Ecordova_FileWriter_Data *pd) +{ + DBG("(%p)", obj); + return pd->state; +} + +static void +_write_cb(void *data, Ecore_Thread *thread) +{ + Ecordova_FileWriter_Data *pd = data; + DBG("(%p)", pd->obj); + + if (ecore_thread_check(thread)) + return; + + if (!pd->stream && !_stream_init(pd)) + return; + + if (!_offset_set(pd)) + return; + + _progress_notify(0, pd->data_size, pd->obj, thread); + if (ecore_thread_check(thread)) + return; + + size_t total = pd->data_size; + char *buffer = pd->data; + do + { + size_t written = fwrite(buffer, sizeof(char), total, pd->stream); + if (!written) + { + pd->error = _translate_errno(errno); + return; + } + + buffer += written; + total -= written; + pd->offset += written; + + _progress_notify(pd->data_size - total, pd->data_size, pd->obj, thread); + if (ecore_thread_check(thread)) + return; + } + while (total > 0); + + pd->length = MAX(pd->offset, pd->length); +} + +static void +_truncate_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileWriter_Data *pd = data; + DBG("(%p)", pd->obj); + + if (ecore_thread_check(thread)) + return; + + if (!pd->stream && !_stream_init(pd)) + return; + + if (!_offset_set(pd)) + return; + + if (ftruncate(fileno(pd->stream), pd->truncate_size) != 0) + { + pd->error = _translate_errno(errno); + return; + } + + pd->length = pd->truncate_size; + pd->offset = MIN(pd->offset, pd->truncate_size); +} + +static bool +_stream_init(Ecordova_FileWriter_Data *pd) +{ + pd->stream = fopen(pd->url, "rb+"); + if (!pd->stream) + { + pd->error = _translate_errno(errno); + return false; + } + + return true; +} + +static bool +_offset_set(Ecordova_FileWriter_Data *pd) +{ + int error = fseek(pd->stream, pd->offset, SEEK_SET); + if (error) + { + pd->error = _translate_errno(errno); + return false; + } + + return true; +} + +static void +_progress_notify(size_t written, size_t total, Eo *obj, Ecore_Thread *thread) +{ + Ecordova_ProgressEvent *progress = malloc(sizeof(Ecordova_ProgressEvent)); + *progress = (Ecordova_ProgressEvent) + { + .type = "progress", + .length_computable = EINA_TRUE, + .loaded = written, + .total = total, + .target = obj + }; + if (!ecore_thread_feedback(thread, progress)) + free(progress); +} + +static void +_write_progress_cb(void *data, + Ecore_Thread *thread EINA_UNUSED, + void *msg_data) +{ + Ecordova_FileWriter_Data *pd = data; + DBG("(%p)", pd->obj); + Ecordova_ProgressEvent *event = msg_data; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_PROGRESS, &event)); + free(event); +} + +static void +_write_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileWriter_Data *pd = data; + DBG("(%p)", pd->obj); + + // If DONE (cancelled), then don't do anything + if (ECORDOVA_FILEWRITER_STATE_DONE == pd->state) + return; + pd->thread = NULL; + pd->state = ECORDOVA_FILEWRITER_STATE_DONE; + + if (pd->error) + { + Ecordova_ProgressEvent error = {.type = "error", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_ERROR, + &error)); + } + else + { + Ecordova_ProgressEvent write = {.type = "write", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_WRITE, + &write)); + } + + Ecordova_ProgressEvent writeend = {.type = "writeend", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, + &writeend)); +} + +static void +_write_abort_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_FileWriter_Data *pd = data; + DBG("(%p)", pd->obj); + + pd->thread = NULL; + + Ecordova_ProgressEvent on_abort = {.type = "abort", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_ABORT, + &on_abort)); + + Ecordova_ProgressEvent writeend = {.type = "writeend", .target = pd->obj}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, + &writeend)); +} + +#include "ecordova_filewriter.eo.c" diff --git a/src/lib/ecordova/ecordova_filewriter.eo b/src/lib/ecordova/ecordova_filewriter.eo new file mode 100644 index 0000000000..6a3f9a1a5c --- /dev/null +++ b/src/lib/ecordova/ecordova_filewriter.eo @@ -0,0 +1,112 @@ +enum Ecordova_FileWriter_State { + INIT = 0, + WRITING = 1, + DONE = 2 +} + +enum Ecordova_FileError { + NOT_FOUND_ERR = 1, + SECURITY_ERR = 2, + ABORT_ERR = 3, + NOT_READABLE_ERR = 4, + ENCODING_ERR = 5, + NO_MODIFICATION_ALLOWED_ERR = 6, + INVALID_STATE_ERR = 7, + SYNTAX_ERR = 8, + INVALID_MODIFICATION_ERR = 9, + QUOTA_EXCEEDED_ERR = 10, + TYPE_MISMATCH_ERR = 11, + PATH_EXISTS_ERR = 12 +} + +class Ecordova.FileWriter (Eo.Base) { + [[This class writes to the device file system.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_FileWriter constructor. + @.constructor + + @since 2.3 + ]] + params { + file: Ecordova.File*; [[File object containing file properties]] + } + } + abort { + [[Abort writing file.]] + return: Ecordova_FileError; + } + write { + [[Writes data to the file]] + params { + data: const(char)*; [[text or blob to be written]] + size: long; [[the blob's size]] + } + return: Ecordova_FileError; + } + seek { + [[Moves the file pointer to the location specified. + + If the offset is a negative number the position of the file + pointer is rewound. If the offset is greater than the file + size the position is set to the end of the file.]] + params { + offset: long; [[location to move the file pointer to.]] + } + return: Ecordova_FileError; + } + truncate { + [[Truncates the file to the size specified.]] + params { + size: long; [[size to chop the file at.]] + } + return: Ecordova_FileError; + } + @property error { + get {} + values { + value: Ecordova_FileError; + } + } + @property position { + get {} + values { + state: long; + } + } + @property state { + get {} + values { + state: Ecordova_FileWriter_State; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + on,write,start: const(ProgressEvent)*; + [[When writing starts]] + + on,progress: const(ProgressEvent)*; + [[While writing the file, and reporting partial file data]] + + on,write: const(ProgressEvent)*; + [[When the write has successfully completed.]] + + on,write,end: const(ProgressEvent)*; + [[When the request has completed (either in success or failure).]] + + on,abort: const(ProgressEvent)*; + [[When the write has been aborted. For instance, by invoking the abort() + method.]] + + on,error: const(ProgressEvent)*; + [[When the write has failed (see errors).]] + } +} diff --git a/src/lib/ecordova/ecordova_filewriter_private.h b/src/lib/ecordova/ecordova_filewriter_private.h new file mode 100644 index 0000000000..459d1c671f --- /dev/null +++ b/src/lib/ecordova/ecordova_filewriter_private.h @@ -0,0 +1,26 @@ +#ifndef _ECORDOVA_FILEWRITER_PRIVATE_H +#define _ECORDOVA_FILEWRITER_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_FileWriter_Data Ecordova_FileWriter_Data; + +/** + * Ecordova.FileWriter private data + */ +struct _Ecordova_FileWriter_Data +{ + Eo *obj; + Ecordova_FileError error; + Ecordova_FileWriter_State state; + char *url; + long offset; + long length; + FILE *stream; + char *data; + long data_size; + long truncate_size; + Ecore_Thread *thread; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_geolocation.c b/src/lib/ecordova/ecordova_geolocation.c new file mode 100644 index 0000000000..dff3e7b476 --- /dev/null +++ b/src/lib/ecordova/ecordova_geolocation.c @@ -0,0 +1,282 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_geolocation_private.h" +#include <ecore_timer.eo.h> + +#define MY_CLASS ECORDOVA_GEOLOCATION_CLASS +#define MY_CLASS_NAME "Ecordova_Geolocation" + +typedef struct +{ + Eo *obj; + location_manager_h manager; + Ecore_Timer *timer; + bool current; + bool is_ready; +} Ecordova_Geolocation_Watch; + +static void _watch_free(Ecordova_Geolocation_Watch*); +static Eina_Bool _interval_cb(void *); +static void _state_changed_cb(location_service_state_e, void *); +static void _notify(Ecordova_Geolocation_Watch *); +static Ecordova_Geolocation_Watch *_create_watch(Eo *, const Ecordova_Geolocation_Options *, bool); + +static Eo_Base * +_ecordova_geolocation_eo_base_constructor(Eo *obj, + Ecordova_Geolocation_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->watchers = eina_hash_int32_new(EINA_FREE_CB(_watch_free)); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_geolocation_constructor(Eo *obj EINA_UNUSED, + Ecordova_Geolocation_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_geolocation_eo_base_destructor(Eo *obj, + Ecordova_Geolocation_Data *pd) +{ + DBG("(%p)", obj); + + eina_hash_free(pd->watchers); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_geolocation_current_position_get(Eo *obj, + Ecordova_Geolocation_Data *pd EINA_UNUSED, + const Ecordova_Geolocation_Options *options) +{ + Ecordova_Geolocation_Watch *watch = _create_watch(obj, options, true); + if (!watch) + { + Ecordova_Geolocation_Error error = { + .code = ECORDOVA_GEOLOCATION_ERRORCODE_PERMISSION_DENIED, + .message = "Unknown" + }; + eo_do(obj, + eo_event_callback_call(ECORDOVA_GEOLOCATION_EVENT_ERROR, &error)); + return; + } +} + +static Ecordova_Geolocation_WatchID +_ecordova_geolocation_position_watch(Eo *obj, + Ecordova_Geolocation_Data *pd, + const Ecordova_Geolocation_Options *options) +{ + Ecordova_Geolocation_Watch *watch = _create_watch(obj, options, false); + if (!watch) + { + Ecordova_Geolocation_Error error = { + .code = ECORDOVA_GEOLOCATION_ERRORCODE_PERMISSION_DENIED, + .message = "Unknown" + }; + eo_do(obj, + eo_event_callback_call(ECORDOVA_GEOLOCATION_EVENT_ERROR, &error)); + return 0; + } + + watch->timer = eo_add(ECORE_TIMER_CLASS, NULL, + ecore_obj_timer_constructor(options->timeout / 1000.0, _interval_cb, watch)); + + static Ecordova_Geolocation_WatchID id = 0; + ++id; + eina_hash_add(pd->watchers, &id, watch); + return id; +} + +static void +_ecordova_geolocation_watch_clear(Eo *obj, + Ecordova_Geolocation_Data *pd, + Ecordova_Geolocation_WatchID watch_id) +{ + Ecordova_Geolocation_Watch *watch = eina_hash_find(pd->watchers, &watch_id); + if (!watch) + { + eo_do(obj, eo_event_callback_call(ECORDOVA_DEVICEORIENTATION_EVENT_ERROR, + NULL)); + return; + } + + Eina_Bool ret = eina_hash_del(pd->watchers, &watch_id, NULL); + EINA_SAFETY_ON_FALSE_RETURN(ret); +} + +static void +_watch_free(Ecordova_Geolocation_Watch *watch) +{ + if (watch->timer) + eo_unref(watch->timer); + + int ret = location_manager_unset_service_state_changed_cb(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE != ret, on_error); + + ret = location_manager_stop(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE != ret, on_error); + + ret = location_manager_destroy(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE != ret, on_error); + +on_error: + free(watch); +} + +static Eina_Bool +_interval_cb(void *data) +{ + Ecordova_Geolocation_Watch *watch = data; + if (!watch->is_ready) + { + Ecordova_Geolocation_Error error = { + .code = ECORDOVA_GEOLOCATION_ERRORCODE_TIMEOUT, + .message = "Timeout" + }; + eo_do(watch->obj, + eo_event_callback_call(ECORDOVA_GEOLOCATION_EVENT_ERROR, &error)); + return ECORE_CALLBACK_RENEW; + } + + _notify(watch); + + return ECORE_CALLBACK_RENEW; +} + +static void +_state_changed_cb(location_service_state_e state, void *data) +{ + if (LOCATIONS_SERVICE_ENABLED != state) + return; + + Ecordova_Geolocation_Watch *watch = data; + watch->is_ready = true; + + if (watch->current) + { + _notify(watch); + _watch_free(watch); + } +} + +static void +_notify(Ecordova_Geolocation_Watch* watch) +{ + double altitude; + double latitude; + double longitude; + double climb; + double direction; + double speed; + location_accuracy_level_e level; + double horizontal; + double vertical; + time_t timestamp; + + int ret = location_manager_get_location(watch->manager, + &altitude, + &latitude, + &longitude, + &climb, + &direction, + &speed, + &level, + &horizontal, + &vertical, + ×tamp); + if (LOCATIONS_ERROR_NONE != ret) + { + Ecordova_Geolocation_Error error = {0}; + switch (ret) + { + case LOCATIONS_ERROR_INVALID_PARAMETER: + error.code = ECORDOVA_GEOLOCATION_ERRORCODE_POSITION_UNAVAILABLE; + error.message = "Invalid parameter"; + break; + default: + error.code = ECORDOVA_GEOLOCATION_ERRORCODE_POSITION_UNAVAILABLE; + error.message = "Unknown"; + break; + } + eo_do(watch->obj, + eo_event_callback_call(ECORDOVA_GEOLOCATION_EVENT_ERROR, &error)); + return; + } + + Ecordova_Geolocation_Position position = { + .coords = { + .latitude = latitude, + .longitude = longitude, + .altitude = altitude, + .accuracy = horizontal, + .altitude_accuracy = vertical, + .heading = direction, + .speed = speed / 3.6 + }, + .timestamp = timestamp + }; + + const Eo_Event_Description * const event = + watch->current ? ECORDOVA_GEOLOCATION_EVENT_CURRENT_SUCCESS + : ECORDOVA_GEOLOCATION_EVENT_WATCH_SUCCESS; + eo_do(watch->obj, eo_event_callback_call(event, &position)); +} + +static Ecordova_Geolocation_Watch * +_create_watch(Eo *obj, + const Ecordova_Geolocation_Options *options, + bool is_current) +{ + const Ecordova_Geolocation_Options default_options = { + .enable_high_accuracy = EINA_FALSE, + .timeout = 1000, + .maximum_age = 0 + }; + if (!options) + options = &default_options; + + Ecordova_Geolocation_Watch *watch = calloc(1, sizeof(Ecordova_Geolocation_Watch)); + watch->obj = obj; + watch->current = is_current; + + int ret = LOCATIONS_ERROR_NONE; + + if (options->enable_high_accuracy) + ret = location_manager_create(LOCATIONS_METHOD_GPS, &watch->manager); + else + ret = location_manager_create(LOCATIONS_METHOD_GPS, &watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE == ret, on_error_1); + + ret = location_manager_set_service_state_changed_cb(watch->manager, + _state_changed_cb, + watch); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE == ret, on_error_2); + + ret = location_manager_start(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE == ret, on_error_3); + return watch; + +on_error_3: + ret = location_manager_unset_service_state_changed_cb(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE == ret, on_error_2); + +on_error_2: + ret = location_manager_destroy(watch->manager); + EINA_SAFETY_ON_FALSE_GOTO(LOCATIONS_ERROR_NONE == ret, on_error_1); + +on_error_1: + free(watch); + return NULL; +} + +#include "ecordova_geolocation.eo.c" diff --git a/src/lib/ecordova/ecordova_geolocation.eo b/src/lib/ecordova/ecordova_geolocation.eo new file mode 100644 index 0000000000..1339149d60 --- /dev/null +++ b/src/lib/ecordova/ecordova_geolocation.eo @@ -0,0 +1,152 @@ +type Ecordova_Geolocation_WatchID: int; + +struct Ecordova_Geolocation_Coordinates { + [[A Coordinates object is attached to a Position object that is available + to callback functions in requests for the current position. It contains + a set of properties that describe the geographic coordinates of a + position.]] + + latitude: double; + [[Latitude in decimal degrees.]] + + longitude: double; + [[Longitude in decimal degrees.]] + + altitude: double; + [[Height of the position in meters above the ellipsoid.]] + + accuracy: double; + [[Accuracy level of the latitude and longitude coordinates in meters.]] + + altitude_accuracy: double; + [[Accuracy level of the altitude coordinate in meters.]] + + heading: double; + [[Direction of travel, specified in degrees counting clockwise relative to + the true north.]] + + speed: double; + [[Current ground speed of the device, specified in meters per second.]] +} + +struct Ecordova_Geolocation_Position { + [[Contains Position coordinates and timestamp, created by the geolocation API.]] + + coords: Ecordova_Geolocation_Coordinates; + [[A set of geographic coordinates.]] + + timestamp: time; + [[Creation timestamp for coords.]] +} + +struct Ecordova_Geolocation_Options { + enable_high_accuracy: bool; + [[Provides a hint that the application needs the best possible results. By + default, the device attempts to retrieve a Position using network-based + methods. Setting this property to true tells the framework to use more + accurate methods, such as satellite positioning.]] + + timeout: int; + [[The maximum length of time (milliseconds) that is allowed to pass from + the call to navigator.geolocation.current_position_get or + geolocation.position_watch until the corresponding geolocationSuccess + callback executes. If the geolocationSuccess callback is not invoked + within this time, the geolocationError callback is passed a + PositionError.TIMEOUT error code. (Note that when used in conjunction + with geolocation.position_watch, the geolocationError callback could be + called on an interval every timeout milliseconds!)]] + + maximum_age: int; + [[Accept a cached position whose age is no greater than the specified time + in milliseconds.]] +} + +enum Ecordova_Geolocation_ErrorCode { + PERMISSION_DENIED, + [[Returned when users do not allow the app to retrieve position + information. This is dependent on the platform.]] + + POSITION_UNAVAILABLE, + [[Returned when the device is unable to retrieve a position. In general, + this means the device is not connected to a network or can't get a + satellite fix.]] + + TIMEOUT + [[Returned when the device is unable to retrieve a position within the + time specified by the timeout included in geolocationOptions. When used + with navigator.geolocation.position_watch, this error could be repeatedly + passed to the geolocationError callback every timeout milliseconds.]] +} + +struct Ecordova_Geolocation_Error { + [[The PositionError object is passed to the geolocationError callback + function when an error occurs with navigator.geolocation.]] + + code: int; + [[One of the predefined error codes listed above.]] + + message: const(char)*; + [[Error message describing the details of the error encountered.]] +} + +class Ecordova.Geolocation (Eo.Base) { + [[Ecordova Geolocation Plugin + Plugin ID: org.apache.cordova.geolocation + http://plugins.cordova.io/#/package/org.apache.cordova.geolocation + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Geolocation constructor. + @.constructor + + @since 2.3 + ]] + } + current_position_get { + [[Returns the device's current position to the geolocationSuccess + callback with a Position object as the parameter. If there is an + error, the geolocationError callback is passed a PositionError + object.]] + params { + options: const(Ecordova_Geolocation_Options)*; + [[The geolocation options.]] + } + } + position_watch { + [[Returns the device's current position when a change in position + is detected. When the device retrieves a new location, the + geolocationSuccess callback executes with a Position object as + the parameter. If there is an error, the geolocationError + callback executes with a PositionError object as the parameter.]] + params { + options: const(Ecordova_Geolocation_Options)*; + [[The geolocation options.]] + } + return: Ecordova_Geolocation_WatchID; + [[returns a watch id that references the watch position interval. + The watch id should be used with navigator.geolocation.watch_clear + to stop watching for changes in position.]] + } + watch_clear { + [[Stop watching for changes to the device's location referenced by + the watchID parameter.]] + params { + watch_id: Ecordova_Geolocation_WatchID; + [[The id of the position_watch interval to clear.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + current,success: Ecordova_Geolocation_Position*; + watch,success: Ecordova_Geolocation_Position*; + error: Ecordova_Geolocation_Error*; + } +} diff --git a/src/lib/ecordova/ecordova_geolocation_private.h b/src/lib/ecordova/ecordova_geolocation_private.h new file mode 100644 index 0000000000..e31ad6eef6 --- /dev/null +++ b/src/lib/ecordova/ecordova_geolocation_private.h @@ -0,0 +1,19 @@ +#ifndef _ECORDOVA_GEOLOCATION_PRIVATE_H +#define _ECORDOVA_GEOLOCATION_PRIVATE_H + +#include "ecordova_private.h" + +#include <locations.h> + +typedef struct _Ecordova_Geolocation_Data Ecordova_Geolocation_Data; + +/** + * Ecordova.Geolocation private data + */ +struct _Ecordova_Geolocation_Data +{ + Eo *obj; + Eina_Hash *watchers; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_globalization.c b/src/lib/ecordova/ecordova_globalization.c new file mode 100644 index 0000000000..9320c87cdb --- /dev/null +++ b/src/lib/ecordova/ecordova_globalization.c @@ -0,0 +1,1122 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_globalization_private.h" + +#include <utils_i18n.h> + +#define MY_CLASS ECORDOVA_GLOBALIZATION_CLASS +#define MY_CLASS_NAME "Ecordova_Globalization" + +static void _date_time_options_to_style(const Ecordova_Globalization_DateTimeOptions *, i18n_udate_format_style_e *, i18n_udate_format_style_e *); + + +static Eo_Base * +_ecordova_globalization_eo_base_constructor(Eo *obj, + Ecordova_Globalization_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_globalization_constructor(Eo *obj EINA_UNUSED, + Ecordova_Globalization_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_globalization_eo_base_destructor(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_globalization_preferred_language_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error); + + char language[64] = {0}; + int32_t language_len; + ret = i18n_ulocale_get_language(locale, + language, + sizeof(language), + &language_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error); + + Ecordova_Globalization_Language glanguage = { + .value = language + }; + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_PREFERRED_LANGUAGE_SUCCESS, + &glanguage)); + return; + +on_error: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting preferred language" + })); +} + +static void +_ecordova_globalization_locale_name_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error); + + Ecordova_Globalization_Locale glocale = { + .value = locale + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_LOCALE_NAME_SUCCESS, + &glocale)); + return; + +on_error: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting locale name" + })); +} + +static void +_ecordova_globalization_date_to_string(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + time_t date, + const Ecordova_Globalization_DateTimeOptions *options) +{ + DBG("(%p)", obj); + + i18n_udate_format_style_e date_style; + i18n_udate_format_style_e time_style; + _date_time_options_to_style(options, &date_style, &time_style); + + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + i18n_udate_format_h format = NULL; + ret = i18n_udate_create(time_style, + date_style, + locale, + NULL, + 0, + NULL, + 0, + &format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + struct tm date_time; + localtime_r(&date, &date_time); + + i18n_ucalendar_h calendar = NULL; + ret = i18n_ucalendar_create(NULL, + 0, + locale, + I18N_UCALENDAR_DEFAULT, + &calendar); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + ret = i18n_ucalendar_set_date_time(calendar, + date_time.tm_year, + date_time.tm_mon, + date_time.tm_mday, + date_time.tm_hour, + date_time.tm_min, + date_time.tm_sec); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + i18n_udate udate; + ret = i18n_ucalendar_get_milliseconds(calendar, &udate); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + i18n_uchar uch_result[64] = {0}; + int32_t uch_result_len; + ret = i18n_udate_format_date(format, + udate, + uch_result, + sizeof(uch_result), + NULL, + &uch_result_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + char result[64] = {0}; + i18n_ustring_copy_au_n(result, uch_result, sizeof(uch_result)); + + Ecordova_Globalization_String gstring = { + .value = result + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_DATE_TO_STRING_SUCCESS, + &gstring)); + + i18n_ucalendar_destroy(calendar); + i18n_udate_destroy(format); + return; + +on_error_2: + i18n_ucalendar_destroy(calendar); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error in date_to_string" + })); +} + +static void +_ecordova_globalization_currency_pattern_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const char *currency_code EINA_UNUSED) +{ + DBG("(%p)", obj); + + i18n_unumber_format_h num_format = NULL; + int ret = i18n_unumber_create(I18N_UNUMBER_CURRENCY, + NULL, + 0, + NULL, + NULL, + &num_format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + + i18n_uchar uch_buffer[64] = {0}; + int32_t uch_buffer_len; + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_INTL_CURRENCY_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char code[64] = {0}; + i18n_ustring_copy_au_n(code, uch_buffer, sizeof(uch_buffer)); + + + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_DECIMAL_SEPARATOR_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char decimal[64] = {0}; + i18n_ustring_copy_au_n(decimal, uch_buffer, sizeof(uch_buffer)); + + + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_GROUPING_SEPARATOR_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char grouping[64] = {0}; + i18n_ustring_copy_au_n(grouping, uch_buffer, sizeof(uch_buffer)); + + + int32_t len = i18n_unumber_to_pattern(num_format, + 1, + uch_buffer, + sizeof(uch_buffer)); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + EINA_SAFETY_ON_TRUE_GOTO(len > (int32_t)sizeof(uch_buffer), on_error_2); + char pattern[64] = {0}; + i18n_ustring_copy_au_n(pattern, uch_buffer, sizeof(uch_buffer)); + + + double rounding = i18n_unumber_get_double_attribute(num_format, + I18N_UNUMBER_ROUNDING_INCREMENT); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + DBG("rounding: %f", rounding); + + + int32_t fraction = i18n_unumber_get_attribute(num_format, + I18N_UNUMBER_MAX_FRACTION_DIGITS); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + DBG("fraction: %d", fraction); + + Ecordova_Globalization_CurrencyPattern number_pattern = { + .pattern = pattern, + .code = code, + .fraction = fraction, + .rounding = rounding, + .decimal = decimal, + .grouping = grouping + }; + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_CURRENCY_PATTERN_SUCCESS, + &number_pattern)); + + i18n_unumber_destroy(num_format); + return; + +on_error_2: + i18n_unumber_destroy(num_format); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting currency pattern" + })); +} + +static void +_ecordova_globalization_date_names_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const Ecordova_Globalization_DateNamesOptions *options) +{ + DBG("(%p)", obj); + if (!options) + { + static const Ecordova_Globalization_DateNamesOptions default_options = { + .type = ECORDOVA_GLOBALIZATION_DATENAMESTYPE_WIDE, + .item = ECORDOVA_GLOBALIZATION_DATENAMESITEM_MONTHS + }; + options = &default_options; + }; + + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + i18n_udatepg_h dtpg = NULL; + ret = i18n_udatepg_create(locale, &dtpg); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + const char *custom_format = NULL; + int count = 0; + switch (options->item) + { + case ECORDOVA_GLOBALIZATION_DATENAMESITEM_MONTHS: + count = 12; + if (ECORDOVA_GLOBALIZATION_DATENAMESTYPE_WIDE == options->type) + custom_format = "LLLL"; + else + custom_format = "LLL"; + break; + case ECORDOVA_GLOBALIZATION_DATENAMESITEM_DAYS: + count = 7; + if (ECORDOVA_GLOBALIZATION_DATENAMESTYPE_WIDE == options->type) + custom_format = "EEEE"; + else + custom_format = "EEE"; + break; + } + + i18n_uchar uch_custom_format[64]; + i18n_ustring_copy_ua(uch_custom_format, custom_format); + i18n_uchar uch_pattern[64] = {0}; + int32_t uch_pattern_len; + ret = i18n_udatepg_get_best_pattern(dtpg, + uch_custom_format, + i18n_ustring_get_length(uch_custom_format), + uch_pattern, + sizeof(uch_pattern), + &uch_pattern_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + char pattern[64] = {0}; + i18n_ustring_copy_au_n(pattern, uch_pattern, sizeof(uch_pattern)); + INF("pattern=%s", pattern); + + i18n_udate_format_h format = NULL; + ret = i18n_udate_create(I18N_UDATE_PATTERN, + I18N_UDATE_PATTERN, + locale, + NULL, + 0, + uch_pattern, + i18n_ustring_get_length(uch_pattern), + &format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + i18n_ucalendar_h calendar = NULL; + ret = i18n_ucalendar_create(NULL, + 0, + locale, + I18N_UCALENDAR_DEFAULT, + &calendar); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + Eina_Array *names = eina_array_new(count); + + int month = 0; + int day = 1; + for (int i = 0; i < count; ++i) + { + ret = i18n_ucalendar_set_date_time(calendar, + 2012, + month, + day, + 12, + 0, + 0); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + i18n_udate udate; + ret = i18n_ucalendar_get_milliseconds(calendar, &udate); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + i18n_uchar uch_result[64] = {0}; + int32_t uch_result_len; + ret = i18n_udate_format_date(format, + udate, + uch_result, + sizeof(uch_result), + NULL, + &uch_result_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + char result[64] = {0}; + i18n_ustring_copy_au_n(result, uch_result, sizeof(uch_result)); + DBG("result=%s", result); + + eina_array_push(names, strdup(result)); + + if (ECORDOVA_GLOBALIZATION_DATENAMESITEM_MONTHS == options->item) + ++month; + else + ++day; + } + + Ecordova_Globalization_DateNames date_names = { + .value = names + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_DATE_NAMES_SUCCESS, + &date_names)); + + char *name; + unsigned int i; + Eina_Array_Iterator it; + EINA_ARRAY_ITER_NEXT(names, i, name, it) + free(name); + eina_array_free(names); + + i18n_ucalendar_destroy(calendar); + i18n_udate_destroy(format); + i18n_udatepg_destroy(dtpg); + return; + +on_error_3: + EINA_ARRAY_ITER_NEXT(names, i, name, it) + free(name); + eina_array_free(names); + + i18n_ucalendar_destroy(calendar); + +on_error_2: + i18n_udatepg_destroy(dtpg); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting date names" + })); +} + +static void +_ecordova_globalization_date_pattern_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const Ecordova_Globalization_DateTimeOptions *options) +{ + DBG("(%p)", obj); + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + i18n_timezone_h timezone = NULL; + ret = i18n_timezone_create_default(&timezone); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + char *timezone_str = NULL; + ret = i18n_timezone_get_id(timezone, &timezone_str); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t utc_offset = 0; + ret = i18n_timezone_get_raw_offset(timezone, &utc_offset); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + int32_t dst_offset = 0; + ret = i18n_timezone_get_dst_savings(timezone, &dst_offset); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + i18n_udate_format_style_e date_style; + i18n_udate_format_style_e time_style; + _date_time_options_to_style(options, &date_style, &time_style); + + i18n_udate_format_h format = NULL; + ret = i18n_udate_create(time_style, + date_style, + locale, + NULL, + 0, + NULL, + 0, + &format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_3); + + + i18n_uchar uch_pattern[64] = {0}; + int32_t uch_pattern_len = i18n_udate_to_pattern(format, + 1, + uch_pattern, + sizeof(uch_pattern)); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_4); + EINA_SAFETY_ON_TRUE_GOTO(uch_pattern_len > (int32_t)sizeof(uch_pattern), on_error_4); + + char pattern[64] = {0}; + i18n_ustring_copy_au_n(pattern, uch_pattern, sizeof(uch_pattern)); + + Ecordova_Globalization_DatePattern date_pattern = { + .pattern = pattern, + .timezone = timezone_str, + .utc_offset = utc_offset / 1000, + .dst_offset = dst_offset / 1000 + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_DATE_PATTERN_SUCCESS, + &date_pattern)); + i18n_udate_destroy(format); + free(timezone_str); + i18n_timezone_destroy(timezone); + return; + +on_error_4: + i18n_udate_destroy(format); + +on_error_3: + free(timezone_str); + +on_error_2: + i18n_timezone_destroy(timezone); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting date pattern" + })); +} + +static void +_ecordova_globalization_first_day_of_week_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + i18n_ucalendar_h calendar = NULL; + ret = i18n_ucalendar_create(NULL, + 0, + locale, + I18N_UCALENDAR_DEFAULT, + &calendar); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + int32_t value = 0; + ret = i18n_ucalendar_get_attribute(calendar, + I18N_UCALENDAR_FIRST_DAY_OF_WEEK, + &value); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + Ecordova_Globalization_FirstDayOfWeek first_day_of_week = { + .value = value + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_FIRST_DAY_OF_WEEK_SUCCESS, + &first_day_of_week)); + i18n_ucalendar_destroy(calendar); + return; + +on_error_2: + i18n_ucalendar_destroy(calendar); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting first day of week" + })); +} + +static void +_ecordova_globalization_number_pattern_get(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const Ecordova_Globalization_NumberPatternOptions *options) +{ + DBG("(%p)", obj); + + if (!options) + { + static const Ecordova_Globalization_NumberPatternOptions default_options = { + .type = ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL + }; + options = &default_options; + } + + i18n_unumber_format_style_e format_style = I18N_UNUMBER_CURRENCY; + switch (options->type) + { + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL: + format_style = I18N_UNUMBER_DECIMAL; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_PERCENT: + format_style = I18N_UNUMBER_PERCENT; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_CURRENCY: + format_style = I18N_UNUMBER_CURRENCY; + break; + } + + i18n_unumber_format_h num_format = NULL; + int ret = i18n_unumber_create(format_style, + NULL, + 0, + NULL, + NULL, + &num_format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + + i18n_uchar uch_buffer[64] = {0}; + int32_t uch_buffer_len; + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_PLUS_SIGN_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char positive[64] = {0}; + i18n_ustring_copy_au_n(positive, uch_buffer, sizeof(uch_buffer)); + + + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_MINUS_SIGN_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char negative[64] = {0}; + i18n_ustring_copy_au_n(negative, uch_buffer, sizeof(uch_buffer)); + + + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_DECIMAL_SEPARATOR_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char decimal[64] = {0}; + i18n_ustring_copy_au_n(decimal, uch_buffer, sizeof(uch_buffer)); + + + ret = i18n_unumber_get_symbol(num_format, + I18N_UNUMBER_GROUPING_SEPARATOR_SYMBOL, + uch_buffer, + sizeof(uch_buffer), + &uch_buffer_len); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + char grouping[64] = {0}; + i18n_ustring_copy_au_n(grouping, uch_buffer, sizeof(uch_buffer)); + + + int32_t len = i18n_unumber_to_pattern(num_format, + 1, + uch_buffer, + sizeof(uch_buffer)); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + EINA_SAFETY_ON_TRUE_GOTO(len > (int32_t)sizeof(uch_buffer), on_error_2); + char pattern[64] = {0}; + i18n_ustring_copy_au_n(pattern, uch_buffer, sizeof(uch_buffer)); + + + double rounding = i18n_unumber_get_double_attribute(num_format, + I18N_UNUMBER_ROUNDING_INCREMENT); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + DBG("rounding: %f", rounding); + + + int32_t fraction = i18n_unumber_get_attribute(num_format, + I18N_UNUMBER_MAX_FRACTION_DIGITS); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + DBG("fraction: %d", fraction); + + Ecordova_Globalization_NumberPattern number_pattern = { + .pattern = pattern, + //.symbol = + .fraction = fraction, + .rounding = rounding, + .positive = positive, + .negative = negative, + .decimal = decimal, + .grouping = grouping + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_NUMBER_PATTERN_SUCCESS, + &number_pattern)); + + i18n_unumber_destroy(num_format); + return; + +on_error_2: + i18n_unumber_destroy(num_format); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting currency pattern" + })); +} + +static void +_ecordova_globalization_day_light_savings_time_is(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + time_t date) +{ + DBG("(%p)", obj); + + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + struct tm date_time; + localtime_r(&date, &date_time); + + i18n_ucalendar_h calendar = NULL; + ret = i18n_ucalendar_create(NULL, + 0, + locale, + I18N_UCALENDAR_DEFAULT, + &calendar); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + ret = i18n_ucalendar_set_date_time(calendar, + date_time.tm_year, + date_time.tm_mon, + date_time.tm_mday, + date_time.tm_hour, + date_time.tm_min, + date_time.tm_sec); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + i18n_ubool value = 0; + ret = i18n_ucalendar_is_in_daylight_time(calendar, &value); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + Ecordova_Globalization_DayLightSavingsTime dst = { + .dst = !!value + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_DAY_LIGHT_SAVINGS_TIME_SUCCESS, + &dst)); + + i18n_ucalendar_destroy(calendar); + return; + +on_error_2: + i18n_ucalendar_destroy(calendar); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error getting if the date/time is in dst" + })); +} + +static void +_ecordova_globalization_number_to_string(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + double number, + const Ecordova_Globalization_NumberPatternOptions *options) +{ + DBG("(%p)", obj); + + if (!options) + { + static const Ecordova_Globalization_NumberPatternOptions default_options = { + .type = ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL + }; + options = &default_options; + } + + i18n_unumber_format_style_e format_style = I18N_UNUMBER_CURRENCY; + switch (options->type) + { + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL: + format_style = I18N_UNUMBER_DECIMAL; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_PERCENT: + format_style = I18N_UNUMBER_PERCENT; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_CURRENCY: + format_style = I18N_UNUMBER_CURRENCY; + break; + } + + i18n_unumber_format_h num_format = NULL; + int ret = i18n_unumber_create(format_style, + NULL, + 0, + NULL, + NULL, + &num_format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error); + + i18n_uchar uch_buffer[64] = {0}; + int32_t uch_buffer_len = i18n_unumber_format_double(num_format, + number, + uch_buffer, + sizeof(uch_buffer), + NULL); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, format_error); + EINA_SAFETY_ON_TRUE_GOTO(uch_buffer_len > (int32_t)sizeof(uch_buffer), format_error); + + char value[64] = {0}; + i18n_ustring_copy_au_n(value, uch_buffer, sizeof(uch_buffer)); + + Ecordova_Globalization_String gstring = { + .value = value + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_NUMBER_TO_STRING_SUCCESS, + &gstring)); + + i18n_unumber_destroy(num_format); + return; + +on_error: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error formatting number" + })); + return; + +format_error: + i18n_unumber_destroy(num_format); + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_FORMATTING_ERROR, + .message = "Format error" + })); +} + +static void +_ecordova_globalization_string_to_date(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const char *dateString, + const Ecordova_Globalization_DateTimeOptions *options) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(dateString); + + i18n_udate_format_style_e date_style; + i18n_udate_format_style_e time_style; + _date_time_options_to_style(options, &date_style, &time_style); + + const char *locale; + int ret = i18n_ulocale_get_default(&locale); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + i18n_udate_format_h format = NULL; + ret = i18n_udate_create(time_style, + date_style, + locale, + NULL, + 0, + NULL, + 0, + &format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + + i18n_uchar uch_date[64] = {0}; + i18n_ustring_copy_ua(uch_date, dateString); + + i18n_udate udate; + ret = i18n_udate_parse(format, + uch_date, + -1, + NULL, + &udate); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, parse_error); + + i18n_ucalendar_h calendar = NULL; + ret = i18n_ucalendar_create(NULL, + 0, + locale, + I18N_UCALENDAR_DEFAULT, + &calendar); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_1); + + ret = i18n_ucalendar_set_milliseconds(calendar, udate); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t year = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_YEAR, &year); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t month = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_MONTH, &month); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t day = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_DATE, &day); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t hour = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_HOUR_OF_DAY, &hour); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t minute = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_MINUTE, &minute); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t second = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_SECOND, &second); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + int32_t millisecond = 0; + ret = i18n_ucalendar_get(calendar, I18N_UCALENDAR_MILLISECOND, &millisecond); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error_2); + + + Ecordova_Globalization_DateTime date_time = { + .year = year, + .month = month, + .day = day, + .hour = hour, + .minute = minute, + .second = second, + .millisecond = millisecond + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_STRING_TO_DATE_SUCCESS, + &date_time)); + + i18n_ucalendar_destroy(calendar); + i18n_udate_destroy(format); + return; + +on_error_2: + i18n_ucalendar_destroy(calendar); + +on_error_1: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error in string_to_date" + })); + return; + +parse_error: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_PARSING_ERROR, + .message = "Parse error" + })); +} + +static void +_ecordova_globalization_string_to_number(Eo *obj, + Ecordova_Globalization_Data *pd EINA_UNUSED, + const char *string, + const Ecordova_Globalization_NumberPatternOptions *options) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(string); + + if (!options) + { + static const Ecordova_Globalization_NumberPatternOptions default_options = { + .type = ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL + }; + options = &default_options; + } + + i18n_unumber_format_style_e format_style = I18N_UNUMBER_CURRENCY; + switch (options->type) + { + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_DECIMAL: + format_style = I18N_UNUMBER_DECIMAL; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_PERCENT: + format_style = I18N_UNUMBER_PERCENT; + break; + case ECORDOVA_GLOBALIZATION_NUMBERPATTERNTYPE_CURRENCY: + format_style = I18N_UNUMBER_CURRENCY; + break; + } + + i18n_uchar uch_number[strlen(string) + 1]; + + i18n_unumber_format_h num_format = NULL; + int ret = i18n_unumber_create(format_style, + NULL, + 0, + NULL, + NULL, + &num_format); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, on_error); + + + i18n_ustring_copy_ua(uch_number, string); + + double value = i18n_unumber_parse_double(num_format, + uch_number, + -1, + NULL); + ret = get_last_result(); + EINA_SAFETY_ON_FALSE_GOTO(I18N_ERROR_NONE == ret, parse_error); + + Ecordova_Globalization_Number gnumber = { + .value = value + }; + eo_do(obj, eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_STRING_TO_NUMBER_SUCCESS, + &gnumber)); + + i18n_unumber_destroy(num_format); + return; + +on_error: + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_UNKNOWN_ERROR, + .message = "Unknown error parsing string" + })); + return; + +parse_error: + i18n_unumber_destroy(num_format); + eo_do(obj, + eo_event_callback_call(ECORDOVA_GLOBALIZATION_EVENT_ERROR, + &(Ecordova_Globalization_Error){ + .code = ECORDOVA_GLOBALIZATION_ERRORCODE_PARSING_ERROR, + .message = "Parser error" + })); +} + +static void +_date_time_options_to_style(const Ecordova_Globalization_DateTimeOptions *options, + i18n_udate_format_style_e *date_style, + i18n_udate_format_style_e *time_style) +{ + EINA_SAFETY_ON_NULL_RETURN(date_style); + EINA_SAFETY_ON_NULL_RETURN(time_style); + + if (!options) + { + static const Ecordova_Globalization_DateTimeOptions default_options = { + .format_length = ECORDOVA_GLOBALIZATION_DATEFORMATLENGTH_SHORT, + .selector = ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE_AND_TIME + }; + options = &default_options; + }; + + *date_style = I18N_UDATE_NONE; + *time_style = I18N_UDATE_NONE; + + switch (options->format_length) + { + case ECORDOVA_GLOBALIZATION_DATEFORMATLENGTH_SHORT: + switch (options->selector) + { + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE: + *date_style = I18N_UDATE_SHORT; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_TIME: + *time_style = I18N_UDATE_SHORT; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE_AND_TIME: + *date_style = I18N_UDATE_SHORT; + *time_style = I18N_UDATE_SHORT; + break; + } + break; + case ECORDOVA_GLOBALIZATION_DATEFORMATLENGTH_MEDIUM: + switch (options->selector) + { + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE: + *date_style = I18N_UDATE_MEDIUM; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_TIME: + *time_style = I18N_UDATE_MEDIUM; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE_AND_TIME: + *date_style = I18N_UDATE_MEDIUM; + *time_style = I18N_UDATE_MEDIUM; + break; + } + break; + case ECORDOVA_GLOBALIZATION_DATEFORMATLENGTH_LONG: + switch (options->selector) + { + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE: + *date_style = I18N_UDATE_LONG; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_TIME: + *time_style = I18N_UDATE_LONG; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE_AND_TIME: + *date_style = I18N_UDATE_LONG; + *time_style = I18N_UDATE_LONG; + break; + } + break; + case ECORDOVA_GLOBALIZATION_DATEFORMATLENGTH_FULL: + switch (options->selector) + { + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE: + *date_style = I18N_UDATE_FULL; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_TIME: + *time_style = I18N_UDATE_FULL; + break; + case ECORDOVA_GLOBALIZATION_DATESELECTOR_DATE_AND_TIME: + *date_style = I18N_UDATE_FULL; + *time_style = I18N_UDATE_FULL; + break; + } + break; + }; +} + +#include "ecordova_globalization.eo.c" diff --git a/src/lib/ecordova/ecordova_globalization.eo b/src/lib/ecordova/ecordova_globalization.eo new file mode 100644 index 0000000000..6c3925ff78 --- /dev/null +++ b/src/lib/ecordova/ecordova_globalization.eo @@ -0,0 +1,390 @@ +struct Ecordova_Globalization_Language { + value: const(char)*; +} + +struct Ecordova_Globalization_Locale { + value: const(char)*; +} + +struct Ecordova_Globalization_String { + value: const(char)*; +} + +enum Ecordova_Globalization_DateFormatLength { + SHORT, + MEDIUM, + LONG, + FULL +} + +enum Ecordova_Globalization_DateSelector { + DATE, + TIME, + DATE_AND_TIME +} + +struct Ecordova_Globalization_DateTimeOptions { + format_length: int; + selector: int; +} + +enum Ecordova_Globalization_ErrorCode { + [[Represents a error from the Globalization API.]] + UNKNOWN_ERROR = 0, + FORMATTING_ERROR = 1, + PARSING_ERROR = 2, + PATTERN_ERROR = 3 +} + +struct Ecordova_Globalization_Error { + [[An object representing a error from the Globalization API.]] + + code: int; + [[One of the codes representing the error type]] + + message: const(char)*; + [[A text message that includes the error's explanation and/or details]] +} + +struct Ecordova_Globalization_CurrencyPattern { + pattern: const(char)*; + [[The currency pattern to format and parse currency values. The patterns + follow Unicode Technical Standard #35.]] + + code: const(char)*; + [[The ISO 4217 currency code for the pattern.]] + + fraction: int; + [[The number of fractional digits to use when parsing and formatting + currency.]] + + rounding: double; + [[The rounding increment to use when parsing and formatting.]] + + decimal: const(char)*; + [[The decimal symbol to use for parsing and formatting.]] + + grouping: const(char)*; + [[The grouping symbol to use for parsing and formatting.]] +} + +enum Ecordova_Globalization_DateNamesType { + NARROW, + WIDE +} + +enum Ecordova_Globalization_DateNamesItem { + MONTHS, + DAYS +} + +struct Ecordova_Globalization_DateNamesOptions { + type: int; + item: int; +} + +struct Ecordova_Globalization_DateNames { + value: array<char*>*; +} + +struct Ecordova_Globalization_DatePattern { + pattern: const(char)*; + [[The date and time pattern to format and parse dates. The patterns follow + Unicode Technical Standard #35.]] + + timezone: const(char)*; + [[The abbreviated name of the time zone on the client.]] + + utc_offset: int; + [[The current difference in seconds between the client's time zone and + coordinated universal time.]] + + dst_offset: int; + [[The current daylight saving time offset in seconds between the client's + non-daylight saving's time zone and the client's daylight saving's + time zone.]] +} + +struct Ecordova_Globalization_FirstDayOfWeek { + value: int; + [[The days of the week are numbered starting from 1, where 1 is assumed to + be Sunday.]] +} + +struct Ecordova_Globalization_NumberPattern { + pattern: const(char)*; + [[The number pattern to format and parse numbers. The patterns follow + Unicode Technical Standard #35.]] + + symbol: const(char)*; + [[The symbol to use when formatting and parsing, such as a percent or + currency symbol.]] + + fraction: int; + [[The number of fractional digits to use when parsing and formatting + numbers.]] + + rounding: double; + [[The rounding increment to use when parsing and formatting.]] + + positive: const(char)*; + [[The symbol to use for positive numbers when parsing and formatting.]] + + negative: const(char)*; + [[The symbol to use for negative numbers when parsing and formatting.]] + + decimal: const(char)*; + [[The decimal symbol to use for parsing and formatting.]] + + grouping: const(char)*; + [[The grouping symbol to use for parsing and formatting.]] +} + +enum Ecordova_Globalization_NumberPatternType { + DECIMAL, + PERCENT, + CURRENCY +} + +struct Ecordova_Globalization_NumberPatternOptions { + type: int; +} + +struct Ecordova_Globalization_DayLightSavingsTime { + dst: bool; + [[A true value indicates that daylight savings time is in effect for the + given date, and false indicates that it is not.]] +} + +struct Ecordova_Globalization_DateTime { + year: int; + [[The four digit year.]] + + month: int; + [[The month from (0-11).]] + + day: int; + [[The day from (1-31).]] + + hour: int; + [[The hour from (0-23).]] + + minute: int; + [[The minute from (0-59).]] + + second: int; + [[The second from (0-59).]] + + millisecond: int; + [[The milliseconds (from 0-999), not available on all platforms.]] +} + +struct Ecordova_Globalization_Number { + value: double; +} + +class Ecordova.Globalization (Eo.Base) { + [[Ecordova Globalization Plugin + Plugin ID: org.apache.cordova.globalization + http://plugins.cordova.io/#/package/org.apache.cordova.globalization + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Globalization constructor. + @.constructor + + @since 2.3 + ]] + } + preferred_language_get { + [[Get the BCP 47 language tag for the client's current language. + + Returns the BCP-47 compliant language identifier tag to the + successCallback with a properties object as a parameter. That + object should have a value property with a String value. + + If there is an error getting the language, then the + errorCallback executes with a GlobalizationError object as a + parameter. The error's expected code is + GlobalizationError.UNKNOWN_ERROR.]] + } + locale_name_get { + [[Returns the BCP 47 compliant tag for the client's current locale + setting. + + Returns the BCP 47 compliant locale identifier string to the + successCallback with a properties object as a parameter. That + object should have a value property with a String value. The + locale tag will consist of a two-letter lower case language + code, two-letter upper case country code, and (unspecified) + variant code, separated by a hyphen. + + If there is an error getting the locale, then the errorCallback + executes with a GlobalizationError object as a parameter. The + error's expected code is GlobalizationError.UNKNOWN_ERROR.]] + } + date_to_string { + [[Returns a date formatted as a string according to the client's + locale and timezone. + + Returns the formatted date String via a value property + accessible from the object passed as a parameter to the + successCallback. + + If there is an error formatting the date, then the errorCallback + executes with a GlobalizationError object as a parameter. + The error's expected code is + GlobalizationError.FORMATTING_ERROR.]] + params { + date: time; + [[The date to convert to string]] + + options: const(Ecordova_Globalization_DateTimeOptions)*; + [[The options parameter is optional, and its default values + are: {format_length:'short', selector:'date and time'}]] + } + } + currency_pattern_get { + [[Returns a pattern string to format and parse currency values + according to the client's user preferences and ISO 4217 currency + code.]] + params { + currency_code: const(char)*; + [[The inbound currency_code parameter should be a String of one + of the ISO 4217 currency codes, for example 'USD'.]] + } + } + date_names_get { + [[Returns the array of names to the successCallback with a + properties object as a parameter. That object contains a value + property with an Array of String values. The array features + names starting from either the first month in the year or the + first day of the week, depending on the option selected. + + If there is an error obtaining the names, then the errorCallback + executes with a GlobalizationError object as a parameter. The + error's expected code is GlobalizationError.UNKNOWN_ERROR.]] + params { + options: const(Ecordova_Globalization_DateNamesOptions)*; + [[The options parameter is optional, and its default values + are: {type:'wide', item:'months'}]] + } + } + date_pattern_get { + [[Returns a pattern string to format and parse dates according to + the client's user preferences. + + If there is an error obtaining the pattern, the errorCallback + executes with a GlobalizationError object as a parameter. The + error's expected code is GlobalizationError.PATTERN_ERROR.]] + params { + options: const(Ecordova_Globalization_DateTimeOptions)*; + [[The options parameter is optional, and defaults + to: {format_length:'short', selector:'date and time'}]] + } + } + first_day_of_week_get { + [[Returns the first day of the week according to the client's user + preferences and calendar. + + The days of the week are numbered starting from 1, where 1 is + assumed to be Sunday. Returns the day to the successCallback + with a properties object as a parameter. That object should have + a value property with a Number value. + + If there is an error obtaining the pattern, then the + errorCallback executes with a GlobalizationError object as a + parameter. The error's expected code is + GlobalizationError.UNKNOWN_ERROR.]] + } + number_pattern_get { + [[Returns a pattern string to format and parse numbers according + to the client's user preferences.]] + params { + options: const(Ecordova_Globalization_NumberPatternOptions)*; + [[The options parameter is optional, and default values + are: {type:'decimal'}]] + } + } + day_light_savings_time_is { + [[Indicates whether daylight savings time is in effect for a given + date using the client's time zone and calendar. + + Indicates whether or not daylight savings time is in effect to + the successCallback with a properties object as a parameter. + That object should have a dst property with a Boolean value. A + true value indicates that daylight savings time is in effect for + the given date, and false indicates that it is not. + + If there is an error reading the date, then the errorCallback + executes. The error's expected code is + GlobalizationError.UNKNOWN_ERROR.]] + params { + date: time; + } + } + number_to_string { + [[Returns a number formatted as a string according to the client's + user preferences.]] + params { + number: double; + + options: const(Ecordova_Globalization_NumberPatternOptions)*; + [[The options parameter is optional, and its default values + are: {type:'decimal'}]] + } + } + string_to_date { + [[Parses a date formatted as a string, according to the client's + user preferences and calendar using the time zone of the client, + and returns the corresponding date object.]] + params { + dateString: const(char)*; + options: const(Ecordova_Globalization_DateTimeOptions)*; + } + } + string_to_number { + [[Parses a number formatted as a string according to the client's + user preferences and returns the corresponding number. + + Returns the number to the successCallback with a properties + object as a parameter. That object should have a value property + with a Number value. + + If there is an error parsing the number string, then the + errorCallback executes with a GlobalizationError object as a + parameter. The error's expected code is + GlobalizationError.PARSING_ERROR.]] + params { + string: const(char)*; + + options: const(Ecordova_Globalization_NumberPatternOptions)*; + [[The options parameter is optional, and defaults to the + following values: {type:'decimal'}]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + preferred,language,success: const(Ecordova_Globalization_Language)*; + locale,name,success: const(Ecordova_Globalization_Locale)*; + date,to,string,success: const(Ecordova_Globalization_String)*; + currency,pattern,success: const(Ecordova_Globalization_CurrencyPattern)*; + date,names,success: const(Ecordova_Globalization_DateNames)*; + date,pattern,success: const(Ecordova_Globalization_DatePattern)*; + first,day,of,week,success: const(Ecordova_Globalization_FirstDayOfWeek)*; + number,pattern,success: const(Ecordova_Globalization_NumberPattern)*; + day,light,savings,time,success: const(Ecordova_Globalization_DayLightSavingsTime)*; + number,to,string,success: const(Ecordova_Globalization_String)*; + string,to,date,success: const(Ecordova_Globalization_DateTime)*; + string,to,number,success: const(Ecordova_Globalization_Number)*; + error: Ecordova_Globalization_ErrorCode; + } +} diff --git a/src/lib/ecordova/ecordova_globalization_private.h b/src/lib/ecordova/ecordova_globalization_private.h new file mode 100644 index 0000000000..1df1384a87 --- /dev/null +++ b/src/lib/ecordova/ecordova_globalization_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_GLOBALIZATION_PRIVATE_H +#define _ECORDOVA_GLOBALIZATION_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Globalization_Data Ecordova_Globalization_Data; + +/** + * Ecordova.Globalization private data + */ +struct _Ecordova_Globalization_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_inappbrowser.c b/src/lib/ecordova/ecordova_inappbrowser.c new file mode 100644 index 0000000000..4d6cc1686e --- /dev/null +++ b/src/lib/ecordova/ecordova_inappbrowser.c @@ -0,0 +1,78 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_inappbrowser_private.h" + +#define MY_CLASS ECORDOVA_INAPPBROWSER_CLASS +#define MY_CLASS_NAME "Ecordova_InAppBrowser" + +static Eo_Base * +_ecordova_inappbrowser_eo_base_constructor(Eo *obj, + Ecordova_InAppBrowser_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_inappbrowser_constructor(Eo *obj EINA_UNUSED, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_inappbrowser_eo_base_destructor(Eo *obj, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_InAppBrowser* +_ecordova_inappbrowser_open(Eo *obj EINA_UNUSED, + void *pd EINA_UNUSED, + const char *url EINA_UNUSED, + const char *target EINA_UNUSED, + const Eina_Hash *options EINA_UNUSED) +{ + ERR("Not implemented."); + return NULL; +} + +static void +_ecordova_inappbrowser_close(Eo *obj EINA_UNUSED, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_inappbrowser_show(Eo *obj EINA_UNUSED, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_inappbrowser_script_execute(Eo *obj EINA_UNUSED, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED, + const Ecordova_InAppBrowser_InjectDetails *injectDetails EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_inappbrowser_css_insert(Eo *obj EINA_UNUSED, + Ecordova_InAppBrowser_Data *pd EINA_UNUSED, + const Ecordova_InAppBrowser_InjectDetails *injectDetails EINA_UNUSED) +{ + ERR("Not implemented."); +} + +#include "ecordova_inappbrowser.eo.c" diff --git a/src/lib/ecordova/ecordova_inappbrowser.eo b/src/lib/ecordova/ecordova_inappbrowser.eo new file mode 100644 index 0000000000..d2e16da78f --- /dev/null +++ b/src/lib/ecordova/ecordova_inappbrowser.eo @@ -0,0 +1,89 @@ +struct Ecordova_InAppBrowser_InjectDetails { + file: const(char)*; [[URL of the script/stylesheet to inject.]] + code: const(char)*; [[Text of the script/stylesheet to inject.]] +} + +struct Ecordova_InAppBrowser_EventProperties { + type: const(char)*; [[the eventname, either loadstart, loadstop, loaderror, or exit.]] + url: const(char)*; [[the URL that was loaded.]] + code: const(char)*; [[the error code, only in the case of loaderror.]] + message: const(char)*; [[the error message, only in the case of loaderror.]] +} + +class Ecordova.InAppBrowser (Eo.Base) { + [[Ecordova InAppBrowser Plugin + Plugin ID: org.apache.cordova.inappbrowser + http://plugins.cordova.io/#/package/org.apache.cordova.inappbrowser + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_InAppBrowser constructor. + @.constructor + + @since 2.3 + ]] + } + open @class { + params { + url: const(char)*; + [[The URL to load. Call encodeURI() on this if the URL + contains Unicode characters.]] + + target: const(char)*; + [[The target in which to load the URL, an optional parameter + that defaults to _self.]] + + options: const(hash<char*,char*>)*; + [[Options for the InAppBrowser. Optional, defaulting + to: location=yes.]] + + } + return: Ecordova.InAppBrowser*; + } + close { + [[Closes the InAppBrowser window.]] + } + show { + [[Displays an InAppBrowser window that was opened hidden. Calling + this has no effect if the InAppBrowser was already visible.]] + } + script_execute { + [[Injects JavaScript code into the InAppBrowser window]] + params { + injectDetails: const(Ecordova_InAppBrowser_InjectDetails)*; + [[details of the script to run, specifying either a file or + code key.]] + } + } + css_insert { + [[Injects CSS into the InAppBrowser window.]] + params { + injectDetails: const(Ecordova_InAppBrowser_InjectDetails)*; + [[details of the script to run, specifying either a file or + code key.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + load,start: const(Ecordova_InAppBrowser_EventProperties)*; + load,stop: const(Ecordova_InAppBrowser_EventProperties)*; + load,error: const(Ecordova_InAppBrowser_EventProperties)*; + exit: const(Ecordova_InAppBrowser_EventProperties)*; + + script,executed; + [[If the injected script is of type code, the callback executes with a + single parameter, which is the return value of the script, wrapped + in an Array. For multi-line scripts, this is the return value of the + last statement, or the last expression evaluated.]] + + css,injected; [[executes after the CSS is injected.]] + } +} diff --git a/src/lib/ecordova/ecordova_inappbrowser_private.h b/src/lib/ecordova/ecordova_inappbrowser_private.h new file mode 100644 index 0000000000..4526e8773b --- /dev/null +++ b/src/lib/ecordova/ecordova_inappbrowser_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_INAPPBROWSER_PRIVATE_H +#define _ECORDOVA_INAPPBROWSER_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_InAppBrowser_Data Ecordova_InAppBrowser_Data; + +/** + * Ecordova.InAppBrowser private data + */ +struct _Ecordova_InAppBrowser_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_main.c b/src/lib/ecordova/ecordova_main.c new file mode 100644 index 0000000000..701e953dce --- /dev/null +++ b/src/lib/ecordova/ecordova_main.c @@ -0,0 +1,98 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_private.h" +#include "ecordova_systeminfo.eo.h" + +#include <Ecore_File.h> + +static int _ecordova_init_count = 0; +int _ecordova_log_dom = -1; +Eo* _ecordova_systeminfo = NULL; + +EAPI int +ecordova_init(void) +{ + if (_ecordova_init_count++ > 0) + return _ecordova_init_count; + + if (!eina_init()) + { + fputs("Ecordova: Unable to initialize eina\n", stderr); + return 0; + } + + _ecordova_log_dom = eina_log_domain_register("ecordova", EINA_COLOR_CYAN); + if (_ecordova_log_dom < 0) + { + EINA_LOG_ERR("Unable to create an 'ecordova' log domain"); + goto on_error_1; + } + + if (!ecore_init()) + { + ERR("Unable to initialize ecore"); + goto on_error_2; + } + + if (!ecore_file_init()) + { + ERR("Unable to initialize ecore_file"); + goto on_error_3; + } + + if (!eio_init()) + { + ERR("Unable to initialize eio"); + goto on_error_4; + } + + _ecordova_systeminfo = eo_add(ECORDOVA_SYSTEMINFO_CLASS, NULL, + ecordova_systeminfo_constructor()); + if (!_ecordova_systeminfo) + { + ERR("Unable to initialize systeminfo service"); + goto on_error_4; + } + + return _ecordova_init_count; + +on_error_4: + ecore_file_shutdown(); + +on_error_3: + ecore_shutdown(); + +on_error_2: + eina_log_domain_unregister(_ecordova_log_dom); + +on_error_1: + _ecordova_log_dom = -1; + eina_shutdown(); + return 0; +} + +EAPI int +ecordova_shutdown(void) +{ + if (_ecordova_init_count <= 0) + { + ERR("Init count not greater than 0 in shutdown."); + _ecordova_init_count = 0; + return 0; + } + + if (--_ecordova_init_count) + return _ecordova_init_count; + + eo_unref(_ecordova_systeminfo); + eio_shutdown(); + ecore_file_shutdown(); + ecore_shutdown(); + eina_log_domain_unregister(_ecordova_log_dom); + _ecordova_log_dom = -1; + eina_shutdown(); + return 0; +} + diff --git a/src/lib/ecordova/ecordova_media.c b/src/lib/ecordova/ecordova_media.c new file mode 100644 index 0000000000..926a2cad49 --- /dev/null +++ b/src/lib/ecordova/ecordova_media.c @@ -0,0 +1,531 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_media_private.h" + +#define MY_CLASS ECORDOVA_MEDIA_CLASS +#define MY_CLASS_NAME "Ecordova_Media" + +typedef void(* Action_Cb)(Ecordova_Media_Data *, void *); + +typedef struct { + Action_Cb callback; + void *data; +} Action; + +static void _completed_cb(void *); +static void _error_cb(int, void *); +static void _prepared_cb(void *); +static void _set_position_cb(void *); +static void _execute(Ecordova_Media_Data *, Action_Cb, void *); +static void _pause_action(Ecordova_Media_Data *, void *); +static void _start_action(Ecordova_Media_Data *, void *); +static void _stop_action(Ecordova_Media_Data *, void *); +static void _get_position_action(Ecordova_Media_Data *, void *); +static void _set_position_action(Ecordova_Media_Data *, void *); +static void _set_volume_action(Ecordova_Media_Data *, void *); +static void _update_status(Ecordova_Media_Data *); +static void _release(Ecordova_Media_Data *); +static void _state_changed_cb(recorder_state_e, recorder_state_e, bool, void *); +static void _start_record(Ecordova_Media_Data *); + +static Eo_Base * +_ecordova_media_eo_base_constructor(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->player = NULL; + pd->recorder = NULL; + pd->status = ECORDOVA_MEDIA_STATUS_MEDIA_NONE; + pd->pending = NULL; + pd->record_pending = false; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_media_constructor(Eo *obj, + Ecordova_Media_Data *pd, + const char *src) +{ + DBG("(%p)", obj); + EINA_SAFETY_ON_NULL_RETURN(src); + + int ret = player_create(&pd->player); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + ret = recorder_create_audiorecorder(&pd->recorder); + EINA_SAFETY_ON_FALSE_RETURN(RECORDER_ERROR_NONE == ret); + + ret = player_set_uri(pd->player, src); + if (PLAYER_ERROR_NONE != ret) + { + ERR("Error setting source media: %s", src); + _release(pd); + return; + } + + ret = player_set_completed_cb(pd->player, + _completed_cb, + pd); + if (PLAYER_ERROR_NONE != ret) + { + ERR("Error setting completed callback: %d", ret); + _release(pd); + return; + } + + ret = player_set_error_cb(pd->player, + _error_cb, + pd); + if (PLAYER_ERROR_NONE != ret) + { + ERR("Error setting completed callback: %d", ret); + _release(pd); + return; + } + + ret = recorder_set_state_changed_cb(pd->recorder, + _state_changed_cb, + pd); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting recorder state changed callback: %d", ret); + _release(pd); + return; + } + + ret = recorder_set_audio_encoder(pd->recorder, RECORDER_AUDIO_CODEC_AAC); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting audio encoder: %d", ret); + _release(pd); + return; + } + + ret = recorder_attr_set_audio_samplerate(pd->recorder, 44100); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting audio sample rate: %d", ret); + _release(pd); + return; + } + + ret = recorder_set_file_format(pd->recorder, RECORDER_FILE_FORMAT_3GP); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting file format for audio encoder: %d", ret); + _release(pd); + return; + } + + ret = recorder_attr_set_audio_encoder_bitrate(pd->recorder, 28800); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting audio encoder bitrate: %d", ret); + _release(pd); + return; + } + + ret = recorder_set_filename(pd->recorder, src); + if (RECORDER_ERROR_NONE != ret) + { + ERR("Error setting recorder filename: %d", ret); + _release(pd); + return; + } +} + +static void +_ecordova_media_eo_base_destructor(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + + _release(pd); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_media_current_position_get(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + _execute(pd, _get_position_action, NULL); +} + +static int +_ecordova_media_duration_get(Eo *obj EINA_UNUSED, Ecordova_Media_Data *pd) +{ + int duration = -1; + + int ret = player_get_duration(pd->player, &duration); + EINA_SAFETY_ON_FALSE_RETURN_VAL(PLAYER_ERROR_NONE == ret, -1); + + return duration / 1000; +} + +static void +_ecordova_media_pause(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + _execute(pd, _pause_action, NULL); +} + +static void +_ecordova_media_play(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + _execute(pd, _start_action, NULL); +} + +static void +_ecordova_media_release(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + _release(pd); +} + +static void +_ecordova_media_seek(Eo *obj, Ecordova_Media_Data *pd, int milliseconds) +{ + DBG("(%p)", obj); + int *data = malloc(sizeof(milliseconds)); + *data = milliseconds; + _execute(pd, _set_position_action, data); +} + +static void +_ecordova_media_volume_set(Eo *obj, Ecordova_Media_Data *pd, double volume) +{ + DBG("(%p)", obj); + double *data = malloc(sizeof(volume)); + *data = volume; + _execute(pd, _set_volume_action, data); +} + +static void +_ecordova_media_record_start(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + + recorder_state_e state = RECORDER_STATE_NONE; + int ret = recorder_get_state(pd->recorder, &state); + EINA_SAFETY_ON_FALSE_RETURN(RECORDER_ERROR_NONE == ret); + + switch (state) + { + case RECORDER_STATE_NONE: + case RECORDER_STATE_CREATED: + ret = recorder_prepare(pd->recorder); + EINA_SAFETY_ON_FALSE_RETURN(RECORDER_ERROR_NONE == ret); + pd->record_pending = true; + return; + case RECORDER_STATE_READY: + if (pd->record_pending) + { + ERR("%s", "The recorder has already been started and is pending."); + return; + } + _start_record(pd); + return; + case RECORDER_STATE_RECORDING: + ERR("%s", "The recorder is already recording."); + return; + case RECORDER_STATE_PAUSED: + ERR("%s", "Unreachable!"); + return; + } +} + +static void +_ecordova_media_stop(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + _execute(pd, _stop_action, NULL); +} + +static void +_ecordova_media_record_stop(Eo *obj, Ecordova_Media_Data *pd) +{ + DBG("(%p)", obj); + + recorder_state_e state = RECORDER_STATE_NONE; + int ret = recorder_get_state(pd->recorder, &state); + EINA_SAFETY_ON_FALSE_RETURN(RECORDER_ERROR_NONE == ret); + + switch (state) + { + case RECORDER_STATE_NONE: + case RECORDER_STATE_CREATED: + case RECORDER_STATE_READY: + ERR("%s", "The recorder is not recording."); + return; + case RECORDER_STATE_RECORDING: + ret = recorder_commit(pd->recorder); + return; + case RECORDER_STATE_PAUSED: + ERR("%s", "Unreachable!"); + return; + } +} + +static void +_completed_cb(void *data) +{ + Ecordova_Media_Data *pd = data; + DBG("(%p)", pd->obj); + + // TODO: Check what thread this callback is running to make sure it's on the main thread + + _update_status(pd); + + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_SUCCESS, NULL)); +} + +static void +_error_cb(int error EINA_UNUSED, void *data) +{ + Ecordova_Media_Data *pd = data; + DBG("(%p)", pd->obj); + + // TODO: ¿ check error ? + + // TODO: Check what thread this callback is running to make sure it's on the main thread + + _update_status(pd); + + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_ERROR, NULL)); +} + +static void +_prepared_cb(void *data) +{ + Ecordova_Media_Data *pd = data; + DBG("(%p)", pd->obj); + + Action *action; + EINA_LIST_FREE(pd->pending, action) + { + action->callback(pd, action->data); + free(action->data); + free(action); + } + +} + +static void +_set_position_cb(void *data) +{ + Ecordova_Media_Data *pd = data; + DBG("(%p)", pd->obj); + + // TODO: Check what thread this callback is running to make sure it's on the main thread + + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_SUCCESS, NULL)); +} + +static void +_execute(Ecordova_Media_Data *pd, Action_Cb action_cb, void *data) +{ + DBG("(%p)", pd->obj); + + switch (pd->status) + { + case ECORDOVA_MEDIA_STATUS_MEDIA_NONE: + { + int ret = player_prepare_async(pd->player, + _prepared_cb, + pd); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + pd->status = ECORDOVA_MEDIA_STATUS_MEDIA_STARTING; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_STATUS, &pd->status)); + // fall-through + } + case ECORDOVA_MEDIA_STATUS_MEDIA_STARTING: + { + Action *action = malloc(sizeof(Action)); + action->callback = action_cb; + action->data = data; + + pd->pending = eina_list_append(pd->pending, action); + return; + } + default: + action_cb(pd, data); + } +} + +static void +_pause_action(Ecordova_Media_Data *pd, void *data EINA_UNUSED) +{ + DBG("(%p)", pd->obj); + + int ret = player_pause(pd->player); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + _update_status(pd); +} + +static void +_stop_action(Ecordova_Media_Data *pd, void *data EINA_UNUSED) +{ + DBG("(%p)", pd->obj); + + int ret = player_stop(pd->player); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + _update_status(pd); +} + +static void +_start_action(Ecordova_Media_Data *pd, void *data EINA_UNUSED) +{ + DBG("(%p)", pd->obj); + + int ret = player_stop(pd->player); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + _update_status(pd); +} + +static void +_get_position_action(Ecordova_Media_Data *pd, void *data EINA_UNUSED) +{ + DBG("(%p)", pd->obj); + + int position = 0; + int ret = player_get_play_position(pd->player, &position); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + position /= 1000; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_POSITION, &position)); +} + +static void +_set_position_action(Ecordova_Media_Data *pd, void *data) +{ + DBG("(%p)", pd->obj); + + int *position = data; + int ret = player_set_play_position(pd->player, + *position, + false, + _set_position_cb, + pd); + free(position); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); +} + +static void +_set_volume_action(Ecordova_Media_Data *pd, void *data) +{ + DBG("(%p)", pd->obj); + + double *volume = data; + int ret = player_set_volume(pd->player, *volume, *volume); + free(volume); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_SUCCESS, NULL)); +} + +static void +_update_status(Ecordova_Media_Data *pd) +{ + player_state_e state = PLAYER_STATE_NONE; + + int ret = player_get_state(pd->player, &state); + EINA_SAFETY_ON_FALSE_RETURN(PLAYER_ERROR_NONE == ret); + + Ecordova_Media_Status new_status = ECORDOVA_MEDIA_STATUS_MEDIA_NONE; + switch (state) + { + case PLAYER_STATE_NONE: + new_status = ECORDOVA_MEDIA_STATUS_MEDIA_NONE; + break; + case PLAYER_STATE_IDLE: + new_status = ECORDOVA_MEDIA_STATUS_MEDIA_STARTING; + break; + case PLAYER_STATE_READY: + new_status = ECORDOVA_MEDIA_STATUS_MEDIA_STOPPED; + break; + case PLAYER_STATE_PLAYING: + new_status = ECORDOVA_MEDIA_STATUS_MEDIA_RUNNING; + break; + case PLAYER_STATE_PAUSED: + new_status = ECORDOVA_MEDIA_STATUS_MEDIA_PAUSED; + break; + } + + if (new_status == pd->status) + return; + + pd->status = new_status; + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_STATUS, &pd->status)); +} + +static void +_release(Ecordova_Media_Data *pd) +{ + if (pd->player) + { + player_unset_error_cb(pd->player); + player_unset_completed_cb(pd->player); + if (ECORDOVA_MEDIA_STATUS_MEDIA_NONE != pd->status) + player_unprepare(pd->player); + player_destroy(pd->player); + pd->player = NULL; + } + + if (pd->recorder) + { + recorder_unset_state_changed_cb(pd->recorder); + recorder_state_e state = RECORDER_STATE_NONE; + if (RECORDER_ERROR_NONE == recorder_get_state(pd->recorder, &state) && + RECORDER_STATE_NONE != state && RECORDER_STATE_CREATED != state) + recorder_unprepare(pd->recorder); + recorder_destroy(pd->recorder); + pd->recorder = NULL; + } + + Action *action; + EINA_LIST_FREE(pd->pending, action) + { + free(action->data); + free(action); + } +} + +static void +_state_changed_cb(recorder_state_e previous, + recorder_state_e current, + bool by_policy EINA_UNUSED, + void *data) +{ + Ecordova_Media_Data *pd = data; + if (RECORDER_STATE_READY == current && pd->record_pending) + { + _start_record(pd); + return; + } + + if (RECORDER_STATE_RECORDING == current) + pd->record_pending = false; + + // TODO: Check what thread this callback is running to make sure it's on the main thread + if (RECORDER_STATE_RECORDING == previous && RECORDER_STATE_READY == current) + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIA_EVENT_MEDIA_SUCCESS, NULL)); +} + +static void +_start_record(Ecordova_Media_Data *pd) +{ + int ret = recorder_start(pd->recorder); + EINA_SAFETY_ON_FALSE_RETURN(RECORDER_ERROR_NONE == ret); +} + +#include "ecordova_media.eo.c" diff --git a/src/lib/ecordova/ecordova_media.eo b/src/lib/ecordova/ecordova_media.eo new file mode 100644 index 0000000000..e64b2b3d8a --- /dev/null +++ b/src/lib/ecordova/ecordova_media.eo @@ -0,0 +1,108 @@ +enum Ecordova_Media_Status { + MEDIA_NONE = 0, + MEDIA_STARTING = 1, + MEDIA_RUNNING = 2, + MEDIA_PAUSED = 3, + MEDIA_STOPPED = 4 +} + +enum Ecordova_Media_ErrorCode { + MEDIA_ERR_ABORTED = 1, + MEDIA_ERR_NETWORK = 2, + MEDIA_ERR_DECODE = 3, + MEDIA_ERR_NONE_SUPPORTED = 4 +} + +struct Ecordova_Media_Error { + [[A MediaError object is returned to the mediaError callback function when an error occurs.]] + code: int; [[One of the predefined error codes]] + message: const(char)*; [[An error message describing the details of the error.]] +} + +class Ecordova.Media (Eo.Base) { + [[Ecordova Media Plugin + Plugin ID: org.apache.cordova.media + http://plugins.cordova.io/#/package/org.apache.cordova.media + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Media constructor. + @.constructor + + @since 2.3 + ]] + params { + src: const(char)*; [[A URI containing the audio content.]] + } + } + current_position_get { + [[Returns the current position within an audio file. Also updates + the Media object's position parameter.]] + } + duration_get { + [[Returns the duration of an audio file in seconds. If the + duration is unknown, it returns a value of -1.]] + return: int; + } + pause { + [[Pauses playing an audio file.]] + } + play { + [[Starts or resumes playing an audio file.]] + } + release { + [[Releases the underlying operating system's audio resources. This + is particularly important for Android, since there are a finite + amount of OpenCore instances for media playback. Applications + should call the release function for any Media resource that is + no longer needed.]] + } + seek { + [[Sets the current position within an audio file.]] + params { + milliseconds: int; + [[The position to set the playback position within the audio, + in milliseconds.]] + } + } + volume_set { + [[Set the volume for an audio file.]] + params { + volume: double; + [[The volume to set for playback. The value must be within the + range of 0.0 to 1.0.]] + } + } + record_start { + [[Starts recording an audio file.]] + } + stop { + [[Stops playing an audio file.]] + } + record_stop { + [[Stops recording an audio file.]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + media,position: int; + [[The callback that is passed the current position in seconds.]] + + media,success; + [[The callback that executes after a Media object has completed the + current play, record, or stop action.]] + + media,error; + [[The callback that executes if an error occurs.]] + + media,status: Ecordova_Media_Status; + [[The callback that executes to indicate status changes.]] + } +} diff --git a/src/lib/ecordova/ecordova_media_private.h b/src/lib/ecordova/ecordova_media_private.h new file mode 100644 index 0000000000..84d7dbe9ec --- /dev/null +++ b/src/lib/ecordova/ecordova_media_private.h @@ -0,0 +1,26 @@ +#ifndef _ECORDOVA_MEDIA_PRIVATE_H +#define _ECORDOVA_MEDIA_PRIVATE_H + +#include "ecordova_private.h" + +#include <player.h> +#include <recorder.h> + +#include <stdbool.h> + +typedef struct _Ecordova_Media_Data Ecordova_Media_Data; + +/** + * Ecordova.Media private data + */ +struct _Ecordova_Media_Data +{ + Eo *obj; + player_h player; + recorder_h recorder; + Ecordova_Media_Status status; + Eina_List *pending; + bool record_pending; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_mediafile.c b/src/lib/ecordova/ecordova_mediafile.c new file mode 100644 index 0000000000..b227835cde --- /dev/null +++ b/src/lib/ecordova/ecordova_mediafile.c @@ -0,0 +1,187 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_mediafile_private.h" +#include <stdbool.h> +#include <limits.h> + +#define MY_CLASS ECORDOVA_MEDIAFILE_CLASS +#define MY_CLASS_NAME "Ecordova_MediaFile" + +static void _extract_cb(void *, Ecore_Thread *); +static void _extract_end_cb(void *, Ecore_Thread *); +static void _extract_cancel_cb(void *, Ecore_Thread *); +static void _internal_error_notify(Ecordova_MediaFile_Data *pd); +static bool _bool_metadata_get(metadata_extractor_h, metadata_extractor_attr_e); +static int _int_metadata_get(metadata_extractor_h, metadata_extractor_attr_e); + +#define NO_ERROR (INT_MAX) + +static Eo_Base * +_ecordova_mediafile_eo_base_constructor(Eo *obj, Ecordova_MediaFile_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->extractor = NULL; + pd->error = NO_ERROR; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_mediafile_constructor(Eo *obj, + Ecordova_MediaFile_Data *pd EINA_UNUSED, + const char *name, + const char *url, + const char *type, + time_t last_modified_date, + long size) +{ + DBG("(%p)", obj); + eo_do_super(obj, MY_CLASS, ecordova_file_constructor(name, + url, + type, + last_modified_date, + size)); +} + +static void +_ecordova_mediafile_eo_base_destructor(Eo *obj, Ecordova_MediaFile_Data *pd) +{ + DBG("(%p)", obj); + + if (pd->extractor) + metadata_extractor_destroy(pd->extractor); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_mediafile_format_data_get(Eo *obj EINA_UNUSED, + Ecordova_MediaFile_Data *pd) +{ + Ecore_Thread *thread = ecore_thread_run(_extract_cb, + _extract_end_cb, + _extract_cancel_cb, + pd); + if (!thread) + _internal_error_notify(pd); +} + +static void +_extract_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_MediaFile_Data *pd = data; + int ret; + + if (!pd->extractor) + { + ret = metadata_extractor_create(&pd->extractor); + if (METADATA_EXTRACTOR_ERROR_NONE != ret) + goto on_error; + + const char *url = eo_do_super_ret(pd->obj, + MY_CLASS, + url, + ecordova_file_url_get()); + ret = metadata_extractor_set_path(pd->extractor, url); + if (METADATA_EXTRACTOR_ERROR_NONE != ret) + goto on_error; + } + + pd->metadata.codecs = NULL; // TODO: what is it? + pd->metadata.duration = _int_metadata_get(pd->extractor, METADATA_DURATION) / 1000; + + bool has_video = _bool_metadata_get(pd->extractor, METADATA_HAS_VIDEO); + bool has_audio = _bool_metadata_get(pd->extractor, METADATA_HAS_AUDIO); + + DBG("has_video=%d, has_audio=%d", has_video, has_audio); + if (has_video) + { + pd->metadata.width = _int_metadata_get(pd->extractor, METADATA_VIDEO_WIDTH); + pd->metadata.height = _int_metadata_get(pd->extractor, METADATA_VIDEO_HEIGHT); + pd->metadata.bitrate = _int_metadata_get(pd->extractor, METADATA_VIDEO_BITRATE); + } + else + { + pd->metadata.bitrate = _int_metadata_get(pd->extractor, METADATA_AUDIO_BITRATE); + } + + pd->error = NO_ERROR; + return; + +on_error: + pd->error = ECORDOVA_CAPTURE_ERRORCODE_CAPTURE_INTERNAL_ERR; +} + +static void +_extract_end_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_MediaFile_Data *pd = data; + if (NO_ERROR != pd->error) + { + Ecordova_Capture_Error error = {.code = pd->error}; + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_MEDIAFILE_EVENT_ERROR, + &error)); + return; + } + + eo_do(pd->obj, + eo_event_callback_call(ECORDOVA_MEDIAFILE_EVENT_SUCCESS, + &pd->metadata)); +} + +static void +_extract_cancel_cb(void *data, Ecore_Thread *thread EINA_UNUSED) +{ + Ecordova_MediaFile_Data *pd = data; + _internal_error_notify(pd); +} + +static void +_internal_error_notify(Ecordova_MediaFile_Data *pd) +{ + Ecordova_Capture_Error error = { + .code = ECORDOVA_CAPTURE_ERRORCODE_CAPTURE_INTERNAL_ERR + }; + eo_do(pd->obj, eo_event_callback_call(ECORDOVA_MEDIAFILE_EVENT_ERROR, &error)); +} + +static bool +_bool_metadata_get(metadata_extractor_h extractor, + metadata_extractor_attr_e attr) +{ + bool result = false; + char *value = NULL; + int ret = metadata_extractor_get_metadata(extractor, attr, &value); + if (value) + { + result = strcmp(value, "TRUE") == 0; + free(value); + } + + EINA_SAFETY_ON_FALSE_RETURN_VAL(METADATA_EXTRACTOR_ERROR_NONE == ret, false); + return result; +} + +static int +_int_metadata_get(metadata_extractor_h extractor, + metadata_extractor_attr_e attr) +{ + int result = 0; + char *value = NULL; + int ret = metadata_extractor_get_metadata(extractor, attr, &value); + if (value) + { + result = atoi(value); + free(value); + } + + EINA_SAFETY_ON_FALSE_RETURN_VAL(METADATA_EXTRACTOR_ERROR_NONE == ret, 0); + return result; +} + +#include "ecordova_mediafile.eo.c" diff --git a/src/lib/ecordova/ecordova_mediafile.eo b/src/lib/ecordova/ecordova_mediafile.eo new file mode 100644 index 0000000000..b1ebd52eb5 --- /dev/null +++ b/src/lib/ecordova/ecordova_mediafile.eo @@ -0,0 +1,62 @@ +struct Ecordova_MediaFileData { + [[Encapsulates format information about a media file.]] + + codecs: const(char)*; + [[The actual format of the audio and video content.]] + + bitrate: int; + [[The average bitrate of the content. The value is zero for images.]] + + height: int; + [[The height of the image or video in pixels. The value is zero for audio + clips.]] + + width: int; + [[The width of the image or video in pixels. The value is zero for audio + clips.]] + + duration: int; + [[The length of the video or sound clip in seconds. The value is zero for + images.]] +} + +class Ecordova.MediaFile (Ecordova.File) { + [[Represents a single media file.]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_MediaFile constructor. + @.constructor + + @since 2.3 + ]] + params { + name: const(char)*; [[name of the file, without path information]] + url: const(char)*; [[the full path of the file, including the name]] + type: const(char)*; [[mime type]] + last_modified_date: time; [[last modified date]] + size: long; [[size of the file in bytes]] + } + } + format_data_get { + [[Retrieves format information about the media capture file. + + This function asynchronously attempts to retrieve the format + information for the media file. If successful, it invokes the + MediaFileDataSuccessCB callback with a MediaFileData object. If + the attempt fails, this function invokes the + MediaFileDataErrorCB callback.]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + success: const(Ecordova_MediaFileData)*; + error: const(Ecordova.Capture.Error)*; + } +} diff --git a/src/lib/ecordova/ecordova_mediafile_private.h b/src/lib/ecordova/ecordova_mediafile_private.h new file mode 100644 index 0000000000..1e7e8621d2 --- /dev/null +++ b/src/lib/ecordova/ecordova_mediafile_private.h @@ -0,0 +1,21 @@ +#ifndef _ECORDOVA_MEDIAFILE_PRIVATE_H +#define _ECORDOVA_MEDIAFILE_PRIVATE_H + +#include "ecordova_private.h" + +#include <metadata_extractor.h> + +typedef struct _Ecordova_MediaFile_Data Ecordova_MediaFile_Data; + +/** + * Ecordova.MediaFile private data + */ +struct _Ecordova_MediaFile_Data +{ + Eo *obj; + metadata_extractor_h extractor; + Ecordova_Capture_ErrorCode error; + Ecordova_MediaFileData metadata; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_networkinformation.c b/src/lib/ecordova/ecordova_networkinformation.c new file mode 100644 index 0000000000..e89dc7018a --- /dev/null +++ b/src/lib/ecordova/ecordova_networkinformation.c @@ -0,0 +1,98 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_networkinformation_private.h" + +#define MY_CLASS ECORDOVA_NETWORKINFORMATION_CLASS +#define MY_CLASS_NAME "Ecordova_NetworkInformation" + +static void _type_changed_cb(connection_type_e, void *); +static Ecordova_NetworkInformation_ConnectionType _to_connection_type(connection_type_e); + +static Eo_Base * +_ecordova_networkinformation_eo_base_constructor(Eo *obj, + Ecordova_NetworkInformation_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + pd->connection = NULL; + int ret = connection_create(&pd->connection); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONNECTION_ERROR_NONE == ret, NULL); + + ret = connection_set_type_changed_cb(pd->connection, _type_changed_cb, obj); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONNECTION_ERROR_NONE == ret, NULL); + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_networkinformation_constructor(Eo *obj EINA_UNUSED, + Ecordova_NetworkInformation_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_networkinformation_eo_base_destructor(Eo *obj, + Ecordova_NetworkInformation_Data *pd) +{ + DBG("(%p)", obj); + + int ret = connection_unset_type_changed_cb(pd->connection); + EINA_SAFETY_ON_FALSE_RETURN(CONNECTION_ERROR_NONE == ret); + + ret = connection_destroy(pd->connection); + EINA_SAFETY_ON_FALSE_RETURN(CONNECTION_ERROR_NONE == ret); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Ecordova_NetworkInformation_ConnectionType +_ecordova_networkinformation_type_get(Eo *obj, + Ecordova_NetworkInformation_Data *pd) +{ + DBG("(%p)", obj); + + connection_type_e type; + + int ret = connection_get_type(pd->connection, &type); + EINA_SAFETY_ON_FALSE_RETURN_VAL(CONNECTION_ERROR_NONE == ret, + ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_UNKNOWN); + + return _to_connection_type(type); +} + +static Ecordova_NetworkInformation_ConnectionType +_to_connection_type(connection_type_e type) +{ + switch (type) + { + case CONNECTION_TYPE_DISCONNECTED: + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_NONE; + case CONNECTION_TYPE_WIFI: + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_WIFI; + case CONNECTION_TYPE_CELLULAR: + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_CELL; + case CONNECTION_TYPE_ETHERNET: + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_ETHERNET; + case CONNECTION_TYPE_BT: + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_UNKNOWN; + } + + return ECORDOVA_NETWORKINFORMATION_CONNECTIONTYPE_UNKNOWN; +} + +static void +_type_changed_cb(connection_type_e type, void *user_data) +{ + Eo *obj = user_data; + + const Eo_Event_Description *event = + CONNECTION_TYPE_DISCONNECTED == type ? ECORDOVA_NETWORKINFORMATION_EVENT_OFFLINE + : ECORDOVA_NETWORKINFORMATION_EVENT_ONLINE; + eo_do(obj, eo_event_callback_call(event, NULL)); +} + +#include "ecordova_networkinformation.eo.c" diff --git a/src/lib/ecordova/ecordova_networkinformation.eo b/src/lib/ecordova/ecordova_networkinformation.eo new file mode 100644 index 0000000000..bff5810354 --- /dev/null +++ b/src/lib/ecordova/ecordova_networkinformation.eo @@ -0,0 +1,61 @@ +enum Ecordova_NetworkInformation_ConnectionType { + UNKNOWN, + ETHERNET, + WIFI, + CELL_2G, + CELL_3G, + CELL_4G, + CELL, + NONE +} + +class Ecordova.NetworkInformation (Eo.Base) { + [[Ecordova Network-Information Plugin + Plugin ID: org.apache.cordova.network-information + http://plugins.cordova.io/#/package/org.apache.cordova.network-information + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_NetworkInformation constructor. + @.constructor + + @since 2.3 + ]] + } + @property type { + [[This property offers a fast way to determine the device's + network connection state, and type of connection.]] + get{} + values { + value: Ecordova_NetworkInformation_ConnectionType; + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + offline; + [[The event fires when an application goes offline, and the device is + not connected to the Internet. + + The offline event fires when a previously connected device loses a + network connection so that an application can no longer access the + Internet. It relies on the same information as the Connection API, + and fires when the value of connection.type becomes NONE.]] + + online; + [[This event fires when an application goes online, and the device + becomes connected to the Internet. + + The online event fires when a previously unconnected device receives + a network connection to allow an application access to the Internet. + It relies on the same information as the Connection API, and fires + when the connection.type changes from NONE to any other value.]] + } +} diff --git a/src/lib/ecordova/ecordova_networkinformation_private.h b/src/lib/ecordova/ecordova_networkinformation_private.h new file mode 100644 index 0000000000..1af4d1e22e --- /dev/null +++ b/src/lib/ecordova/ecordova_networkinformation_private.h @@ -0,0 +1,19 @@ +#ifndef _ECORDOVA_NETWORKINFORMATION_PRIVATE_H +#define _ECORDOVA_NETWORKINFORMATION_PRIVATE_H + +#include "ecordova_private.h" + +#include <net_connection.h> + +typedef struct _Ecordova_NetworkInformation_Data Ecordova_NetworkInformation_Data; + +/** + * Ecordova.NetworkInformation private data + */ +struct _Ecordova_NetworkInformation_Data +{ + Eo *obj; + connection_h connection; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_private.h b/src/lib/ecordova/ecordova_private.h new file mode 100644 index 0000000000..231e2d93d7 --- /dev/null +++ b/src/lib/ecordova/ecordova_private.h @@ -0,0 +1,54 @@ +#ifndef _ECORDOVA_PRIVATE_H +#define _ECORDOVA_PRIVATE_H + +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "Ecordova.h" +#ifdef EAPI +# undef EAPI +#endif + +#ifdef _WIN32 +# ifdef EFL_ECORDOVA_BUILD +# ifdef DLL_EXPORT +# define EAPI __declspec(dllexport) +# else +# define EAPI +# endif /* ! DLL_EXPORT */ +# else +# define EAPI __declspec(dllimport) +# endif /* ! EFL_ECORDOVA_BUILD */ +#else +# ifdef __GNUC__ +# if __GNUC__ >= 4 +# define EAPI __attribute__ ((visibility("default"))) +# else +# define EAPI +# endif +# else +# define EAPI +# endif +#endif + +#include <stdbool.h> + +/* logging support */ +extern int _ecordova_log_dom; + +#define CRI(...) EINA_LOG_DOM_CRIT(_ecordova_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_ecordova_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_ecordova_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_ecordova_log_dom, __VA_ARGS__) +#define DBG(...) EINA_LOG_DOM_DBG(_ecordova_log_dom, __VA_ARGS__) + +#define MIN(x, y) (((x) > (y)) ? (y) : (x)) +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) + +extern Eo* _ecordova_systeminfo; + +#undef EAPI +#define EAPI + +#endif diff --git a/src/lib/ecordova/ecordova_splashscreen.c b/src/lib/ecordova/ecordova_splashscreen.c new file mode 100644 index 0000000000..6b3498d454 --- /dev/null +++ b/src/lib/ecordova/ecordova_splashscreen.c @@ -0,0 +1,51 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_splashscreen_private.h" + +#define MY_CLASS ECORDOVA_SPLASHSCREEN_CLASS +#define MY_CLASS_NAME "Ecordova_Splashscreen" + +static Eo_Base * +_ecordova_splashscreen_eo_base_constructor(Eo *obj, + Ecordova_Splashscreen_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_splashscreen_constructor(Eo *obj EINA_UNUSED, + Ecordova_Splashscreen_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_splashscreen_eo_base_destructor(Eo *obj, + Ecordova_Splashscreen_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_splashscreen_show(Eo *obj EINA_UNUSED, + Ecordova_Splashscreen_Data *pd EINA_UNUSED) +{ + ERR("Not implemented."); +} + +static void +_ecordova_splashscreen_hide(Eo *obj EINA_UNUSED, + Ecordova_Splashscreen_Data *pd EINA_UNUSED) +{ + ERR("Not implemented."); +} + +#include "ecordova_splashscreen.eo.c" diff --git a/src/lib/ecordova/ecordova_splashscreen.eo b/src/lib/ecordova/ecordova_splashscreen.eo new file mode 100644 index 0000000000..88346ad04c --- /dev/null +++ b/src/lib/ecordova/ecordova_splashscreen.eo @@ -0,0 +1,29 @@ +class Ecordova.Splashscreen (Eo.Base) { + [[Ecordova Splashscreen Plugin + Plugin ID: org.apache.cordova.splashscreen + http://plugins.cordova.io/#/package/org.apache.cordova.splashscreen + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Splashscreen constructor. + @.constructor + + @since 2.3 + ]] + } + show { + [[Displays the splash screen.]] + } + hide { + [[Dismiss the splash screen.]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_splashscreen_private.h b/src/lib/ecordova/ecordova_splashscreen_private.h new file mode 100644 index 0000000000..5f1fc311c7 --- /dev/null +++ b/src/lib/ecordova/ecordova_splashscreen_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_SPLASHSCREEN_PRIVATE_H +#define _ECORDOVA_SPLASHSCREEN_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Splashscreen_Data Ecordova_Splashscreen_Data; + +/** + * Ecordova.Splashscreen private data + */ +struct _Ecordova_Splashscreen_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_systeminfo.c b/src/lib/ecordova/ecordova_systeminfo.c new file mode 100644 index 0000000000..0dc4f7f971 --- /dev/null +++ b/src/lib/ecordova/ecordova_systeminfo.c @@ -0,0 +1,139 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_systeminfo_private.h" + +#include <vconf.h> + +#include <stdbool.h> +#include <stdint.h> +#include <stdlib.h> + +#define MY_CLASS ECORDOVA_SYSTEMINFO_CLASS +#define MY_CLASS_NAME "Ecordova_SystemInfo" + +static Eina_Bool _add_cb(void *, Eo *, const Eo_Event_Description *, void *); +static Eina_Bool _del_cb(void *, Eo *, const Eo_Event_Description *, void *); +static void _battery_callback_add(Ecordova_SystemInfo_Data *); +static void _battery_callback_del(Ecordova_SystemInfo_Data *); +static void _on_battery_changed(keynode_t *, void *); + +static Eo * +_ecordova_systeminfo_eo_base_constructor(Eo *obj, + Ecordova_SystemInfo_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_systeminfo_constructor(Eo *obj, + Ecordova_SystemInfo_Data *pd) +{ + DBG("(%p)", obj); + + eo_do(obj, eo_event_callback_add(EO_EV_CALLBACK_ADD, _add_cb, pd)); + eo_do(obj, eo_event_callback_add(EO_EV_CALLBACK_DEL, _del_cb, pd)); +} + +static void +_ecordova_systeminfo_eo_base_destructor(Eo *obj, + Ecordova_SystemInfo_Data *pd) +{ + DBG("(%p)", obj); + + if (pd->ref_count > 0) + _battery_callback_del(pd); + + eo_do(obj, eo_event_callback_del(EO_EV_CALLBACK_ADD, _add_cb, pd)); + eo_do(obj, eo_event_callback_del(EO_EV_CALLBACK_DEL, _del_cb, pd)); + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static Eina_Bool +_add_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc, + void *event_info) +{ + Ecordova_SystemInfo_Data *pd = (Ecordova_SystemInfo_Data*)data; + const Eo_Callback_Array_Item *array = (const Eo_Callback_Array_Item*)event_info; + + for (size_t i = 0; (desc = array[i].desc); ++i) + { + if (ECORDOVA_SYSTEMINFO_EVENT_BATTERY_CHANGED != desc) continue; + + ++pd->ref_count; + if (1 == pd->ref_count) + _battery_callback_add(pd); + } + + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_del_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc, + void *event_info) +{ + Ecordova_SystemInfo_Data *pd = (Ecordova_SystemInfo_Data*)data; + const Eo_Callback_Array_Item *array = (const Eo_Callback_Array_Item*)event_info; + + for (size_t i = 0; (desc = array[i].desc); ++i) + { + if (ECORDOVA_SYSTEMINFO_EVENT_BATTERY_CHANGED != desc) continue; + + --pd->ref_count; + if (0 == pd->ref_count) + _battery_callback_del(pd); + } + + return EO_CALLBACK_CONTINUE; +} + +static void +_register_vconf_callback(Ecordova_SystemInfo_Data *pd, + const char *in_key, + vconf_callback_fn cb) +{ + if (0 != vconf_notify_key_changed(in_key, cb, pd)) + ERR("%s, %s", "Failed to register vconf callback", in_key); +} + +static void +_unregister_vconf_callback(const char *in_key, + vconf_callback_fn cb) +{ + if (0 != vconf_ignore_key_changed(in_key, cb)) + ERR("%s, %s", "Failed to unregister vconf callback", in_key); +} + +static void +_battery_callback_add(Ecordova_SystemInfo_Data *pd) +{ + _register_vconf_callback(pd, VCONFKEY_SYSMAN_BATTERY_CAPACITY, _on_battery_changed); + _register_vconf_callback(pd, VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, _on_battery_changed); +} + +static void +_battery_callback_del(Ecordova_SystemInfo_Data *pd EINA_UNUSED) +{ + _unregister_vconf_callback(VCONFKEY_SYSMAN_BATTERY_CHARGE_NOW, _on_battery_changed); + _unregister_vconf_callback(VCONFKEY_SYSMAN_BATTERY_CAPACITY, _on_battery_changed); +} + +static void +_on_battery_changed(keynode_t *node EINA_UNUSED, void *data) +{ + Ecordova_SystemInfo_Data *pd = (Ecordova_SystemInfo_Data*)data; + eo_do(pd->obj, eo_event_callback_call + (ECORDOVA_SYSTEMINFO_EVENT_BATTERY_CHANGED, NULL)); +} + +#include "ecordova_systeminfo.eo.c" diff --git a/src/lib/ecordova/ecordova_systeminfo.eo b/src/lib/ecordova/ecordova_systeminfo.eo new file mode 100644 index 0000000000..2405400396 --- /dev/null +++ b/src/lib/ecordova/ecordova_systeminfo.eo @@ -0,0 +1,27 @@ +class Ecordova.SystemInfo (Eo.Base) { + [[Ecordova System Information Service + wrt-plugins-tizen SystemInfo.h + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_SystemInfo constructor. + @.constructor + + @since 2.3 + ]] + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } + events { + battery,changed; + [[This event fires when the percentage of battery charge changes by at + least 1 percent, or if the device is plugged in or unplugged.]] + } +} diff --git a/src/lib/ecordova/ecordova_systeminfo_private.h b/src/lib/ecordova/ecordova_systeminfo_private.h new file mode 100644 index 0000000000..fceab8c631 --- /dev/null +++ b/src/lib/ecordova/ecordova_systeminfo_private.h @@ -0,0 +1,19 @@ +#ifndef _ECORDOVA_SYSTEMINFO_PRIVATE_H +#define _ECORDOVA_SYSTEMINFO_PRIVATE_H + +#include "ecordova_private.h" +#include "ecordova_systeminfo.eo.h" + +typedef struct _Ecordova_SystemInfo_Data Ecordova_SystemInfo_Data; + +/** + * Ecordova.SystemInfo private data + */ +struct _Ecordova_SystemInfo_Data +{ + Eo *obj; + int ref_count; + bool info_initialized; +}; + +#endif diff --git a/src/lib/ecordova/ecordova_vibration.c b/src/lib/ecordova/ecordova_vibration.c new file mode 100644 index 0000000000..6d2427e1d5 --- /dev/null +++ b/src/lib/ecordova/ecordova_vibration.c @@ -0,0 +1,66 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_vibration_private.h" + +#include <haptic.h> + +#define MY_CLASS ECORDOVA_VIBRATION_CLASS +#define MY_CLASS_NAME "Ecordova_Vibration" + +static size_t _ref_count = 0; + +static Eo_Base * +_ecordova_vibration_eo_base_constructor(Eo *obj, Ecordova_Vibration_Data *pd) +{ + DBG("(%p)", obj); + + pd->obj = obj; + + ++_ref_count; + if (1 == _ref_count) + { + int ret = haptic_initialize(); + EINA_SAFETY_ON_FALSE_RETURN_VAL(HAPTIC_ERROR_NONE == ret, NULL); + } + + return eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor()); +} + +static void +_ecordova_vibration_constructor(Eo *obj EINA_UNUSED, + Ecordova_Vibration_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); +} + +static void +_ecordova_vibration_eo_base_destructor(Eo *obj, + Ecordova_Vibration_Data *pd EINA_UNUSED) +{ + DBG("(%p)", obj); + + EINA_SAFETY_ON_FALSE_RETURN(_ref_count >= 1); + + --_ref_count; + if (0 == _ref_count) + { + int ret = haptic_deinitialize(); + EINA_SAFETY_ON_FALSE_RETURN(HAPTIC_ERROR_NONE == ret); + } + + eo_do_super(obj, MY_CLASS, eo_destructor()); +} + +static void +_ecordova_vibration_vibrate(Eo *obj, + Ecordova_Vibration_Data *pd EINA_UNUSED, + int time) +{ + DBG("(%p)", obj); + int ret = haptic_vibrate_monotone(0, time, 100); + EINA_SAFETY_ON_FALSE_RETURN(HAPTIC_ERROR_NONE == ret); +} + +#include "ecordova_vibration.eo.c" diff --git a/src/lib/ecordova/ecordova_vibration.eo b/src/lib/ecordova/ecordova_vibration.eo new file mode 100644 index 0000000000..498791024f --- /dev/null +++ b/src/lib/ecordova/ecordova_vibration.eo @@ -0,0 +1,30 @@ +class Ecordova.Vibration (Eo.Base) { + [[Ecordova Vibration Plugin + Plugin ID: org.apache.cordova.vibration + http://plugins.cordova.io/#/package/org.apache.cordova.vibration + ]] + legacy_prefix: null; + methods { + constructor { + [[Custom Ecordova_Vibration constructor. + @.constructor + + @since 2.3 + ]] + } + vibrate { + [[This function has three different functionalities based on + parameters passed to it.]] + params { + time: int; [[Milliseconds to vibrate the device.]] + } + } + } + implements { + Eo.Base.constructor; + Eo.Base.destructor; + } + constructors { + .constructor; + } +} diff --git a/src/lib/ecordova/ecordova_vibration_private.h b/src/lib/ecordova/ecordova_vibration_private.h new file mode 100644 index 0000000000..f2921bfa47 --- /dev/null +++ b/src/lib/ecordova/ecordova_vibration_private.h @@ -0,0 +1,16 @@ +#ifndef _ECORDOVA_VIBRATION_PRIVATE_H +#define _ECORDOVA_VIBRATION_PRIVATE_H + +#include "ecordova_private.h" + +typedef struct _Ecordova_Vibration_Data Ecordova_Vibration_Data; + +/** + * Ecordova.Vibration private data + */ +struct _Ecordova_Vibration_Data +{ + Eo *obj; +}; + +#endif diff --git a/src/tests/ecordova/44khz32kbps.mp3 b/src/tests/ecordova/44khz32kbps.mp3 Binary files differnew file mode 100644 index 0000000000..df1594f2b7 --- /dev/null +++ b/src/tests/ecordova/44khz32kbps.mp3 diff --git a/src/tests/ecordova/ecordova_batterystatus_test.c b/src/tests/ecordova/ecordova_batterystatus_test.c new file mode 100644 index 0000000000..140723402d --- /dev/null +++ b/src/tests/ecordova/ecordova_batterystatus_test.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_batterystatus_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_batterystatus_new(void) +{ + return eo_add(ECORDOVA_BATTERYSTATUS_CLASS, + NULL, + ecordova_batterystatus_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *batterystatus = _batterystatus_new(); + eo_unref(batterystatus); +} +END_TEST + +void +ecordova_batterystatus_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); +} diff --git a/src/tests/ecordova/ecordova_batterystatus_test.h b/src/tests/ecordova/ecordova_batterystatus_test.h new file mode 100644 index 0000000000..55f29347e8 --- /dev/null +++ b/src/tests/ecordova/ecordova_batterystatus_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_BATTERYSTATUS_TEST_H +#define _ECORDOVA_BATTERYSTATUS_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_batterystatus_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_console_test.c b/src/tests/ecordova/ecordova_console_test.c new file mode 100644 index 0000000000..35e916b81e --- /dev/null +++ b/src/tests/ecordova/ecordova_console_test.c @@ -0,0 +1,140 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_console_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_console_new(void) +{ + return eo_add(ECORDOVA_CONSOLE_CLASS, + NULL, + ecordova_console_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *console = _console_new(); + eo_unref(console); +} +END_TEST + +START_TEST(level) +{ + Ecordova_Device *console = _console_new(); + + Ecordova_Console_LoggerLevel level = eo_do_ret(console, + level, + ecordova_console_level_get()); + ck_assert_int_eq(level, ECORDOVA_CONSOLE_LOGGERLEVEL_LOG); // default value + + for (Ecordova_Console_LoggerLevel new_level = ECORDOVA_CONSOLE_LOGGERLEVEL_LOG; + new_level < ECORDOVA_CONSOLE_LOGGERLEVEL_LAST; + ++new_level) + { + eo_do(console, ecordova_console_level_set(new_level)); + level = eo_do_ret(console, level, ecordova_console_level_get()); + ck_assert_int_eq(level, new_level); + } + + eo_unref(console); +} +END_TEST + +START_TEST(console_use) +{ + Ecordova_Device *console = _console_new(); + + Eina_Bool actual_value = eo_do_ret(console, + actual_value, + ecordova_console_use_get()); + ck_assert_int_eq(actual_value, EINA_FALSE); // default value + + Eina_Bool expected_value = !actual_value; + eo_do(console, ecordova_console_use_set(expected_value)); + actual_value = eo_do_ret(console, actual_value, ecordova_console_use_get()); + ck_assert_int_eq(expected_value, actual_value); + + expected_value = !actual_value; + eo_do(console, ecordova_console_use_set(expected_value)); + actual_value = eo_do_ret(console, actual_value, ecordova_console_use_get()); + ck_assert_int_eq(expected_value, actual_value); + + eo_unref(console); +} +END_TEST + +START_TEST(logger_use) +{ + Ecordova_Device *console = _console_new(); + + Eina_Bool actual_value = eo_do_ret(console, + actual_value, + ecordova_console_logger_use_get()); + ck_assert_int_eq(actual_value, EINA_TRUE); // default value + + Eina_Bool expected_value = !actual_value; + eo_do(console, ecordova_console_logger_use_set(expected_value)); + actual_value = eo_do_ret(console, actual_value, ecordova_console_logger_use_get()); + ck_assert_int_eq(expected_value, actual_value); + + expected_value = !actual_value; + eo_do(console, ecordova_console_logger_use_set(expected_value)); + actual_value = eo_do_ret(console, actual_value, ecordova_console_logger_use_get()); + ck_assert_int_eq(expected_value, actual_value); + + eo_unref(console); +} +END_TEST + +START_TEST(logging) +{ + Ecordova_Device *console = _console_new(); + + eo_do(console, ecordova_console_logger_use_set(EINA_TRUE)); + eo_do(console, ecordova_console_use_set(EINA_TRUE)); + + for (Ecordova_Console_LoggerLevel new_level = ECORDOVA_CONSOLE_LOGGERLEVEL_LOG; + new_level < ECORDOVA_CONSOLE_LOGGERLEVEL_LAST; + ++new_level) + { + eo_do(console, ecordova_console_level_set(new_level)); + + eo_do(console, ecordova_console_log("log")); + eo_do(console, ecordova_console_error("error")); + eo_do(console, ecordova_console_warn("warn")); + eo_do(console, ecordova_console_info("info")); + eo_do(console, ecordova_console_debug("debug")); + } + + eo_unref(console); +} +END_TEST + +void +ecordova_console_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, level); + tcase_add_test(tc, logger_use); + tcase_add_test(tc, console_use); + tcase_add_test(tc, logging); +} diff --git a/src/tests/ecordova/ecordova_console_test.h b/src/tests/ecordova/ecordova_console_test.h new file mode 100644 index 0000000000..1d303fd499 --- /dev/null +++ b/src/tests/ecordova/ecordova_console_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_CONSOLE_TEST_H +#define _ECORDOVA_CONSOLE_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_console_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_contacts_test.c b/src/tests/ecordova/ecordova_contacts_test.c new file mode 100644 index 0000000000..29d40be36e --- /dev/null +++ b/src/tests/ecordova/ecordova_contacts_test.c @@ -0,0 +1,255 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contacts_test.h" +#include "ecordova_suite.h" +#include <Ecore.h> +#include <ecore_timer.eo.h> +#include <contacts.h> + +#define CHECK(x) {int ret = x; ck_assert_int_eq(CONTACTS_ERROR_NONE, ret);} + +static int _contact_id; +static const char *_contact_name = "test"; + +static void +_setup(void) +{ + DBG("%s", "_setup"); + + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); + + DBG("%s", "_setup"); + + CHECK(contacts_connect()); + + // TODO: delete all record in the database to be back to square 1 and make tests deterministic + DBG("%s", "_setup"); + + // create contact record + contacts_record_h contact = NULL; + CHECK(contacts_record_create(_contacts_contact._uri, &contact)); + + // add name + contacts_record_h name = NULL; + CHECK(contacts_record_create(_contacts_name._uri, &name)); + CHECK(contacts_record_set_str(name, _contacts_name.first, _contact_name)); + CHECK(contacts_record_add_child_record(contact, _contacts_contact.name, name)); + + CHECK(contacts_db_insert_record(contact, &_contact_id)); + + CHECK(contacts_record_destroy(contact, true)); +} + +static void +_teardown(void) +{ + CHECK(contacts_db_delete_record(_contacts_contact._uri, _contact_id)); + + CHECK(contacts_disconnect()); + + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Eina_Bool +_find_all_success_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *success = data; + Eina_Array *contacts = event_info; + fail_if(NULL == contacts); + + size_t count = eina_array_count(contacts); + DBG("# of contacts: %zu", count); + + *success = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_find_one_contact_success_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_Contact **contact = data; + Eina_Array *contacts = event_info; + fail_if(NULL == contacts); + + size_t count = eina_array_count(contacts); + DBG("# of contacts: %zu", count); + + fail_if(1 != count); + *contact = eina_array_data_get(contacts, 0); + eo_ref(*contact); + + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *error = data; + Ecordova_Contacts_Error *error_code = event_info; + fail_if(NULL == error_code); + + *error = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_timeout_cb(void *data) +{ + bool *timeout = data; + *timeout = true; + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +START_TEST(find_all) +{ + Ecordova_Contacts *contacts = eo_add(ECORDOVA_CONTACTS_CLASS, NULL); + fail_if(NULL == contacts); + + bool success = false; + bool error = false; + bool timeout = false; + + Ecore_Timer *timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_FIND_SUCCESS, _find_all_success_cb, &success)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_ERROR, _error_cb, &error)); + + Eina_List *fields = eina_list_append(NULL, "*"); + Ecordova_Contacts_FindOptions *options = NULL; + eo_do(contacts, ecordova_contacts_find(fields, options)); + + ecore_main_loop_begin(); + + eina_list_free(fields); + + eo_unref(timer); + + fail_unless(success); + fail_if(error); + fail_if(timeout); + + eo_unref(contacts); +} +END_TEST + +START_TEST(find_by_id) +{ + Ecordova_Contacts *contacts = eo_add(ECORDOVA_CONTACTS_CLASS, NULL); + fail_if(NULL == contacts); + + Ecordova_Contact *contact = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_FIND_SUCCESS, _find_one_contact_success_cb, &contact)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_ERROR, _error_cb, &error)); + + + char buf[64]; + snprintf(buf, sizeof(buf), "%d", _contact_id); + + Eina_List *fields = eina_list_append(NULL, "id"); + Ecordova_Contacts_FindOptions options = { + .filter = buf + }; + eo_do(contacts, ecordova_contacts_find(fields, &options)); + + ecore_main_loop_begin(); + + eina_list_free(fields); + + eo_unref(timer); + + fail_unless(!!contact); + fail_if(error); + fail_if(timeout); + + int actual_id, expected_id = _contact_id; + eo_do(contact, actual_id = ecordova_contact_id_get()); + ck_assert_int_eq(expected_id, actual_id); + + Ecordova_ContactName *contact_name = NULL; + eo_do(contact, contact_name = ecordova_contact_name_get()); + fail_unless(!!contact_name); + + const char *actual_given_name, *expected_given_name = _contact_name; + eo_do(contact_name, actual_given_name = ecordova_contactname_given_name_get()); + ck_assert_str_eq(expected_given_name, actual_given_name); + + eo_unref(contact); + eo_unref(contacts); +} +END_TEST + +START_TEST(find_by_name) +{ + Ecordova_Contacts *contacts = eo_add(ECORDOVA_CONTACTS_CLASS, NULL); + fail_if(NULL == contacts); + + Ecordova_Contact *contact = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_FIND_SUCCESS, _find_one_contact_success_cb, &contact)); + eo_do(contacts, eo_event_callback_add(ECORDOVA_CONTACTS_EVENT_ERROR, _error_cb, &error)); + + Eina_List *fields = eina_list_append(NULL, "name"); + Ecordova_Contacts_FindOptions options = { + .filter = "test", + .multiple = false + }; + eo_do(contacts, ecordova_contacts_find(fields, &options)); + + ecore_main_loop_begin(); + + eina_list_free(fields); + + eo_unref(timer); + + fail_unless(!!contact); + fail_if(error); + fail_if(timeout); + + int actual_id, expected_id = _contact_id; + eo_do(contact, actual_id = ecordova_contact_id_get()); + ck_assert_int_eq(expected_id, actual_id); + + Ecordova_ContactName *contact_name = NULL; + eo_do(contact, contact_name = ecordova_contact_name_get()); + fail_unless(!!contact_name); + + const char *actual_given_name, *expected_given_name = _contact_name; + eo_do(contact_name, actual_given_name = ecordova_contactname_given_name_get()); + ck_assert_str_eq(expected_given_name, actual_given_name); + + eo_unref(contact); + eo_unref(contacts); +} +END_TEST + +void +ecordova_contacts_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, find_all); + tcase_add_test(tc, find_by_id); + tcase_add_test(tc, find_by_name); +} diff --git a/src/tests/ecordova/ecordova_contacts_test.h b/src/tests/ecordova/ecordova_contacts_test.h new file mode 100644 index 0000000000..6a96318ae6 --- /dev/null +++ b/src/tests/ecordova/ecordova_contacts_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_CONTACTS_TEST_H +#define _ECORDOVA_CONTACTS_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_contacts_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_device_test.c b/src/tests/ecordova/ecordova_device_test.c new file mode 100644 index 0000000000..710b3b4e77 --- /dev/null +++ b/src/tests/ecordova/ecordova_device_test.c @@ -0,0 +1,100 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_device_test.h" +#include "ecordova_suite.h" + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_device_new(void) +{ + return eo_add(ECORDOVA_DEVICE_CLASS, NULL, ecordova_device_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *device = _device_new(); + eo_unref(device); +} +END_TEST + +START_TEST(model_get) +{ + Ecordova_Device *device = _device_new(); + + const char *model = eo_do_ret(device, model, ecordova_device_model_get()); + fail_if(NULL == model); + + INF("%s", model); + + eo_unref(device); +} +END_TEST + +START_TEST(platform_get) +{ + Ecordova_Device *device = _device_new(); + + const char *platform = eo_do_ret(device, + platform, + ecordova_device_platform_get()); + fail_if(NULL == platform); + + INF("%s", platform); + + eo_unref(device); +} +END_TEST + +START_TEST(uuid_get) +{ + Ecordova_Device *device = _device_new(); + + const char *uuid = eo_do_ret(device, uuid, ecordova_device_uuid_get()); + fail_if(NULL == uuid); + + INF("%s", uuid); + + eo_unref(device); +} +END_TEST + +START_TEST(version_get) +{ + Ecordova_Device *device = _device_new(); + + const char *version = eo_do_ret(device, + version, + ecordova_device_version_get()); + fail_if(NULL == version); + + INF("%s", version); + + eo_unref(device); +} +END_TEST + +void +ecordova_device_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, model_get); + tcase_add_test(tc, platform_get); + //tcase_add_test(tc, uuid_get); // disabled: returns NULL + tcase_add_test(tc, version_get); +} diff --git a/src/tests/ecordova/ecordova_device_test.h b/src/tests/ecordova/ecordova_device_test.h new file mode 100644 index 0000000000..7d943cc323 --- /dev/null +++ b/src/tests/ecordova/ecordova_device_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_DEVICE_TEST_H +#define _ECORDOVA_DEVICE_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_device_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_devicemotion_test.c b/src/tests/ecordova/ecordova_devicemotion_test.c new file mode 100644 index 0000000000..fec15b5538 --- /dev/null +++ b/src/tests/ecordova/ecordova_devicemotion_test.c @@ -0,0 +1,89 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_devicemotion_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_devicemotion_new(void) +{ + return eo_add(ECORDOVA_DEVICEMOTION_CLASS, + NULL, + ecordova_devicemotion_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *devicemotion = _devicemotion_new(); + eo_unref(devicemotion); +} +END_TEST + +static Eina_Bool +_current_acceleration_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *called = data; + *called = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +static Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *error = data; + *error = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(current_acceleration_get) +{ + Ecordova_Device *devicemotion = _devicemotion_new(); + + bool success_event_called = false; + bool error_event_called = false; + eo_do(devicemotion, eo_event_callback_add(ECORDOVA_DEVICEMOTION_EVENT_CURRENT_SUCCESS, _current_acceleration_get_cb, &success_event_called)); + eo_do(devicemotion, eo_event_callback_add(ECORDOVA_DEVICEMOTION_EVENT_ERROR, _error_cb, &error_event_called)); + eo_do(devicemotion, ecordova_devicemotion_current_acceleration_get()); + + if (!success_event_called && !error_event_called) + ecore_main_loop_begin(); + + fail_if(error_event_called); + fail_unless(success_event_called); + + eo_unref(devicemotion); +} +END_TEST + +void +ecordova_devicemotion_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + //tcase_add_test(tc, current_acceleration_get); // disabled: not supported +} diff --git a/src/tests/ecordova/ecordova_devicemotion_test.h b/src/tests/ecordova/ecordova_devicemotion_test.h new file mode 100644 index 0000000000..dcc3d6ce9a --- /dev/null +++ b/src/tests/ecordova/ecordova_devicemotion_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_DEVICEMOTION_TEST_H +#define _ECORDOVA_DEVICEMOTION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_devicemotion_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_deviceorientation_test.c b/src/tests/ecordova/ecordova_deviceorientation_test.c new file mode 100644 index 0000000000..df814eb19c --- /dev/null +++ b/src/tests/ecordova/ecordova_deviceorientation_test.c @@ -0,0 +1,89 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_deviceorientation_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_deviceorientation_new(void) +{ + return eo_add(ECORDOVA_DEVICEORIENTATION_CLASS, + NULL, + ecordova_deviceorientation_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *deviceorientation = _deviceorientation_new(); + eo_unref(deviceorientation); +} +END_TEST + +static Eina_Bool +_current_heading_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *called = data; + *called = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +static Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *error = data; + *error = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(current_heading_get) +{ + Ecordova_Device *deviceorientation = _deviceorientation_new(); + + bool success_event_called = false; + bool error_event_called = false; + eo_do(deviceorientation, eo_event_callback_add(ECORDOVA_DEVICEORIENTATION_EVENT_CURRENT_SUCCESS, _current_heading_get_cb, &success_event_called)); + eo_do(deviceorientation, eo_event_callback_add(ECORDOVA_DEVICEORIENTATION_EVENT_ERROR, _error_cb, &error_event_called)); + eo_do(deviceorientation, ecordova_deviceorientation_current_heading_get()); + + if (!success_event_called && !error_event_called) + ecore_main_loop_begin(); + + fail_if(error_event_called); + fail_unless(success_event_called); + + eo_unref(deviceorientation); +} +END_TEST + +void +ecordova_deviceorientation_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + //tcase_add_test(tc, current_heading_get); // disabled: not supported +} diff --git a/src/tests/ecordova/ecordova_deviceorientation_test.h b/src/tests/ecordova/ecordova_deviceorientation_test.h new file mode 100644 index 0000000000..12249a9bfd --- /dev/null +++ b/src/tests/ecordova/ecordova_deviceorientation_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_DEVICEORIENTATION_TEST_H +#define _ECORDOVA_DEVICEORIENTATION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_deviceorientation_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_directoryentry_test.c b/src/tests/ecordova/ecordova_directoryentry_test.c new file mode 100644 index 0000000000..51477cd5b8 --- /dev/null +++ b/src/tests/ecordova/ecordova_directoryentry_test.c @@ -0,0 +1,434 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +Ecordova_DirectoryEntry * +directoryentry_new(const char *name, + const char *path, + const char *url) +{ + return eo_add(ECORDOVA_DIRECTORYENTRY_CLASS, + NULL, + ecordova_directoryentry_constructor(name, path, NULL, url)); +} + +void +check_exists(const char *url) +{ + Eina_Bool ret = ecore_file_exists(url); + ck_assert_int_eq(EINA_TRUE, ret); +} + +void +check_doesnt_exist(const char *url) +{ + Eina_Bool ret = ecore_file_exists(url); + ck_assert_int_eq(EINA_FALSE, ret); +} + +Ecordova_DirectoryEntry * +_create_tmpdir(Eina_Tmpstr **tmpdir) +{ + Eina_Bool tmpdir_created = eina_file_mkdtemp("directoryentry_test_XXXXXX", tmpdir); + ck_assert_int_eq(EINA_TRUE, tmpdir_created); + check_exists(*tmpdir); + + const char *last_path_separator = strrchr(*tmpdir, '/'); + fail_if(NULL == last_path_separator); + + const char *name = last_path_separator + 1; + size_t len = last_path_separator - *tmpdir; + char path[len + 1]; + strncpy(path, *tmpdir, len); + path[len] = '\0'; + + return directoryentry_new(name, path, *tmpdir); +} + +START_TEST(smoke) +{ + Ecordova_DirectoryEntry *directory_entry = + directoryentry_new("", TESTS_BUILD_DIR, TESTS_BUILD_DIR); + eo_unref(directory_entry); +} +END_TEST + +Eina_Bool +_timeout_cb(void *data) +{ + bool *timeout = data; + *timeout = true; + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +Eina_Bool +_entry_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_Entry **entry = data; + fail_if(NULL == event_info); + + *entry = eo_ref((Ecordova_Entry*)event_info); + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *error = data; + Ecordova_FileError *error_code = event_info; + fail_if(NULL == error_code); + + *error = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +void +_check_entry_name(Ecordova_Entry *entry, const char *expected_name) +{ + const char *actual_name = eo_do_ret(entry, + actual_name, + ecordova_entry_name_get()); + fail_if(NULL == actual_name); + ck_assert_str_eq(expected_name, actual_name); +} + +void +_check_entry_path(Ecordova_Entry *entry, const char *expected_path) +{ + const char *actual_path = eo_do_ret(entry, + actual_path, + ecordova_entry_path_get()); + fail_if(NULL == actual_path); + ck_assert_str_eq(expected_path, actual_path); +} + +static void +_check_entry_is_file(Ecordova_Entry *entry) +{ + Eina_Bool is_file; + Eina_Bool is_directory; + eo_do(entry, + is_file = ecordova_entry_file_is_get(), + is_directory = ecordova_entry_directory_is_get()); + fail_unless(is_file); + fail_if(is_directory); +} + +static void +_check_entry_is_directory(Ecordova_Entry *entry) +{ + Eina_Bool is_directory; + Eina_Bool is_file; + eo_do(entry, + is_directory = ecordova_entry_directory_is_get(), + is_file = ecordova_entry_file_is_get()); + fail_unless(is_directory); + fail_if(is_file); +} + +static bool +_entry_get(Ecordova_Entry *obj, + const char *url, + Ecordova_FileFlags flags, + Ecordova_Entry **entry, + const Eo_Event_Description *get_event, + void(*entry_get)(const char *, Ecordova_FileFlags)) +{ + *entry = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(obj, eo_event_callback_add(get_event, _entry_get_cb, entry), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + entry_get(url, flags)); + + ecore_main_loop_begin(); + + eo_do(obj, eo_event_callback_del(get_event, _entry_get_cb, entry), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error; +} + +bool +fileentry_get(Ecordova_DirectoryEntry *directory_entry, + const char *url, + Ecordova_FileFlags flags, + Ecordova_FileEntry **entry) +{ + DBG("Getting file entry: %s", url); + return _entry_get(directory_entry, + url, + flags, + (Ecordova_Entry**)entry, + ECORDOVA_DIRECTORYENTRY_EVENT_FILE_GET, + ecordova_directoryentry_file_get); +} + +bool +directoryentry_get(Ecordova_DirectoryEntry *directory_entry, + const char *url, + Ecordova_FileFlags flags, + Ecordova_DirectoryEntry **entry) +{ + DBG("Getting directory entry: %s", url); + return _entry_get(directory_entry, + url, + flags, + (Ecordova_Entry**)entry, + ECORDOVA_DIRECTORYENTRY_EVENT_DIRECTORY_GET, + ecordova_directoryentry_directory_get); +} + +START_TEST(get_file) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + const char *filepath = tmpdir; + size_t len = strlen(filepath) + 1 + strlen(filename) + 1; + char fileurl[len]; + snprintf(fileurl, len, "%s/%s", tmpdir, filename); + + Ecordova_FileEntry *file_entry = NULL; + bool error = false; + + // create exclusive + Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(fileurl); + + _check_entry_is_file(file_entry); + _check_entry_name(file_entry, filename); + _check_entry_path(file_entry, filepath); + + eo_unref(file_entry); + + + // create exclusive on a existing file is an error + flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(NULL != file_entry); + fail_unless(error); + + + // just create on an existing file opens it + flags = ECORDOVA_FILEFLAGS_CREATE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + + _check_entry_is_file(file_entry); + _check_entry_name(file_entry, filename); + _check_entry_path(file_entry, filepath); + + eo_unref(file_entry); + + // just opens it + flags = 0; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + + _check_entry_is_file(file_entry); + _check_entry_name(file_entry, filename); + _check_entry_path(file_entry, filepath); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + + // error on non existent file + flags = 0; + error = fileentry_get(directory_entry, ",**??non_existent??**,", flags, &file_entry); + fail_if(NULL != file_entry); + fail_unless(error); + + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(get_dir) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *dir_name = "some_path"; + const char *dir_path = tmpdir; + size_t len = strlen(dir_path) + 1 + strlen(dir_name) + 1; + char dir_url[len]; + snprintf(dir_url, len, "%s/%s", tmpdir, dir_name); + + Ecordova_DirectoryEntry *child_entry = NULL; + bool error = false; + + // create exclusive + Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = directoryentry_get(directory_entry, dir_name, flags, &child_entry); + fail_if(error); + fail_unless(NULL != child_entry); + check_exists(dir_url); + + _check_entry_is_directory(child_entry); + _check_entry_name(child_entry, dir_name); + _check_entry_path(child_entry, dir_path); + + eo_unref(child_entry); + + + // create exclusive on a existing directory is an error + flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = directoryentry_get(directory_entry, dir_name, flags, &child_entry); + fail_if(NULL != child_entry); + fail_unless(error); + + + // just the create flag on an existing directory opens it + flags = ECORDOVA_FILEFLAGS_CREATE; + error = directoryentry_get(directory_entry, dir_name, flags, &child_entry); + fail_if(error); + fail_unless(NULL != child_entry); + + _check_entry_is_directory(child_entry); + _check_entry_name(child_entry, dir_name); + _check_entry_path(child_entry, dir_path); + + eo_unref(child_entry); + + // just opens it + flags = 0; + error = directoryentry_get(directory_entry, dir_name, flags, &child_entry); + fail_if(error); + fail_unless(NULL != child_entry); + + _check_entry_is_directory(child_entry); + _check_entry_name(child_entry, dir_name); + _check_entry_path(child_entry, dir_path); + + fail_if(error = entry_remove(child_entry)); + eo_unref(child_entry); + + + // error on non existent directory + flags = 0; + error = directoryentry_get(directory_entry, ",**??non_existent??**,", flags, &child_entry); + fail_if(NULL != child_entry); + fail_unless(error); + + + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +static bool +_directoryentry_recursively_remove(Ecordova_DirectoryEntry *directory_entry) +{ + return entry_do(directory_entry, + ECORDOVA_DIRECTORYENTRY_EVENT_REMOVE_SUCCESS, + ecordova_directoryentry_recursively_remove); +} + +START_TEST(remove_recursively) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + // removes an existent directory recursively + const char *child_name = "child"; + size_t len = strlen(tmpdir) + 1 + strlen(child_name) + 1; + char child_dir[len]; + snprintf(child_dir, len, "%s/%s", tmpdir, child_name); + + Eina_Bool ret = ecore_file_mkdir(child_dir); + ck_assert_int_eq(EINA_TRUE, ret); + + bool error = _directoryentry_recursively_remove(directory_entry); + fail_if(error); + + check_doesnt_exist(child_dir); + check_doesnt_exist(tmpdir); + + // error on non existent directory + error = _directoryentry_recursively_remove(directory_entry); + fail_unless(error); + + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(create_reader) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + Ecordova_DirectoryReader *directory_reader = + eo_do_ret(directory_entry, + directory_reader, + ecordova_directoryentry_reader_create()); + fail_if(NULL == directory_reader); + eo_unref(directory_reader); + + bool error = false; + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + eina_tmpstr_del(tmpdir); +} +END_TEST + +void +ecordova_directoryentry_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, get_file); + tcase_add_test(tc, get_dir); + tcase_add_test(tc, remove_recursively); + tcase_add_test(tc, create_reader); +} diff --git a/src/tests/ecordova/ecordova_directoryentry_test.h b/src/tests/ecordova/ecordova_directoryentry_test.h new file mode 100644 index 0000000000..7220400550 --- /dev/null +++ b/src/tests/ecordova/ecordova_directoryentry_test.h @@ -0,0 +1,24 @@ +#ifndef _ECORDOVA_DIRECTORYENTRY_TEST_H +#define _ECORDOVA_DIRECTORYENTRY_TEST_H + +#include <Ecordova.h> + +#include <Ecore_File.h> + +#include <check.h> + +#include <stdbool.h> + +void ecordova_directoryentry_test(TCase *); +Ecordova_DirectoryEntry *_create_tmpdir(Eina_Tmpstr **); +Eina_Bool _timeout_cb(void *); +Eina_Bool _entry_get_cb(void *, Eo *, const Eo_Event_Description *, void *); +Eina_Bool _error_cb(void *, Eo *, const Eo_Event_Description *, void *); + +bool directoryentry_get(Ecordova_DirectoryEntry *directory_entry, const char *, Ecordova_FileFlags, Ecordova_DirectoryEntry **); +bool fileentry_get(Ecordova_DirectoryEntry *directory_entry, const char *, Ecordova_FileFlags, Ecordova_FileEntry **); +void check_exists(const char *url); +void check_doesnt_exist(const char *url); +Ecordova_DirectoryEntry *directoryentry_new(const char *, const char *, const char *); + +#endif diff --git a/src/tests/ecordova/ecordova_directoryreader_test.c b/src/tests/ecordova/ecordova_directoryreader_test.c new file mode 100644 index 0000000000..2e08dff327 --- /dev/null +++ b/src/tests/ecordova/ecordova_directoryreader_test.c @@ -0,0 +1,132 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_directoryreader_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + + +static Ecordova_DirectoryReader * +_directoryreader_new(const char *path) +{ + return eo_add(ECORDOVA_DIRECTORYREADER_CLASS, + NULL, + ecordova_directoryreader_constructor(path)); +} + +START_TEST(smoke) +{ + Ecordova_DirectoryReader *directory_reader = _directoryreader_new(TESTS_BUILD_DIR); + eo_unref(directory_reader); +} +END_TEST + +static Eina_Bool +_timeout_cb(void *data) +{ + bool *timeout = data; + *timeout = true; + ecore_main_loop_quit(); + return ECORE_CALLBACK_CANCEL; +} + +static Eina_Bool +_success_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *success = data; + Eina_List *entries = event_info; + fail_if(NULL == entries); + + *success = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *error = data; + Ecordova_FileError *error_code = event_info; + fail_if(NULL == error_code); + + *error = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +START_TEST(read_entries_success) +{ + Ecordova_DirectoryReader *directory_reader = _directoryreader_new(TESTS_BUILD_DIR); + + bool success = false; + bool error = false; + bool timeout = false; + + Ecore_Timer *timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + eo_do(directory_reader, eo_event_callback_add(ECORDOVA_DIRECTORYREADER_EVENT_SUCCESS, _success_cb, &success)); + eo_do(directory_reader, eo_event_callback_add(ECORDOVA_DIRECTORYREADER_EVENT_ERROR, _error_cb, &error)); + eo_do(directory_reader, ecordova_directoryreader_entries_read()); + ecore_main_loop_begin(); + + eo_unref(timer); + fail_if(error); + fail_if(timeout); + fail_unless(success); + + eo_unref(directory_reader); +} +END_TEST + +START_TEST(read_entries_error) +{ + Ecordova_DirectoryReader *directory_reader = _directoryreader_new("/**??this_directory_doesn't_exist??**"); + + bool success = false; + bool error = false; + bool timeout = false; + + Ecore_Timer *timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + eo_do(directory_reader, eo_event_callback_add(ECORDOVA_DIRECTORYREADER_EVENT_SUCCESS, _success_cb, &success)); + eo_do(directory_reader, eo_event_callback_add(ECORDOVA_DIRECTORYREADER_EVENT_ERROR, _error_cb, &error)); + eo_do(directory_reader, ecordova_directoryreader_entries_read()); + ecore_main_loop_begin(); + + eo_unref(timer); + fail_if(success); + fail_if(timeout); + fail_unless(error); + + eo_unref(directory_reader); +} +END_TEST + +void +ecordova_directoryreader_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, read_entries_success); + tcase_add_test(tc, read_entries_error); +} diff --git a/src/tests/ecordova/ecordova_directoryreader_test.h b/src/tests/ecordova/ecordova_directoryreader_test.h new file mode 100644 index 0000000000..e01bad91e4 --- /dev/null +++ b/src/tests/ecordova/ecordova_directoryreader_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_DIRECTORYREADER_TEST_H +#define _ECORDOVA_DIRECTORYREADER_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_directoryreader_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_entry_test.c b/src/tests/ecordova/ecordova_entry_test.c new file mode 100644 index 0000000000..49d9c72479 --- /dev/null +++ b/src/tests/ecordova/ecordova_entry_test.c @@ -0,0 +1,748 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_entry_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +START_TEST(smoke) +{ + Ecordova_Entry *entry = eo_add(ECORDOVA_ENTRY_CLASS, + NULL, + ecordova_entry_constructor(EINA_FALSE, + EINA_TRUE, + "", + TESTS_BUILD_DIR, + NULL, + TESTS_BUILD_DIR)); + eo_unref(entry); +} +END_TEST + +static bool +_move_copy_to(Ecordova_Entry *source, + Ecordova_Entry *parent_dest, + const char *new_name, + Ecordova_Entry **entry, + const Eo_Event_Description *event_desc, + void(*method)(Ecordova_DirectoryEntry*, const char *)) +{ + *entry = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(source, eo_event_callback_add(event_desc, _entry_get_cb, entry), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + method(parent_dest, new_name)); + + ecore_main_loop_begin(); + + eo_do(source, eo_event_callback_del(event_desc, _entry_get_cb, entry), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error; +} + +static bool +_move_to(Ecordova_Entry *source, + Ecordova_Entry *parent_dest, + const char *new_name, + Ecordova_Entry **moved_entry) +{ + return _move_copy_to(source, + parent_dest, + new_name, + moved_entry, + ECORDOVA_ENTRY_EVENT_MOVE_SUCCESS, + ecordova_entry_move); +} + +static bool +_copy_to(Ecordova_Entry *source, + Ecordova_Entry *parent_dest, + const char *new_name, + Ecordova_Entry **copied_entry) +{ + return _move_copy_to(source, + parent_dest, + new_name, + copied_entry, + ECORDOVA_ENTRY_EVENT_COPY_SUCCESS, + ecordova_entry_copy); +} + +Eina_Bool +_do_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + bool *removed = data; + fail_if(NULL != event_info); + + *removed = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +bool +entry_do(Ecordova_Entry *entry, + const Eo_Event_Description *event_desc, + void(*method)()) +{ + bool success = false; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(entry, eo_event_callback_add(event_desc, _do_cb, &success), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + method()); + + ecore_main_loop_begin(); + + eo_do(entry, eo_event_callback_del(event_desc, _do_cb, &success), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error || !success; +} + +static bool +_entry_parent_get(Ecordova_Entry *obj, Ecordova_Entry **parent) +{ + *parent = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(obj, eo_event_callback_add(ECORDOVA_ENTRY_EVENT_PARENT_GET, _entry_get_cb, parent), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + ecordova_entry_parent_get()); + + ecore_main_loop_begin(); + + eo_do(obj, eo_event_callback_del(ECORDOVA_ENTRY_EVENT_PARENT_GET, _entry_get_cb, parent), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error; +} + +Eina_Bool +_metadata_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_Metadata *metadata = data; + fail_if(NULL == event_info); + + *metadata = *(Ecordova_Metadata*)event_info; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static bool +_entry_metadataget(Ecordova_Entry *obj, Ecordova_Metadata *metadata) +{ + *metadata = (Ecordova_Metadata){0}; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(obj, eo_event_callback_add(ECORDOVA_ENTRY_EVENT_METADATA_GET, _metadata_get_cb, metadata), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + ecordova_entry_metadata_get()); + + ecore_main_loop_begin(); + + eo_do(obj, eo_event_callback_del(ECORDOVA_ENTRY_EVENT_METADATA_GET, _metadata_get_cb, metadata), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error; +} + +bool +entry_remove(Ecordova_Entry *entry) +{ + return entry_do(entry, + ECORDOVA_ENTRY_EVENT_REMOVE_SUCCESS, + ecordova_entry_remove); +} + +START_TEST(dir_remove) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + // non empty directory cannot be removed, gives error + fail_unless(error = entry_remove(directory_entry)); + + // turns the directory empty + fail_if(error = entry_remove(file_entry)); + + // removes the directory + fail_if(error = entry_remove(directory_entry)); + + // non existent directory gives error + fail_unless(error = entry_remove(directory_entry)); + + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(dir_move_to) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *child_name1 = "child1"; + size_t len = strlen(tmpdir) + 1 + strlen(child_name1) + 1; + char child_dir1[len]; + snprintf(child_dir1, len, "%s/%s", tmpdir, child_name1); + Eina_Bool ret = ecore_file_mkdir(child_dir1); + ck_assert_int_eq(EINA_TRUE, ret); + + const char *child_name2 = "child2"; + len = strlen(tmpdir) + 1 + strlen(child_name2) + 1; + char child_dir2[len]; + snprintf(child_dir2, len, "%s/%s", tmpdir, child_name2); + ret = ecore_file_mkdir(child_dir2); + ck_assert_int_eq(EINA_TRUE, ret); + + Ecordova_DirectoryEntry *child_entry1 = NULL; + Ecordova_DirectoryEntry *child_entry2 = NULL; + bool error = false; + + error = directoryentry_get(directory_entry, child_dir1, 0, &child_entry1); + fail_if(error); + fail_unless(NULL != child_entry1); + error = directoryentry_get(directory_entry, child_dir2, 0, &child_entry2); + fail_if(error); + fail_unless(NULL != child_entry2); + + Ecordova_DirectoryEntry *moved_entry = NULL; + error = _move_to(child_entry1, child_entry2, NULL, &moved_entry); + fail_if(error); + fail_unless(NULL != moved_entry); + + fail_if(error = entry_remove(moved_entry)); + fail_if(error = entry_remove(child_entry2)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(moved_entry); + eo_unref(child_entry2); + eo_unref(child_entry1); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(dir_rename) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *child_name = "child"; + size_t len = strlen(tmpdir) + 1 + strlen(child_name) + 1; + char child_dir[len]; + snprintf(child_dir, len, "%s/%s", tmpdir, child_name); + Eina_Bool ret = ecore_file_mkdir(child_dir); + ck_assert_int_eq(EINA_TRUE, ret); + + Ecordova_DirectoryEntry *child_entry = NULL; + bool error = false; + + error = directoryentry_get(directory_entry, child_dir, 0, &child_entry); + fail_if(error); + fail_unless(NULL != child_entry); + + Ecordova_DirectoryEntry *renamed_entry = NULL; + error = _move_to(child_entry, directory_entry, "renamed", &renamed_entry); + fail_if(error); + fail_unless(NULL != renamed_entry); + + fail_if(error = entry_remove(renamed_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(renamed_entry); + eo_unref(child_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(dir_copy) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *child_name = "child"; + size_t len = strlen(tmpdir) + 1 + strlen(child_name) + 1; + char child_dir[len]; + snprintf(child_dir, len, "%s/%s", tmpdir, child_name); + Eina_Bool ret = ecore_file_mkdir(child_dir); + ck_assert_int_eq(EINA_TRUE, ret); + + const char *filename = "file.txt"; + len = strlen(child_dir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", child_dir, filename); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_DirectoryEntry *child_entry = NULL; + + error = directoryentry_get(directory_entry, child_dir, 0, &child_entry); + fail_if(error); + fail_unless(NULL != child_entry); + + const char *NEW_DIR_NAME = "copied"; + Ecordova_DirectoryEntry *copied_dir_entry = NULL; + error = _copy_to(child_entry, directory_entry, NEW_DIR_NAME, &copied_dir_entry); + fail_if(error); + fail_unless(NULL != copied_dir_entry); + + len = strlen(tmpdir) + 1 + strlen(NEW_DIR_NAME) + 1 + strlen(filename) + 1; + char copied_file_url[len]; + snprintf(copied_file_url, len, "%s/%s/%s", tmpdir, NEW_DIR_NAME, filename); + + check_exists(copied_file_url); + Ecordova_FileEntry *copied_file_entry = NULL; + error = fileentry_get(directory_entry, copied_file_url, 0, &copied_file_entry); + fail_if(error); + fail_unless(NULL != copied_file_entry); + + fail_if(error = entry_remove(file_entry)); + fail_if(error = entry_remove(child_entry)); + fail_if(error = entry_remove(copied_file_entry)); + fail_if(error = entry_remove(copied_dir_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(file_entry); + eo_unref(copied_file_entry); + eo_unref(copied_dir_entry); + eo_unref(child_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(dir_parent) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *dir_name = "child"; + size_t len = strlen(tmpdir) + 1 + strlen(dir_name) + 1; + char dir_url[len]; + snprintf(dir_url, len, "%s/%s", tmpdir, dir_name); + + bool error = false; + + Ecordova_FileEntry *dir_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = directoryentry_get(directory_entry, dir_url, flags, &dir_entry); + fail_if(error); + fail_unless(NULL != dir_entry); + check_exists(dir_url); + + Ecordova_FileEntry *parent_entry = NULL; + fail_if(error = _entry_parent_get(dir_entry, &parent_entry)); + + const char *directory_name; + const char *parent_name; + eo_do(directory_entry, + directory_name = ecordova_entry_name_get(), + parent_name = ecordova_entry_name_get()); + fail_if(NULL == directory_name); + fail_if(NULL == parent_name); + ck_assert_str_eq(directory_name, parent_name); + + fail_if(error = entry_remove(dir_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(parent_entry); + eo_unref(dir_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(dir_root_parent) +{ + Ecordova_DirectoryEntry *directory_entry = directoryentry_new("", "/", "/"); + + Ecordova_FileEntry *parent_entry = NULL; + bool error = false; + fail_if(error = _entry_parent_get(directory_entry, &parent_entry)); + + const char *directory_name = ""; + fail_if(NULL == directory_name); + const char *parent_name = eo_do_ret(parent_entry, + parent_name, + ecordova_entry_name_get()); + fail_if(NULL == parent_name); + ck_assert_str_eq(directory_name, parent_name); + + eo_unref(parent_entry); + eo_unref(directory_entry); +} +END_TEST + +START_TEST(dir_metadata) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *dir_name = "child"; + size_t len = strlen(tmpdir) + 1 + strlen(dir_name) + 1; + char dir_url[len]; + snprintf(dir_url, len, "%s/%s", tmpdir, dir_name); + + bool error = false; + + Ecordova_FileEntry *dir_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, dir_url, flags, &dir_entry); + fail_if(error); + fail_unless(NULL != dir_entry); + check_exists(dir_url); + + Ecordova_Metadata metadata = {0}; + fail_if(error = _entry_metadataget(dir_entry, &metadata)); + ck_assert_int_ne(0, metadata.modification_date); + + fail_if(error = entry_remove(dir_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(dir_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_remove) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + const char *filepath = tmpdir; + size_t len = strlen(filepath) + 1 + strlen(filename) + 1; + char fileurl[len]; + snprintf(fileurl, len, "%s/%s", tmpdir, filename); + + Ecordova_FileEntry *file_entry = NULL; + bool error = false; + + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(fileurl); + + fail_if(error = entry_remove(file_entry)); + // non existent gives error + fail_unless(error = entry_remove(file_entry)); + eo_unref(file_entry); + + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_move_to) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + const char *dirname = "dir"; + len = strlen(tmpdir) + 1 + strlen(dirname) + 1; + char dir_url[len]; + snprintf(dir_url, len, "%s/%s", tmpdir, dirname); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_DirectoryEntry *dir_entry = NULL; + error = directoryentry_get(directory_entry, dir_url, flags, &dir_entry); + fail_if(error); + fail_unless(NULL != dir_entry); + check_exists(dir_url); + + Ecordova_FileEntry *moved_entry = NULL; + error = _move_to(file_entry, dir_entry, NULL, &moved_entry); + fail_if(error); + fail_unless(NULL != moved_entry); + check_doesnt_exist(file_url); + + fail_if(error = entry_remove(moved_entry)); + fail_if(error = entry_remove(dir_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(moved_entry); + eo_unref(dir_entry); + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_rename) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_FileEntry *renamed_entry = NULL; + error = _move_to(file_entry, directory_entry, "renamed.txt", &renamed_entry); + fail_if(error); + fail_unless(NULL != renamed_entry); + check_doesnt_exist(file_url); + + fail_if(error = entry_remove(renamed_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(renamed_entry); + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_copy) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + + const char *copiedfilename = "copied.txt"; + len = strlen(tmpdir) + 1 + strlen(copiedfilename) + 1; + char copied_file_url[len]; + snprintf(copied_file_url, len, "%s/%s", tmpdir, copiedfilename); + + Ecordova_FileEntry *copied_entry = NULL; + error = _copy_to(file_entry, directory_entry, copiedfilename, &copied_entry); + fail_if(error); + fail_unless(NULL != copied_entry); + check_exists(copied_file_url); + + fail_if(error = entry_remove(file_entry)); + fail_if(error = entry_remove(copied_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(copied_entry); + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_parent) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_FileEntry *parent_entry = NULL; + fail_if(error = _entry_parent_get(file_entry, &parent_entry)); + + const char *directory_name = eo_do_ret(directory_entry, + directory_name, + ecordova_entry_name_get()); + const char *parent_name = eo_do_ret(parent_entry, + parent_name, + ecordova_entry_name_get()); + fail_if(NULL == directory_name); + fail_if(NULL == parent_name); + ck_assert_str_eq(directory_name, parent_name); + + fail_if(error = entry_remove(file_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(parent_entry); + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +START_TEST(file_metadata) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + size_t len = strlen(tmpdir) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + bool error = false; + + Ecordova_FileEntry *file_entry = NULL; + const Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, file_url, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_Metadata metadata = {0}; + fail_if(error = _entry_metadataget(file_entry, &metadata)); + ck_assert_int_ne(0, metadata.modification_date); + + fail_if(error = entry_remove(file_entry)); + fail_if(error = entry_remove(directory_entry)); + + eo_unref(file_entry); + eo_unref(directory_entry); + + eina_tmpstr_del(tmpdir); +} +END_TEST + +void +ecordova_entry_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, dir_remove); + tcase_add_test(tc, dir_move_to); + tcase_add_test(tc, dir_rename); + tcase_add_test(tc, dir_copy); + tcase_add_test(tc, dir_parent); + tcase_add_test(tc, dir_root_parent); + tcase_add_test(tc, dir_metadata); + tcase_add_test(tc, file_remove); + tcase_add_test(tc, file_move_to); + tcase_add_test(tc, file_rename); + tcase_add_test(tc, file_copy); + tcase_add_test(tc, file_parent); + tcase_add_test(tc, file_metadata); +} diff --git a/src/tests/ecordova/ecordova_entry_test.h b/src/tests/ecordova/ecordova_entry_test.h new file mode 100644 index 0000000000..ba737f3b09 --- /dev/null +++ b/src/tests/ecordova/ecordova_entry_test.h @@ -0,0 +1,14 @@ +#ifndef _ECORDOVA_ENTRY_TEST_H +#define _ECORDOVA_ENTRY_TEST_H + +#include <Ecordova.h> + +#include <check.h> + +#include <stdbool.h> + +void ecordova_entry_test(TCase *); +bool entry_remove(Ecordova_Entry *); +bool entry_do(Ecordova_Entry *, const Eo_Event_Description *, void(*)()); + +#endif diff --git a/src/tests/ecordova/ecordova_file_test.c b/src/tests/ecordova/ecordova_file_test.c new file mode 100644 index 0000000000..7696062a50 --- /dev/null +++ b/src/tests/ecordova/ecordova_file_test.c @@ -0,0 +1,140 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_file_test.h" +#include "ecordova_suite.h" + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_File * +_file_new(const char *name, + const char *url, + const char *type, + time_t last_modified_date, + long size) +{ + return eo_add(ECORDOVA_FILE_CLASS, + NULL, + ecordova_file_constructor(name, + url, + type, + last_modified_date, + size)); +} + +START_TEST(smoke) +{ + Ecordova_File *file = _file_new("", "", "", 0, 0); + eo_unref(file); +} +END_TEST + +static void +_check_start_end(Ecordova_File *file, long expected_start, long expected_end) +{ + long actual_start, actual_end; + eo_do(file, + actual_start = ecordova_file_start_get(), + actual_end = ecordova_file_end_get()); + ck_assert_int_eq(expected_start, actual_start); + ck_assert_int_eq(expected_end, actual_end); +} + +START_TEST(slice) +{ + const long start = 0; + const long size = 100; + Ecordova_File *file = _file_new("", "", "", 0, size); + _check_start_end(file, start, size); + + + { + // normal slice + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(50, 70)); + _check_start_end(sliced_file, 50, 70); + eo_unref(sliced_file); + } + + { + // cannot past end + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(150, 170)); + _check_start_end(sliced_file, size, size); + eo_unref(sliced_file); + } + + { + // negative values slices backwards + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(-70, -20)); + _check_start_end(sliced_file, size - 70, size - 20); + eo_unref(sliced_file); + } + + { + // negative values slices backwards (swapped) + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(-20, -70)); + _check_start_end(sliced_file, size - 70, size - 20); + eo_unref(sliced_file); + } + + eo_unref(file); +} +END_TEST + +START_TEST(name_get) +{ + const char *expected_filename = "filename.txt"; + Ecordova_File *file = _file_new(expected_filename, "", "", 0, 0); + + const char *actual_filename = eo_do_ret(file, + actual_filename, + ecordova_file_name_get()); + ck_assert_str_eq(expected_filename, actual_filename); + + eo_unref(file); +} +END_TEST + +START_TEST(url_get) +{ + const char *expected_url = "/tmp/filename.txt"; + Ecordova_File *file = _file_new("filename.txt", expected_url, "", 0, 0); + + const char *actual_url = eo_do_ret(file, + actual_url, + ecordova_file_url_get()); + ck_assert_str_eq(expected_url, actual_url); + + eo_unref(file); +} +END_TEST + +void +ecordova_file_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, slice); + tcase_add_test(tc, name_get); + tcase_add_test(tc, url_get); +} diff --git a/src/tests/ecordova/ecordova_file_test.h b/src/tests/ecordova/ecordova_file_test.h new file mode 100644 index 0000000000..766006404e --- /dev/null +++ b/src/tests/ecordova/ecordova_file_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_FILE_TEST_H +#define _ECORDOVA_FILE_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_file_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_fileentry_test.c b/src/tests/ecordova/ecordova_fileentry_test.c new file mode 100644 index 0000000000..452c290cfe --- /dev/null +++ b/src/tests/ecordova/ecordova_fileentry_test.c @@ -0,0 +1,211 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_fileentry_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> +#include <stdlib.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_File * +_fileentry_new(const char *name, + const char *path, + Ecordova_FileSystem *file_system, + const char *url) +{ + return eo_add(ECORDOVA_FILEENTRY_CLASS, + NULL, + ecordova_fileentry_constructor(name, + path, + file_system, + url)); +} + +Ecordova_FileEntry * +create_tmpfile(Eina_Tmpstr **url, unsigned char **content, size_t *size) +{ + *size = (rand() % 1024) + 1; + return create_tmpfile_size(url, content, *size); +} + +Ecordova_FileEntry * +create_tmpfile_size(Eina_Tmpstr **url, unsigned char **content, size_t size) +{ + int fd = eina_file_mkstemp("fileentry_test_XXXXXX.bin", url); + ck_assert_int_ne(-1, fd); + check_exists(*url); + + const char *last_path_separator = strrchr(*url, '/'); + fail_if(NULL == last_path_separator); + + const char *name = last_path_separator + 1; + size_t len = last_path_separator - *url; + char path[len + 1]; + strncpy(path, *url, len); + path[len] = '\0'; + + *content = generate_random_buffer_size(size); + + write(fd, *content, size); + close(fd); + + return _fileentry_new(name, path, NULL, *url); +} + +unsigned char * +generate_random_buffer(size_t *size) +{ + *size = (rand() % 1024) + 1; + return generate_random_buffer_size(*size); +} + +unsigned char * +generate_random_buffer_size(size_t size) +{ + unsigned char *content = malloc(size); + for (size_t i = 0; i < size; ++i) + content[i] = (unsigned char)(rand() % 256); + return content; +} + +START_TEST(smoke) +{ + Ecordova_File *fileentry = _fileentry_new("", "", NULL, ""); + eo_unref(fileentry); +} +END_TEST + +Eina_Bool +_eo_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Eo **object = data; + fail_if(NULL == event_info); + + *object = eo_ref((Eo*)event_info); + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static bool +_fileentry_eo_get(Ecordova_Entry *file, + Eo **obj, + const Eo_Event_Description *desc, + void(*method)(void)) +{ + *obj = NULL; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(file, eo_event_callback_add(desc, _eo_get_cb, obj), + eo_event_callback_add(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error), + method()); + + ecore_main_loop_begin(); + + eo_do(file, eo_event_callback_del(desc, _eo_get_cb, obj), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _error_cb, &error)); + + eo_unref(timeout_timer); + + fail_if(timeout); + + return error; +} + +static bool +_fileentry_writer_create(Ecordova_FileEntry *file_entry, + Ecordova_FileWriter **filewriter) +{ + return _fileentry_eo_get(file_entry, + filewriter, + ECORDOVA_FILEENTRY_EVENT_CREATE_WRITER, + ecordova_fileentry_writer_create); +} + +bool +fileentry_file_get(Ecordova_FileEntry *file_entry, + Ecordova_File **file) +{ + return _fileentry_eo_get(file_entry, + file, + ECORDOVA_FILEENTRY_EVENT_FILE, + ecordova_fileentry_file); +} + +START_TEST(writer_create) +{ + bool error = false; + Eina_Tmpstr *url = NULL; + unsigned char *content = NULL; + size_t size = 0; + Ecordova_FileEntry *file_entry = create_tmpfile(&url, &content, &size); + + Ecordova_FileWriter *filewriter = NULL; + fail_if(error = _fileentry_writer_create(file_entry, &filewriter)); + fail_if(NULL == filewriter); + eo_unref(filewriter); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(content); + eina_tmpstr_del(url); +} +END_TEST + +START_TEST(file_get) +{ + bool error = false; + Eina_Tmpstr *url = NULL; + unsigned char *content = NULL; + size_t size = 0; + Ecordova_FileEntry *file_entry = create_tmpfile(&url, &content, &size); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + fail_if(NULL == file); + + long actual_end = eo_do_ret(file, actual_end, ecordova_file_end_get()); + long expected_end = size; + ck_assert_int_eq(expected_end, actual_end); + + eo_unref(file); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(content); + eina_tmpstr_del(url); +} +END_TEST + +void +ecordova_fileentry_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, writer_create); + tcase_add_test(tc, file_get); +} diff --git a/src/tests/ecordova/ecordova_fileentry_test.h b/src/tests/ecordova/ecordova_fileentry_test.h new file mode 100644 index 0000000000..f1c4f04321 --- /dev/null +++ b/src/tests/ecordova/ecordova_fileentry_test.h @@ -0,0 +1,17 @@ +#ifndef _ECORDOVA_FILEENTRY_TEST_H +#define _ECORDOVA_FILEENTRY_TEST_H + +#include <Ecordova.h> + +#include <check.h> + +#include <stdbool.h> + +void ecordova_fileentry_test(TCase *); +Ecordova_FileEntry *create_tmpfile(Eina_Tmpstr **, unsigned char **, size_t *); +Ecordova_FileEntry *create_tmpfile_size(Eina_Tmpstr **, unsigned char **, size_t); +bool fileentry_file_get(Ecordova_FileEntry *, Ecordova_File **); +unsigned char * generate_random_buffer(size_t*); +unsigned char * generate_random_buffer_size(size_t); + +#endif diff --git a/src/tests/ecordova/ecordova_filereader_test.c b/src/tests/ecordova/ecordova_filereader_test.c new file mode 100644 index 0000000000..d4ed04309e --- /dev/null +++ b/src/tests/ecordova/ecordova_filereader_test.c @@ -0,0 +1,294 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filereader_test.h" +#include "ecordova_fileentry_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> +#include <stdlib.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_FileReader * +_filereader_new(void) +{ + return eo_add(ECORDOVA_FILEREADER_CLASS, + NULL, + ecordova_filereader_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_File *filereader = _filereader_new(); + eo_unref(filereader); +} +END_TEST + +static Eina_Bool +_main_loop_quit(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_bool_set(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_ProgressEvent *progress = event_info; + fail_if(NULL == progress); + + bool *value = data; + *value = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +bool +filereader_read(Ecordova_File *file, char **content, size_t *length) +{ + *content = NULL; + *length = 0; + bool error = false; + bool timeout = false; + + Ecordova_File *filereader = _filereader_new(); + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(filereader, eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, _main_loop_quit, NULL), + eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_ERROR, _bool_set, &error), + ecordova_filereader_read(file)); + + ecore_main_loop_begin(); + + eo_do(filereader, eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, _main_loop_quit, NULL), + eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_ERROR, _bool_set, &error)); + + eo_unref(timeout_timer); + + if (!error && !timeout) + { + *length = eo_do_ret(filereader, *length, ecordova_filereader_length_get()); + if (*length) + { + const char *result = eo_do_ret(filereader, result, ecordova_filereader_result_get()); + *content = malloc(*length); + memcpy(*content, result, *length); + } + } + + eo_unref(filereader); + + fail_if(timeout); + + return error; +} + +START_TEST(read_all) +{ + bool error = false; + + Eina_Tmpstr *file_url = NULL; + unsigned char *expected_content = NULL; + size_t expected_size = 0; + Ecordova_FileEntry *file_entry = create_tmpfile(&file_url, + &expected_content, + &expected_size); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + + char *actual_content; + size_t actual_size; + fail_if(error = filereader_read(file, &actual_content, &actual_size)); + ck_assert_int_eq(expected_size, actual_size); + ck_assert_int_eq(0, memcmp(expected_content, actual_content, expected_size)); + free(actual_content); + + eo_unref(file); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(expected_content); + eina_tmpstr_del(file_url); +} +END_TEST + +START_TEST(read_slice) +{ + bool error = false; + + Eina_Tmpstr *file_url = NULL; + unsigned char *file_content = NULL; + size_t file_size = 1024; + Ecordova_FileEntry *file_entry = create_tmpfile_size(&file_url, + &file_content, + file_size); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + + // take the middle + size_t start = file_size / 3; + size_t end = file_size / 3 * 2; + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(start, end)); + fail_if(NULL == sliced_file); + + size_t expected_size = end - start; + unsigned char *expected_content = &file_content[start]; + + char *actual_content; + size_t actual_size; + fail_if(error = filereader_read(sliced_file, &actual_content, &actual_size)); + ck_assert_int_eq(expected_size, actual_size); + ck_assert_int_eq(0, memcmp(expected_content, actual_content, expected_size)); + free(actual_content); + + + eo_unref(sliced_file); + eo_unref(file); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(file_content); + eina_tmpstr_del(file_url); +} +END_TEST + +START_TEST(read_slice_page_size) +{ + bool error = false; + + Eina_Tmpstr *file_url = NULL; + unsigned char *file_content = NULL; + const size_t file_size = eina_cpu_page_size() * 5; + Ecordova_FileEntry *file_entry = create_tmpfile_size(&file_url, + &file_content, + file_size); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + + // take the middle + size_t start = file_size / 3; + size_t end = file_size / 3 * 2; + Ecordova_File *sliced_file = eo_do_ret(file, + sliced_file, + ecordova_file_slice(start, end)); + fail_if(NULL == sliced_file); + + size_t expected_size = end - start; + unsigned char *expected_content = &file_content[start]; + + char *actual_content; + size_t actual_size; + fail_if(error = filereader_read(sliced_file, &actual_content, &actual_size)); + ck_assert_int_eq(expected_size, actual_size); + ck_assert_int_eq(0, memcmp(expected_content, actual_content, expected_size)); + free(actual_content); + + eo_unref(sliced_file); + eo_unref(file); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(file_content); + eina_tmpstr_del(file_url); +} +END_TEST + +static Eina_Bool +_filereader_abort(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Ecordova_File *filereader = data; + fail_if(NULL == filereader); + eo_do(filereader, ecordova_filereader_abort()); + return EO_CALLBACK_CONTINUE; +} + +START_TEST(aborting) +{ + Eina_Tmpstr *file_url = NULL; + unsigned char *file_content = NULL; + size_t file_size = 0; + Ecordova_FileEntry *file_entry = create_tmpfile(&file_url, + &file_content, + &file_size); + + bool error = false; + bool timeout = false; + bool aborted = false; + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + + Ecordova_File *filereader = _filereader_new(); + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(filereader, eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, _main_loop_quit, NULL), + eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_PROGRESS, _filereader_abort, filereader), + eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_ABORT, _bool_set, &aborted), + eo_event_callback_add(ECORDOVA_FILEREADER_EVENT_ON_ERROR, _bool_set, &error), + ecordova_filereader_read(file)); + + ecore_main_loop_begin(); + + eo_do(filereader, eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_LOAD_END, _main_loop_quit, NULL), + eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_PROGRESS, _filereader_abort, filereader), + eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_ABORT, _bool_set, &aborted), + eo_event_callback_del(ECORDOVA_FILEREADER_EVENT_ON_ERROR, _bool_set, &error)); + + eo_unref(timeout_timer); + eo_unref(filereader); + + fail_if(timeout); + fail_if(error); + fail_unless(aborted); + + eo_unref(file); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + free(file_content); + eina_tmpstr_del(file_url); +} +END_TEST + +void +ecordova_filereader_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, read_all); + tcase_add_test(tc, read_slice); + tcase_add_test(tc, read_slice_page_size); + tcase_add_test(tc, aborting); +} diff --git a/src/tests/ecordova/ecordova_filereader_test.h b/src/tests/ecordova/ecordova_filereader_test.h new file mode 100644 index 0000000000..d7f59ce4fd --- /dev/null +++ b/src/tests/ecordova/ecordova_filereader_test.h @@ -0,0 +1,13 @@ +#ifndef _ECORDOVA_FILEREADER_TEST_H +#define _ECORDOVA_FILEREADER_TEST_H + +#include <Ecordova.h> + +#include <check.h> + +#include <stdbool.h> + +void ecordova_filereader_test(TCase *); +bool filereader_read(Ecordova_File *file, char **content, size_t *length); + +#endif diff --git a/src/tests/ecordova/ecordova_filetransfer_test.c b/src/tests/ecordova/ecordova_filetransfer_test.c new file mode 100644 index 0000000000..0aa435a388 --- /dev/null +++ b/src/tests/ecordova/ecordova_filetransfer_test.c @@ -0,0 +1,227 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filetransfer_test.h" +#include "ecordova_fileentry_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_filereader_test.h" +#include "ecordova_suite.h" + +#include <Ecore_Con.h> +#include <Ecore_File.h> + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); + ecore_con_init(); +} + +static void +_teardown(void) +{ + ecore_con_shutdown(); + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_filetransfer_new(void) +{ + return eo_add(ECORDOVA_FILETRANSFER_CLASS, + NULL, + ecordova_filetransfer_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *filetransfer = _filetransfer_new(); + eo_unref(filetransfer); +} +END_TEST + +static unsigned char *expected_content = NULL; +static size_t expected_size = 0; + +static Eina_Bool +_client_add_cb(void *data EINA_UNUSED, + int type EINA_UNUSED, + Ecore_Con_Event_Client_Add *event EINA_UNUSED) +{ + return EINA_TRUE; +} + +static Eina_Bool +_client_del_cb(void *data EINA_UNUSED, + int type EINA_UNUSED, + Ecore_Con_Event_Client_Del *event) +{ + ecore_con_client_del(event->client); + return EINA_TRUE; +} + +static Eina_Bool +_client_data_cb(void *data EINA_UNUSED, + int type EINA_UNUSED, + Ecore_Con_Event_Client_Data *event) +{ + const char response_template[] = + "HTTP/1.1 200 OK\r\n" + "Server: filetransfer test httpserver\r\n" + "Content-Length: %s\r\n" + "Connection: close\r\n" + "Content-Type: application/octet-stream\r\n\r\n"; + + char content_length[15]; + snprintf(content_length, sizeof(content_length), "%ld", expected_size); + + size_t min_len = strlen(response_template) + strlen(content_length) + expected_size; + char *buffer = malloc(min_len); + snprintf(buffer, min_len, response_template, content_length); + + size_t len = strlen(buffer); + memcpy(&buffer[len], expected_content, expected_size); + len += expected_size; + + ecore_con_client_send(event->client, buffer, len); + ecore_con_client_flush(event->client); + free(buffer); + + return EINA_TRUE; +} + +static Eina_Bool +_server_del_cb(void *data EINA_UNUSED, + int type EINA_UNUSED, + Ecore_Con_Event_Server_Del *event EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EINA_TRUE; +} + +static Eina_Bool +_main_loop_quit_cb(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_set_bool(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + fail_if(NULL == data); + bool *value = data; + *value = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_on_progress_cb(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + return EO_CALLBACK_CONTINUE; +} + +START_TEST(download) +{ + const int server_port = 8181; + const char *server_address = "127.0.0.1"; + Ecore_Con_Server *httpserver = ecore_con_server_add(ECORE_CON_REMOTE_TCP, server_address, server_port, NULL); + fail_if(NULL == httpserver); + ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_ADD, (Ecore_Event_Handler_Cb)_client_add_cb, NULL); + ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DEL, (Ecore_Event_Handler_Cb)_client_del_cb, NULL); + ecore_event_handler_add(ECORE_CON_EVENT_CLIENT_DATA, (Ecore_Event_Handler_Cb)_client_data_cb, NULL); + ecore_event_handler_add(ECORE_CON_EVENT_SERVER_DEL, (Ecore_Event_Handler_Cb)_server_del_cb, NULL); + + Eina_Tmpstr *file_url = NULL; + Ecordova_FileEntry *file_entry = create_tmpfile(&file_url, &expected_content, &expected_size); + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *tmpdir_entry = _create_tmpdir(&tmpdir); + + const char *filename = ecore_file_file_get(file_url); + const char *protocol = "http://"; + size_t len = strlen(protocol) + strlen(server_address) + 1 + 15 + 1 + strlen(filename) + 1; + char source[len]; + snprintf(source, len, "%s%s:%d/%s", protocol, server_address, server_port, filename); + + const char *download_extension = ".download"; + len = strlen(file_url) + strlen(download_extension) + 1; + char target[len]; + snprintf(target, len, "%s%s", file_url, download_extension); + + Ecordova_Device *filetransfer = _filetransfer_new(); + + bool error = false; + bool timeout = false; + + eo_do(filetransfer, eo_event_callback_add(ECORDOVA_FILETRANSFER_EVENT_DOWNLOAD_SUCCESS, _main_loop_quit_cb, NULL), + eo_event_callback_add(ECORDOVA_FILETRANSFER_EVENT_ON_PROGRESS, _on_progress_cb, NULL), + eo_event_callback_add(ECORDOVA_FILETRANSFER_EVENT_ERROR, _set_bool, &error), + ecordova_filetransfer_download(source, target, EINA_FALSE, NULL)); + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + ecore_main_loop_begin(); + eo_unref(timeout_timer); + + fail_if(error); + fail_if(timeout); + + eo_unref(filetransfer); + + + + Ecordova_FileEntry *downloaded_entry = NULL; + error = fileentry_get(tmpdir_entry, target, 0, &downloaded_entry); + fail_if(error); + fail_unless(NULL != downloaded_entry); + check_exists(file_url); + + + Ecordova_File *downloaded_file = NULL; + fail_if(error = fileentry_file_get(downloaded_entry, &downloaded_file)); + + char *actual_content; + size_t actual_size; + fail_if(error = filereader_read(downloaded_file, &actual_content, &actual_size)); + ck_assert_int_eq(expected_size, actual_size); + ck_assert_int_eq(0, memcmp(expected_content, actual_content, expected_size)); + free(actual_content); + + eo_unref(downloaded_file); + + fail_if(error = entry_remove(downloaded_entry)); + eo_unref(downloaded_entry); + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + fail_if(error = entry_remove(tmpdir_entry)); + eo_unref(tmpdir_entry); + free(expected_content); + eina_tmpstr_del(file_url); + eina_tmpstr_del(tmpdir); + + ecore_con_server_del(httpserver); + ecore_main_loop_begin(); +} +END_TEST + +void +ecordova_filetransfer_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, download); +} diff --git a/src/tests/ecordova/ecordova_filetransfer_test.h b/src/tests/ecordova/ecordova_filetransfer_test.h new file mode 100644 index 0000000000..1aab4ddbe6 --- /dev/null +++ b/src/tests/ecordova/ecordova_filetransfer_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_FILETRANSFER_TEST_H +#define _ECORDOVA_FILETRANSFER_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_filetransfer_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_filewriter_test.c b/src/tests/ecordova/ecordova_filewriter_test.c new file mode 100644 index 0000000000..8fdc86f49a --- /dev/null +++ b/src/tests/ecordova/ecordova_filewriter_test.c @@ -0,0 +1,218 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_filewriter_test.h" +#include "ecordova_fileentry_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_filereader_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> +#include <stdlib.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_FileWriter * +_filewriter_new(Ecordova_File *file) +{ + return eo_add(ECORDOVA_FILEWRITER_CLASS, + NULL, + ecordova_filewriter_constructor(file)); +} + +static Eina_Bool +_main_loop_quit(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_bool_set(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_ProgressEvent *progress = event_info; + fail_if(NULL == progress); + + bool *value = data; + *value = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static bool +_filewriter_write(Ecordova_File *file, const char *content, size_t length) +{ + bool error = false; + bool timeout = false; + + Ecordova_File *filewriter = _filewriter_new(file); + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(filewriter, eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, _main_loop_quit, NULL), + eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_ERROR, _bool_set, &error), + ecordova_filewriter_write(content, length)); + + ecore_main_loop_begin(); + + eo_do(filewriter, eo_event_callback_del(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, _main_loop_quit, NULL), + eo_event_callback_del(ECORDOVA_ENTRY_EVENT_ERROR, _bool_set, &error)); + + eo_unref(timeout_timer); + + eo_unref(filewriter); + + fail_if(timeout); + + return error; +} + +START_TEST(writer_all) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + const char *filepath = tmpdir; + size_t len = strlen(filepath) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + Ecordova_FileEntry *file_entry = NULL; + bool error = false; + + // create exclusive + Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + size_t expected_size; + unsigned char *expected_content = generate_random_buffer(&expected_size); + fail_if(error = _filewriter_write(file, (char*)expected_content, expected_size)); + eo_unref(file); + + fail_if(error = fileentry_file_get(file_entry, &file)); + char *actual_content; + size_t actual_size; + fail_if(error = filereader_read(file, &actual_content, &actual_size)); + ck_assert_int_eq(expected_size, actual_size); + ck_assert_int_eq(0, memcmp(expected_content, actual_content, expected_size)); + free(actual_content); + eo_unref(file); + + free(expected_content); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + eina_tmpstr_del(tmpdir); +} +END_TEST + +static Eina_Bool +_filewriter_abort(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + Ecordova_File *filewriter = data; + fail_if(NULL == filewriter); + eo_do(filewriter, ecordova_filewriter_abort()); + return EO_CALLBACK_CONTINUE; +} + +START_TEST(aborting) +{ + Eina_Tmpstr *tmpdir = NULL; + Ecordova_DirectoryEntry *directory_entry = _create_tmpdir(&tmpdir); + + const char *filename = "file.txt"; + const char *filepath = tmpdir; + size_t len = strlen(filepath) + 1 + strlen(filename) + 1; + char file_url[len]; + snprintf(file_url, len, "%s/%s", tmpdir, filename); + + Ecordova_FileEntry *file_entry = NULL; + bool error = false; + bool timeout = false; + bool aborted = false; + + // create exclusive + Ecordova_FileFlags flags = ECORDOVA_FILEFLAGS_CREATE | ECORDOVA_FILEFLAGS_EXCLUSIVE; + error = fileentry_get(directory_entry, filename, flags, &file_entry); + fail_if(error); + fail_unless(NULL != file_entry); + check_exists(file_url); + + Ecordova_File *file = NULL; + fail_if(error = fileentry_file_get(file_entry, &file)); + size_t size; + unsigned char *content = generate_random_buffer(&size); + + Ecordova_File *filewriter = _filewriter_new(file); + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(filewriter, eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, _main_loop_quit, NULL), + eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_PROGRESS, _filewriter_abort, filewriter), + eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_ABORT, _bool_set, &aborted), + eo_event_callback_add(ECORDOVA_FILEWRITER_EVENT_ON_ERROR, _bool_set, &error), + ecordova_filewriter_write((char*)content, size)); + + ecore_main_loop_begin(); + + eo_do(filewriter, eo_event_callback_del(ECORDOVA_FILEWRITER_EVENT_ON_WRITE_END, _main_loop_quit, NULL), + eo_event_callback_del(ECORDOVA_FILEWRITER_EVENT_ON_PROGRESS, _filewriter_abort, filewriter), + eo_event_callback_del(ECORDOVA_FILEWRITER_EVENT_ON_ABORT, _bool_set, &aborted), + eo_event_callback_del(ECORDOVA_FILEWRITER_EVENT_ON_ERROR, _bool_set, &error)); + + eo_unref(timeout_timer); + eo_unref(filewriter); + + fail_if(timeout); + fail_if(error); + fail_unless(aborted); + + eo_unref(file); + free(content); + + fail_if(error = entry_remove(file_entry)); + eo_unref(file_entry); + fail_if(error = entry_remove(directory_entry)); + eo_unref(directory_entry); + eina_tmpstr_del(tmpdir); +} +END_TEST + +void +ecordova_filewriter_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, writer_all); + tcase_add_test(tc, aborting); +} diff --git a/src/tests/ecordova/ecordova_filewriter_test.h b/src/tests/ecordova/ecordova_filewriter_test.h new file mode 100644 index 0000000000..b7ac34ade2 --- /dev/null +++ b/src/tests/ecordova/ecordova_filewriter_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_FILEWRITER_TEST_H +#define _ECORDOVA_FILEWRITER_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_filewriter_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_geolocation_test.c b/src/tests/ecordova/ecordova_geolocation_test.c new file mode 100644 index 0000000000..04bc8f573f --- /dev/null +++ b/src/tests/ecordova/ecordova_geolocation_test.c @@ -0,0 +1,93 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_geolocation_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_geolocation_new(void) +{ + return eo_add(ECORDOVA_GEOLOCATION_CLASS, + NULL, + ecordova_geolocation_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *geolocation = _geolocation_new(); + eo_unref(geolocation); +} +END_TEST + +static Eina_Bool +_current_position_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *called = data; + *called = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +static Eina_Bool +_error_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + bool *error = data; + *error = true; + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(current_position_get) +{ + Ecordova_Device *geolocation = _geolocation_new(); + + bool success_event_called = false; + bool error_event_called = false; + eo_do(geolocation, eo_event_callback_add(ECORDOVA_GEOLOCATION_EVENT_CURRENT_SUCCESS, _current_position_get_cb, &success_event_called)); + eo_do(geolocation, eo_event_callback_add(ECORDOVA_GEOLOCATION_EVENT_ERROR, _error_cb, &error_event_called)); + const Ecordova_Geolocation_Options options = { + .enable_high_accuracy = false, + .timeout = 4000 + }; + eo_do(geolocation, ecordova_geolocation_current_position_get(&options)); + + if (!success_event_called && !error_event_called) + ecore_main_loop_begin(); + + fail_if(error_event_called); + fail_unless(success_event_called); + + eo_unref(geolocation); +} +END_TEST + +void +ecordova_geolocation_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + //tcase_add_test(tc, current_position_get); // disabled: not supported +} diff --git a/src/tests/ecordova/ecordova_geolocation_test.h b/src/tests/ecordova/ecordova_geolocation_test.h new file mode 100644 index 0000000000..a65c34df59 --- /dev/null +++ b/src/tests/ecordova/ecordova_geolocation_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_GEOLOCATION_TEST_H +#define _ECORDOVA_GEOLOCATION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_geolocation_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_globalization_test.c b/src/tests/ecordova/ecordova_globalization_test.c new file mode 100644 index 0000000000..d55ab728a5 --- /dev/null +++ b/src/tests/ecordova/ecordova_globalization_test.c @@ -0,0 +1,594 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_globalization_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_globalization_new(void) +{ + return eo_add(ECORDOVA_GLOBALIZATION_CLASS, + NULL, + ecordova_globalization_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *globalization = _globalization_new(); + eo_unref(globalization); +} +END_TEST + +static Eina_Bool +_preferred_language_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + char **language = data; + fail_if(NULL == event_info); + Ecordova_Globalization_Language *glanguage = event_info; + + *language = strdup(glanguage->value); + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(preferred_language_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + char *language = NULL; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_PREFERRED_LANGUAGE_SUCCESS, + _preferred_language_get_cb, + &language)); + eo_do(globalization, ecordova_globalization_preferred_language_get()); + fail_if(NULL == language); + + DBG("language: %s", language); + free(language); + + eo_unref(globalization); +} +END_TEST + +static Eina_Bool +_locale_name_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + char **locale = data; + fail_if(NULL == event_info); + Ecordova_Globalization_Locale *glocale = event_info; + + *locale = strdup(glocale->value); + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(locale_name_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + char *locale = NULL; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_LOCALE_NAME_SUCCESS, + _locale_name_get_cb, + &locale)); + eo_do(globalization, ecordova_globalization_locale_name_get()); + fail_if(NULL == locale); + + DBG("locale: %s", locale); + free(locale); + + eo_unref(globalization); +} +END_TEST + +static Eina_Bool +_to_string_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + char **string = data; + fail_if(NULL == event_info); + Ecordova_Globalization_String *gstring = event_info; + + *string = strdup(gstring->value); + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(date_to_string) +{ + Ecordova_Device *globalization = _globalization_new(); + + time_t date_time; + time(&date_time); + + char *string = NULL; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DATE_TO_STRING_SUCCESS, + _to_string_cb, + &string)); + eo_do(globalization, ecordova_globalization_date_to_string(date_time, NULL)); + fail_if(NULL == string); + + DBG("string: %s", string); + free(string); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_CurrencyPattern pattern; +} CurrencyPatternData; + +static Eina_Bool +_currency_pattern_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + CurrencyPatternData *currency_pattern_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_CurrencyPattern *currency_pattern = event_info; + + currency_pattern_data->success = true; + currency_pattern_data->pattern = (Ecordova_Globalization_CurrencyPattern){ + .pattern = currency_pattern->pattern ? strdup(currency_pattern->pattern) : NULL, + .code = currency_pattern->code ? strdup(currency_pattern->code) : NULL, + .fraction = currency_pattern->fraction, + .rounding = currency_pattern->rounding, + .decimal = currency_pattern->decimal ? strdup(currency_pattern->decimal) : NULL, + .grouping = currency_pattern->grouping ? strdup(currency_pattern->grouping) : NULL + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(currency_pattern_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + CurrencyPatternData currency_pattern_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_CURRENCY_PATTERN_SUCCESS, + _currency_pattern_get_cb, + ¤cy_pattern_data)); + eo_do(globalization, ecordova_globalization_currency_pattern_get(NULL)); + fail_unless(currency_pattern_data.success); + + DBG("pattern: %s", currency_pattern_data.pattern.pattern); + DBG("code: %s", currency_pattern_data.pattern.code); + DBG("fraction: %d", currency_pattern_data.pattern.fraction); + DBG("rounding: %f", currency_pattern_data.pattern.rounding); + DBG("decimal: %s", currency_pattern_data.pattern.decimal); + DBG("grouping: %s", currency_pattern_data.pattern.grouping); + free((char*)currency_pattern_data.pattern.pattern); + free((char*)currency_pattern_data.pattern.code); + free((char*)currency_pattern_data.pattern.decimal); + free((char*)currency_pattern_data.pattern.grouping); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_DateNames names; +} DateNamesData; + +static Eina_Bool +_date_names_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + DateNamesData *date_names_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_DateNames *date_names = event_info; + fail_if(NULL == date_names->value); + date_names_data->success = true; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(date_names_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + DateNamesData date_names_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DATE_NAMES_SUCCESS, + _date_names_get_cb, + &date_names_data)); + eo_do(globalization, ecordova_globalization_date_names_get(NULL)); + fail_unless(date_names_data.success); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_DatePattern pattern; +} DatePatternData; + +static Eina_Bool +_date_pattern_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + DatePatternData *date_pattern_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_DatePattern *date_pattern = event_info; + + date_pattern_data->success = true; + date_pattern_data->pattern = (Ecordova_Globalization_DatePattern){ + .pattern = date_pattern->pattern ? strdup(date_pattern->pattern) : NULL, + .timezone = date_pattern->timezone ? strdup(date_pattern->timezone) : NULL, + .utc_offset = date_pattern->utc_offset, + .dst_offset = date_pattern->dst_offset, + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(date_pattern_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + DatePatternData date_pattern_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DATE_PATTERN_SUCCESS, + _date_pattern_get_cb, + &date_pattern_data)); + eo_do(globalization, ecordova_globalization_date_pattern_get(NULL)); + fail_unless(date_pattern_data.success); + + DBG("pattern: %s", date_pattern_data.pattern.pattern); + DBG("timezone: %s", date_pattern_data.pattern.timezone); + DBG("utc_offset: %d", date_pattern_data.pattern.utc_offset); + DBG("dst_offset: %d", date_pattern_data.pattern.dst_offset); + free((char*)date_pattern_data.pattern.pattern); + free((char*)date_pattern_data.pattern.timezone); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_FirstDayOfWeek first_day_of_week; +} FirstDayOfWeekData; + +static Eina_Bool +_first_day_of_week_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + FirstDayOfWeekData *first_day_of_week_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_FirstDayOfWeek *first_day_of_week = event_info; + + first_day_of_week_data->success = true; + first_day_of_week_data->first_day_of_week = (Ecordova_Globalization_FirstDayOfWeek){ + .value = first_day_of_week->value, + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(first_day_of_week_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + FirstDayOfWeekData first_day_of_week_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DATE_PATTERN_SUCCESS, + _first_day_of_week_get_cb, + &first_day_of_week_data)); + eo_do(globalization, ecordova_globalization_date_pattern_get(NULL)); + fail_unless(first_day_of_week_data.success); + + DBG("first_day_of_week: %d", first_day_of_week_data.first_day_of_week.value); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_NumberPattern pattern; +} NumberPatternData; + +static Eina_Bool +_number_pattern_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + NumberPatternData *number_pattern_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_NumberPattern *number_pattern = event_info; + + number_pattern_data->success = true; + number_pattern_data->pattern = (Ecordova_Globalization_NumberPattern){ + .pattern = number_pattern->pattern ? strdup(number_pattern->pattern) : NULL, + .symbol = number_pattern->symbol ? strdup(number_pattern->symbol) : NULL, + .fraction = number_pattern->fraction, + .rounding = number_pattern->rounding, + .positive = number_pattern->positive ? strdup(number_pattern->positive) : NULL, + .negative = number_pattern->negative ? strdup(number_pattern->negative) : NULL, + .decimal = number_pattern->decimal ? strdup(number_pattern->decimal) : NULL, + .grouping = number_pattern->grouping ? strdup(number_pattern->grouping) : NULL + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(number_pattern_get) +{ + Ecordova_Device *globalization = _globalization_new(); + + NumberPatternData number_pattern_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_NUMBER_PATTERN_SUCCESS, + _number_pattern_get_cb, + &number_pattern_data)); + eo_do(globalization, ecordova_globalization_number_pattern_get(NULL)); + fail_unless(number_pattern_data.success); + + DBG("pattern: %s", number_pattern_data.pattern.pattern); + DBG("symbol: %s", number_pattern_data.pattern.symbol); + DBG("fraction: %d", number_pattern_data.pattern.fraction); + DBG("rounding: %f", number_pattern_data.pattern.rounding); + DBG("positive: %s", number_pattern_data.pattern.positive); + DBG("negative: %s", number_pattern_data.pattern.negative); + DBG("decimal: %s", number_pattern_data.pattern.decimal); + DBG("grouping: %s", number_pattern_data.pattern.grouping); + free((char*)number_pattern_data.pattern.pattern); + free((char*)number_pattern_data.pattern.symbol); + free((char*)number_pattern_data.pattern.positive); + free((char*)number_pattern_data.pattern.negative); + free((char*)number_pattern_data.pattern.decimal); + free((char*)number_pattern_data.pattern.grouping); + + eo_unref(globalization); +} +END_TEST + + +typedef struct { + bool success; + Ecordova_Globalization_DayLightSavingsTime is_day_light_savings_time; +} DayLightSavingsTimeIsData; + +static Eina_Bool +_is_day_light_savings_time_get_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + DayLightSavingsTimeIsData *is_day_light_savings_time_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_DayLightSavingsTime *is_day_light_savings_time = event_info; + + is_day_light_savings_time_data->success = true; + is_day_light_savings_time_data->is_day_light_savings_time = (Ecordova_Globalization_DayLightSavingsTime){ + .dst = is_day_light_savings_time->dst, + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(is_day_light_savings_time) +{ + Ecordova_Device *globalization = _globalization_new(); + + time_t date_time; + time(&date_time); + + DayLightSavingsTimeIsData is_day_light_savings_time_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DAY_LIGHT_SAVINGS_TIME_SUCCESS, + _is_day_light_savings_time_get_cb, + &is_day_light_savings_time_data)); + eo_do(globalization, ecordova_globalization_day_light_savings_time_is(date_time)); + fail_unless(is_day_light_savings_time_data.success); + + DBG("is_day_light_savings_time: %d", is_day_light_savings_time_data.is_day_light_savings_time.dst); + + eo_unref(globalization); +} +END_TEST + +START_TEST(number_to_string) +{ + Ecordova_Device *globalization = _globalization_new(); + + double number = 12345.6789; + + char *string = NULL; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_NUMBER_TO_STRING_SUCCESS, + _to_string_cb, + &string)); + eo_do(globalization, ecordova_globalization_number_to_string(number, NULL)); + fail_if(NULL == string); + + DBG("string: %s", string); + free(string); + + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_DateTime date_time; +} StringToDateData; + +static Eina_Bool +_string_to_date_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + StringToDateData *date_time_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_DateTime *date_time = event_info; + + date_time_data->success = true; + date_time_data->date_time = (Ecordova_Globalization_DateTime){ + .year = date_time->year, + .month = date_time->month, + .day = date_time->day, + .hour = date_time->hour, + .minute = date_time->minute, + .second = date_time->second, + .millisecond = date_time->millisecond + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(string_to_date) +{ + Ecordova_Device *globalization = _globalization_new(); + + time_t date_time; + time(&date_time); + + char *dateString = NULL; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_DATE_TO_STRING_SUCCESS, + _to_string_cb, + &dateString)); + eo_do(globalization, ecordova_globalization_date_to_string(date_time, NULL)); + fail_if(NULL == dateString); + + StringToDateData string_to_date_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_STRING_TO_DATE_SUCCESS, + _string_to_date_cb, + &string_to_date_data)); + eo_do(globalization, ecordova_globalization_string_to_date(dateString, NULL)); + fail_unless(string_to_date_data.success); + + DBG("year: %d", string_to_date_data.date_time.year); + DBG("month: %d", string_to_date_data.date_time.month); + DBG("day: %d", string_to_date_data.date_time.day); + DBG("hour: %d", string_to_date_data.date_time.hour); + DBG("minute: %d", string_to_date_data.date_time.minute); + DBG("second: %d", string_to_date_data.date_time.second); + DBG("millisecond: %d", string_to_date_data.date_time.millisecond); + + free(dateString); + eo_unref(globalization); +} +END_TEST + +typedef struct { + bool success; + Ecordova_Globalization_Number number; +} StringToNumberData; + +static Eina_Bool +_string_to_number_cb(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + fail_if(NULL == data); + StringToNumberData *string_to_number_data = data; + fail_if(NULL == event_info); + Ecordova_Globalization_Number *number = event_info; + + string_to_number_data->success = true; + string_to_number_data->number = (Ecordova_Globalization_Number){ + .value = number->value, + }; + + ecore_main_loop_quit(); + return EINA_FALSE; +} + +START_TEST(string_to_number) +{ + Ecordova_Device *globalization = _globalization_new(); + + StringToNumberData string_to_number_data = {0}; + eo_do(globalization, + eo_event_callback_add(ECORDOVA_GLOBALIZATION_EVENT_STRING_TO_NUMBER_SUCCESS, + _string_to_number_cb, + &string_to_number_data)); + eo_do(globalization, ecordova_globalization_string_to_number("12345.6789", NULL)); + fail_unless(string_to_number_data.success); + + DBG("number: %f", string_to_number_data.number.value); + + eo_unref(globalization); +} +END_TEST + +void +ecordova_globalization_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, preferred_language_get); + tcase_add_test(tc, locale_name_get); + tcase_add_test(tc, date_to_string); + tcase_add_test(tc, currency_pattern_get); + tcase_add_test(tc, date_names_get); + tcase_add_test(tc, date_pattern_get); + tcase_add_test(tc, first_day_of_week_get); + tcase_add_test(tc, number_pattern_get); + tcase_add_test(tc, is_day_light_savings_time); + tcase_add_test(tc, number_to_string); + tcase_add_test(tc, string_to_date); + tcase_add_test(tc, string_to_number); +} diff --git a/src/tests/ecordova/ecordova_globalization_test.h b/src/tests/ecordova/ecordova_globalization_test.h new file mode 100644 index 0000000000..a64102b838 --- /dev/null +++ b/src/tests/ecordova/ecordova_globalization_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_GLOBALIZATION_TEST_H +#define _ECORDOVA_GLOBALIZATION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_globalization_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_media_test.c b/src/tests/ecordova/ecordova_media_test.c new file mode 100644 index 0000000000..c8d79ca314 --- /dev/null +++ b/src/tests/ecordova/ecordova_media_test.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_media_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_media_new(void) +{ + return eo_add(ECORDOVA_MEDIA_CLASS, + NULL, + ecordova_media_constructor("")); +} + +START_TEST(smoke) +{ + Ecordova_Device *media = _media_new(); + eo_unref(media); +} +END_TEST + +void +ecordova_media_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + //tcase_add_test(tc, smoke); // disabled: not supported on common profile +} diff --git a/src/tests/ecordova/ecordova_media_test.h b/src/tests/ecordova/ecordova_media_test.h new file mode 100644 index 0000000000..f26291efb1 --- /dev/null +++ b/src/tests/ecordova/ecordova_media_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_MEDIA_TEST_H +#define _ECORDOVA_MEDIA_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_media_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_mediafile_test.c b/src/tests/ecordova/ecordova_mediafile_test.c new file mode 100644 index 0000000000..e58e954ccd --- /dev/null +++ b/src/tests/ecordova/ecordova_mediafile_test.c @@ -0,0 +1,142 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_mediafile_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_suite.h" + +#include <Eio.h> + +#include <stdbool.h> +#include <stdlib.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_MediaFile * +_mediafile_new(const char *name, + const char *url, + const char *type, + time_t last_modified_date, + long size) +{ + return eo_add(ECORDOVA_MEDIAFILE_CLASS, + NULL, + ecordova_mediafile_constructor(name, + url, + type, + last_modified_date, + size)); +} + +START_TEST(smoke) +{ + Ecordova_Media *mediafile = _mediafile_new("test.txt", "/", "text/plain", 0, 0); + eo_unref(mediafile); +} +END_TEST + +static Eina_Bool +_main_loop_quit(void *data EINA_UNUSED, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info EINA_UNUSED) +{ + fail_if(NULL == data); + Ecordova_MediaFileData *mediafile_data = data; + fail_if(NULL == event_info); + *mediafile_data = *(Ecordova_MediaFileData*)event_info; + + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static Eina_Bool +_bool_set(void *data, + Eo *obj EINA_UNUSED, + const Eo_Event_Description *desc EINA_UNUSED, + void *event_info) +{ + Ecordova_ProgressEvent *progress = event_info; + fail_if(NULL == progress); + + bool *value = data; + *value = true; + ecore_main_loop_quit(); + return EO_CALLBACK_CONTINUE; +} + +static bool +_format_data_get(Ecordova_MediaFile *file, Ecordova_MediaFileData *data) +{ + *data = (Ecordova_MediaFileData){0}; + bool error = false; + bool timeout = false; + + Ecore_Timer *timeout_timer = eo_add(ECORE_TIMER_CLASS, NULL, ecore_obj_timer_constructor(10, _timeout_cb, &timeout)); + + eo_do(file, eo_event_callback_add(ECORDOVA_MEDIAFILE_EVENT_SUCCESS, _main_loop_quit, data), + eo_event_callback_add(ECORDOVA_MEDIAFILE_EVENT_ERROR, _bool_set, &error), + ecordova_mediafile_format_data_get()); + + ecore_main_loop_begin(); + + eo_do(file, eo_event_callback_del(ECORDOVA_MEDIAFILE_EVENT_SUCCESS, _main_loop_quit, data), + eo_event_callback_del(ECORDOVA_MEDIAFILE_EVENT_ERROR, _bool_set, &error)); + + eo_unref(timeout_timer); + fail_if(timeout); + + return error; +} + + +START_TEST(formatdata_get) +{ + const char *media_sample_filename = "44khz32kbps.mp3"; + size_t len = strlen(TESTS_SRC_DIR) + 1 + strlen(media_sample_filename) + 1; + char media_sample_url[len]; + snprintf(media_sample_url, len, "%s/%s", TESTS_SRC_DIR, media_sample_filename); + + Eina_File *media_sample_file = eina_file_open(media_sample_url, EINA_FALSE); + size_t media_sample_file_size = eina_file_size_get(media_sample_file); + time_t media_sample_modified_date = eina_file_mtime_get(media_sample_file); + eina_file_close(media_sample_file); + + Ecordova_Media *mediafile = _mediafile_new(media_sample_filename, + media_sample_url, + "audio/mpeg3", + media_sample_modified_date, + media_sample_file_size); + + bool error = false; + Ecordova_MediaFileData mediafile_data; + fail_if(error = _format_data_get(mediafile, &mediafile_data)); + fail_unless(NULL == mediafile_data.codecs); + ck_assert_int_eq(32000, mediafile_data.bitrate); + ck_assert_int_eq(0, mediafile_data.height); + ck_assert_int_eq(0, mediafile_data.width); + ck_assert_int_eq(55, mediafile_data.duration); + eo_unref(mediafile); +} +END_TEST + +void +ecordova_mediafile_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, smoke); + tcase_add_test(tc, formatdata_get); +} diff --git a/src/tests/ecordova/ecordova_mediafile_test.h b/src/tests/ecordova/ecordova_mediafile_test.h new file mode 100644 index 0000000000..ace9eac0dc --- /dev/null +++ b/src/tests/ecordova/ecordova_mediafile_test.h @@ -0,0 +1,12 @@ +#ifndef _ECORDOVA_MEDIAFILE_TEST_H +#define _ECORDOVA_MEDIAFILE_TEST_H + +#include <Ecordova.h> + +#include <check.h> + +#include <stdbool.h> + +void ecordova_mediafile_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_networkinformation_test.c b/src/tests/ecordova/ecordova_networkinformation_test.c new file mode 100644 index 0000000000..a3b98b53dc --- /dev/null +++ b/src/tests/ecordova/ecordova_networkinformation_test.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_networkinformation_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_networkinformation_new(void) +{ + return eo_add(ECORDOVA_NETWORKINFORMATION_CLASS, + NULL, + ecordova_networkinformation_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *networkinformation = _networkinformation_new(); + eo_unref(networkinformation); +} +END_TEST + +void +ecordova_networkinformation_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + //tcase_add_test(tc, smoke); // disabled: not supported on common profile +} diff --git a/src/tests/ecordova/ecordova_networkinformation_test.h b/src/tests/ecordova/ecordova_networkinformation_test.h new file mode 100644 index 0000000000..dadce8818c --- /dev/null +++ b/src/tests/ecordova/ecordova_networkinformation_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_NETWORKINFORMATION_TEST_H +#define _ECORDOVA_NETWORKINFORMATION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_networkinformation_test(TCase *); + +#endif diff --git a/src/tests/ecordova/ecordova_suite.c b/src/tests/ecordova/ecordova_suite.c new file mode 100644 index 0000000000..032e3616c1 --- /dev/null +++ b/src/tests/ecordova/ecordova_suite.c @@ -0,0 +1,163 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_contacts_test.h" +#include "ecordova_device_test.h" +#include "ecordova_devicemotion_test.h" +#include "ecordova_deviceorientation_test.h" +#include "ecordova_geolocation_test.h" +#include "ecordova_batterystatus_test.h" +#include "ecordova_console_test.h" +#include "ecordova_filetransfer_test.h" +#include "ecordova_media_test.h" +#include "ecordova_networkinformation_test.h" +#include "ecordova_vibration_test.h" +#include "ecordova_directoryreader_test.h" +#include "ecordova_directoryentry_test.h" +#include "ecordova_entry_test.h" +#include "ecordova_file_test.h" +#include "ecordova_fileentry_test.h" +#include "ecordova_filereader_test.h" +#include "ecordova_filewriter_test.h" +#include "ecordova_mediafile_test.h" +#include "ecordova_globalization_test.h" + +#include <Eina.h> + +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> + +#include "ecordova_suite.h" + +int _test_ecordova_log_dom = -1; + +typedef struct _Ecordova_Test_Case Ecordova_Test_Case; + +struct _Ecordova_Test_Case +{ + const char *test_case; + void (*build)(TCase *tc); +}; + +static const Ecordova_Test_Case etc[] = { + //{ "contacts", ecordova_contacts_test }, + { "device", ecordova_device_test }, + { "devicemotion", ecordova_devicemotion_test }, + { "deviceorientation", ecordova_deviceorientation_test }, + { "geolocation", ecordova_geolocation_test }, + { "batterystatus", ecordova_batterystatus_test }, + { "console", ecordova_console_test }, + { "filetransfer", ecordova_filetransfer_test }, + { "media", ecordova_media_test }, + { "networkinformation", ecordova_networkinformation_test }, + { "vibration", ecordova_vibration_test }, + { "directoryreader", ecordova_directoryreader_test }, + { "directoryentry", ecordova_directoryentry_test }, + { "entry", ecordova_entry_test }, + { "file", ecordova_file_test }, + { "fileentry", ecordova_fileentry_test }, + { "filereader", ecordova_filereader_test }, + { "filewriter", ecordova_filewriter_test }, + { "mediafile", ecordova_mediafile_test }, + { "globalization", ecordova_globalization_test }, + { NULL, NULL } +}; + +static void +_list_tests(void) +{ + const Ecordova_Test_Case *it = etc; + fputs("Available Test Cases:\n", stderr); + for (; it->test_case; it++) + fprintf(stderr, "\t%s\n", it->test_case); +} + +static bool +_use_test(int argc, const char **argv, const char *test_case) +{ + if (argc < 1) + return true; + + for (; argc > 0; argc--, argv++) + if (strcmp(test_case, *argv) == 0) + return true; + return false; +} + +static Suite * +_ecordova_suite_build(int argc, const char **argv) +{ + Suite *s = suite_create("Ecordova"); + + for (int i = 0; etc[i].test_case; ++i) + { + if (!_use_test(argc, argv, etc[i].test_case)) continue; + TCase *tc = tcase_create(etc[i].test_case); + + etc[i].build(tc); + + suite_add_tcase(s, tc); + //tcase_set_timeout(tc, 0); + } + + return s; +} + +static void +_init_logging(void) +{ + if (!eina_init()) + ck_abort_msg("Ecordova: Unable to initialize eina"); + + _test_ecordova_log_dom = eina_log_domain_register("test_ecordova", EINA_COLOR_LIGHTBLUE); + if (_test_ecordova_log_dom < 0) + ck_abort_msg("Could not register log domain: test_ecordova"); +} + +static void +_shutdown_logging(void) +{ + eina_log_domain_unregister(_test_ecordova_log_dom); + _test_ecordova_log_dom = -1; +} + +int +main(int argc, char **argv) +{ + for (int i = 1; i < argc; ++i) + { + if ((strcmp(argv[i], "-h") == 0) || + (strcmp(argv[i], "--help") == 0)) + { + fprintf(stderr, "Usage:\n\t%s [test_case1 .. [test_caseN]]\n", argv[0]); + _list_tests(); + return 0; + } + else if ((strcmp(argv[i], "-l") == 0) || + (strcmp(argv[i], "--list") == 0)) + { + _list_tests(); + return 0; + } + } + + srand(time(NULL)); + + _init_logging(); + + Suite *s = _ecordova_suite_build(argc - 1, (const char **)argv + 1); + SRunner *sr = srunner_create(s); + + srunner_set_xml(sr, TESTS_BUILD_DIR "/check-results.xml"); + + srunner_run_all(sr, CK_ENV); + int failed_count = srunner_ntests_failed(sr); + srunner_free(sr); + + _shutdown_logging(); + + return (failed_count == 0) ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/src/tests/ecordova/ecordova_suite.h b/src/tests/ecordova/ecordova_suite.h new file mode 100644 index 0000000000..8f4ad28912 --- /dev/null +++ b/src/tests/ecordova/ecordova_suite.h @@ -0,0 +1,14 @@ +#ifndef _SOAP_SUITE_H +#define _SOAP_SUITE_H + +#include <check.h> + +extern int _test_ecordova_log_dom; + +#define CRI(...) EINA_LOG_DOM_CRIT(_test_ecordova_log_dom, __VA_ARGS__) +#define ERR(...) EINA_LOG_DOM_ERR(_test_ecordova_log_dom, __VA_ARGS__) +#define WRN(...) EINA_LOG_DOM_WARN(_test_ecordova_log_dom, __VA_ARGS__) +#define INF(...) EINA_LOG_DOM_INFO(_test_ecordova_log_dom, __VA_ARGS__) +#define DBG(...) EINA_LOG_DOM_DBG(_test_ecordova_log_dom, __VA_ARGS__) + +#endif diff --git a/src/tests/ecordova/ecordova_vibration_test.c b/src/tests/ecordova/ecordova_vibration_test.c new file mode 100644 index 0000000000..aaf228da4d --- /dev/null +++ b/src/tests/ecordova/ecordova_vibration_test.c @@ -0,0 +1,44 @@ +#ifdef HAVE_CONFIG_H +# include <config.h> +#endif + +#include "ecordova_vibration_test.h" +#include "ecordova_suite.h" + +#include <stdbool.h> + +static void +_setup(void) +{ + int ret = ecordova_init(); + ck_assert_int_eq(ret, 1); +} + +static void +_teardown(void) +{ + int ret = ecordova_shutdown(); + ck_assert_int_eq(ret, 0); +} + +static Ecordova_Device * +_vibration_new(void) +{ + return eo_add(ECORDOVA_VIBRATION_CLASS, + NULL, + ecordova_vibration_constructor()); +} + +START_TEST(smoke) +{ + Ecordova_Device *vibration = _vibration_new(); + eo_unref(vibration); +} +END_TEST + +void +ecordova_vibration_test(TCase *tc) +{ + tcase_add_checked_fixture(tc, _setup, _teardown); + //tcase_add_test(tc, smoke); // disabled: not supported on common profile +} diff --git a/src/tests/ecordova/ecordova_vibration_test.h b/src/tests/ecordova/ecordova_vibration_test.h new file mode 100644 index 0000000000..84792a3a7e --- /dev/null +++ b/src/tests/ecordova/ecordova_vibration_test.h @@ -0,0 +1,9 @@ +#ifndef _ECORDOVA_VIBRATION_TEST_H +#define _ECORDOVA_VIBRATION_TEST_H + +#include <Ecordova.h> +#include <check.h> + +void ecordova_vibration_test(TCase *); + +#endif diff --git a/src/tests/evas/evas_test_textblock.c b/src/tests/evas/evas_test_textblock.c index d15465c9fb..9499230837 100644 --- a/src/tests/evas/evas_test_textblock.c +++ b/src/tests/evas/evas_test_textblock.c @@ -16,6 +16,14 @@ #include "evas_tests_helpers.h" +#if !defined(ck_assert_int_le) +# define ck_assert_int_le(X, Y) _ck_assert_int(X, <=, Y) +#endif + +#if !defined(ck_assert_int_gt) +# define ck_assert_int_gt(X, Y) _ck_assert_int(X, >, Y) +#endif + /* Functions defined in evas_object_textblock.c */ EAPI Eina_Bool _evas_textblock_check_item_node_link(Evas_Object *obj); |