Store previous complication state in DB 80/170780/9
authorhyunho <hhstark.kang@samsung.com>
Thu, 22 Feb 2018 05:35:23 +0000 (14:35 +0900)
committerHyunho Kang <hhstark.kang@samsung.com>
Fri, 2 Mar 2018 00:22:56 +0000 (00:22 +0000)
Change-Id: I1d2f1cb176a3507a96ff0b833e3f0248e8ed8c07
Signed-off-by: hyunho <hhstark.kang@samsung.com>
packaging/libwatchface-complication.spec
watchface-complication/CMakeLists.txt
watchface-complication/complication-implementation.h
watchface-complication/complication.cc
watchface-complication/complication.h
watchface-complication/include/watchface-complication.h
watchface-complication/watchface-complication.cc

index 9d51b3c..a51497d 100644 (file)
@@ -19,6 +19,7 @@ BuildRequires: pkgconfig(sqlite3)
 BuildRequires: pkgconfig(libxml-2.0)
 BuildRequires: pkgconfig(gmock)
 BuildRequires: pkgconfig(appsvc)
+BuildRequires: pkgconfig(capi-appfw-app-common)
 
 %description
 API for creating a new watchface complication and managing it.
index d7d4b5a..6c70cb3 100644 (file)
@@ -17,6 +17,7 @@ pkg_check_modules(watchface-complication REQUIRED
        dlog
        aul
        appsvc
+       capi-appfw-app-common
 )
 
 FOREACH(flag ${watchface-complication_CFLAGS})
index 68fb345..d4f25f3 100644 (file)
 
 #include <gio/gio.h>
 
+#include <sqlite3.h>
 #include <memory>
 #include <string>
 #include <list>
