Add download api
authorDeqing Huang <deqing.huang@intel.com>
Wed, 24 Jul 2013 10:01:55 +0000 (18:01 +0800)
committerDeqing Huang <deqing.huang@intel.com>
Thu, 15 Aug 2013 01:52:52 +0000 (09:52 +0800)
download/download.gyp [new file with mode: 0644]
download/download_api.js [new file with mode: 0644]
download/download_context.cc [new file with mode: 0644]
download/download_context.h [new file with mode: 0644]
download/download_context_desktop.cc [new file with mode: 0644]
download/download_context_mobile.cc [new file with mode: 0644]
examples/download.html [new file with mode: 0644]
examples/index.html
packaging/tizen-extensions-crosswalk.spec
tizen-wrt.gyp

diff --git a/download/download.gyp b/download/download.gyp
new file mode 100644 (file)
index 0000000..3a2d46a
--- /dev/null
@@ -0,0 +1,31 @@
+{
+  'includes':[
+    '../common/common.gypi',
+  ],
+  'targets': [
+    {
+      'target_name': 'tizen_download',
+      'type': 'loadable_module',
+      'sources': [
+        'download_api.js',
+        'download_context.cc',
+        'download_context.h',
+        'download_context_desktop.cc',
+        'download_context_mobile.cc',
+      ],
+      'conditions': [
+        ['extension_host_os=="mobile"', {
+          'variables': {
+            'packages': [
+              'capi-appfw-application',
+              'capi-web-url-download',
+            ]
+          },
+        }],
+      ],
+      'includes': [
+        '../common/pkg-config.gypi',
+      ],
+    },
+  ],
+}
diff --git a/download/download_api.js b/download/download_api.js
new file mode 100644 (file)
index 0000000..619721b
--- /dev/null
@@ -0,0 +1,213 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+var repliedMsg;
+var currentUID = 0;
+var startListeners = [];
+
+var postMessage = function(msg) {
+  extension.postMessage(JSON.stringify(msg));
+};
+
+var errorMap = {
+  "DOWNLOAD_ERROR_NONE" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "Download successful",
+  },
+  "DOWNLOAD_UNKNOWN_ERROR" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "Unknown error",
+  },
+  "DOWNLOAD_ERROR_INVALID_PARAMETER" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "Invalid parameter",
+  },
+  "DOWNLOAD_ERROR_FIELD_NOT_FOUND" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "Specified field not found",
+  },
+  "DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "Full of available downloading items from server",
+  },
+  "DOWNLOAD_ERROR_FILE_ALREADY_EXISTS" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "It is failed to rename the downloaded file",
+  },
+  "DOWNLOAD_ERROR_INVALID_URL" : {
+    code : tizen.WebAPIException.SYNTAX_ERR,
+    name : "SyntaxError",
+    message : "Invalid URL",
+  },
+  "DOWNLOAD_ERROR_INVALID_DESTINATION" : {
+    code : tizen.WebAPIException.SYNTAX_ERR,
+    name : "SyntaxError",
+    message : "Invalid destination",
+  },
+  "DOWNLOAD_ERROR_INVALID_STATE" : {
+    code : tizen.WebAPIException.INVALID_STATE_ERR,
+    name : "InvalidStateError",
+    message : "Invalid state",
+  },
+  "DOWNLOAD_ERROR_ALREADY_COMPLETED" : {
+    code : tizen.WebAPIException.INVALID_STATE_ERR,
+    name : "InvalidStateError",
+    message : "The download is already completed",
+  },
+  "DOWNLOAD_ERROR_CANNOT_RESUME" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "It cannot resume",
+  },
+  "DOWNLOAD_ERROR_IO_ERROR" : {
+    code : tizen.WebAPIException.INVALID_ACCESS_ERR,
+    name : "InvalidAccessError",
+    message : "Internal I/O error",
+  },
+  "DOWNLOAD_ERROR_OUT_OF_MEMORY" : {
+    code : tizen.WebAPIException.QUOTA_EXCEEDED_ERR,
+    name : "QuotaExceededError",
+    message : "Out of memory",
+  },
+  "DOWNLOAD_ERROR_NO_SPACE" : {
+    code : tizen.WebAPIException.QUOTA_EXCEEDED_ERR,
+    name : "QuotaExceededError",
+    message : "No space left on device",
+  },
+  "DOWNLOAD_ERROR_NETWORK_UNREACHABLE" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "Network is unreachable",
+  },
+  "DOWNLOAD_ERROR_CONNECTION_TIMED_OUT" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "HTTP session timeout",
+  },
+  "DOWNLOAD_ERROR_CONNECTION_FAILED" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "Connection failed",
+  },
+  "DOWNLOAD_ERROR_REQUEST_TIMEOUT" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "There are no action after client create a download id",
+  },
+  "DOWNLOAD_ERROR_RESPONSE_TIMEOUT" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "It does not call start API in some time although the download is created",
+  },
+  "DOWNLOAD_ERROR_TOO_MANY_REDIRECTS" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "In case of too may redirects from http response header",
+  },
+  "DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE" : {
+    code : tizen.WebAPIException.NETWORK_ERR,
+    name : "NetworkError",
+    message : "The download cannot handle the HTTP status value",
+  },
+  "DOWNLOAD_ERROR_SYSTEM_DOWN" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "There are no response from client after rebooting download daemon",
+  },
+  "DOWNLOAD_ERROR_NO_DATA" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "No data because the set API is not called",
+  },
+  "DOWNLOAD_ERROR_ID_NOT_FOUND" : {
+    code : tizen.WebAPIException.NOT_SUPPORTED_ERR,
+    name : "NotSupportedError",
+    message : "The download id is not existed in download service module",
+  },
+};
+
+var downloadState = {};
+
+extension.setMessageListener(function(msg) {
+  var m = JSON.parse(msg);
+  if (m.cmd == "DownloadReplyProgress") {
+    downloadState[m.uid] = "DOWNLOADING";
+    startListeners[m.uid].onprogress(m.uid, m.receivedSize, m.totalSize);
+  } else if (m.cmd == "DownloadReplyComplete") {
+    downloadState[m.uid] = "COMPLETED";
+    startListeners[m.uid].oncompleted(m.uid, m.fullPath);
+  } else if (m.cmd == "DownloadReplyPause") {
+    downloadState[m.uid] = "PAUSED";
+    startListeners[m.uid].onpaused(m.uid);
+  } else if (m.cmd == "DownloadReplyCancel") {
+    downloadState[m.uid] = "CANCELED";
+    startListeners[m.uid].oncanceled(m.uid);
+  } else if (m.cmd == "DownloadReplyFail") {
+    downloadState[m.uid] = "FAILED";
+    startListeners[m.uid].onfailed(m.uid,
+        new tizen.WebAPIError(errorMap[m.errorCode].code,
+                              errorMap[m.errorCode].message,
+                              errorMap[m.errorCode].name));
+  }
+});
+
+tizen.DownloadRequest = function(url, destination, filename) {
+  this.url = url;
+  this.uid = (++currentUID).toString();
+  typeof destination != 'undefined' && (this.destination = destination);
+  typeof filename != 'undefined' && (this.filename = filename);
+}
+
+exports.start = function(request, listener) {
+  if (!(request instanceof tizen.DownloadRequest)) {
+    console.log(
+        "tizen.download.start(): argument of invalid type " + typeof(request));
+    return;
+  }
+  startListeners[request.uid] = listener;
+  postMessage({
+    "cmd": "DownloadStart",
+    "url": request.url,
+    "uid": request.uid,
+    "destination": request.destination,
+    "filename": request.filename,
+  });
+  return request.uid;
+}
+
+exports.setListener = function(downloadId, listener) {
+  startListeners[request.uid] = listener;
+}
+
+exports.pause = function(downloadId) {
+  postMessage({
+    "cmd": "DownloadPause",
+    "uid": downloadId,
+  });
+}
+
+exports.resume = function(downloadId) {
+  postMessage({
+    "cmd": "DownloadResume",
+    "uid": downloadId,
+  });
+}
+
+exports.cancel = function(downloadId) {
+  postMessage({
+    "cmd": "DownloadCancel",
+    "uid": downloadId,
+  });
+}
+
+// TODO(hdq): Use sync message to get state from manager.
+exports.getState = function(downloadId) {
+  return downloadState[downloadId];
+}
diff --git a/download/download_context.cc b/download/download_context.cc
new file mode 100644 (file)
index 0000000..02a2e8e
--- /dev/null
@@ -0,0 +1,291 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "download/download_context.h"
+
+#include "common/picojson.h"
+
+#define CHECK(x, args) do { \
+  int retval = (x); \
+  if (retval != DOWNLOAD_ERROR_NONE) { \
+    fprintf(stderr, "Download error: %s returned %s at %s:%d \n", #x, \
+                    ConvertErrorToString(retval), __FILE__, __LINE__); \
+    OnFailedInfo(args, ToString(ConvertErrorToString(retval))); \
+    return; \
+  } \
+} while (0)
+
+#define CHECK_DO(x, args, y) do { \
+  int retval = (x); \
+  if (retval != DOWNLOAD_ERROR_NONE) { \
+    fprintf(stderr, "Download error: %s returned %s at %s:%d \n", #x, \
+                    ConvertErrorToString(retval), __FILE__, __LINE__); \
+    OnFailedInfo(args, ToString(ConvertErrorToString(retval))); \
+    y; \
+  } \
+} while (0)
+
+CXWalkExtension* xwalk_extension_init(int32_t api_version) {
+  return ExtensionAdapter<DownloadContext>::Initialize();
+}
+
+DownloadContext::DownloadContext(ContextAPI* api)
+    : api_(api) {
+}
+
+DownloadContext::~DownloadContext() {
+  delete (api_);
+  for (DownloadArgsVector::iterator it = args_.begin();
+       it != args_.end(); it++) {
+    delete (*it);
+  }
+}
+
+const char DownloadContext::name[] = "tizen.download";
+
+// This will be generated from download_api.js.
+extern const char kSource_download_api[];
+
+const char* DownloadContext::GetJavaScript() {
+  return kSource_download_api;
+}
+
+const char* DownloadContext::ConvertErrorToString(int error) {
+  switch (error) {
+  case DOWNLOAD_ERROR_NONE:
+    return "DOWNLOAD_ERROR_NONE";
+  case DOWNLOAD_ERROR_INVALID_PARAMETER:
+    return "DOWNLOAD_ERROR_INVALID_PARAMETER";
+  case DOWNLOAD_ERROR_OUT_OF_MEMORY:
+    return "DOWNLOAD_ERROR_OUT_OF_MEMORY";
+  case DOWNLOAD_ERROR_NETWORK_UNREACHABLE:
+    return "DOWNLOAD_ERROR_NETWORK_UNREACHABLE";
+  case DOWNLOAD_ERROR_CONNECTION_TIMED_OUT:
+    return "DOWNLOAD_ERROR_CONNECTION_TIMED_OUT";
+  case DOWNLOAD_ERROR_NO_SPACE:
+    return "DOWNLOAD_ERROR_NO_SPACE";
+  case DOWNLOAD_ERROR_FIELD_NOT_FOUND:
+    return "DOWNLOAD_ERROR_FIELD_NOT_FOUND";
+  case DOWNLOAD_ERROR_INVALID_STATE:
+    return "DOWNLOAD_ERROR_INVALID_STATE";
+  case DOWNLOAD_ERROR_CONNECTION_FAILED:
+    return "DOWNLOAD_ERROR_CONNECTION_FAILED";
+  case DOWNLOAD_ERROR_INVALID_URL:
+    return "DOWNLOAD_ERROR_INVALID_URL";
+  case DOWNLOAD_ERROR_INVALID_DESTINATION:
+    return "DOWNLOAD_ERROR_INVALID_DESTINATION";
+  case DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS:
+    return "DOWNLOAD_ERROR_TOO_MANY_DOWNLOADS";
+  case DOWNLOAD_ERROR_QUEUE_FULL:
+    return "DOWNLOAD_ERROR_QUEUE_FULL";
+  case DOWNLOAD_ERROR_ALREADY_COMPLETED:
+    return "DOWNLOAD_ERROR_ALREADY_COMPLETED";
+  case DOWNLOAD_ERROR_FILE_ALREADY_EXISTS:
+    return "DOWNLOAD_ERROR_FILE_ALREADY_EXISTS";
+  case DOWNLOAD_ERROR_CANNOT_RESUME:
+    return "DOWNLOAD_ERROR_CANNOT_RESUME";
+  case DOWNLOAD_ERROR_TOO_MANY_REDIRECTS:
+    return "DOWNLOAD_ERROR_TOO_MANY_REDIRECTS";
+  case DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE:
+    return "DOWNLOAD_ERROR_UNHANDLED_HTTP_CODE";
+  case DOWNLOAD_ERROR_REQUEST_TIMEOUT:
+    return "DOWNLOAD_ERROR_REQUEST_TIMEOUT";
+  case DOWNLOAD_ERROR_RESPONSE_TIMEOUT:
+    return "DOWNLOAD_ERROR_RESPONSE_TIMEOUT";
+  case DOWNLOAD_ERROR_SYSTEM_DOWN:
+    return "DOWNLOAD_ERROR_SYSTEM_DOWN";
+  case DOWNLOAD_ERROR_ID_NOT_FOUND:
+    return "DOWNLOAD_ERROR_ID_NOT_FOUND";
+  case DOWNLOAD_ERROR_NO_DATA:
+    return "DOWNLOAD_ERROR_NO_DATA";
+  case DOWNLOAD_ERROR_IO_ERROR:
+    return "DOWNLOAD_ERROR_IO_ERROR";
+  default:
+    return "DOWNLOAD_UNKNOWN_ERROR";
+  }
+}
+
+void DownloadContext::HandleMessage(const char* message) {
+  picojson::value v;
+
+  std::string err;
+  picojson::parse(v, message, message + strlen(message), &err);
+  if (!err.empty()) {
+    fprintf(stderr, "Ignoring message.\n");
+    return;
+  }
+
+  std::string cmd = v.get("cmd").to_str();
+  if (cmd == "DownloadStart")
+    HandleStart(v);
+  else if (cmd == "DownloadPause")
+    HandleGeneral(v, download_pause, "HandlePause");
+  else if (cmd == "DownloadResume")
+    HandleGeneral(v, download_start, "HandleResume");
+  else if (cmd == "DownloadCancel")
+    HandleGeneral(v, download_cancel, "HandleCancel");
+  // TODO(hdq): add getstate
+}
+
+void DownloadContext::OnStateChanged(int download_id,
+                                     download_state_e state, void* user_data) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_data);
+  switch (state) {
+  case DOWNLOAD_STATE_DOWNLOADING:
+    OnStartInfo(download_id, user_data);
+    break;
+  case DOWNLOAD_STATE_PAUSED:
+    OnPausedInfo(user_data);
+    break;
+  case DOWNLOAD_STATE_COMPLETED:
+    OnFinishedInfo(download_id, user_data);
+    break;
+  case DOWNLOAD_STATE_CANCELED:
+    OnCanceledInfo(user_data);
+    break;
+  case DOWNLOAD_STATE_FAILED: {
+      download_error_e error;
+      CHECK(download_get_error(download_id, &error), args);
+      args->context->OnFailedInfo(args, ToString(ConvertErrorToString(error)));
+    }
+    break;
+  default:
+    fprintf(stderr, "Ignore state: %d\n", state);
+    break;
+  }
+}
+
+void DownloadContext::OnStartInfo(int download_id, void* user_param) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+  const std::string uid(args->download_uid);
+  DownloadItemRefPtr downloadItem = args->context->downloads_[uid];
+
+  long long unsigned file_size = 0;
+  CHECK(download_get_content_size(download_id, &file_size), args);
+  downloadItem->file_size = file_size;
+}
+
+void DownloadContext::OnProgressInfo(int download_id,
+                                     long long unsigned received,
+                                     void* user_param) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+  DownloadItemRefPtr downloadItem =
+    args->context->downloads_[args->download_uid];
+
+  picojson::value::object o;
+  o["cmd"] = picojson::value("DownloadReplyProgress");
+  o["uid"] = picojson::value(args->download_uid);
+  o["receivedSize"] = picojson::value(ToString(received));
+  o["totalSize"] = picojson::value(ToString(downloadItem->file_size));
+  picojson::value v(o);
+  args->context->api_->PostMessage(v.serialize().c_str());
+}
+
+void DownloadContext::OnFinishedInfo(int download_id, void* user_param) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+  const std::string uid(args->download_uid);
+  DownloadItemRefPtr downloadItem = args->context->downloads_[uid];
+  char* path = 0;
+  CHECK(download_get_downloaded_file_path(download_id, &path), args);
+  std::string full_path = ToString(path);
+  free(path);
+
+  picojson::value::object o;
+  o["cmd"] = picojson::value("DownloadReplyComplete");
+  o["fullPath"] = picojson::value(full_path);
+  o["uid"] = picojson::value(args->download_uid);
+  picojson::value v(o);
+  args->context->api_->PostMessage(v.serialize().c_str());
+}
+
+void DownloadContext::OnPausedInfo(void* user_param) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+
+  picojson::value::object o;
+  o["cmd"] = picojson::value("DownloadReplyPause");
+  o["uid"] = picojson::value(args->download_uid);
+  picojson::value v(o);
+  args->context->api_->PostMessage(v.serialize().c_str());
+}
+
+void DownloadContext::OnCanceledInfo(void* user_param) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+
+  picojson::value::object o;
+  o["cmd"] = picojson::value("DownloadReplyCancel");
+  o["uid"] = picojson::value(args->download_uid);
+  picojson::value v(o);
+  args->context->api_->PostMessage(v.serialize().c_str());
+}
+
+void DownloadContext::OnFailedInfo(void* user_param,
+                                   const std::string& error) {
+  DownloadArgs* args = static_cast<DownloadArgs*>(user_param);
+
+  picojson::value::object o;
+  o["cmd"] = picojson::value("DownloadReplyFail");
+  o["uid"] = picojson::value(args->download_uid);
+  o["errorCode"] = picojson::value(error);
+  picojson::value v(o);
+  args->context->api_->PostMessage(v.serialize().c_str());
+}
+
+void DownloadContext::HandleStart(const picojson::value& msg) {
+  // Add to Downloads map
+  DownloadItemRefPtr d(new DownloadItem);
+  d->url  = msg.get("url").to_str();
+  d->destination = GetFullDestinationPath(msg.get("destination").to_str());
+  std::string filename = msg.get("filename").to_str();
+  d->filename = (filename == "null") ? std::string() : filename;
+  std::string uid = msg.get("uid").to_str();
+  d->uid = uid;
+
+  DownloadArgs* args = new DownloadArgs(uid, this);
+  args_.push_back(args);
+
+  // create and start
+  CHECK(download_create(&d->downloadID), args);
+  CHECK(download_set_state_changed_cb(d->downloadID, OnStateChanged,
+                                      static_cast<void* >(args)), args);
+  CHECK(download_set_progress_cb(d->downloadID, OnProgressInfo,
+                                 static_cast<void*>(args)), args);
+  CHECK(download_set_destination(d->downloadID, d->destination.c_str()), args);
+
+  if (!d->filename.empty()) {
+    CHECK(download_set_file_name(d->downloadID, d->filename.c_str()), args);
+  }
+  downloads_[uid] = d;  // FIXME if uid duplicate we will lose previous item
+
+  CHECK(download_set_url(d->downloadID, d->url.c_str()), args);
+  CHECK(download_start(d->downloadID), args);
+}
+
+template <typename FnType>
+bool DownloadContext::HandleGeneral(const picojson::value& msg,
+                                    FnType fn,
+                                    const char* fn_name) {
+  std::string uid = msg.get("uid").to_str();
+  if (uid == "null") {
+    fprintf(stderr, "%s - ERROR: Undefined download UID\n", fn_name);
+    return false;
+  }
+
+  DownloadArgs* args = new DownloadArgs(uid, this);
+  args_.push_back(args);
+  int downloadID = downloads_[uid]->downloadID;
+  CHECK_DO(fn(downloadID), args, return false);
+  return true;
+}
+
+std::string DownloadContext::GetUID(int downloadID) const {
+  std::string uid("null");
+  for (DownloadItemMap::const_iterator it = downloads_.begin();
+       it != downloads_.end(); it++) {
+    if (it->second->downloadID == downloadID) {
+      uid = it->second->uid;
+      break;
+    }
+  }
+  return uid;
+}
diff --git a/download/download_context.h b/download/download_context.h
new file mode 100644 (file)
index 0000000..3a169be
--- /dev/null
@@ -0,0 +1,107 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef DOWNLOAD_DOWNLOAD_CONTEXT_H_
+#define DOWNLOAD_DOWNLOAD_CONTEXT_H_
+
+#include <tr1/memory>
+#include <map>
+#include <vector>
+#include <string>
+#include <sstream>
+
+#include "common/extension_adapter.h"
+#include "web/download.h"
+
+namespace picojson {
+class value;
+}
+
+class DownloadContext {
+ public:
+  explicit DownloadContext(ContextAPI* api);
+  ~DownloadContext();
+
+  // ExtensionAdapter implementation.
+  static const char name[];
+  static const char* GetJavaScript();
+  void HandleMessage(const char* message);
+  void HandleSyncMessage(const char* message) {}
+
+ private:
+  void HandleStart(const picojson::value& msg);
+  template <typename FnType>
+  bool HandleGeneral(const picojson::value& msg,
+                     FnType fn,
+                     const char* fn_name);
+  struct DownloadArgs {
+    std::string download_uid;
+    DownloadContext* context;
+    DownloadArgs(std::string uid, DownloadContext* c)
+      : download_uid(uid), context(c) {}
+  };
+  typedef std::vector<DownloadArgs*> DownloadArgsVector;
+  DownloadArgsVector args_;
+  static void OnStateChanged(int download_id,
+                             download_state_e state,
+                             void* user_data);
+  static void OnProgressInfo(int download_id,
+                             long long unsigned received,
+                             void* user_param);
+  static void OnStartInfo(int download_id, void* user_param);
+  static void OnFinishedInfo(int download_id, void* user_param);
+  static void OnPausedInfo(void* user_param);
+  static void OnCanceledInfo(void* user_param);
+  static void OnFailedInfo(void* user_param,
+                           const std::string& error);
+
+  ContextAPI* api_;
+
+  struct DownloadItem {
+    std::string url;
+    std::string destination;
+    std::string filename;
+    std::string uid;
+
+    int downloadID;
+    char* file_type;
+    long long unsigned file_size;
+    char* tmp_saved_path;
+    char* content_name;
+
+    DownloadItem() {}
+    ~DownloadItem() {
+      download_unset_state_changed_cb(downloadID);
+      download_unset_progress_cb(downloadID);
+      download_destroy(downloadID);
+    }
+
+   private:
+    explicit DownloadItem(DownloadItem const&);
+    void operator= (DownloadItem const&);
+  };
+  typedef std::tr1::shared_ptr<DownloadItem> DownloadItemRefPtr;
+  typedef std::map<std::string, DownloadItemRefPtr> DownloadItemMap;
+  DownloadItemMap downloads_;
+
+  // FullDestPath = HomePath + DestPath
+  // TODO(hdq): This depends on filesystem api?
+  std::string GetFullDestinationPath(const std::string destination) const;
+
+  // helpers
+  template <typename T>
+  static std::string ToString(T a) {
+    std::ostringstream ss;
+    ss << a;
+    return ss.str();
+  }
+  std::string GetUID(int downloadID) const;
+  static const char* ConvertErrorToString(int error);  // download_error_e
+
+ private:
+  explicit DownloadContext(DownloadContext const&);
+  void operator=(DownloadContext const&);
+};
+
+#endif  // DOWNLOAD_DOWNLOAD_CONTEXT_H_
diff --git a/download/download_context_desktop.cc b/download/download_context_desktop.cc
new file mode 100644 (file)
index 0000000..1e4b36f
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "download/download_context.h"
+
+#include <pwd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+std::string DownloadContext::GetFullDestinationPath(
+    const std::string destination) const {
+  std::string folder = (destination == "null") ? std::string() : destination;
+
+  std::string path(getenv("HOME"));
+  if (path.empty()) {
+    struct passwd password_entry;
+    struct passwd *password_entry_ptr;
+    char buf[1024];
+    if (!getpwuid_r(getuid(), &password_entry,
+                    buf, sizeof buf, &password_entry_ptr)) {
+      path = password_entry.pw_dir;
+    }
+  }
+  return (path + '/' + folder);
+}
diff --git a/download/download_context_mobile.cc b/download/download_context_mobile.cc
new file mode 100644 (file)
index 0000000..2fd9095
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "download/download_context.h"
+
+std::string DownloadContext::GetFullDestinationPath(
+    const std::string destination) const {
+  std::string folder = (destination == "null") ? std::string() : destination;
+
+  // TODO(hdq): User should be able to choose store to external storage
+  //            i.e. /opt/storage/sdcard/Downloads
+  std::string path("/opt/usr/media/");
+  if (folder.empty()) {
+    return (path + "Downloads/");
+  }
+  return (path + folder);
+}
diff --git a/examples/download.html b/examples/download.html
new file mode 100644 (file)
index 0000000..a07631c
--- /dev/null
@@ -0,0 +1,163 @@
+<h1>Hello Tizen Download API</h1>
+
+<body>
+  <style type="text/css">
+    span { border: none; }
+    div { max-width: 500px; }
+  </style>
+  <span contenteditable="true" id="download1"></span><br>
+  <span contenteditable="true" id="download2"></span><br>
+  <textarea cols=80 rows=20 id="output"></textarea><br>
+  <button id="bnstart1">Click 1</button>
+  <button id="bnstart2">Click 2</button><br>
+  <button id="button1">Click 1</button>
+  <button id="button2">Click 2</button><br>
+  <button id="b1cancel">Click 1</button>
+  <button id="b2cancel">Click 2</button><br>
+  <button id="bnstate1">Click 1</button>
+  <button id="bnstate2">Click 2</button>
+</body>
+
+<script>
+
+var output = document.getElementById("output");
+
+function handle(button, text, callback) {
+  var b = document.getElementById(button);
+  b.innerText = text;
+  b.addEventListener("click", callback);
+}
+
+var listener = {
+  onprogress: function(id, receivedSize, totalSize) {
+    if (urls[id] == url1)
+      document.getElementById("download1").innerHTML = "Download1 received " + receivedSize + "/" + totalSize + " bytes"
+    else if (urls[id] == url2)
+      document.getElementById("download2").innerHTML = "Download2 received " + receivedSize + "/" + totalSize + " bytes"
+    else
+      output.value += "> listener::onprogress - unexpected call: id " + id + ", size " + receivedSize + "/" + totalSize + "\n";
+  },
+  oncompleted: function(id, fullPath) {
+    output.value += "> Download complete, id:" + id + ", saved to path: " + fullPath + "\n";
+  },
+  onpaused: function(id) {
+    output.value += "> Download is paused by user\n";
+  },
+  oncanceled: function(id) {
+    output.value += "> Download is canceled by user\n";
+  },
+  onfailed: function(id, error) {
+    output.value += "> Download of id[" + id + "] failed with error:" +
+                    " code[" + error.code + "]" +
+                    " name[" + error.name + "]" +
+                    " message[" + error.message + "]\n";
+  }
+};
+
+// Test big file downloading
+var url1 = "http://ipv4.download.thinkbroadband.com/200MB.zip";
+// Test https downloading
+var url2 = "https://developer.tizen.org/sites/all/themes/tizen_theme/logo.png";
+var urls = { "1" : url1, "2" : url2 }
+
+var r1, id1, r2, id2;
+var paused1=false;
+var paused2=false;
+
+handle("bnstart1", "Start Download1", function() {
+  r1 = new tizen.DownloadRequest(url1); // Test saving to default folder
+  id1 = tizen.download.start(r1, listener);
+  urls[id1] = url1;
+  output.value += "> asked to start download " + url1 + " to default location.\n";
+});
+
+handle("button1", "Pause Download1", function() {
+  if (typeof id1 == "undefined") {
+    output.value += "> Download1 is not started yet.\n";
+    return;
+  }
+
+  if (paused1) {
+    output.value += "> asked to resume download of " + url1 + "\n";
+    tizen.download.resume(id1);
+    paused1 = false;
+    var b = document.getElementById("button1");
+    b.innerText = "Pause Download1";
+  } else {
+    output.value += "> asked to pause download of " + url1 + "\n";
+    tizen.download.pause(id1);
+    paused1 = true;
+    var b = document.getElementById("button1");
+    b.innerText = "Resume Download1";
+  }
+});
+
+handle("bnstart2", "Start Download2", function() {
+  r2 = new tizen.DownloadRequest(url2, "Images"); // Test saving to specific folder
+  id2 = tizen.download.start(r2, listener);
+  urls[id2] = url2;
+  output.value += "> asked to start download "+url2+" to Images folder\n";
+});
+
+handle("button2", "Pause Download2", function() {
+  if (typeof id2 == "undefined") {
+    output.value += "> Download1 is not started yet.\n";
+    return;
+  }
+
+  if (paused2) {
+    output.value += "> asked to resume download of " + url2 + "\n";
+    tizen.download.resume(id2);
+    paused2 = false;
+    var b = document.getElementById("button2");
+    b.innerText = "Pause Download2";
+  } else {
+    output.value += "> asked to pause download of " + url2 + "\n";
+    tizen.download.pause(id2);
+    paused2 = true;
+    var b = document.getElementById("button2");
+    b.innerText = "Resume Download2";
+  }
+});
+
+handle("b1cancel", "Cancel Download1", function() {
+  if (typeof id1 == "undefined") {
+    output.value += "> Download1 is not started yet.\n";
+    return;
+  }
+
+  output.value += "> asked to cancel download of " + url1 + "\n";
+  tizen.download.cancel(id1);
+});
+
+handle("b2cancel", "Cancel Download2", function() {
+  if (typeof id2 == "undefined") {
+    output.value += "> Download2 is not started yet.\n";
+    return;
+  }
+
+  output.value += "> asked to cancel download of " + url2 + "\n";
+  tizen.download.cancel(id2);
+});
+
+handle("bnstate1", "GetState of Download1", function() {
+  if (typeof id1 == "undefined") {
+    output.value += "> Download1 is not started yet.\n";
+    return;
+  }
+
+  output.value += "> asked to get state of download1, id:" + id1 + "\n";
+  output.value += "> return: " + tizen.download.getState(id1) + "\n";
+});
+
+handle("bnstate2", "GetState of Download2", function() {
+  if (typeof id2 == "undefined") {
+    output.value += "> Download2 is not started yet.\n";
+    return;
+  }
+
+  output.value += "> asked to get state of download2, id:" + id2 + "\n";
+  output.value += "> return: " + tizen.download.getState(id2) + "\n";
+});
+
+</script>
index ff712b4..f915103 100644 (file)
@@ -9,5 +9,6 @@
 <a href="time.html"><h5>- Time Test</h5></a>
 <a href="power.html"><h5>- Power API Test</h5></a>
 <a href="bluetooth.html"><h5>- Bluetooth Test</h5></a>
+<a href="download.html"><h5>- Download Test</h5></a>
 </body>
 
index 6355abf..8c99608 100644 (file)
@@ -10,12 +10,14 @@ Source1:    %{name}
 Source1001: %{name}.manifest
 
 BuildRequires: python
+BuildRequires: pkgconfig(capi-appfw-application)
 BuildRequires: pkgconfig(capi-network-bluetooth)
 BuildRequires: pkgconfig(capi-network-connection)
 BuildRequires: pkgconfig(capi-system-device)
 BuildRequires: pkgconfig(capi-system-info)
 BuildRequires: pkgconfig(capi-system-power)
 BuildRequires: pkgconfig(capi-system-system-settings)
+BuildRequires: pkgconfig(capi-web-url-download)
 BuildRequires: pkgconfig(dbus-glib-1)
 BuildRequires: pkgconfig(glib-2.0)
 BuildRequires: pkgconfig(libudev)
index 9350c4c..294eba2 100644 (file)
@@ -12,6 +12,7 @@
         'system_setting/system_setting.gyp:*',
         'time/time.gyp:*',
         'tizen/tizen.gyp:*',
+        'download/download.gyp:*',
       ],
     },
   ],