Опубликовано: 25 Октября 2017
(Демо приложение №4)
Модуль App Control. Запуск сторонних приложений

В этом уроке мы рассмотрим модуль управления приложениями, его основные функции и типы данных, виды запуска приложений, а также возможностью делиться функциональностью приложения с другими приложениями.

Также в этом уроке мы завершим наше 4-е демо-приложение, которое мы начали в 24-м уроке, добавив в него возможность запуска выбраного приложения. А также реализуем запуск Samsung Galaxy Apps на мобильном устройстве привязанном к часам на странице с интересующимся нас приложением.

Краткое описание модуля

Модуль управления приложениями (App Control) используется для запуска одного приложения из другого.

Управление приложением - это также возможность делиться функциональностью одного приложения с другим, используя необходимые возможности существующего приложения для экономии времени на ее разработку.

Приведем пример, мы хотим в наше приложение добавить возможность телефоного вызова необходимого номера, для этого мы можем запустить встроенное приложение устройства для звонков, с указанием какая функциональность нас интересует. Вызывающее приложение также может получить результат выполнения запрашиваемой функциональности от вызываемого приложения если таковое в нем реализовано.

В модуле управления приложением реализовано два вида запуска приложений: явное и неявное. При этом вид запуска зависит от того известен ли идентификатор приложения которое вы хотите запустить.

Перед тем как рассмотреть виды запуска приложений, рассмотрим общие функции и типы данных управления приложением.

Основные функции и типы данных App Control

Для использования функций и типов данных модуля управления приложениями, необходимо включить в проект следующий заголовочный файл

#include <app_control.h>

Также следует заметить что данный заголовочный файл включен в заголовочный файл "app.h".

Для использования в своем приложении функциональности данного модуля, необходимо добавить следующую привилегию в манифест файл.

http://tizen.org/privilege/appmanager.launch

Основным типом рассмотриваемого модуля является app_control_h.

В первую очередь необходимо создать обьект управления приложением, для этого необходимо вызвать следующую функцию

int app_control_create(app_control_h *app_control);

Входным параметром является указатель на обьект управления приложением. Функция возвращает код 0 в случае успешного выполнения, и отрицательное число в случае ошибки.

Следующая функция необходима для отправки запроса к платформе на запуск настроеного обьекта приложения

int app_control_send_launch_request(app_control_h app_control,
                                    app_control_reply_cb callback,
                                    void *user_data);

Первый параметр это указатель на обьект управления приложением, второй параметр это указатель на функцию для получения результата выполнения вызываемого приложения, если таковое имеет место, и третий параметр это указатель на данные пользователя передываемые в функцию переданную во втором параметре. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Ну и в конце необходимо удалять созданный обьект управления приложений, используя следующую функцию

int app_control_destroy(app_control_h app_control);

Функция принимает в качестве параметра удаляемый обьект и возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Примеры использования выше перечисленных функций, будут рассмотренны ниже в этом уроке.

Явный запуск приложения

Теперь перейдем к рассмотрению видов запуска приложения, первым рассмотрим явный запуск. Этот вид подразумевает запуск с явным указанием идентификатора приложения которое мы хотим запустить из своего приложения.

Для того чтобы указать какое приложение нас интересует рассмотрим следующую фукцию

int app_control_set_app_id(app_control_h app_control, const char *app_id);

Первый параметр это обьект управления приложением, а второй это указатель на строку содержащую идентификатор приложения. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Теперь давайте добавим в наше демо-приложение возможность запуска выбраного приложения применив рассмореные выше функции.

Для начала нам необходимо добавить привилегию на использование функций для запуска приложений в манифест файл.

http://tizen.org/privilege/appmanager.launch

Для этого открываем файл tizen-mainfest.xml проекта.

Переходим во вкладку Privileges.

nw_027_01_en

Нажимаем кнопку "+".

nw_027_02_en

В открывашемся диалоговом окне выбираем необходимую привилегию и нажимаем OK.

Сохраняем файл.

Нам нет необходимости в нашем демо приложении подключать заголовочный файл для работы с модулем управление приложением "app_control.h", как было описано выше он неявно включен в заголовочный файл "app.h", который уже включен в наше приложение.

Теперь внесем изменение в функцию создания виджета rotary selector. Добавим в поле data структуры Eext_Object_Item созданого элемента указатель на структуру Application_Info, содержащую информацию о приложении, которую мы будем обрабатывать при нажатии на элемент rotary selector.