+#include <dlog.h>
+#include <app_common.h>
 
 #include "watchface-complication/complication.h"
 #include "watchface-complication/complication-connector.h"
 
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+
+#define LOG_TAG "WATCHFACE_COMPLICATION"
+
 namespace watchface_complication {
 
 class Complication::Impl : ComplicationConnector::IEventListener {
@@ -35,7 +44,10 @@ class Complication::Impl : ComplicationConnector::IEventListener {
                 const std::string& interface_name,
                 const std::string& signal_name,
                 GVariant* parameters) override;
-  virtual ~Impl() = default;
+  virtual ~Impl() {
+    if (setting_db_ != NULL)
+      sqlite3_close(setting_db_);
+  }
 
  private:
   friend class Complication;
@@ -44,6 +56,28 @@ class Complication::Impl : ComplicationConnector::IEventListener {
        const std::string& default_provider_id,
        ComplicationType default_type,
        std::shared_ptr<IEditable::Geometry> geo);
+  int FindCandidateDataIdx(std::string provider_id, int type);
+  void RestoreStateOrSetDefault();
+  void StoreSetting(int comp_id, char* provider_id, int type);
+  std::unique_ptr<Bundle> LoadSetting();
+  void OpenSettingDataDB();
+
+  static std::string GetSettingDataPath() {
+    char* app_data_path = app_get_data_path();
+    char setting_data_path[PATH_MAX] = {0, };
+
+    if (app_data_path == NULL) {
+      LOGE("fail to get app data path");
+      return nullptr;
+    }
+
+    snprintf(setting_data_path, PATH_MAX, "%s.watchface_setting.db",
+        app_data_path);
+    LOGI("get setting data path %s", setting_data_path);
+    free(app_data_path);
+
+    return std::string(setting_data_path);
+  }
 
  private:
   Complication* parent_;
@@ -60,6 +94,9 @@ class Complication::Impl : ComplicationConnector::IEventListener {
   EditableShapeType shape_type_;
   std::string name_;
   int subscribe_id_;
+  sqlite3* setting_db_ = NULL;
+  static const std::string provider_id_key_;
+  static const std::string provider_type_key_;
 };
 
 }  // namespace watchface_complication
index 85f02d2..9360a1d 100644 (file)
 
 #define LOG_TAG "WATCHFACE_COMPLICATION"
 #define MAX_PACKAGE_STR_SIZE 512
+#define QUERY_MAXLEN   4096
 
 namespace watchface_complication {
 
+const std::string Complication::Impl::provider_id_key_ = "__PROVIDER_ID_KEY__";
+const std::string Complication::Impl::provider_type_key_ = "__PROVIDER_TYPE_KEY__";
 Complication::Complication(int id, int support_types,
                            const std::string& default_provider_id,
                            ComplicationType default_type,
@@ -51,25 +54,21 @@ Complication::Impl::Impl(Complication* parent, int id,
   : parent_(parent), complication_id_(id), support_types_(support_types),
     default_provider_id_(default_provider_id), default_type_(default_type),
     geo_(geo) {
-  // TODO(?): Make candidates using support_types
+  // TODO(?): Make candidates using DB (support_types)
   for (int type = ShortText; type <= Image; type *= 2 ) {
     if ((type & support_types_) == 0)
       continue;
     std::list<std::string> provider_list =
-                                           parent_->GetProviderList(static_cast<ComplicationType>(type));
+        parent_->GetProviderList(static_cast<ComplicationType>(type));
     for (auto& provider_id : provider_list) {
       bundle* data = bundle_create();
-      bundle_add_str(data, "PROVIDER_ID", provider_id.c_str());
-      bundle_add_str(data, "PROVIDER_SUPPORT_TYPE", std::to_string(type).c_str());
+      bundle_add_str(data, provider_id_key_.c_str(), provider_id.c_str());
+      bundle_add_str(data, provider_type_key_.c_str(), std::to_string(type).c_str());
       candidates_list_.emplace_back(new Bundle(data));
       bundle_free(data);
     }
   }
-
-  // TODO(?): Get previous provider id from storage
-  cur_provider_id_ = default_provider_id_;
-  cur_type_ = default_type_;
-
+  RestoreStateOrSetDefault();
   subscribe_id_ = ComplicationConnector::GetInst().SubscribeSignal(
                     ComplicationConnector::Complication, cur_provider_id_,
                     complication_id_, this);
@@ -95,6 +94,139 @@ void Complication::Impl::OnSignal(const std::string& sender_name,
   parent_->OnDataUpdated(std::string(provider_id), cur_type_, data);
 }
 
+int Complication::Impl::FindCandidateDataIdx(std::string provider_id, int type) {
+  int idx = 0;
+  int ret = -1;
+  for (auto& i : candidates_list_) {
+    char* temp_provider_id = NULL;
+    char* temp_type = NULL;
+    Bundle& data = *(i.get());
+    bundle_get_str(data.GetRaw(), provider_id_key_.c_str(), &temp_provider_id);
+    bundle_get_str(data.GetRaw(), provider_type_key_.c_str(), &temp_type);
+    if (temp_provider_id != NULL && temp_type != NULL) {
+      if (strcmp(temp_provider_id, provider_id.c_str()) == 0
+          && atoi(temp_type) == type) {
+        ret = idx;
+        LOGI("find cur idx %d", ret);
+        break;
+      }
+    }
+    idx++;
+  }
+  return ret;
+}
+
+void Complication::Impl::OpenSettingDataDB() {
+  int ret = SQLITE_OK;
+  int open_flags = (SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE);
+
+  if (setting_db_ != NULL) {
+    LOGI("setting db already exist");
+    return;
+  }
+  std::string table_command =
+    "CREATE TABLE IF NOT EXISTS complication_setting" \
+    "(complication_id INTEGER NOT NULL, setting_data TEXT NOT NULL, "\
+    "PRIMARY KEY(complication_id))";
+  std::string db_path = GetSettingDataPath();
+
+  ret = sqlite3_open_v2(db_path.c_str(), &setting_db_, open_flags, NULL);
+  if (ret != SQLITE_OK) {
+    LOGE("database creation failed with error: %d", ret);
+    return;
+  }
+  ret = sqlite3_exec(setting_db_, table_command.c_str(), NULL, NULL, NULL);
+  if (ret != SQLITE_OK)
+    LOGE("database table creation failed: %d", ret);
+}
+
+void Complication::Impl::RestoreStateOrSetDefault() {
+  char* prev_provider_id = NULL;
+  char* prev_provider_type = NULL;
+  std::unique_ptr<Bundle> setting_data = LoadSetting();
+  int ret;
+
+  if (setting_data == nullptr) {
+    cur_provider_id_ = default_provider_id_;
+    cur_type_ = default_type_;
+  } else {
+    bundle_get_str(setting_data.get()->GetRaw(), provider_id_key_.c_str(),
+        &prev_provider_id);
+    if (prev_provider_id != NULL)
+      cur_provider_id_ = std::string(prev_provider_id);
+    else
+      cur_provider_id_ = default_provider_id_;
+
+    bundle_get_str(setting_data.get()->GetRaw(), provider_type_key_.c_str(),
+        &prev_provider_type);
+    if (prev_provider_type != NULL)
+      cur_type_ = static_cast<ComplicationType>(atoi(prev_provider_type));
+    else
+      cur_type_ = default_type_;
+
+    LOGI("get setting from bundle %s, %s", prev_provider_id, prev_provider_type);
+  }
+
+  cur_data_idx_ = 0;
+  ret = FindCandidateDataIdx(cur_provider_id_, cur_type_);
+  if (ret != -1)
+    cur_data_idx_ = ret;
+}
+
+void Complication::Impl::StoreSetting(int comp_id, char* provider_id, int type) {
+  int r;
+  char* error = NULL;
+  char query[QUERY_MAXLEN];
+  bundle* setting_data = bundle_create();
+  bundle_raw* raw_data;
+  int raw_len;
+  char buf[32];
+
+  snprintf(buf, sizeof(buf), "%d", type);
+  bundle_add_str(setting_data, provider_id_key_.c_str(), provider_id);
+  bundle_add_str(setting_data, provider_type_key_.c_str(), buf);
+  bundle_encode(setting_data, &raw_data, &raw_len);
+  sqlite3_snprintf(QUERY_MAXLEN, query,
+      "INSERT OR REPLACE INTO complication_setting(complication_id, " \
+      "setting_data)" \
+      "VALUES (%d, %Q)",
+      comp_id, raw_data);
+  LOGI("complication setting insert sql : %s", query);
+  r = sqlite3_exec(setting_db_, query, NULL, NULL, &error);
+  if (r != SQLITE_OK) {
+    LOGE("sqlite3_exec error(query = %s, error = %s)", query, error);
+    sqlite3_free(error);
+  }
+}
+
+std::unique_ptr<Bundle> Complication::Impl::LoadSetting() {
+  char query[QUERY_MAXLEN];
+  std::unique_ptr<Bundle> setting_data = nullptr;
+  const char* raw_data;
+  sqlite3_stmt* stmt;
+  int ret;
+
+  OpenSettingDataDB();
+  sqlite3_snprintf(QUERY_MAXLEN, query,
+      "SELECT setting_data FROM complication_setting WHERE " \
+      "complication_id = %d ", complication_id_);
+  LOGI("complication setting select sql : %s", query);
+  ret = sqlite3_prepare_v2(setting_db_, query, strlen(query), &stmt, NULL);
+  if (ret != SQLITE_OK) {
+    LOGE("execute query = %s fail %s\n", query,        sqlite3_errmsg(setting_db_));
+    sqlite3_close_v2(setting_db_);
+    return nullptr;
+  }
+
+  if (sqlite3_step(stmt) == SQLITE_ROW) {
+    raw_data = reinterpret_cast<const char*>(sqlite3_column_text(stmt, 0));
+    setting_data = std::unique_ptr<Bundle>(new Bundle(std::string(raw_data)));
+  }
+  sqlite3_finalize(stmt);
+
+  return std::move(setting_data);
+}
+
 int Complication::GetId() {
   return impl_->complication_id_;
 }
@@ -147,13 +279,16 @@ int Complication::SetCurDataIdx(int cur_data_idx) {
   impl_->cur_data_idx_ = cur_data_idx;
 
   Bundle& data = GetCurData();
-  bundle_get_str(data.GetRaw(), "PROVIDER_ID", &provider_id);
-  bundle_get_str(data.GetRaw(), "PROVIDER_SUPPORT_TYPE", &type);
+  bundle_get_str(data.GetRaw(), impl_->provider_id_key_.c_str(), &provider_id);
+  bundle_get_str(data.GetRaw(), impl_->provider_type_key_.c_str(), &type);
   impl_->cur_provider_id_ = std::string(provider_id);
   impl_->cur_type_ = static_cast<ComplicationType>(atoi(type));
   LOGI("cur idx %d, cur provider %s, cur type %d", impl_->cur_data_idx_,
     impl_->cur_provider_id_.c_str(), impl_->cur_type_);
 
+  impl_->StoreSetting(impl_->complication_id_, provider_id, atoi(type));
+  LOGI("set %s, %d", provider_id, atoi(type));
+
   ComplicationConnector::GetInst().UnSubscribeSignal(impl_->subscribe_id_);
   impl_->subscribe_id_ = ComplicationConnector::GetInst().SubscribeSignal(
                     ComplicationConnector::Complication, impl_->cur_provider_id_,
@@ -163,6 +298,26 @@ int Complication::SetCurDataIdx(int cur_data_idx) {
   return 0;
 }
 
+const char* Complication::GetCurProviderId() {
+  char* provider_id = NULL;
+  Bundle& data = GetCurData();
+  bundle_get_str(data.GetRaw(), impl_->provider_id_key_.c_str(), &provider_id);
+
+  return provider_id;
+}
+
+int Complication::GetCurType() {
+  char* type;
+  int ret = -1;
+  Bundle& data = GetCurData();
+  bundle_get_str(data.GetRaw(), impl_->provider_type_key_.c_str(), &type);
+
+  if (type)
+    ret = atoi(type);
+
+  return ret;
+}
+
 const std::string& Complication::GetName() {
   return impl_->name_;
 }
@@ -226,4 +381,12 @@ void Complication::SendDataUpdateRequest() {
   LOGI("emit signal done");
 }
 
+const char* Complication::GetProviderIdKey() {
+  return Complication::Impl::provider_id_key_.c_str();
+}
+
+const char* Complication::GetProviderTypeKey() {
+  return Complication::Impl::provider_type_key_.c_str();
+}
+
 }  // namespace watchface_complication
index 61fa40e..08e53a5 100644 (file)
@@ -44,6 +44,8 @@ class EXPORT_API Complication : public IEditable
   Bundle& GetCurData() override;
   Bundle& GetNthData(int nth) override;
   int GetCurDataIdx() override;
+  const char* GetCurProviderId();
+  int GetCurType();
   const std::string& GetName() override;
   const IEditable::EditableShapeType GetShapeType() override;
   int SetCurData(Bundle* data) override;
@@ -60,6 +62,8 @@ class EXPORT_API Complication : public IEditable
   void SetShapeType(IEditable::EditableShapeType shape_type);
   std::list<std::string> GetProviderList(
                                          ComplicationType type);
+  static const char* GetProviderIdKey();
+  static const char* GetProviderTypeKey();
 
  private:
   class Impl;
index 72f8a1f..9775906 100644 (file)
@@ -45,8 +45,8 @@ typedef int (*on_complication_update)(int complication_id,
                                       const bundle *data,
                                       void *user_data);
 
-int complication_get_cur_provider(complication_h handle,
-    const char *cur_provider);
+int complication_get_cur_provider_id(complication_h handle,
+    const char **cur_provider);
 int complication_get_cur_type(complication_h handle,
     complication_type *cur_type);
 int complication_get_support_types(complication_h handle, int *support_types);
@@ -63,6 +63,10 @@ int complication_create(int complication_id, const char *default_provider_id,
                         complication_shape_type type,
                         complication_h *created_handle);
 int complication_destroy(complication_h handle);
+int complication_get_provider_id(const bundle *candidate,
+    const char **provider_id);
+int complication_get_type(const bundle *candidate,
+    complication_type *cur_type);
 
 #ifdef __cplusplus
 }
index 7d1a8f1..9477562 100644 (file)
@@ -146,6 +146,7 @@ extern "C" EXPORT_API int complication_create(int complication_id,
     const char *default_provider_id, complication_type default_type,
     int support_types, complication_shape_type shape_type,
     complication_h *created_handle) {
+  // TODO(?) check default value and type is valid
   auto ws = new WatchComplicationStub(
               complication_id,
               support_types,
@@ -167,3 +168,51 @@ extern "C" EXPORT_API int complication_destroy(complication_h handle) {
 
   return 0;
 }
+
+extern "C" EXPORT_API int complication_get_cur_provider_id(complication_h handle,
+    const char** cur_provider_id) {
+  auto sh = static_cast<SharedHandle<WatchComplicationStub>*>(handle);
+  auto ptr = SharedHandle<WatchComplicationStub>::Share(sh);
+  *cur_provider_id = ptr.get()->GetCurProviderId();
+
+  return 0;
+}
+
+extern "C" EXPORT_API int complication_get_cur_type(complication_h handle,
+    complication_type* cur_type) {
+  auto sh = static_cast<SharedHandle<WatchComplicationStub>*>(handle);
+  auto ptr = SharedHandle<WatchComplicationStub>::Share(sh);
+  int ret = ptr.get()->GetCurType();
+  if (ret != -1)
+    *cur_type = static_cast<complication_type>(ret);
+
+  return 0;
+}
+
+extern "C" EXPORT_API int complication_get_provider_id(const bundle* candidate,
+    const char** provider_id) {
+  char* val = NULL;
+
+  bundle_get_str(const_cast<bundle*>(candidate),
+      Complication::GetProviderIdKey(), &val);
+  if (val != NULL)
+    *provider_id = val;
+  else
+    return -1;
+
+  return 0;
+}
+
+extern "C" EXPORT_API int complication_get_type(const bundle* candidate,
+    complication_type* cur_type) {
+  char* val = NULL;
+
+  bundle_get_str(const_cast<bundle*>(candidate),
+      Complication::GetProviderTypeKey(), &val);
+  if (val != NULL)
+    *cur_type = static_cast<complication_type>(atoi(val));
+  else
+    return -1;
+
+  return 0;
+}
\ No newline at end of file