platform/shared/common/RhodesApp.cpp in rhodes-2.0.3 vs platform/shared/common/RhodesApp.cpp in rhodes-2.1.0

- old
+ new

@@ -1,81 +1,76 @@ #include "RhodesApp.h" #include "common/RhoMutexLock.h" #include "common/IRhoClassFactory.h" #include "common/RhoConf.h" #include "common/RhoFilePath.h" +#include "common/RhoAppAdapter.h" #include "net/INetRequest.h" -#include "net/HttpServer.h" -#include "ruby/ext/rho/rhoruby.h" -//#include <math.h> #include "sync/ClientRegister.h" #include "sync/SyncThread.h" -#include "net/AsyncHttp.h" -#include "unzip/unzip.h" #include "net/URI.h" + +#include "net/HttpServer.h" +#include "ruby/ext/rho/rhoruby.h" +#include "net/AsyncHttp.h" #include "rubyext/WebView.h" +#include "rubyext/GeoLocation.h" #ifdef OS_WINCE #include <winsock.h> #endif -//#include "shttpd/src/shttpd.h" -//#include "shttpd/src/std_includes.h" using rho::net::HttpHeader; using rho::net::HttpHeaderList; using rho::net::CHttpServer; extern "C" { -void rho_sync_create(); -void rho_sync_destroy(); -void rho_sync_doSyncAllSources(int show_status_popup); void rho_map_location(char* query); void rho_appmanager_load( void* httpContext, const char* szQuery); void rho_db_init_attr_manager(); +void rho_sys_app_exit(); } namespace rho { namespace common{ IMPLEMENT_LOGCLASS(CRhodesApp,"RhodesApp"); -CRhodesApp* CRhodesApp::m_pInstance = 0; /*static*/ CRhodesApp* CRhodesApp::Create(const String& strRootPath) { if ( m_pInstance != null) - return m_pInstance; + return (CRhodesApp*)m_pInstance; m_pInstance = new CRhodesApp(strRootPath); - return m_pInstance; + return (CRhodesApp*)m_pInstance; } /*static*/void CRhodesApp::Destroy() { if ( m_pInstance ) delete m_pInstance; m_pInstance = 0; } -CRhodesApp::CRhodesApp(const String& strRootPath) : CRhoThread(createClassFactory()) +CRhodesApp::CRhodesApp(const String& strRootPath) : CRhodesAppBase(strRootPath) { - m_strRhoRootPath = strRootPath; m_bExit = false; + m_bDeactivationMode = false; - m_ptrFactory = createClassFactory(); + m_ptrFactory = rho_impl_createClassFactory(); m_NetRequest = m_ptrFactory->createNetRequest(); - m_oGeoLocation.init(m_ptrFactory); #if defined( OS_WINCE ) || defined (OS_WINDOWS) //initializing winsock WSADATA WsaData; int result = WSAStartup(MAKEWORD(2,2),&WsaData); #endif - //rho_logconf_Init(m_strRhoRootPath.c_str()); initAppUrls(); - //start(epNormal); + + initHttpServer(); getSplashScreen().init(); } void CRhodesApp::startApp() @@ -84,34 +79,43 @@ } void CRhodesApp::run() { LOG(INFO) + "Starting RhodesApp main routine..."; - initHttpServer(); RhoRubyStart(); + rubyext::CGeoLocation::Create(m_ptrFactory); + rho_db_init_attr_manager(); LOG(INFO) + "Starting sync engine..."; - rho_sync_create(); + sync::CSyncThread::Create(rho_impl_createClassFactory()); LOG(INFO) + "RhoRubyInitApp..."; RhoRubyInitApp(); - LOG(INFO) + "activate app"; - rho_ruby_activateApp(); + //LOG(INFO) + "activate app"; + //rho_ruby_activateApp(); getSplashScreen().hide(); LOG(INFO) + "navigate to first start url"; navigateToUrl(getFirstStartUrl()); - m_httpServer->run(); + //rho_clientregister_create("iphone_client"); + + while (!m_bExit) { + m_httpServer->run(); + if (m_bExit) + break; + wait(-1); + } LOG(INFO) + "RhodesApp thread shutdown"; + rubyext::CGeoLocation::Destroy(); RhoRubyStop(); - rho_sync_destroy(); + sync::CSyncThread::Destroy(); } CRhodesApp::~CRhodesApp(void) { stopApp(); @@ -125,66 +129,133 @@ void CRhodesApp::stopApp() { if (!m_bExit) { m_bExit = true; - m_httpServer->stop(); + m_httpServer->stop(); + stopWait(); stop(2000); } net::CAsyncHttp::Destroy(); } -class CRhoCallbackCall : public common::CRhoThread +template <typename T> +class CRhoCallInThread : public common::CRhoThread { - common::CAutoPtr<common::IRhoClassFactory> m_ptrFactory; - String m_strCallback, m_strBody; public: - CRhoCallbackCall(const String& strCallback, const String& strBody, common::IRhoClassFactory* factory) : CRhoThread(factory), - m_ptrFactory(factory), m_strCallback(strCallback), m_strBody(strBody) - { start(epNormal); } + CRhoCallInThread(T* cb) + :CRhoThread(rho_impl_createClassFactory()), m_cb(cb) + { + start(epNormal); + } private: virtual void run() { - common::CAutoPtr<net::INetRequest> pNetRequest = m_ptrFactory->createNetRequest(); - common::CAutoPtr<net::INetResponse> presp = pNetRequest->pushData( m_strCallback, m_strBody, null ); + m_cb->run(*this); delete this; } + +private: + common::CAutoPtr<T> m_cb; }; +template <typename T> +void rho_rhodesapp_call_in_thread(T *cb) +{ + new CRhoCallInThread<T>(cb); +} + +class CRhoCallbackCall +{ + common::CAutoPtr<common::IRhoClassFactory> m_ptrFactory; + String m_strCallback, m_strBody; +public: + CRhoCallbackCall(const String& strCallback, const String& strBody, common::IRhoClassFactory* factory) + :m_ptrFactory(factory), m_strCallback(strCallback), m_strBody(strBody) + {} + + void run(common::CRhoThread &) + { + common::CAutoPtr<net::INetRequest> pNetRequest = m_ptrFactory->createNetRequest(); + common::CAutoPtr<net::INetResponse> presp = pNetRequest->pushData( m_strCallback, m_strBody, null ); + } +}; + void CRhodesApp::runCallbackInThread(const String& strCallback, const String& strBody) { - new CRhoCallbackCall(strCallback, strBody, createClassFactory() ); + rho_rhodesapp_call_in_thread(new CRhoCallbackCall(strCallback, strBody, rho_impl_createClassFactory() ) ); } static void callback_activateapp(void *arg, String const &strQuery) { rho_ruby_activateApp(); String strMsg; rho_http_sendresponse(arg, strMsg.c_str()); } +static void callback_deactivateapp(void *arg, String const &strQuery) +{ + rho_ruby_deactivateApp(); + String strMsg; + rho_http_sendresponse(arg, strMsg.c_str()); +} + static void callback_loadserversources(void *arg, String const &strQuery) { - rho_ruby_loadserversources(strQuery.c_str()); + RhoAppAdapter.loadServerSources(strQuery); String strMsg; rho_http_sendresponse(arg, strMsg.c_str()); } +class CRhoActivateApp +{ + String m_strUrl; +public: + CRhoActivateApp(const String& strUrl) :m_strUrl(strUrl) {} + void run(common::CRhoThread &thisThread) + { + while (!rho_is_local_server_started()) + thisThread.wait(1); + common::CAutoPtr<common::IRhoClassFactory> factory = rho_impl_createClassFactory(); + common::CAutoPtr<net::INetRequest> pNetRequest = factory->createNetRequest(); + NetResponse(resp, pNetRequest->pullData( m_strUrl, null ) ); + if ( !resp.isOK() ) + LOG(ERROR) + "activate app failed. Code: " + resp.getRespCode() + "; Error body: " + resp.getCharData(); + } +}; + void CRhodesApp::callAppActiveCallback(boolean bActive) { - m_httpServer->pause(!bActive); + LOG(INFO) + "callAppActiveCallback"; if (bActive) { + this->stopWait(); + String strUrl = m_strHomeUrl + "/system/activateapp"; + // Activation callback need to be runned in separate thread + // Otherwise UI thread will be blocked. This can cause deadlock if user defined + // activate callback contains code which need to hold UI thread for execute + rho_rhodesapp_call_in_thread( new CRhoActivateApp( strUrl ) ); + } + else + { + // Deactivation callback must be called in place (not in separate thread!) + // This is because system can kill application at any time after this callback finished + // So to guarantee user code is executed on deactivation, we must not exit from this function + // until application finish executing of user-defined deactivation callback. + // However, blocking UI thread can cause problem with API refering to UI (such as WebView.navigate etc) + // To fix this problem, new mode 'deactivation' introduced. When this mode active, no UI operations allowed. + // All such operation will throw exception in ruby code when calling in 'deactivate' mode. + m_bDeactivationMode = true; + String strUrl = m_strHomeUrl + "/system/deactivateapp"; NetResponse(resp,getNet().pullData( strUrl, null )); if ( !resp.isOK() ) - LOG(ERROR) + "activate app failed. Code: " + resp.getRespCode() + "; Error body: " + resp.getCharData(); - - LOG(INFO) + "navigate to first start url"; - navigateToUrl(getFirstStartUrl()); + LOG(ERROR) + "deactivate app failed. Code: " + resp.getRespCode() + "; Error body: " + resp.getCharData(); + m_httpServer->stop(); + m_bDeactivationMode = false; } } void CRhodesApp::callCameraCallback(String strCallbackUrl, const String& strImagePath, const String& strError, boolean bCancel ) @@ -202,10 +273,28 @@ strBody += "&rho_callback=1"; NetRequest( getNet().pushData( strCallbackUrl, strBody, null ) ); } +void CRhodesApp::callSignatureCallback(String strCallbackUrl, const String& strSignaturePath, + const String& strError, boolean bCancel ) + { + strCallbackUrl = canonicalizeRhoUrl(strCallbackUrl); + String strBody; + if ( bCancel || strError.length() > 0 ) + { + if ( bCancel ) + strBody = "status=cancel&message=User canceled operation."; + else + strBody = "status=error&message=" + strError; + }else + strBody = "status=ok&signature_uri=db%2Fdb-files%2F" + strSignaturePath; + + strBody += "&rho_callback=1"; + NetRequest( getNet().pushData( strCallbackUrl, strBody, null ) ); + } + void CRhodesApp::callDateTimeCallback(String strCallbackUrl, long lDateTime, const char* szData, int bCancel ) { strCallbackUrl = canonicalizeRhoUrl(strCallbackUrl); String strBody; if ( bCancel ) @@ -318,10 +407,11 @@ m_httpServer->register_uri("/system/map", callback_map); m_httpServer->register_uri("/system/shared", callback_shared); m_httpServer->register_uri("/AppManager/loader/load", callback_AppManager_load); m_httpServer->register_uri("/system/getrhomessage", callback_getrhomessage); m_httpServer->register_uri("/system/activateapp", callback_activateapp); + m_httpServer->register_uri("/system/deactivateapp", callback_deactivateapp); m_httpServer->register_uri("/system/loadserversources", callback_loadserversources); } const char* CRhodesApp::getFreeListeningPort() { @@ -421,29 +511,20 @@ return m_strListeningPorts.c_str(); } void CRhodesApp::initAppUrls() { + CRhodesAppBase::initAppUrls(); m_currentTabIndex = 0; m_strHomeUrl = "http://localhost:"; m_strHomeUrl += getFreeListeningPort(); - m_strBlobsDirPath = getRhoRootPath() + "db/db-files"; - m_strDBDirPath = getRhoRootPath() + "db"; m_strLoadingPagePath = "file://" + getRhoRootPath() + "apps/app/loading.html"; m_strLoadingPngPath = getRhoRootPath() + "apps/app/loading.png"; } -String CRhodesApp::resolveDBFilesPath(const String& strFilePath) -{ - if ( String_startsWith(strFilePath, getRhoRootPath()) ) - return strFilePath; - - return CFilePath::join(getRhoRootPath(), strFilePath); -} - void CRhodesApp::keepLastVisitedUrl(String strUrl) { //LOG(INFO) + "Current URL: " + strUrl; m_currentUrls[m_currentTabIndex] = canonicalizeRhoUrl(strUrl); @@ -542,30 +623,22 @@ //rho::String strAppUrl = getAppBackUrl(); if ( m_strAppBackUrlOrig.length() > 0 ) loadUrl(m_strAppBackUrlOrig); else if ( strcasecmp(getCurrentUrl().c_str(),getStartUrl().c_str()) != 0 ) + { +#ifdef OS_MACOSX + if (RHOCONF().getBool("jqtouch_mode")) + { + rho_webview_execute_js("window.Rho.jqt.goBack()", 0); + return; + } +#endif rho_webview_navigate_back(); + } } -String CRhodesApp::canonicalizeRhoUrl(const String& strUrl) -{ - if (strUrl.length() == 0 ) - return m_strHomeUrl; - - if ( strncmp("http://", strUrl.c_str(), 7 ) == 0 || - strncmp("https://", strUrl.c_str(), 8 ) == 0 || - strncmp("javascript:", strUrl.c_str(), 11 ) == 0 || - strncmp("mailto:", strUrl.c_str(), 7) == 0 || - strncmp("tel:", strUrl.c_str(), 4) == 0 || - strncmp("wtai:", strUrl.c_str(), 5) == 0 - ) - return strUrl; - - return CFilePath::join(m_strHomeUrl,strUrl); -} - boolean CRhodesApp::sendLog() { String strDevicePin = rho::sync::CClientRegister::getInstance() ? rho::sync::CClientRegister::getInstance()->getDevicePin() : ""; String strClientID = rho::sync::CSyncThread::getSyncEngine().readClientID(); @@ -600,11 +673,10 @@ for (int i = 0; i < (int)m_arCallbackObjects.size(); i++) { if ( m_arCallbackObjects.elementAt(i) == 0 ) nIndex = i; } -// rho_ruby_holdValue(valObject); if ( nIndex == -1 ) { m_arCallbackObjects.addElement(pCallbackObject); nIndex = m_arCallbackObjects.size()-1; }else @@ -647,13 +719,11 @@ synchronized(m_mxPushCallback) { if ( m_strPushCallback.length() == 0 ) return false; - String strBody = "status=ok&message="; - net::URI::urlEncode(strData, strBody); - strBody += "&rho_callback=1"; + String strBody = strData + "&rho_callback=1"; if ( m_strPushCallbackParams.length() > 0 ) strBody += "&" + m_strPushCallbackParams; common::CAutoPtr<net::INetRequest> pNetRequest = m_ptrFactory->createNetRequest(); NetResponse(resp,pNetRequest->pushData( m_strPushCallback, strBody, null )); @@ -664,11 +734,11 @@ const char* szData = resp.getCharData(); return !(szData && strcmp(szData,"rho_push") == 0); } } - return true; + return false; } void CRhodesApp::setScreenRotationNotification(String strUrl, String strParams) { synchronized(m_mxScreenRotationCallback) @@ -708,11 +778,16 @@ boolean callback = false; if (String_startsWith(url, "callback:") ) { callback = true; url = url.substr(9); + }else if ( strcasecmp(url.c_str(), "exit")==0 || strcasecmp(url.c_str(), "close") == 0 ) + { + rho_sys_app_exit(); + return; } + url = canonicalizeRhoUrl(url); if (callback) { common::CAutoPtr<net::INetRequest> pNetRequest = m_ptrFactory->createNetRequest(); NetResponse(resp, pNetRequest->pushData( url, "rho_callback=1", null )); @@ -720,15 +795,23 @@ } else navigateToUrl(url); } +boolean CRhodesApp::isLocalServerStarted() +{ + if (!m_httpServer) + return false; + return m_httpServer->started(); +} + } //namespace common } //namespace rho extern "C" { +using namespace rho::common; unsigned long rho_rhodesapp_GetCallbackObject(int nIndex) { return RHODESAPP().getCallbackObject(nIndex); } @@ -880,10 +963,16 @@ const char* strError, int bCancel ) { RHODESAPP().callCameraCallback(strCallbackUrl, strImagePath, strError, bCancel != 0); } +void rho_rhodesapp_callSignatureCallback(const char* strCallbackUrl, const char* strSignaturePath, + const char* strError, int bCancel ) +{ + RHODESAPP().callSignatureCallback(strCallbackUrl, strSignaturePath, strError, bCancel != 0); +} + void rho_rhodesapp_callDateTimeCallback(const char* strCallbackUrl, long lDateTime, const char* szData, int bCancel ) { RHODESAPP().callDateTimeCallback(strCallbackUrl, lDateTime, szData, bCancel != 0); } @@ -892,11 +981,12 @@ RHODESAPP().callPopupCallback(strCallbackUrl, id, title); } void rho_rhodesapp_callAppActiveCallback(int nActive) { - RHODESAPP().callAppActiveCallback(nActive!=0); + if ( rho::common::CRhodesApp::getInstance() ) + RHODESAPP().callAppActiveCallback(nActive!=0); } void rho_rhodesapp_setViewMenu(unsigned long valMenu) { RHODESAPP().getAppMenu().setAppMenu(valMenu); @@ -1013,57 +1103,49 @@ return out; } void rho_net_request(const char *url) { - rho::common::CAutoPtr<rho::common::IRhoClassFactory> factory = rho::common::createClassFactory(); + rho::common::CAutoPtr<rho::common::IRhoClassFactory> factory = rho_impl_createClassFactory(); rho::common::CAutoPtr<rho::net::INetRequest> request = factory->createNetRequest(); request->pullData(url, null); } -int rho_unzip_file(const char* szZipPath) +void rho_rhodesapp_load_url(const char *url) { -#ifdef UNICODE - rho::StringW strZipPathW; - rho::common::convertToStringW(szZipPath, strZipPathW); - HZIP hz = OpenZipFile(strZipPathW.c_str(), ""); - if ( !hz ) - return 0; + RHODESAPP().loadUrl(url); +} - // Set base for unziping - SetUnzipBaseDir(hz, rho::common::convertToStringW(RHODESAPP().getDBDirPath()).c_str() ); -#else - HZIP hz = OpenZipFile(szZipPath, ""); - if ( !hz ) +int rho_rhodesapp_check_mode() +{ + if (RHODESAPP().deactivationMode()) + { + LOG(ERROR) + "Operation is not allowed in 'deactivation' mode"; return 0; + } + return 1; +} - // Set base for unziping - SetUnzipBaseDir(hz, RHODESAPP().getDBDirPath().c_str() ); -#endif +int rho_is_local_server_started() +{ + return RHODESAPP().isLocalServerStarted(); +} - ZIPENTRY ze; - ZRESULT res = 0; - // Get info about the zip - // -1 gives overall information about the zipfile - res = GetZipItem(hz,-1,&ze); - int numitems = ze.index; - - // Iterate through items and unzip them - for (int zi = 0; zi<numitems; zi++) - { - // fetch individual details, e.g. the item's name. - res = GetZipItem(hz,zi,&ze); - if ( res == ZR_OK ) - res = UnzipItem(hz, zi, ze.name); - } - - CloseZip(hz); - - return res == ZR_OK ? 1 : 0; +#if defined(OS_ANDROID) && defined(RHO_LOG_ENABLED) +int rho_log(const char *fmt, ...) +{ + va_list vl; + va_start(vl, fmt); + int ret = __android_log_vprint(ANDROID_LOG_INFO, "RhoLog", fmt, vl); + va_end(vl); + return ret; } -void rho_rhodesapp_load_url(const char *url) +unsigned long long rho_cur_time() { - RHODESAPP().loadUrl(url); + timeval tv; + gettimeofday(&tv, NULL); + return ((unsigned long long)tv.tv_sec)*1000000 + tv.tv_usec; } +#endif } //extern "C"