The Sync Adapter App sample demonstrates how you can add different sync requests to the sync manager to schedule data synchronization tasks.
The following figure illustrates the home screen of the application.
Figure: Sync Adapter App home screen view
The application opens with the main screen, which displays a list of Sync Adapter App features:
- To create an account, click Create an account.
- To set the sync in the accountless mode, click Set as accountless.
- To request all sync jobs with corresponding settings, click Start sync with settings.
- To get and remove all registered sync jobs, click Manage sync jobs.
The application uses the libaccounts-svc module to work with the accounts database and the elementary module to support the UI requirements.
Prerequisites
- Sync Adapter App's app ID must be org.tizen.syncadapterapp.ui, because the Sync Adapter Service App uses this ID as a fixed value when it communicates with the Sync Adapter App UI application. Otherwise, the application control communication does not operate properly.
In addition, the Sync Adapter App package name must be org.tizen.syncadapterapp (same as the Sync Adapter Service App).
- The Sync Adapter App and Sync Adapter Service App packages must be coupled during the build process. To couple the packages:
- On the Tizen IDE, right-click the Sync Adapter App project and select Properties.
- Select Project References and find Sync Adapter Service App.
- Select the Sync Adapter Service App check box and click OK.
In the Project Explorer view, a with the Sync Adapter App message appears next to the Sync Adapter Service App project name showing that you have coupled it successfully.
-
To ensure proper application execution, the following privileges must be set:
- http://tizen.org/privilege/account.read
- http://tizen.org/privilege/account.write
- http://tizen.org/privilege/alarm.set
- http://tizen.org/privilege/appmanager.launch
- http://tizen.org/privilege/calendar.read
- http://tizen.org/privilege/contact.read
Implementation
Application Initialization
To initialize the application:
- Launch the Sync Adapter Service App, which acts as a sync adapter to this UI application. The service application must be built and included along with the Sync Adapter App UI application in the package.
The SYNC_ADAPTER_SERVICE_APP_ID must be defined as the App ID of the service application which acts as a sync adapter. For more information, see Sync Adapter Service App Sample Overview.
#define SYNC_ADAPTER_SERVICE_APP_ID "org.tizen.syncadapterapp.service" static void create_sync_main_menu(appdata_s *ad) { app_control_h app_control; int ret = app_control_create(&app_control); if (ret != APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Creating app_control handle failed [%s]", get_error_message(ret)); return; } ret = app_control_set_app_id(app_control, SYNC_ADAPTER_SERVICE_APP_ID); if (ret != APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "Setting app id failed [%s]", get_error_message(ret)); return; } ret = app_control_send_launch_request(app_control, NULL, NULL); if (ret == APP_CONTROL_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "Launching sync service app successfully [%s]", SYNC_ADAPTER_SERVICE_APP_ID); else { dlog_print(DLOG_INFO, LOG_TAG, "Launching sync service app failed [%s]", get_error_message(ret)); return; } }
- If you click Start Sync on the Start sync with settings screen without selecting an account mode, the Sync Request API cannot deliver the request, and a popup text is shown at the bottom of the screen (on the left in the following figure).
You must first create an account or set up as accountless on the main screen. Afterwards, you can add sync jobs with the account mode you selected.
-
Click Create an account to create a dummy account and pass it to the Sync Manager API (the result is shown in the middle in the above figure). Enable sync support on the account.
static void create_account() { account_id = 10; int ret = account_create(&account); dlog_print(DLOG_INFO, LOG_TAG, "account_create = %d", ret); ret = account_set_user_name(account, "dummy_user"); dlog_print(DLOG_INFO, LOG_TAG, "account_set_user_name = %d", ret); ret = account_set_email_address(account, "dummy_user@syncadapterapp.com"); dlog_print(DLOG_INFO, LOG_TAG, "account_set_email_address = %d", ret); ret = account_set_capability(account, ACCOUNT_SUPPORTS_CAPABILITY_CALENDAR, ACCOUNT_CAPABILITY_ENABLED); dlog_print(DLOG_INFO, LOG_TAG, "account_set_capability = %d", ret); ret = account_set_capability(account, ACCOUNT_SUPPORTS_CAPABILITY_CONTACT, ACCOUNT_CAPABILITY_ENABLED); dlog_print(DLOG_INFO, LOG_TAG, "account_set_capability = %d", ret); ret = account_set_sync_support(account, ACCOUNT_SYNC_STATUS_IDLE); dlog_print(DLOG_INFO, LOG_TAG, "account_set_sync_support = %d", ret); ret = account_insert_to_db(account, &account_id); dlog_print(DLOG_INFO, LOG_TAG, "Account id is = %d", account_id); is_accountless = false; }
- Click Set as accountless to set up the sync manager without an account (the result is shown on the right in the above figure):
static void set_accountless() { int ret = ACCOUNT_ERROR_NONE; ret = account_delete_from_db_by_package_name(PKG_NAME); if (ret == ACCOUNT_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "account delete success"); if (account) { ret = account_destroy(account); if (ret == ACCOUNT_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "account handle is removed successfully"); } } account_id = -1; is_accountless = true; dlog_print(DLOG_INFO, LOG_TAG, "Dummy account is set again as = %d", account_id); }
-
On Demand Sync
To request an on demand sync:
Figure: On Demand Sync
- To perform an on demand sync, select On Demand Sync as the Sync types on the Start sync with settings screen. Click Start Sync to trigger the corresponding sync manager functions:
static void cb_add_on_demand_sync(void* pData, Evas_Object* pObj, void* pEvent_info) { viewdata_s* viewData = pData; bundle *sync_job_user_data = NULL; sync_job_user_data = bundle_create(); bundle_add_str(sync_job_user_data, "option", displayed_option); int ret = SYNC_ERROR_NONE; if (is_accountless) { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request accountless On Demand Sync"); ret = sync_manager_on_demand_sync_job(NULL, "OnDemand", sync_option, sync_job_user_data, &on_demand_sync_job_id); } else { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request On Demand Sync with an account"); account_query_account_by_user_name(query_account_cb, "dummy_user", NULL); account_h account; account_create(&account); account_query_account_by_account_id(account_id, &account); viewData->account_id = account_id; ret = sync_manager_on_demand_sync_job(account, "OnDemand", sync_option, sync_job_user_data, &on_demand_sync_job_id); account_destroy(account); } if (ret != SYNC_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "Sync manager failed with error code %d", ret); else dlog_print(DLOG_INFO, LOG_TAG, "sync manager added on demand sync id %d", on_demand_sync_job_id); }
- When the sync manager schedules a sync job for this request, the sync callbacks in the Sync Adapter Service App are invoked.
When the sync job is completed, the service application communicates the same information to the Sync Adapter App UI application using an application control. The UI application shows a popup to notify the user about the status.
static void app_control(app_control_h app_control, void *data) { char *operation; int ret = app_control_get_operation(app_control, &operation); if (ret != APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "failed to get operation"); return; } else dlog_print(DLOG_INFO, LOG_TAG, "get operation: [%s]", operation); dlog_print(DLOG_INFO, LOG_TAG, "Sync Job completed by sync-service"); if (strcmp(operation, "http://tizen.org/appcontrol/operation/default")) { Evas_Object *popup; Evas_Object *win = NF; popup = elm_popup_add(win); elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0); eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, eext_popup_back_cb, NULL); evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); if (operation && !strcmp(operation, "http://tizen.org/appcontrol/operation/on_demand_sync_complete")) { dlog_print(DLOG_INFO, LOG_TAG, "On Demand Sync is completed"); elm_object_text_set(popup, "On Demand Sync is completed"); } elm_popup_timeout_set(popup, TIME_FOR_POPUP); evas_object_show(popup); } return; }
Periodic Sync
To request a periodic sync:
Figure: Periodic Sync
- To perform a periodic sync, select Periodic Sync as the Sync types on the Start sync with settings screen. Click Start Sync to trigger the corresponding sync manager functions:
static void cb_add_periodic_sync(void* pData, Evas_Object* pObj, void* pEvent_info) { viewdata_s* viewData = pData; bundle *sync_job_user_data = NULL; sync_job_user_data = bundle_create(); bundle_add_str(sync_job_user_data, "interval", displayed_interval); int ret = SYNC_ERROR_NONE; if (is_accountless) { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request accountless Periodic Sync"); ret = sync_manager_add_periodic_sync_job(NULL, "Periodic", sync_interval, sync_option, sync_job_user_data, &periodic_sync_job_id); } else { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request Periodic Sync with an account"); account_query_account_by_user_name(query_account_cb, "dummy_user", NULL); account_h account; account_create(&account); account_query_account_by_account_id(account_id, &account); viewData->account_id = account_id; ret = sync_manager_add_periodic_sync_job(account, "Periodic", sync_interval, sync_option, sync_job_user_data, &periodic_sync_job_id); account_destroy(account); } if ((sync_option == SYNC_OPTION_NONE) | (sync_option == SYNC_OPTION_NO_RETRY)) { Evas_Object *popup; Evas_Object *win = NF; popup = elm_popup_add(win); elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0); eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, eext_popup_back_cb, NULL); evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); if (sync_interval == SYNC_PERIOD_INTERVAL_30MIN) elm_object_text_set(popup, "It is expected in 30mins"); elm_popup_timeout_set(popup, TIME_FOR_POPUP); evas_object_show(popup); } if (ret != SYNC_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "Sync manager failed with error code %d", ret); else dlog_print(DLOG_INFO, LOG_TAG, "sync manager added periodic sync id %d", periodic_sync_job_id); }
- When the sync manager schedules a sync job for this request, the sync callback functions in the Sync Adapter Service App are invoked periodically. When the job is completed, the service application communicates the same information to the Sync Adapter App UI application by using an application control. The UI application shows a popup to notify the user about the status.
static void app_control(app_control_h app_control, void *data) { char *operation; int ret = app_control_get_operation(app_control, &operation); if (ret != APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "failed to get operation"); return; } else dlog_print(DLOG_INFO, LOG_TAG, "get operation: [%s]", operation); dlog_print(DLOG_INFO, LOG_TAG, "Sync Job completed by sync-service"); if (strcmp(operation, "http://tizen.org/appcontrol/operation/default")) { Evas_Object *popup; Evas_Object *win = NF; popup = elm_popup_add(win); elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0); eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, eext_popup_back_cb, NULL); evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); if (operation && !strcmp(operation, "http://tizen.org/appcontrol/operation/periodic_sync_complete")) { dlog_print(DLOG_INFO, LOG_TAG, "Periodic Sync is completed"); elm_object_text_set(popup, "Periodic Sync is completed"); } elm_popup_timeout_set(popup, TIME_FOR_POPUP); evas_object_show(popup); } return; }
Data Change Sync
To request a data change sync:
Figure: Data Change Sync
- To perform a data change sync, select Data Change Sync as the Sync types on the Start sync with settings screen. Click Start Sync to trigger the corresponding sync manager functions:
static void cb_add_data_change_sync(void* pData, Evas_Object* pObj, void* pEvent_info) { viewdata_s* viewData = pData; int ret = SYNC_ERROR_NONE; int idx = 0; if (is_accountless) { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request accountless Data Change Sync"); for (idx = 0; idx < NUM_OF_CAPABILITY; idx++) { if (data_change_id[idx] == -1) { ret = sync_manager_add_data_change_sync_job(NULL, sync_capability, sync_option, NULL, &data_change_sync_job_id); data_change_id[idx] = data_change_sync_job_id; dlog_print(DLOG_INFO, LOG_TAG, "[accountless] restored data_change_id[%d] = %d", idx, data_change_id[idx]); break; } else { if (idx == NUM_OF_CAPABILITY-1) { dlog_print(DLOG_INFO, LOG_TAG, "data_change_id[idx] is full"); break; } continue; } } } else { dlog_print(DLOG_INFO, LOG_TAG, "Sync Adapter App: request Data Change Sync with an account"); account_query_account_by_user_name(query_account_cb, "dummy_user", NULL); account_h account; account_create(&account); account_query_account_by_account_id(account_id, &account); viewData->account_id = account_id; for (idx = 0; idx < NUM_OF_CAPABILITY; idx++) { if (data_change_id[idx] == -1) { ret = sync_manager_add_data_change_sync_job(account, sync_capability, sync_option, NULL, &data_change_sync_job_id); data_change_id[idx] = data_change_sync_job_id; dlog_print(DLOG_INFO, LOG_TAG, "[account] restored data_change_id[%d] = %d", idx, data_change_id[idx]); break; } else { if (idx == NUM_OF_CAPABILITY-1) { dlog_print(DLOG_INFO, LOG_TAG, "data_change_id[idx] is full"); break; } continue; } } account_destroy(account); } if (ret != SYNC_ERROR_NONE) dlog_print(DLOG_INFO, LOG_TAG, "Sync manager failed with error code %d", ret); else { if (data_change_sync_job_id != -1) dlog_print(DLOG_INFO, LOG_TAG, "sync manager added data change sync id %d", data_change_sync_job_id); } }
- The sync manager stores the data change sync request. Whenever there is a change in the corresponding database (selected through Capability types), the sync manager schedules a sync job for the data. Sync callback functions in Sync Adapter Service App are invoked accordingly.
When the upload of the sync job is completed, the service application communicates the same information to the Sync Adapter App UI application using an application control. The UI application shows a popup to notify the user about the status.
static void app_control(app_control_h app_control, void *data) { char *operation; int ret = app_control_get_operation(app_control, &operation); if (ret != APP_CONTROL_ERROR_NONE) { dlog_print(DLOG_INFO, LOG_TAG, "failed to get operation"); return; } else dlog_print(DLOG_INFO, LOG_TAG, "get operation: [%s]", operation); dlog_print(DLOG_INFO, LOG_TAG, "Sync Job completed by sync-service"); if (strcmp(operation, "http://tizen.org/appcontrol/operation/default")) { Evas_Object *popup; Evas_Object *win = NF; popup = elm_popup_add(win); elm_popup_align_set(popup, ELM_NOTIFY_ALIGN_FILL, 1.0); eext_object_event_callback_add(popup, EEXT_CALLBACK_BACK, eext_popup_back_cb, NULL); evas_object_size_hint_weight_set(popup, EVAS_HINT_EXPAND, EVAS_HINT_EXPAND); else if (operation && !strcmp(operation, "http://tizen.org/appcontrol/operation/data_change_sync_complete")) { dlog_print(DLOG_INFO, LOG_TAG, "Data Change Sync is completed"); elm_object_text_set(popup, "Data Change Sync is completed"); } elm_popup_timeout_set(popup, TIME_FOR_POPUP); evas_object_show(popup); } return; }
Getting All Sync Jobs
To query all sync jobs requested by the Sync Adapter App, click Manage sync jobs in the main screen.
Figure: Get all sync jobs
The corresponding sync manager functions are called:
bool sync_adapter_sample_foreach_sync_job_cb(account_h account, const char *sync_job_name, const char *sync_capability, int sync_job_id, bundle* sync_job_user_data, void *user_data) { char sync_job_info[MAX_SIZE]; memset(sync_job_info, 0, sizeof(sync_job_info)); char* value = NULL; if (sync_job_name) { if (!strcmp(sync_job_name, "OnDemand")) { bundle_get_str(sync_job_user_data, "option", &value); on_demand_sync_job_id = sync_job_id; sprintf(sync_job_info, "[%d] %s %s", sync_job_id, strdup(sync_job_name), strdup(value)); dlog_print(DLOG_INFO, LOG_TAG, "[%d] %s %s", sync_job_id, sync_job_name, displayed_option); } else if (!strcmp(sync_job_name, "Periodic")) { bundle_get_str(sync_job_user_data, "interval", &value); periodic_sync_job_id = sync_job_id; sprintf(sync_job_info, "[%d] %s %s", sync_job_id, strdup(sync_job_name), strdup(value)); dlog_print(DLOG_INFO, LOG_TAG, "[%d] %s %s", sync_job_id, sync_job_name, displayed_interval); } on_demand_sync_job_id = -1; } else if (sync_capability) { if (!strcmp(sync_capability, SYNC_SUPPORTS_CAPABILITY_CALENDAR)) sprintf(sync_job_info, "[%d] Data Change for %s", sync_job_id, strdup("Calendar")); dlog_print(DLOG_INFO, LOG_TAG, "[%d] DataChange", sync_job_id); } else if (cnt_sync_jobs == 0) return true; int idx, temp_idx = 0; for (idx = 0; idx < MAX_NUM; idx++) { if (list_of_sync_jobs[idx][0] == '\0') { remove_sync_job[idx] = sync_job_id; temp_idx = idx; break; } } strcpy(list_of_sync_jobs[temp_idx], sync_job_info); dlog_print(DLOG_INFO, LOG_TAG, "copy to list_of_sync_jobs[%d]: %s", temp_idx, list_of_sync_jobs[temp_idx]); cnt_sync_jobs++; return true; } void on_manage_sync_jobs_cb(void *data, Evas_Object *obj, void *event_info) { int ret = sync_manager_foreach_sync_job(sync_adapter_sample_foreach_sync_job_cb, NULL); for (idx = 0; idx < MAX_NUM; idx++) { if (list_of_sync_jobs[idx][0] != '\0') dlog_print(DLOG_INFO, LOG_TAG, "after copying to list_of_sync_jobs[%d]: %s", idx, list_of_sync_jobs[idx]); } itc = elm_genlist_item_class_new(); itc->item_style = "type1"; itc->func.content_get = get_content_registered_sync_jobs; itc->func.text_get = get_text_registered_sync_jobs; itc->func.del = gl_del_cb; n_items = cnt_sync_jobs; for (idx = 0; idx < n_items; idx++) { foreach_id = calloc(sizeof(list_item_data_s), 1); foreach_id->type = 6; foreach_id->index = idx; it = elm_genlist_item_append(genlist, itc, foreach_id, NULL, ELM_GENLIST_ITEM_TREE, NULL, nf); foreach_id->item = it; } evas_object_smart_callback_add(genlist, "selected", genlist_selected_cb, NULL); elm_genlist_item_class_free(itc); NF = nf; if (ret == SYNC_ERROR_NONE) evas_object_show(genlist); else dlog_print(DLOG_INFO, LOG_TAG, "Error %d", ret); }
Removing All Sync Jobs
To remove registered sync jobs requested by the Sync Adapter App, select the sync jobs to be removed on the Manage sync jobs screen and click Remove.
Figure: Removing sync jobs
The corresponding sync manager functions are called:
void on_remove_selected_sync_jobs_cb(void *data, Evas_Object *obj, void *event_info) { int idx, idx2; for (idx = 0; idx < cnt_sync_jobs; idx++) { if (list_of_remove_sync_job[idx]) { sync_manager_remove_sync_job(remove_sync_job[idx]); list_of_remove_sync_job[idx] = false; for (idx2 = 0; idx2 < NUM_OF_CAPABILITY; idx2++) { if (data_change_id[idx2] == remove_sync_job[idx]) { data_change_id[idx2] = -1; break; } } remove_sync_job[idx] = -1; } } on_demand_sync_job_id = -1; periodic_sync_job_id = -1; if (is_all_checked) { memset(data_change_id, -1, sizeof(data_change_id)); } Evas_Object *nf = data; elm_naviframe_item_pop(nf); on_manage_sync_jobs_cb(data, obj, event_info); }