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

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

Базовый функционал модуля менеджера пакетов

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

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

#include <package_manager.h>

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

"http://tizen.org/privilege/packagemanager.info"

Основным типом данных модуля менеджера пакетов является

package_info_h

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

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

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

int package_manager_foreach_package_info(package_manager_package_info_cb callback,
                                         void *user_data);

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

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

typedef bool (*package_manager_package_info_cb) (package_info_h package_info,void *user_data);

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

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

int package_info_create(const char *package,  package_info_h *package_info);

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

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

int package_manager_get_package_info(const char *package_id, package_info_h *package_info);

Все параметры в этой функции аналогичные предыдущей рассмотреной функции.

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

PACKAGE_MANAGER_ERROR_NO_SUCH_PACKAGE

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

int package_info_destroy(package_info_h package_info);

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

Получаем список всех пакетов установленных на устройстве

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

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

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

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

nw_025_01_en

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

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

nw_025_02_en

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

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

#include <package_manager.h>

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

static bool
_app_create_cb(void *data)
{
   ...

   package_manager_foreach_package_info(_package_foreach_info_get, NULL);

   ...
}

И реализуем сам обработчик с выводом сообщений в окно логгирования.

static bool
_package_foreach_info_get(package_info_h package_info, void *user_data)
{
   dlog_print(DLOG_INFO, "lesson25", "Обработчик пакета");

   return true;
}

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

nw_025_03_ru
Получение информации о пакете

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

Рассмотрим группу функций имеющие одинаковые параметры, для получения текстовой информации о пакете следующего характера:

Получение имени пакета:

int package_info_get_package(package_info_h package_info, char **package);

Получение названия приложения:

int package_info_get_label(package_info_h package_info, char **label);

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

Получение абсолютного пути к иконке приложения:

int package_info_get_icon(package_info_h package_info, char **path);

Получение номера версии

int package_info_get_version(package_info_h package_info, char **version);

Получение типа пакета (wgt - для веб приложений или tpk - для нативных)

int package_info_get_type(package_info_h package_info, char **type);

Получение корневого пути, где установлен пакет

int package_info_get_root_path(package_info_h package_info, char **path);

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

А теперь рассмотрим группу функций носящий проверочный характер, имеющие одинаковые параметры.

Проверка на то, является ли пакет системным

int package_info_is_system_package(package_info_h package_info, bool *system);

Проверка на то, возможно ли удалить пакет

int package_info_is_removable_package(package_info_h package_info, bool *removable);

Проверка на то, является ли пакет предустановленным в прошивке устройства.

int package_info_is_preload_package(package_info_h package_info, bool *preload);

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

Теперь пора применить некоторые рассмотренные выше функции в нашем демо приложении.

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

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

typedef struct _Install_Package_Info {
   char *label;
   char *path_icon;
   char *version;
} Install_Package_Info;

Давайте добавим в структуру UIData список в который мы будет добавлять обьекты Install_Package_Info структуры

typedef struct _UIData {
   ...

   Eina_List *list_package;
} UIData;

Здесь мы используем список данных из встроенной библиотеки Eina в качестве хранилища для информации о пакетах. Мы рассмотрим подробно работу с этим списком в следующих уроках подробнее. Сейчас не будем на нем останавливаться.

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

static bool
_app_create_cb(void *data)
{
   ...

   package_manager_foreach_package_info(_package_foreach_info_get, ui);

   _window_create(ui);

   ...
}

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

static bool
_package_foreach_info_get(package_info_h package_info, void *user_data)
{
   UIData *ui = user_data;
   Install_Package_Info *install_pkg_info = calloc(1, sizeof(Install_Package_Info));
   if (!install_pkg_info) return false;

   memset(install_pkg_info, 0x0, sizeof(Install_Package_Info));

   package_info_get_label(package_info, &(install_pkg_info->label));
   package_info_get_icon(package_info, &(install_pkg_info->path_icon));
   package_info_get_version(package_info, &(install_pkg_info->version));

   bool is_removable = false;
   package_info_is_removable_package(package_info, &is_removable);
   if (is_removable)
     ui->list_package = eina_list_append(ui->list_package, install_pkg_info);

   char *root_path = NULL;
   package_info_get_root_path(package_info, &root_path);
   if (root_path)
     {
        dlog_print(DLOG_INFO, "lesson25", "Корневой каталог пакета: %s", root_path);
        free(root_path);
     }

   dlog_print(DLOG_INFO, "lesson25", "Название: %s", install_pkg_info->label);
   dlog_print(DLOG_INFO, "lesson25", "Путь к иконке: %s", install_pkg_info->path_icon);
   dlog_print(DLOG_INFO, "lesson25", "Версия: %s", install_pkg_info->version);

   if (!is_removable)
     {
        FREE_RESOURCE(install_pkg_info->label);
        FREE_RESOURCE(install_pkg_info->path_icon);
        FREE_RESOURCE(install_pkg_info->version);
        FREE_RESOURCE(install_pkg_info);
     }

   return true;
}

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