Также зарегистрируем обработчик на нажатия элемента в виджете rotary selector, передав в последнем параметре в качестве данных пользователя указатель на структуру UIData.

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);

   ...
}

Ну и реализуем сам обработчик нажатия на элементе rotary selector, добавив вызов рассмотреных выше функций для запуска приложений.

Теперь обработчик выглядит следующим образом.

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, LOG_TAG, "Элемент нажат");

   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, "Ошибка запуска приложения");

   app_control_destroy(a_control);

   dlog_print(DLOG_INFO, LOG_TAG, "ID приложения: %s", application_info->app_id);
}

В этой функции мы получаем указатель на выделенный элемент rotary selector. Далее получаем через поле data структуры элемента, указатель на структуру содержащей информацию о выбраном приложении. В полученой информации о приложении нас интересует поле содержащее идентификатор приложения. Дальше мы создаем обьект управления приложениями, задаем полученный идентификатор приложения, и выполняем запрос на запуск этого приложения и проверям статус выполнения.

Запустим приложение и запустим с виджета rotary selector например, приложение календарь и телефон.

Теперь если мы в виджете rotary selector нажмем на выбраном элементе, то у нас через некоторое время запуститься выбраное приложение, время запуска зависит от приложения.

Тут стоит заметить что мы рассмотрели простой вид запуска приложения, выполнить который можно вызвав функцию

app_manager_open_app(const char *app_id)

Функция была рассмотрена в предыдущем уроке, когда мы рассматривали модуль менеджера приложений. В большенстве случаев, необходимо вызвать приложения указав более детально какая часть функциональности нас интересует. Перечень функций необходимый для этого будет рассмотрен дальше в этом уроке.

Полный исходный код вы можете скачать здесь WearLesson027.

Неявный запуск приложения (Теория)

Неявный запуск, означает запуск приложения платформой в соответсвии от запрашиваемого вами функциональности, не используя конкретный идентификатор целевого приложения как мы делали выше.

Чтобы опредилить какое приложение будет запущено, сервис на уровне платформы сравнивает запрашиваемую операцию, URI (унифицированный или единообразный идентификатор ресурса), MIME типы, со всеми приложениями на устройстве соответсвующие этим условиям. Если только одно приложение соотвествует запрашиваемым условиям, то приложение сразу же запускается, если же найдено несколько приложений, то система предложит пользователю выбрать приложение.

Если мы пишем приложение, и хотим чтобы часть функциональности была доступна другим приложениям, то нам необходимо настроить запрашиваимую операцию, URI, MIME тип в манифест файле приложения.

Ниже приведем перечень функций необходимых для настройки функциональности запуска приложения.

Для установки типа выполняемой операции, необходимо вызвать следующую функцию

int app_control_set_operation(app_control_h app_control, const char *operation);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую выполняемую операцию. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

В заголовочном файле "app_control.h" указано перечень выполняемых операций, ниже перечислим наиболее часто использумые

APP_CONTROL_OPERATION_DEFAULT – операция по умолчанию
APP_CONTROL_OPERATION_VIEW – для просмотра контента
APP_CONTROL_OPERATION_PICK – для выбора контента
APP_CONTROL_OPERATION_CALL – для совершения вызова, к примеру телефоного номера
APP_CONTROL_OPERATION_SEARCH – для поиска содержимого
APP_CONTROL_OPERATION_SEND_TEXT – для отправки текста, например текстового сообщения

Если мы указываем в качестве выполняемой операции APP_CONTROL_OPERATION_DEFAULT, то необходимо явно указать какое приложение нас интересует вызвав функцию app_control_set_app_id() рассмотренную в предыдущем разделе.

Чтобы установить URI данные, рассмотрим следующую функцию

int app_control_set_uri(app_control_h app_control, const char *uri);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую URI данные, например путь к файлу. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Для того чтобы установить явно MIME тип данных, необходимо использовать следующую функцию

int app_control_set_mime(app_control_h app_control, const char *mime);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую MIME тип данных. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Стоит заметить, что установка MIME типа, это некий фильтр, например мы хотим выбрать картинки из галереи, но нас интересуют только картинки имеющие расширение .jpg.

Как было указано выше в большинстве запускаемых приложений реализована функциональность возврат результата на выполнение запрашиваемой функциональности, например, выбор картинки в приложении галерея.

