[Application] Implementation for getAppsUsageInfo method 35/143535/3
authorSzymon Jastrzebski <s.jastrzebsk@partner.samsung.com>
Fri, 11 Aug 2017 11:38:57 +0000 (13:38 +0200)
committerSzymon Jastrzebski <s.jastrzebsk@partner.samsung.com>
Fri, 11 Aug 2017 11:38:57 +0000 (13:38 +0200)
[Verification] Code compiles. Manually tested in Web Console.

Change-Id: I743397bf67e6da504c75e96dbda344f6e0103b54
Signed-off-by: Szymon Jastrzebski <s.jastrzebsk@partner.samsung.com>
src/application/application_api.js
src/application/application_instance.cc
src/application/application_instance.h
src/application/application_manager.cc
src/application/application_manager.h

index 12bfeef..0db0248 100755 (executable)
@@ -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();
index 0de967a..44a51f6 100755 (executable)
@@ -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. "
index 3ae6c32..69cf042 100755 (executable)
@@ -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);
index 354582d..c1b94af 100755 (executable)
@@ -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<std::string, std::string> event_map_ = {
   {SYSTEM_EVENT_BATTERY_CHARGER_STATUS, EVENT_KEY_BATTERY_CHARGER_STATUS},
@@ -98,7 +100,9 @@ const std::map<std::string, std::string> 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<int>(args.get("limit").get<double>());
   }
@@ -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<int>(args.get("limit").get<double>());
+  }
+
+  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<std::string>()) {
+    data_type_in = CONTEXT_HISTORY_RECENTLY_USED_APP;
+  }
+
+  int time_span = kDefaultPeriodOfTime;
+  const picojson::object &JS_filter = args.get("filter").get<picojson::object>();
+  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<int>(time_span_iter->second.get<double>());
+    }
+    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<int>(start_time_iter->second.get<double>());
+      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<int>(end_time_iter->second.get<double>());
+      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<double>(total_count))));
+  object->insert(
+      std::make_pair("totalDuration", picojson::value(static_cast<double>(total_duration))));
+  object->insert(std::make_pair("lastTime", picojson::value(static_cast<double>(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<int>(args.get(kCallbackId).get<double>());
+
+  auto get_apps_usage = [args](const std::shared_ptr<picojson::value>& response)-> void {
+    LoggerD("Entered");
+    PlatformResult result = ApplicationManager::GetContextHistory(args, &response.get()->get<picojson::object>(),
+                                                                  &ApplicationManager::AppsUsageFilter,
+                                                                  &ApplicationManager::AppsUsageAttributes);
+    if (!result) {
+      LogAndReportError(result, &response.get()->get<picojson::object>());
+    }
+  };
+
+  auto get_apps_usage_response = [this, callback_id](
+      const std::shared_ptr<picojson::value>& response) -> void {
+    LoggerD("Entered");
+    picojson::object& obj = response->get<picojson::object>();
+    obj.insert(std::make_pair(kCallbackId, picojson::value(static_cast<double>(callback_id))));
+    Instance::PostMessage(&this->instance_, response->serialize().c_str());
+  };
+
+  auto data = std::shared_ptr<picojson::value>(new picojson::value(picojson::object()));
+
+  TaskQueue::GetInstance().Queue<picojson::value>(
+    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");
 
index ed4a8a1..164fcb5 100755 (executable)
@@ -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
 };