static void
_app_terminate_cb(void *data)
{
   ...

   Install_Package_Info *install_package_info = NULL;
   EINA_LIST_FREE(ui->list_package, install_package_info)
     {
        FREE_RESOURCE(install_package_info->label);
        FREE_RESOURCE(install_package_info->path_icon);
        FREE_RESOURCE(install_package_info->version);
        FREE_RESOURCE(install_package_info);
     }

   ...
}

Мы реализовали макрос FREE_RESOURCE для очистки ресурса следующим образом

#define FREE_RESOURCE(resource) \
   if (resource)                \
     free(resource);

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

Внесем изменение в функцию создания виджета 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);

   Eina_List *iterator                        = NULL;
   Install_Package_Info *install_package_info = NULL;
   EINA_LIST_FOREACH(ui->list_package, iterator, install_package_info)
     {
        Eext_Object_Item *item = eext_rotary_selector_item_append(ui->rotary_selector);

        Evas_Object *icon = elm_image_add(ui->rotary_selector);
        elm_image_file_set(icon, install_package_info->path_icon, NULL);
        evas_object_show(icon);
        eext_rotary_selector_item_part_content_set(item,
                                                   "item,bg_image",
                                                   EEXT_ROTARY_SELECTOR_ITEM_STATE_NORMAL,
                                                   icon);

        eext_rotary_selector_item_part_text_set(item,
                                                "selector,main_text",
                                                install_package_info->label);

        eext_rotary_selector_item_part_text_set(item,
                                                "selector,sub_text",
                                                install_package_info->version);
     }

   elm_object_content_set(ui->conform, ui->rotary_selector);
   evas_object_show(ui->rotary_selector);
}

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

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

- в качестве основного текста вставляем название пакета

- в качестве дополнительного текста вставляем версию пакета

- в качестве фонового контента элемента вставляем иконку пакета.

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

У нас получилась следующая картинка на устройстве Samsung Gear S3.

nw_025_04_en

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

Если мы начнем вращать безелем, то мы увидем что название и номер версии приложений будут меняться в соответствии с выделеным приложением.

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

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

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

int package_info_clone(package_info_h *clone, package_info_h package_info);

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

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

Функции на которые стоит обратить внимание

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

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

Рассмотрим следующую функцию.

int package_manager_get_package_id_by_app_id(const char *app_id, char **package_id);

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

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

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

int package_manager_set_event_cb(package_manager_h manager,
                                 package_manager_event_cb callback,
                                 void *user_data);

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

Необходимо отметить, что тип обьекта package_manager_h отличается от типа обьекта package_info_h рассмотреного ранее и не стоит их путать.

Самое интересное в этой функции - это собственно обработчик, давайте рассмотрим его обьявление

typedef void (*package_manager_event_cb) (const char *type,
                                          const char *package,
                                          package_manager_event_type_e event_type,
                                          package_manager_event_state_e event_state,
                                          int progress,
                                          package_manager_error_e error,
                                          void *user_data);

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

- PACKAGE_MANAGER_EVENT_TYPE_INSTALL – событие связано с установкой пакета.

- PACKAGE_MANAGER_EVENT_TYPE_UNINSTALL – событие связано с удалением пакета.

- PACKAGE_MANAGER_EVENT_TYPE_UPDATE – событие связано с обновлением пакета.

Четвертый параметр это состояние в котором находится пакет с которым связано событие.

Приведем перечень возможных состояний

- PACKAGE_MANAGER_EVENT_STATE_STARTED - начало события

- PACKAGE_MANAGER_EVENT_STATE_PROCESSING – выполнение события в процессе

- PACKAGE_MANAGER_EVENT_STATE_COMPLETED – событие завершения выполнения (успешно)

- PACKAGE_MANAGER_EVENT_STATE_FAILED – событие завершилось ошибкой

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

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

int package_info_foreach_app_from_package(package_info_h package_info,
                                          package_info_app_component_type_e comp_type,
                                          package_info_app_cb callback,
                                          void *user_data);

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

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

Перечислим возможные типы приложений

- PACKAGE_INFO_ALLAPP – все приложения

- PACKAGE_INFO_UIAPP – только имеющие графический интерфейс

- PACKAGE_INFO_SERVICEAPP – только сервисные приложения

Приведем обьявление обработчика получения индитификаторов приложения

typedef bool (*package_info_app_cb) (package_info_app_component_type_e comp_type,
                                     const char *app_id,
                                     void *user_data);

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

Можете скачать проект с полным исходным кодом этого урока здесь WearLesson025.

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

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