Рассмотрим основные функции необходимые для обработки полученного результата. Для начала рассмотрим обьявление функции обработчика, которое указывается в функции app_control_send_launch_request() вторым параметром.

typedef void (*app_control_reply_cb) (app_control_h request,
                                      app_control_h reply,
                                      app_control_result_e result,
                                      void *user_data);

Этот обработчик вызывается, когда вызываемое приложение должно вернуть результат работы запрашиваемой функциональности, если таковое имет место.

Первый параметр - это обьект управление приложения, которое было отправлено из нашего приложения, второй параметр - это обьект управление приложения, который содержит ответ от вызываемого приложения, третий параметр - это код результата запускаемого запроса, четвертый параметр - это данные пользователя.

Приведем перечень возможных значений кодов результата вызываемого приложения.

APP_CONTROL_RESULT_SUCCEEDED – операция выполнена успешно
APP_CONTROL_RESULT_FAILED – ошибка выполнения операции
APP_CONTROL_RESULT_CANCELED – выполнение операции было отменено

После проверки на успешность выполнения операции, необходимо получить сами данные от приложения, для этого необходимо использовать второй параметр reply рассмотреного обработчика.

Необходимо отметить, что ответ всегда содержит понятие пары ключ – значение. Есть два вида получения данных ответа, это случай если нам известны название ключей и случай если неизвестны.

Если мы не знаем название ключей ответа, то для этого необходимо вызвать следующую функцию для выборки по всем возвращаемым ключам вызываемого приложения.

int app_control_foreach_extra_data(app_control_h app_control,
                                   app_control_extra_data_cb callback,
                                   void *user_data);

Первый параметр - это обьект управления приложениями, который содержит в себе ответ от вызываемого приложения, второй параметр - это указатель на функцию обработчик, запускаемую для выборки каждого ключа, третий параметр - это пользовательськие данные. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки.

Рассмотрим обьявление функции обработчика

typedef bool (*app_control_extra_data_cb)(app_control_h app_control,
                                          const char *key,
                                          void *user_data);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую выбранный ключ, и третий параметр - указатель на пользовательские данные. Обработчик должен возвращать булевый признак, означающий продолжить выборку или остановить.

Получить ключ это пол дела, теперь рассмотрим функции которые возвращают значения по указаному ключу. Значения всегда передаются в виде строк как указатель на символ, и могут передаваться как одно значение, так и массив значений.

Для получение одного значения по ключу, необходимо вызывать следующую функцию

int app_control_get_extra_data(app_control_h app_control, const char *key, char **value);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую ключ, третий параметр это двойной указатель содержащий данные которые будет заполнены внутри функции. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки. Необходимо обязатеьно удалять полученные данные.

Для получение массива значений по ключу, необходимо вызывать следующую функцию

int app_control_get_extra_data_array(app_control_h app_control,
                                     const char *key,
                                     char ***value,
                                     int *length);

Первый параметр - это обьект управления приложениями, второй параметр - это указатель на строку содержащую ключ, третий параметр - это адресс массива строк содержащий данные которые будет заполнены внутри функции, четвертый параметр - это указатель на размер полученного массива. Функция возвращает код 0 в случае успешного выполнения, и ненулевое число в случае ошибки. Необходимо удалять полученные данные после завершения работы с ними.

Если вы знаете по какому ключу содержится ответ вызываемого приложения, то можно сразу вызывать функции для получения данных по ключу, без вызова функции выборки всех ключей.

Неявный запуск приложения (Практика)

Давайте теперь перейдем к практике, напишем небольшой пример демонстрирующую работу неявного вызова приложений, используя для этого выше перечисленные функции и возможность обработки ответа на запрашиваемую функциональность.

За основу возьмем шаблон который мы начали в 24-м уроке, когда рассматривали виджет rotary selector. Добавим несколько элементов в rotary selector, при нажатии на первый элемент будем запускать приложение для выбора только png картинок, и возврат результата ввиде пути на выбраную картинку, при нажатии на второй будем запускать приложение для просмотра выбранной картинки. Стоит заметить что названия и индитификаторы приложений нам не известны, все выполняется на уровне платформы.

Сразу добавим привилегию в манифест.

http://tizen.org/privilege/appmanager.launch

Добавим в структуру UIData поле содержащую путь к выбранной картинке

typedef struct _UIData {
   ...

   char *image_path;
} UIData;

Создадим две глобальные целочисленные статические константы, необходимые для того чтобы идентифицировать какой элемент был нажат в rotary selector.

