From: Szymon Jastrzebski Date: Fri, 11 Aug 2017 11:38:57 +0000 (+0200) Subject: [Application] Implementation for getAppsUsageInfo method X-Git-Tag: accepted/tizen/unified/20170816.160521~2^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=d2823f17cc6ac973f6126813f33f43034456af36;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [Application] Implementation for getAppsUsageInfo method [Verification] Code compiles. Manually tested in Web Console. Change-Id: I743397bf67e6da504c75e96dbda344f6e0103b54 Signed-off-by: Szymon Jastrzebski --- diff --git a/src/application/application_api.js b/src/application/application_api.js index 12bfeef..0db0248 100755 --- a/src/application/application_api.js +++ b/src/application/application_api.js @@ -26,6 +26,11 @@ var ApplicationControlLaunchMode = { GROUP: 'GROUP' }; +var ApplicationUsageMode = { + RECENTLY: 'RECENTLY', + FREQUENTLY: 'FREQUENTLY' +}; + // TODO: Please uncomment below lines when system events is ready //var SystemEvent = { // BATTERY_CHARGER_STATUS: 'BATTERY_CHARGER_STATUS', @@ -545,6 +550,102 @@ ApplicationManager.prototype.getBatteryUsageInfo = function() { } }; +ApplicationManager.prototype.getAppsUsageInfo = function() { + var args = AV.validateMethod(arguments, [ + { + name: 'successCallback', + type: AV.Types.FUNCTION + }, + { + name: 'errorCallback', + type: AV.Types.FUNCTION, + optional: true, + nullable: true + }, + { + name: 'mode', + type: AV.Types.ENUM, + values: T.getValues(ApplicationUsageMode), + optional: true, + nullable: true + }, + { + name: 'filter', + type: AV.Types.DICTIONARY, + optional: true, + nullable: true + }, + { + name: 'limit', + type: AV.Types.LONG, + optional: true, + nullable: true + }]); + + var callArgs = {}; + + if (!T.isNullOrUndefined(args.mode)) { + callArgs.mode = args.mode; + } + + if (!T.isNullOrUndefined(args.filter) && typeof args.filter !== 'object') { + setTimeout(function() { + native.callIfPossible(args.errorCallback, + new WebAPIException(WebAPIException.INVALID_VALUES_ERR, + 'filter must be an object.')); + }, 0); + return; + } + + callArgs.filter = {}; + if (!T.isNullOrUndefined(args.filter)) { + var filter = args.filter; + if (!T.isNullOrUndefined(filter.timeSpan)) { + callArgs.filter.timeSpan = Converter.toLong(filter.timeSpan); + } else { + if (!T.isNullOrUndefined(filter.startTime)) { + if (filter.startTime instanceof Date) { + callArgs.filter.startTime = filter.startTime.getTime() / 1000; + } else { + throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, + 'startTime given with invalid type.'); + } + } + + if (!T.isNullOrUndefined(filter.endTime)) { + if (filter.endTime instanceof Date) { + callArgs.filter.endTime = filter.endTime.getTime() / 1000; + } else { + throw new WebAPIException(WebAPIException.TYPE_MISMATCH_ERR, + 'endTime given with invalid type.'); + } + } + } + } + + if (!T.isNullOrUndefined(args.limit)) { + callArgs.limit = args.limit; + } + + var callback = function(result) { + if (native.isFailure(result)) { + native.callIfPossible(args.errorCallback, native.getErrorObject(result)); + } else { + var data = native.getResultObject(result); + var resultArray = []; + data.forEach(function(i) { + resultArray.push(new ApplicationUsage(i)); + }); + args.successCallback(resultArray); + } + }; + + var result = native.call('ApplicationManager_getAppsUsageInfo', callArgs, callback); + if (native.isFailure(result)) { + throw native.getErrorObject(result); + } +}; + function ListenerManager(native, listenerName) { this.listeners = {}; this.nextId = 1; @@ -1292,5 +1393,31 @@ function ApplicationBatteryUsage(data) { }); } +//class ApplicationUsage //////////////////////////////////////////////////////// +function ApplicationUsage(data) { + Object.defineProperties(this, { + appId : { + value : data.appId, + writable : false, + enumerable : true + }, + totalCount : { + value : data.totalCount, + writable : false, + enumerable : true + }, + totalDuration : { + value : data.totalDuration, + writable : false, + enumerable : true + }, + lastTime : { + value : new Date(data.lastTime * 1000), + writable : false, + enumerable : true + } + }); +} + // exports //////////////////////////////////////////////////// exports = new ApplicationManager(); diff --git a/src/application/application_instance.cc b/src/application/application_instance.cc index 0de967a..44a51f6 100755 --- a/src/application/application_instance.cc +++ b/src/application/application_instance.cc @@ -89,6 +89,7 @@ ApplicationInstance::ApplicationInstance() : REGISTER_ASYNC("ApplicationManager_findAppControl", FindAppControl); REGISTER_ASYNC("ApplicationManager_getAppsContext", GetAppsContext); REGISTER_ASYNC("ApplicationManager_getAppsInfo", GetAppsInfo); + REGISTER_ASYNC("ApplicationManager_getAppsUsageInfo", GetAppsUsageInfo); REGISTER_ASYNC("ApplicationManager_getBatteryUsageInfo", GetBatteryUsageInfo); #undef REGISTER_ASYNC } @@ -168,6 +169,13 @@ void ApplicationInstance::GetBatteryUsageInfo(const picojson::value& args, picoj manager_.GetBatteryUsageInfo(args, &out); } +void ApplicationInstance::GetAppsUsageInfo(const picojson::value& args, picojson::object& out) { + LoggerD("Entered"); + CHECK_PRIVILEGE_ACCESS(kPrivilegeAppHistoryRead, &out); + + manager_.GetAppsUsageInfo(args, &out); +} + void ApplicationInstance::AddAppInfoEventListener(const picojson::value& args, picojson::object& out) { LoggerD("Entered"); LoggerW("DEPRECATION WARNING: addAppInfoEventListener() is deprecated and will be removed from next release. " diff --git a/src/application/application_instance.h b/src/application/application_instance.h index 3ae6c32..69cf042 100755 --- a/src/application/application_instance.h +++ b/src/application/application_instance.h @@ -39,6 +39,7 @@ class ApplicationInstance: public common::ParsedInstance { void GetAppSharedURI(const picojson::value& args, picojson::object& out); void GetAppMetaData(const picojson::value& args, picojson::object& out); void GetBatteryUsageInfo(const picojson::value& args, picojson::object& out); + void GetAppsUsageInfo(const picojson::value& args, picojson::object& out); void AddAppInfoEventListener(const picojson::value& args, picojson::object& out); void RemoveAppInfoEventListener(const picojson::value& args, picojson::object& out); void GetRequestedAppControl(const picojson::value& args, picojson::object& out); diff --git a/src/application/application_manager.cc b/src/application/application_manager.cc index 354582d..c1b94af 100755 --- a/src/application/application_manager.cc +++ b/src/application/application_manager.cc @@ -71,6 +71,8 @@ const std::string kStatusType = "statusType"; const std::string kAppId = "appId"; const std::string kListenerId = "listenerId"; const std::string kAppStatusChangeListener = "AppStatusChangeListener"; +const std::string kAppUsageModeFrequently = "FREQUENTLY"; +const std::string kAppUsageModeRecently = "RECENTLY"; const std::map event_map_ = { {SYSTEM_EVENT_BATTERY_CHARGER_STATUS, EVENT_KEY_BATTERY_CHARGER_STATUS}, @@ -98,7 +100,9 @@ const std::map event_map_ = { }; #ifdef TIZEN_MOBILE -const int kMaximumRetrievedObjects = 30; +const int kMaximumBatteryRetrievedObjects = 30; +const int kMaximumAppsRetrievedObjects = 10; +const int kDefaultPeriodOfTime = 30; #endif } @@ -1164,7 +1168,7 @@ PlatformResult ApplicationManager::BatteryUsageFilter(const picojson::value& arg context_history_data_e* data_type_out) { LoggerD("Entered"); int ret = CONTEXT_HISTORY_ERROR_NONE; - int limit = kMaximumRetrievedObjects; + int limit = kMaximumBatteryRetrievedObjects; if (args.contains("limit")) { limit = static_cast(args.get("limit").get()); } @@ -1223,6 +1227,122 @@ PlatformResult ApplicationManager::BatteryUsageAttributes(const context_history_ return PlatformResult(ErrorCode::NO_ERROR); } + +PlatformResult ApplicationManager::AppsUsageFilter(const picojson::value& args, + const context_history_filter_h filter, + context_history_data_e* data_type_out) { + LoggerD("Entered"); + int ret = CONTEXT_HISTORY_ERROR_NONE; + int limit = kMaximumAppsRetrievedObjects; + if (args.contains("limit")) { + limit = static_cast(args.get("limit").get()); + } + + ret = context_history_filter_set_int(filter, CONTEXT_HISTORY_FILTER_RESULT_SIZE, limit); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::INVALID_VALUES_ERR, "limit given with invalid value.", + ("limit given with invalid value: %d (%s)", ret, get_error_message(ret))); + } + + context_history_data_e data_type_in = CONTEXT_HISTORY_FREQUENTLY_USED_APP; + if (args.contains("mode") && kAppUsageModeRecently == args.get("mode").get()) { + data_type_in = CONTEXT_HISTORY_RECENTLY_USED_APP; + } + + int time_span = kDefaultPeriodOfTime; + const picojson::object &JS_filter = args.get("filter").get(); + auto time_span_iter = JS_filter.find("timeSpan"); + if (JS_filter.end() != time_span_iter + || (JS_filter.end() == JS_filter.find("startTime") + && JS_filter.end() == JS_filter.find("endTime"))) { + //In the second case, we treat the filter object just like an empty object. + //The default value of filter will be used instead. + if (JS_filter.end() != time_span_iter) { + time_span = static_cast(time_span_iter->second.get()); + } + ret = context_history_filter_set_int(filter, CONTEXT_HISTORY_FILTER_TIME_SPAN, time_span); + //context_history_filter_set_int may return only success or CONTEXT_HISTORY_ERROR_INVALID_PARAMETER + //Although this should never happen, it's better to check ret's value + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::ABORT_ERR, "Error while setting the default TIME_SPAN value.", + ("Error while setting the default TIME_SPAN value: %d (%s)", ret, get_error_message(ret))); + } + } else { + auto start_time_iter = JS_filter.find("startTime"); + auto end_time_iter = JS_filter.find("endTime"); + if (start_time_iter != JS_filter.end()) { + int start_time = static_cast(start_time_iter->second.get()); + ret = context_history_filter_set_int(filter, CONTEXT_HISTORY_FILTER_START_TIME, start_time); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::INVALID_VALUES_ERR, "startTime given with invalid value.", + ("startTime given with invalid value: %d (%s)", ret, get_error_message(ret))); + } + } + if (end_time_iter != JS_filter.end()) { + int end_time = static_cast(end_time_iter->second.get()); + ret = context_history_filter_set_int(filter, CONTEXT_HISTORY_FILTER_END_TIME, end_time); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::INVALID_VALUES_ERR, "endTime given with invalid value.", + ("endTime given with invalid value: %d (%s)", ret, get_error_message(ret))); + } + } + } + + *data_type_out = data_type_in; + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult ApplicationManager::AppsUsageAttributes(const context_history_record_h record, + picojson::object* object) { + LoggerD("Entered"); + + int ret = CONTEXT_HISTORY_ERROR_NONE; + int total_count = 0; + int total_duration = 0; + int last_time = 0; + char* app_id = nullptr; + SCOPE_EXIT { + free(app_id); + }; + + ret = context_history_record_get_string(record, CONTEXT_HISTORY_APP_ID, &app_id); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::ABORT_ERR, "Failed to get string.", + ("Failed to get string: %d (%s)", ret, get_error_message(ret))); + } + + ret = context_history_record_get_int(record, CONTEXT_HISTORY_TOTAL_COUNT, &total_count); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::ABORT_ERR, "Failed to get total count.", + ("Failed to get total count: %d (%s)", ret, get_error_message(ret))); + } + + ret = context_history_record_get_int(record, CONTEXT_HISTORY_TOTAL_DURATION, &total_duration); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::ABORT_ERR, "Failed to get total duration.", + ("Failed to get total duration: %d (%s)", ret, get_error_message(ret))); + } + + ret = context_history_record_get_int(record, CONTEXT_HISTORY_LAST_TIME, &last_time); + if (CONTEXT_HISTORY_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::ABORT_ERR, "Failed to get last time.", + ("Failed to get last time: %d (%s)", ret, get_error_message(ret))); + } + + object->insert(std::make_pair("appId", picojson::value(app_id))); + object->insert(std::make_pair("totalCount", picojson::value(static_cast(total_count)))); + object->insert( + std::make_pair("totalDuration", picojson::value(static_cast(total_duration)))); + object->insert(std::make_pair("lastTime", picojson::value(static_cast(last_time)))); + + return PlatformResult(ErrorCode::NO_ERROR); +} #endif void ApplicationManager::GetBatteryUsageInfo(const picojson::value& args, picojson::object* out) { @@ -1266,6 +1386,43 @@ void ApplicationManager::GetBatteryUsageInfo(const picojson::value& args, picojs #endif } +void ApplicationManager::GetAppsUsageInfo(const picojson::value& args, picojson::object* out) { + LoggerD("Entered"); + +#ifdef TIZEN_MOBILE + int callback_id = static_cast(args.get(kCallbackId).get()); + + auto get_apps_usage = [args](const std::shared_ptr& response)-> void { + LoggerD("Entered"); + PlatformResult result = ApplicationManager::GetContextHistory(args, &response.get()->get(), + &ApplicationManager::AppsUsageFilter, + &ApplicationManager::AppsUsageAttributes); + if (!result) { + LogAndReportError(result, &response.get()->get()); + } + }; + + auto get_apps_usage_response = [this, callback_id]( + const std::shared_ptr& response) -> void { + LoggerD("Entered"); + picojson::object& obj = response->get(); + obj.insert(std::make_pair(kCallbackId, picojson::value(static_cast(callback_id)))); + Instance::PostMessage(&this->instance_, response->serialize().c_str()); + }; + + auto data = std::shared_ptr(new picojson::value(picojson::object())); + + TaskQueue::GetInstance().Queue( + get_apps_usage, + get_apps_usage_response, + data); +#else + // Context API is supported only for mobile profile, other ones would result with NotSupportedError + LogAndReportError(PlatformResult(ErrorCode::NOT_SUPPORTED_ERR, "This feature is not supported on this profile."), out, + ("NOT_SUPPORTED_ERR: This feature is not supported on this profile")); +#endif +} + void ApplicationManager::GetAppMetaData(const std::string& app_id, picojson::object* out) { LoggerD("Entered"); diff --git a/src/application/application_manager.h b/src/application/application_manager.h index ed4a8a1..164fcb5 100755 --- a/src/application/application_manager.h +++ b/src/application/application_manager.h @@ -58,6 +58,7 @@ class ApplicationManager { void GetAppSharedUri(const std::string& app_id, picojson::object* out); void GetAppMetaData(const std::string& app_id, picojson::object* out); void GetBatteryUsageInfo(const picojson::value& args, picojson::object* out); + void GetAppsUsageInfo(const picojson::value& args, picojson::object* out); void StartAppInfoEventListener(picojson::object* out); void StopAppInfoEventListener(); void GetApplicationInformationSize(const picojson::value& args, picojson::object* out); @@ -97,6 +98,9 @@ class ApplicationManager { static common::PlatformResult BatteryUsageFilter(const picojson::value&, const context_history_filter_h, context_history_data_e* data_type); static common::PlatformResult BatteryUsageAttributes(const context_history_record_h, picojson::object*); + + static common::PlatformResult AppsUsageFilter(const picojson::value&, const context_history_filter_h, context_history_data_e* data_type); + static common::PlatformResult AppsUsageAttributes(const context_history_record_h, picojson::object*); #endif };