In this lesson we'll look at the application management module, its main functions and data types, application launch types, and the ability to share application functionality with other applications.
Also in this lesson we will finish 4th demo application, which was started in the lesson 24, by adding the ability to run the selected application. And also we will show you how to implement on the tied to the watch mobile devices the launch of Samsung Galaxy Apps on the page with the target application.
The Application Control Module (App Control) is used to run one application from another.
Application management is also an opportunity to share the functionality of one application with other apps, using the necessary capabilities of the existing application to save time on its development.
For example, if you want to add to the application the option of telephone call to the required number, you can launch the device built-in application for calls. Don’t forget to indicate, which functionality you are interested in. The calling application can also get from the called application the result of the requested functionality execution if it is implemented in it.
In the application management module, there are two types of application launching: explicit and implicit. The launch type depends on whether the targeted application identifier is known or not.
Before considering the application launch types, let's look at the general functions and data types of application management.
#include <app_control.h>
Also note that this header file is included in "app.h" header file.
To use the functionality of this module in your application, add the following privilege to the manifest file.
"http://tizen.org/privilege/appmanager.launch"
The main type of this module is app_control_h. First of all, create an application management object by calling the following function:
int app_control_create(app_control_h *app_control);
The input parameter is a pointer to the application control object. The function returns code 0 in case of success, and a negative number in case of an error.
The following function is required for sending a request to the platform to run a customized application object:
int app_control_send_launch_request(app_control_h app_control,
app_control_reply_cb callback,
void *user_data);
The first parameter is a pointer to the application control object; the second parameter is pointer to the function for obtaining the result of the called application execution, if any; the third parameter is a pointer to the user data, passed to the reply function. The function returns code 0 in case of success, and digit other from 0 in case of an error.
In the end, delete the created application control object using the following function:
int app_control_destroy(app_control_h app_control);
The function accepts, as a parameter, the object to be deleted and returns code 0 in case of success, and digit other from 0 in case of an error.
Examples of using the above functions will be consider later in this lesson.
Now let's look at the types of application launching, the first one to consider is an explicit launching. This type involves running with an explicit indication of the application identifier that should be run from the application. In order to indicate, which application you are interested in, consider the following function:
int app_control_set_app_id(app_control_h app_control, const char *app_id);
The first parameter is the application control object, and the second is a pointer to the string, containing the application identifier. The function returns code 0 in case of success, and digit other from 0 in case of an error.
Now let's add to the demo application the ability to run the selected application using the functions considered above. First, add to the manifest file the privilege to use the functions for launching the applications.
"http://tizen.org/privilege/appmanager.launch"
To do this, open in the project the tizen-manifest.xml file.
Go to the Privileges tab.
Click "+" button.
In the opened dialog window, select the necessary privilege and click OK.
Save the file.
In this demo application there is no need to include the header file for the work with the application management module "app_control.h". As it was described above, this file is implicitly included in the header file "app.h", which is already included in the application. Now change the function of creating the rotary selector widget. In the data field of the Eext_Object_Item structure of the created element, add a pointer to the Application_Info structure that contains information about the application that will be processed after the click on the rotary selector element.
Also, register the handler for clicking the element event in the rotary selector widget, by passing the pointer to the UIData structure as the last parameter of the user data.
static void
_rotary_selector_create(UIData *ui)
{
...
Application_Info *app_info = NULL;
EINA_LIST_FOREACH(ui->list_package, iterator, app_info)
{
Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);
item->data = app_info;
...
}
evas_object_smart_callback_add(ui->rotary_selector,
"item,clicked",
_item_clicked_cb,
ui);
...
}
Implement the handler for clicking on the rotary selector element, by adding a call to the above mentioned functions for launching applications.
Now the handler looks like this:
static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
dlog_print(DLOG_INFO, LOG_TAG, " Item is pressed");
UIData *ui = (UIData *)data;
Eext_Object_Item *item = eext_rotary_selector_selected_item_get(ui->rotary_selector);
Application_Info *application_info = item->data;
app_control_h a_control;
app_control_create(&a_control);
app_control_set_app_id(a_control, application_info->app_id);
if (app_control_send_launch_request(a_control, NULL, NULL) != APP_CONTROL_ERROR_NONE)
dlog_print(DLOG_INFO, LOG_TAG, "Application launching error");
app_control_destroy(a_control);
dlog_print(DLOG_INFO, LOG_TAG, "Application ID: %s", application_info->app_id);
}
In this function, we get a pointer to the selected element of the rotary selector. Next through the data field of the element structure, you may get a pointer to the structure, containing information about the selected application. In the received information about the application, pay attention to the field, containing the application identifier. Next, create the application management object. Set the received application identifier, and execute the request to run this application and check the execution status. Run the application and run from the rotary selector widget, for example, the calendar and the phone applications.
Click on the selected item in the rotary selector widget, and after some time the selected application will run, the launch time depends on the application. We have looked at a simple type of launching an application, which you can execute by calling the function:
app_manager_open_app(const char *app_id)
This function was considered in the previous lesson, when we looked at the application manager module. In the most cases, it is necessary to call the application, with detailed refinement, in which part of the functionality you are interested. A list of functions, required for this, will be discussed later in this lesson. Full source code is available here WearLesson027
Implicit start means launching the application by the platform in accordance with the functionality you’ve requested, without using the specific identifier of the target application as we did above. To determine which application will be launched, the service, at the platform level, compares the requested operations (URI - unified or uniform resource identifier and MIME types, with all corresponding to these conditions applications, installed on the device). If only one application meets the requirements, then the application starts immediately. If there are several applications were found, the system will prompt user to select the application. If you are writing an application and want some functionality to be available to other applications, you have to configure the requesting operation and URI, MIME type in the application's manifest file.
Below there is a list of functions required to configure functionality for the application launching. To set the type of performed operation, call the following function:
int app_control_set_operation(app_control_h app_control, const char *operation);
The first parameter is the application management object; the second parameter is a pointer to the string, containing the operation to be performed. The function returns code 0 in case of success, and digit other from 0 in case of an error.
The header file "app_control.h" includes the list of operations that are supposed to be performed. Below the list of the most frequently used operations:
APP_CONTROL_OPERATION_DEFAULT – default operation
APP_CONTROL_OPERATION_VIEW – to view content
APP_CONTROL_OPERATION_PICK – to select content
APP_CONTROL_OPERATION_CALL – to make a phone call
APP_CONTROL_OPERATION_SEARCH – to search for content
APP_CONTROL_OPERATION_SEND_TEXT – to send text, such as a text message
If you specify APP_CONTROL_OPERATION_DEFAULT as the operation to be performed, you must explicitly specify, which application you are interested in, by calling the app_control_set_app_id() function, considered in the previous section.
To set URI data, use the following function:
int app_control_set_uri(app_control_h app_control, const char *uri);
The first parameter is an application management object; the second parameter is a pointer to a string containing URI data, for example, the path to the file. The function returns code 0 in case of success, and digit other from 0 in case of an error.
In order to explicitly set the MIME data type, use the function:
int app_control_set_mime(app_control_h app_control, const char *mime);
The first parameter is an application management object; the second parameter is a pointer to a string containing a MIME data type. The function returns code 0 in case of success, and digit other from 0 in case of an error.
Note that setting a MIME type is some kind of filter. For example, you want to select images from the Gallery, but you are interested only in the pictures having the extension .jpg. As it was mentioned above, in the most of launched applications, is an implemented option of returning the result of the requested functionality, for example, selecting a picture in the Gallery application.
Let's consider the basic functions necessary for processing the received result. For the beginning, look at the declaration of the handler function, which is specified in app_control_send_launch_request() as the second parameter.
typedef void (*app_control_reply_cb) (app_control_h request,
app_control_h reply,
app_control_result_e result,
void *user_data);
This handler is invoked, when the called application supposed to return the result of the requested functionality, if any.
The first parameter is the application control object that was sent from the application. The second parameter is the application management object that contains the response from the called application. The third parameter is the result code of the sent request. The fourth parameter is the user data.
Here is a list of possible values for the result codes of the called application:
APP_CONTROL_RESULT_SUCCEEDED – The operation is successful
APP_CONTROL_RESULT_FAILED – Operation error
APP_CONTROL_RESULT_CANCELED – The operation is canceled
After checking whether operation is successful, it is necessary to get the data from the application. For this use the second parameter of the reply of the considered handler.
Note that the reply always contains the concept of key-value pairs. There are two types how you can receive the response data: if the key names are known and if not.
If you do not know the names of the keys, then you need to call the following function to iterate all returned keys of the called application.
int app_control_foreach_extra_data(app_control_h app_control,
app_control_extra_data_cb callback,
void *user_data);
The first parameter is an application management object that contains the response from the called application. The second parameter is a pointer to the handler function that is launched to iterate each key. The third parameter is user data. The function returns code 0 in case of success, and digit other from 0 in case of an error.
Consider the declaration of the handler function:
typedef bool (*app_control_extra_data_cb)(app_control_h app_control,
const char *key,
void *user_data);
The first parameter is the application management object. The second parameter is a pointer to the line containing the selected key. The third parameter is a pointer to the user data. The handler must return a Boolean value - meaning to continue the iteration or to stop.
Get the key is half of the battle, now consider the functions that return values by the specified key. Values are always passed in the form of strings as a pointer to a character, and both a single value and an array of values can be passed.
To get one value by its key, call the following function:
int app_control_get_extra_data(app_control_h app_control, const char *key, char **value);
The first parameter is the application management object. The second parameter is a pointer to the string containing the key. The third parameter is a double pointer containing the data that will be filed inside the function. The function returns code 0 in case of success, and digit other from 0 in case of an error. Received data should be deleted. To get an array of values by its key, call the following function:
int app_control_get_extra_data_array(app_control_h app_control,
const char *key,
char ***value,
int *length);
The first parameter is the application management object. The second parameter is a pointer to the string containing the key. The third parameter is the address of the string array containing the data that will be filled in the function. The fourth parameter is a pointer to the received array size. The function returns code 0 in case of success, and digit other from 0 in case of an error. Received data required to be deleted.
If you know which key contains the response of the called application, you can immediately invoke functions to retrieve data by key, without calling the function of iterating all keys.
Now let’s go to the practical part. We will show you how to write a small example, demonstrating how to implicitly invoke the application, by using the above listed functions and the ability to handle the response of the requested functionality.
As a basis, take the template that you began to write in lesson 24, during considering the rotary selector widget. Add a few elements to the rotary selector, when you click on the first element, you'll run the application to select .png pictures only, and return the result in the form of the path to the selected picture; when you click on the second one, you will run the application to view the selected image. Note that the names and application identifiers are unknown, since everything is executed at the platform level.
At first, add the privilege to the manifest:
"http://tizen.org/privilege/appmanager.launch"
Add to the UIData structure a field containing the path to the selected image:
typedef struct _UIData {
...
char *image_path;
} UIData;
Create two global static Integer constants needed to identify, which element was pressed in the rotary selector:
static const int FIRST_ITEM = 1;
static const int SECOND_ITEM = 2;
Now make changes to the function of creating a rotary selector:
static void
_rotary_selector_create(UIData *ui)
{
ui->rotary_selector = eext_rotary_selector_add(ui->conform);
eext_rotary_object_event_activated_set(ui->rotary_selector, EINA_TRUE);
Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);
item->data = (void *)&FIRST_ITEM;
eext_rotary_selector_item_part_text_set(item,
"selector,main_text",
" Choice");
item = eext_rotary_selector_item_append(ui->rotary_selector);
item->data = (void *)&SECOND_ITEM;
eext_rotary_selector_item_part_text_set(item,
"selector,main_text",
"Review");
evas_object_smart_callback_add(ui->rotary_selector,
"item,clicked",
_item_clicked_cb,
ui);
elm_object_content_set(ui->conform, ui->rotary_selector);
evas_object_show(ui->rotary_selector);
}
In this function, you should create two elements, and assign value to the data field so that the element can be identified. Make changes to the handler by clicking on the widget element:
static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
dlog_print(DLOG_INFO, "lesson27", " Item is pressed");
app_control_h a_control;
UIData *ui = (UIData *)data;
Eext_Object_Item *item = eext_rotary_selector_selected_item_get(ui->rotary_selector);
int *item_data = item->data;
bool is_first_item = (*item_data == FIRST_ITEM);
const char *type_operation = is_first_item ? APP_CONTROL_OPERATION_PICK : APP_CONTROL_OPERATION_VIEW;
app_control_reply_cb reply_cb = is_first_item ? _pick_image_cb : NULL;
app_control_create(&a_control);
app_control_set_operation(a_control, type_operation);
if (!is_first_item)
app_control_set_uri(a_control, ui->image_path);
app_control_set_mime(a_control, "image/png");
if (app_control_send_launch_request(a_control, reply_cb, data) != APP_CONTROL_ERROR_NONE)
dlog_print(DLOG_INFO, "lesson27", " Application launch error ");
app_control_destroy(a_control);
}
Remove the _item_selected_cb() handler.
Everything is simple. First of all, you should take the current element of the widget and check which element was selected. Then depending on the type of the selected item, indicate different functionality for the launched application. If you chose the first element, it will indicate that you are interested in applications that are displaying a list of pictures, and in an application that allows you to select an image and return the result to our application, as well as you are interested only in .png format images. If you chose the second element, then it shows that you are interested in the application that displays the previously selected image, for this you indicate that you are interested in an application that can display images without editing and in the .png format. Let's write a handler to get the selected picture. Since you do not know the names of the keys, call the function (described above) to iterate all of them.
static void
_pick_image_cb(app_control_h request,
app_control_h reply,
app_control_result_e result,
void *user_data)
{
app_control_foreach_extra_data(reply, _app_control_extra_foreach_data_cb, user_data);
}
Now write the function handler to iterate the keys. In this function, select the data by the first key, which contains the absolute path to the selected image, then save the received data in the image_path variable of the UIData structure. Also, the absolute path of the selected image will be outputted to the log. To stop the iteration, return the result.
static bool
_app_control_extra_foreach_data_cb(app_control_h app_control,
const char *key,
void *user_data)
{
UIData *ui = (UIData *)user_data;
char **value = NULL;
int length = 0;
app_control_get_extra_data_array(app_control, key, &value, &length);
if (length > 0)
{
if (ui->image_path)
free(ui->image_path);
ui->image_path = strdup(value[0]);
dlog_print(DLOG_INFO, LOG_TAG, " Image path: %s", ui->image_path);
for (int i = 0; i < length; ++i)
{
free(value[i]);
}
free(value);
}
return false;
}
Run the application.
You can see two elements on the screen. When clicking on the first item you will run the application, in which you can select one picture from the list (note that on the watch to see the list of pictures, there is supposed to be at least one picture in your Gallery application). If you select the picture and click on the second element, an application will be launched, which displays the selected image in full screen. Run the log, and if you select a picture in the launched application, then the absolute path of this picture will be displayed in the log.
Image path: /opt/usr/media/Images/12.png
Image path: /opt/usr/media/Images/18.png
The full code of this example is available here WearLesson027
In this section, we will show you how to continue developing your demo application, on which we stopped in section three of this lesson. We will consider how via watch launch on your phone Samsung Galaxy Apps, with a link to the application. For what you may need such option? For example, you have an application that may depend on one of your applications, but the second application is not installed on the watch, so you can offer to download this application by opening an application store with a link to required application. That is very convenient. Or you want to invite user to evaluate your application in the store, and immediately opening necessary page.
But before begin to write functionality for the demo application, you should consider several functions.
In order to pass parameters to the called application as a key-value pair, call the following function:
int app_control_add_extra_data(app_control_h app_control,
const char *key,
const char *value);
The first parameter is the application management object. The second one is the pointer to the string, containing the key. The third parameter is the pointer to the string containing the value. The function returns code 0 in case of success, and digit other from 0 in case of an error.
In addition, let’s consider the function for passing an array of values by one key:
int app_control_add_extra_data_array(app_control_h app_control,
const char *key,
const char* value[],
int length);
It is similar to the function for passing one parameter, except that instead of a pointer to a string, there is array with pointers to strings with values. Don’t forget to indicate the size of the passed array
After considering necessary functions, let’s start the practice by completing the demo application.
Add the following functionality to the application, for example you have a Calculator application in the list, but it's not installed on the watch. Set the function, which on the click on the element of the rotary selector widget will check whether the application is installed or not and in case it is not, will run on the phone store with the link to the Calculator application.
Let's change the structure of Application_Info by adding a field containing the package ID.
typedef struct _Application_Info {
...
char *pkg_id;
} Application_Info;
Add two global static constants, containing the application identifier and the identifier of the calculator package.
static const char *calculator_pkg_id = "com.samsung.d-calculator-wc1";
static const char *calculator_app_id = "com.samsung.d-calculator-wc1";
These functions are necessary to check whether the Calculator application is installed on the device. For example, select any application you are interested in. For this you should know the application ID and package ID. You’ve previously defined the identifiers for the Calculator by iterating all installed packages. Change the function for iterating application handler, by adding options of saving package identifier.
static bool
_app_manager_app_info_cb(app_info_h app_info, void *user_data)
{
...
app_info_get_package(app_info, &(application_info->pkg_id));
dlog_print(DLOG_INFO, LOG_TAG, " Package ID: %s", application_info->pkg_id);
...
return true;
}
Now in the last handler, clear the memory:
static void
_app_terminate_cb(void *data)
{
...
EINA_LIST_FREE(ui->list_package, app_info)
{
FREE_RESOURCE(app_info->label);
FREE_RESOURCE(app_info->path_icon);
FREE_RESOURCE(app_info->app_id);
FREE_RESOURCE(app_info->pkg_id);
FREE_RESOURCE(app_info);
}
}
Let's rewrite the function of adding the rotary selector widget. Add a checking whether the Calculator application is installed on the watch, and if not, add an empty element to the rotary selector.
static void
_rotary_selector_create(UIData *ui)
{
bool is_find_calculator_app = false;
...
EINA_LIST_FOREACH(ui->list_package, iterator, app_info)
{
if (!strcmp(calculator_app_id, app_info->app_id))
is_find_calculator_app = true;
...
}
if (!is_find_calculator_app)
{
Application_Info *calculator_info = calloc(1, sizeof(Application_Info));
if (calculator_info)
{
memset(calculator_info, 0x0, sizeof(Application_Info));
calculator_info->app_id = strdup(calculator_app_id);
calculator_info->pkg_id = strdup(calculator_pkg_id);
calculator_info->label = strdup("Calculator");
ui->list_package = eina_list_append(ui->list_package, calculator_info);
Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);
item->data = calculator_info;
eext_rotary_selector_item_part_text_set(item,
"selector,main_text",
calculator_info->label);
}
}
...
}
Make changes to the handler of clicking on the widget element, here you will check whether the application is installed. If the application is not installed, call the function to configure and launch the application store on the phone.
static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
...
if (app_control_send_launch_request(a_control, NULL, NULL) == APP_CONTROL_ERROR_APP_NOT_FOUND)
{
_gear_samsung_app_launch(application_info->app_id);
}
...
}
Write the implementation of the function that will run the store with the specified application:
static void
_gear_samsung_app_launch(const char *pkg_id)
{
app_control_h a_control;
char appl_url_link[PATH_MAX] = {'\0'};
const char *samsung_url = "samsungapps://ProductDetail/";
snprintf(appl_url_link,
strlen(samsung_url) + strlen(pkg_id) + 1,
"%s%s",
samsung_url,
pkg_id);
app_control_create(&a_control);
app_control_set_operation(a_control, APP_CONTROL_OPERATION_DEFAULT);
app_control_set_app_id(a_control, "com.samsung.w-manager-service");
app_control_add_extra_data(a_control, "type", "gear");
app_control_add_extra_data(a_control, "deeplink", appl_url_link);
app_control_send_launch_request(a_control, NULL, NULL);
app_control_destroy(a_control);
}
In this function, you should pay attention to the application identifier, installed on the watch, it launches the market on the phone, while indicating that you are interested only in the gear applications. url link to your application in the market is very important as well. For the Calculator, the following link will be obtained by gluing together two lines: "samsungapps: //ProductDetail/com.samsung.d-calculator-wc1".
Edit the handler of hardware Back button:
static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
ui_app_exit();
}
Run the application.
If there is no Calculator application, then you’ll see an empty element at the end of the round selector. If you click on it, the store app will run on the page with a Calculator and this application will be installed. If we install the Calculator, after the application restart - in the list, Calculator icon will appear instead of empty element. Click on it to launch the Calculator application.
The demo # 4 is completed.
Full source code is available here WearLesson027
It is very important system handler. It accepts the information in the form of an application management object. If this object is empty, it means that the application was launched by the user from the system launcher. If it is not empty then this application was started by another application. It has following signature:
app_control_cb app_control;
Add the handler:
typedef void (*app_control_cb) (app_control_h app_control, void *user_data);
As you can see, this handler differs from the others by one parameter, namely by an application management object, which contains additional information in case the application was started by another application. This handler is installed in the ui_app_lifecycle_callback_s structure, which specifies the handlers for creating, deleting the application, etc. Let's describe the life cycle of this handler. If the application is launched for the first time, then at the beginning the application creation handler is called, and only then the application management handler. If the application is running and we open it by another application, then the handler to resume the application is called, and only after that the application management handler. It is very important if the application supposed to have a different view depending on who launches it and with what conditions, for example the gallery starts with the choice of images or with the displaying only one of them.