static const int FIRST_ITEM  = 1;
static const int SECOND_ITEM = 2;

Теперь внесем изменения в функцию создания 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",
                                           "Выбор");

   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",
                                           "Просмотр");

   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);
}

В этой функции мы создаем два элемента, и полю data присваиваем значения, чтобы была возможность идентификации элемента.

Внесем измения в обработчик по нажатию на элемент виджета

static void
_item_clicked_cb(void *data, Evas_Object *obj, void *event_info)
{
   dlog_print(DLOG_INFO, "lesson27", "Элемент нажат");

   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", "Ошибка запуска приложения");

   app_control_destroy(a_control);
}

Сразу же удалим обработчик _item_selected_cb().

Тут все очень просто, сначало мы берем текущий элемент виджета, и проверяем какой элемент был выбран, и в зависимости от типа выбраного элемента указываем разную функциональность запускаемого приложения.

Если мы выбрали первый элемент, то нас интересует приложения которое будет отображать список картинок, при этом мы указываем что нас интересует приложение которое позволяет выбрать картинку и вернуть результат выбора в наше приложение, и указываем что нас интересуют только картинки png формата.

Если мы выбрали второй элемент то нас интересует приложение которое отобразит выбранную ранее картинку, для этого мы указываем что нас интересует приложение которое умеет отображать для просмотра картинки без возможности редактирования и в формате png.

Давайте напишем обработчик для получения выбраной картинки. Т.к. нам неизвестны название ключей, то вызовем описанную выше функцию для выборки всех ключей.

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);
}

Теперь напишем функцию-обработчик выборки ключей. В ней мы выбираем данные по первому ключу, содержащий абсолютный путь выбраной картинки, дальше сохраняем полученные данные в переменной image_path структуры UIData. Также абсолютный путь выбраной картинки мы выведем в лог. И возвращаем результат для прекращения выборки.

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, "Путь к изображению: %s", ui->image_path);

        for (int i = 0; i < length; ++i)
          {
             free(value[i]);
          }

        free(value);
     }

   return false;
}

И наконец запускаем приложение.

На экране мы видим два элемента. При нажатии на первый элемент у нас запустится приложение предлагающее выбрать одну картинку из списка картинок (тут стоит указать, чтобы увидеть список картинок необходимо чтобы ваше приложение галерея на часах содержала хотябы одну картинку), если мы выберем картинку и нажмем на втором элементе, то у нас запустится приложение которое на весь экран отобразит выбранную картинку.

Запустим лог, и если мы будем выбирать картинку в запускаемом приложении, то в лог будет выводится абсолютный путь этой картинки.

Путь к изображению: /opt/usr/media/Images/12.png 
Путь к изображению: /opt/usr/media/Images/18.png

Вы можете взять полный код данного примера здесь WearLesson027.

Запуск Samsung Galaxy Apps на телефоне

В этом разделе мы продолжим наше демо-приложение на котором остановились в разделе 3 этого урока, добавив в него очень интересную возможность запускать с часов Samsung Galaxy Apps приложение на телефоне, со ссылкой на приложение. Зачем это может быть нужно, ну представим такую картину, например, у вас есть приложение которое может зависеть от другого вашего приложения, но второе приложение не установленое на часах, для этого вы можете предложить скачать это приложение, открыв магазин приложений со ссылкой на ваше приложение, что есть очень удобным. Или же вы хотите предложить пользователю оценить ваше приложение в магазине, также сразу открыв ему нужную страницу.

Но перед тем как перейти к написанию функциональности в наше демо приложение, необходимо рассмотреть несколько функций.

Для того чтобы передать вызываемому приложению параметры в виде пары ключ значение, необходимо вызвать следующую функцию

int app_control_add_extra_data(app_control_h app_control,
                               const char *key,
                               const char *value);

Первый параметр - это обьект управления приложениями, второй - это указатель на строку содержащую ключ, третий параметр - это указатель на строку содержащую значение. Функция возвращает код 0 в случае успешного выполнения, и ненулевое значение в случае ошибки.

Ну и в дополнение к передаче одного значения, рассмотрим функцию для передачи массива значений по одному ключу

int app_control_add_extra_data_array(app_control_h app_control,
                                     const char *key,
                                     const char* value[],
                                     int length);

Она похожа на функцию для передачи одного параметра, за исключением того, что вместо указателя на строку передается массив с указателей на строки содержащие значения, с указанием размера передаваемого массива.

