"Intent" является главной возможностью для межпроцессорного взаимодействия в Android. В основном, Intent является объектом, который обрабатывается операционной системой и передаётся затем одному или большему количеству установленных приложений, основываясь на его содержании. Это может быть экземпляр запроса отображения видео, в данном случае тип intent должен быть ACTION_VIEW и mime тип должен быть установлен соответствующий. Приложения могут подписываться на соответствующие Intents в настройках фильтра intent в своих манифест файлах. В первый раз пользователю устройства будет предоставлен выбор, какие типы intent известны приложению и на какие можно подписаться. Если они выбраны, они будут установлены по умолчанию, или выбраны при вопросе от каждого экземпляра приложения при необходимости.
Это первая из нескольких статей, как использовать intents с Qt для Android.
Сам механизм имеет довольно общий характер. То, что описано выше является одним из способов использования, но intents также могут быть использованы и для другого: Одним из таких случаев является запуск сервисов внутри приложения. Это, как правило, называется "explicit intent", что означает, что предусмотрено для запуска полное имя сервиса, и не может быть перехвачено другим приложением.
Другим способом использования intents является широковещательный вариант (broadcast), для экземпляров, когда изменяется временная зона устройства. В это время как действия описанные выше позволяют запускать только одно определённое приложение, выбранное пользователем, то при широковещательном вариант сигнал будет передан во все приложения, которые были на него подписаны.
Данный вариант нацелен на Android устройства, но похожий механизм, называемый app extensions , был введён в iOS 8.
В этой первой статье об intents на Android сфокусируемся на простейшем варианте использования: Создание скрытого intent из Qt для запуска неопределённого приложения на целевом устройстве. Например, сделано простой приложение, которое представляет рецепты еды. Каждый рецепт имеет время приготовления. По клику по кнопке, вы можете установить таймер, используя обратный отсчёт на устройстве. (Для более полезного применения можно представить кнопку установки времени для каждого шага приготовления рецепта, но в этом простом примере будем использовать только один таймер.)
Не будем вдаваться в подробности того, как приложение было написано, но кому интересно, то код может быть найден здесь . Никаких усилий по улучшению внешнего вида приложения приложено не было, поэтому не обращайте на это внимания. Приложение основывается на базе данных SQLite (которая заполнена некоторым фейковым контентом при первом запуске) и имеет очень простой пользовательский интерфейс, написанный на Qt Quick Controls 2, который позволяет пользователю выбрать рецепт из списка, посмотреть его описание на отдельной странице и нажать кнопку в рецепте для установки таймера в систему.
А теперь сфокусируемся на коде, который фактически запрашивает таймер от системы. Это находится в файле recipe.cpp , функции Recipe::createTimer() . В нашем примере мы используем следующий тип intent - AlarmClock.ACTION_SET_TIMER .
Мы будем использовать для этого удобный JNI API из модуля Qt Android Extras . Давайте пройдём по коду шаг за шагом.
QAndroidJniObject ACTION_SET_TIMER = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock", "ACTION_SET_TIMER"); QAndroidJniObject intent("android/content/Intent", "(Ljava/lang/String;)V", ACTION_SET_TIMER.object());
Первым шагом создадим intent. Мы принимаем идентификатор ACTION_SET_TIMER intent. Он является статическим участником в android.provider.AlarmClock классе и имеет строковый тип данных (String). Заметьте, что использование слэшей вместо точек в имени пакета класса. Это соотносится с представление имён пакетов в байткоде (так сложилось по историческим причинам, смотрите спецификацию ) и следовательно также используется в подписи JNI.
Так как мы используем только функции из Qt Android Extras и не используем явные вызовы к JNI API, то мы можем не беспокоиться об эталонном управлении. Это главное удобство данного API.
Следующим шагом мы создадим intent объект. Мы сделаем это построив QAndroidJniObject и передав его в в JNI-разборщик подписей Java конструктора, который желаем вызвать, а также передадим ID действия, которое хотим получили из класса AlarmClock.
QAndroidJniObject EXTRA_MESSAGE = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock", "EXTRA_MESSAGE"); QAndroidJniObject messageObject = QAndroidJniObject::fromString(message); intent.callObjectMethod("putExtra", "(Ljava/lang/String;Ljava/lang/String;)Landroid/content/Intent;", EXTRA_MESSAGE.object(), messageObject.object()); QAndroidJniObject EXTRA_LENGTH = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock", "EXTRA_LENGTH"); intent.callObjectMethod("putExtra", "(Ljava/lang/String;I)Landroid/content/Intent;", EXTRA_LENGTH.object(), jint(m_time * 60)); QAndroidJniObject EXTRA_SKIP_UI = QAndroidJniObject::getStaticObjectField("android/provider/AlarmClock", "EXTRA_SKIP_UI"); intent.callObjectMethod("putExtra", "(Ljava/lang/String;Z)Landroid/content/Intent;", EXTRA_SKIP_UI.object(), jboolean(true));
Следующим шагом, мы установим несколько аргументов, которые мы хотим послать в обработчик действия Activity. Мы передаём в описании таймер и количество секунд для установки.
QAndroidJniObject activity = QtAndroid::androidActivity(); QAndroidJniObject packageManager = activity.callObjectMethod("getPackageManager", "()Landroid/content/pm/PackageManager;"); QAndroidJniObject componentName = intent.callObjectMethod("resolveActivity", "(Landroid/content/pm/PackageManager;)Landroid/content/ComponentName;", packageManager.object());
Затем мы разрешаем данную activity. Она скажет системе проверить, доступно ли приложение для обработки действия ACTION_SET_TIMER. И тогда мы сначала получим текущую activity (в нашем текущем контексте) для того, чтобы получить соответствующий пакетный менеджер. Затем вызовем resolveActivity() на intent для передачи его в пакетный менеджер.
if (componentName.isValid()) { QtAndroid::startActivity(intent, 0); } else { qWarning() << "Unable to resolve activity"; }
Если возвращённое имя компонента является ненулевым, то мы запускаем activity используя удобную функцию в Qt Android Extras. Если соответствующей activity не было найдено, тогда сделаем вывод об ошибке в консоль. Правильное приложение должно сообщать об ошибке в пользовательском интерфейсе, но заметьте, что операционная система также сообщит пользователю о проблемах в этом месте.
Есть ли возможность приведения java типа у QAndroidJniObject? Интересует конкретно class to