Рассмотрев необходимые функции, приступим к практике, завершив наше демо-приложение.

Давайте добавим в него следующую функциональность, представим что у нас в списке есть приложение "Калькулятор", но он не установлен на часы. Сделаем так чтобы при нажатии на элемент виджета rotary selector мы будем проверять установлено приложение или нет и если оно не установленно, то будем запускать магазин на телефоне со ссылкой на приложение "Калькулятор".

Давайте немного изменим структуру Application_Info добавив в него поле содержащее идентификатор пакета.

typedef struct _Application_Info {
   ...

   char *pkg_id;
} Application_Info;

Добавим две глобальные статические константы содержащие идентификатор приложения и идентификатор пакета калькулятора.

static const char *calculator_pkg_id = "com.samsung.d-calculator-wc1";
static const char *calculator_app_id = "com.samsung.d-calculator-wc1";

Они нам необходимы для проверки установлено ли приложения "Калькулятор". Тут стоит заметить, что вы можете для примера выбрать любое интересующее вас приложение, но для этого вам необходимо знать идентификатор приложения и идентификатор пакета для вашего случая.

Мы предварительно определили идентификаторы для калькулятора, пробежавшись функцией по всем установленным пакетам.

Внесем изменение в функцию обработчика выборки установленных приложений, добавив сохранение индитификатора пакета.

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, "ID пакета: %s", application_info->pkg_id);

   ...

   return true;
}

Теперь в завершающем обработчике необходимо очистить память

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);
     }
}

Немного перепишем функцию добавление виджета rotary selector. В ней мы добавим проверку установлено ли на часах приложение "Калькулятор", и если нет мы добавим пустой элемент в 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("Калькулятор");

             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);
          }
     }

   ...
}

Теперь внесем изменения в обработчик по нажатию на элемент виджета, в нем мы будем проверять установлено ли приложение. Если приложение не установлено вызываем функцию для настройки и запуска магазина приложений на телефоне.

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);
     }

   ...
}

И напишем реализацию функции которая будет запускать магазин с указанным приложением.

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);
}

В этой функции стоит обратить внимание, на идентификатор приложения который установлен на часах, именно он и запускает маркет на телефоне, при этом указав, что нас интересует только gear приложения, и очень важна url ссылка на наше прилоежение в маркете, для калькулятора у нас должно получится следующая ссылка в результате склеивания двух строк "samsungapps://ProductDetail/com.samsung.d-calculator-wc1".

Отредактируем наш обработчик аппаратной кнопки "Назад".

static void
_win_back_cb(void *data, Evas_Object *obj, void *event_info)
{
   ui_app_exit();
}

Запустим приложение.

Если у нас не установлено приложение калькулятор, то мы увидим пустой элемент в конце круглого селектора. Если мы на него нажмем то на телефоне запуститься магазин на странице с калькулятором для установки его. Теперь если мы установим "Калькулятор", то перезапустив наше приложение мы увидим уже в списке иконку калькулятора вместо пустого элемента, и нажав на него у нас запустится именно приложение "Калькулятор".

На этом мы заканчиваем наше демо-приложение 4.

Полный исходный код вы можете скачать здесь WearLesson027.

Обработчик события App Control жизенного цикла

Стоит также упомянуть очень важный системный обработчик. В него передается информация в виде обьекта управления приложениями, если этот обьект пуст, значит приложение запустил пользователь с системного лаунчера, если он не пустой то значит ваше приложение было запущено другим приложением. Его сигнатура следующая

app_control_cb app_control;

Давайте приведем обьявление обработчика

typedef void (*app_control_cb) (app_control_h app_control, void *user_data);

Как видно этот обработчик отличается от остальных одним параметром, а иммено обьектом управления приложениями, в котором содержится дополнительная информация если приложение было запущено другим приложением .

Это обработчик устанавливается в струтуре ui_app_lifecycle_callback_s, в котором указываются обработчик на создание приложения, на удаление и т.д.

Давайте опишем жизненый цикл этого обработчика. Если приложение впервые запускается, то вызывается сначало обработчик создания приложение, а затем обработчик управление приложения. Если приложение запущенно и мы его открываем другим приложением, то вызываеся обработчик на возобновление работы приложения (resume), а затем на обработчик управления приложениями, это очень важно если приложение должно иметь разный вид в зависимости от того кто его запускает и с какими условиями, например как запускается галлерея с выбором изображений или отображением одного.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *