Плагин активити#

Activity Plugin — это автономный модуль, который встраивается прямо в GsBaseActivity, добавляя ей новую функцию (NFC-сканер, BLE-монитор, WebRTC-звонки и т.п.) без изменений в бизнес-логике экранов и без пересборки ядра GMF.

К плагинам активити можно получать доступ из главного потока.

Базовые плагины (камера, NFC, QR-сканер) включены «из коробки» и будут пополняться; разработчик может добавить собственный, реализовав тот же интерфейс.

Мотивация#

  • Изолировать платформенные API: весь код работы с NFC, камерой или сенсорами живёт в плагине, а приложение общается только через колбэк-интерфейсы.

  • Повторно использовать логику: один плагин можно подключить сразу во многие приложения.

  • Синхронизировать жизненный цикл: плагин получает те же события (onCreate/Resume/Pause…), что и Activity, и не нарушает транзакции State Manager’а.

Контракт#

interface ActivityPlugin : IPkg {

    /* ───────────── 1. Инициализация ───────────── */

    /** Один раз до показа UI. Можно выполнять работу в IO-диспетчере. */
    suspend fun initializeAsync(activity: GsBaseActivity<*>) {}

    /** Вызывается сразу после инициализации UI. */
    fun onInitializeUi(activity: GsBaseActivity<*>, navigator: Navigator) {}


    /* ───────────── 2. Жизненный цикл ───────────── */

    fun onCreate (activity: GsBaseActivity<*>) {}
    fun onStart  (activity: GsBaseActivity<*>) {}
    fun onResume (activity: GsBaseActivity<*>) {}
    fun onPause  (activity: GsBaseActivity<*>) {}
    fun onStop   (activity: GsBaseActivity<*>) {}
    fun onDestroy(activity: GsBaseActivity<*>) {}

    /** Activity получила новый Intent (например, NFC-метку). */
    fun onNewIntent(activity: GsBaseActivity<*>, intent: Intent) {}
}

Подключение плагина в Activity#

class MainActivity : GsBaseActivity<AppNavigator>() {

    override fun provideModules(): List<KClass<out ActivityPlugin>> =
        listOf(MyAnalyticsPlugin::class)
}
  • Тип-список — передаёте KClass; экземпляр будет лениво создан через session.getSimplePkg().
    Так удобнее, если плагин в разных модулях и нужен DI-контейнер.

Порядок инициализации#

  1. GsBaseActivity собирает StateManager и Navigator.

  2. Вызывает plugin.initializeAsync() параллельно с инициализацией StateManager’а; здесь выполняются тяжёлые операции (запуск CameraX, чтение лицензий и т.п.).

  3. UI создан — onInitializeUi() даёт плагину объект Navigator.

  4. Далее события onCreate/Start/Resume… приходят в том же порядке, что и в Activity.

Обмен данными с экраном#

  • Плагин хранит публичные MutableStateFlow / Callback-свойства — ViewModel или VCI подписывается и получает данные.

  • Если нужно уведомить только текущий экран, проще всего передать лямбду-делегат при вызове (в afterEnter), как показано в NFC-примере, либо получить плагин через сессию.

Пример NFC-плагина#

class NfcActivityPlugin : ActivityPlugin {

    private val isWriteNow = AtomicBoolean(false)
    private var adapter: NfcAdapter? = null
    var onRead: (String) -> Unit = {}

    override suspend fun initializeAsync(activity: GsBaseActivity<*>) {
        adapter = activity.getSystemService(NfcManager::class.java).defaultAdapter
        setupForegroundDispatch(activity)
    }

    override fun onResume(activity: GsBaseActivity<*>) {
        adapter?.enableForegroundDispatch(activity, pendingIntent, filters, null)
    }

    override fun onPause(activity: GsBaseActivity<*>) {
        adapter?.disableForegroundDispatch(activity)
    }

    override fun onNewIntent(activity: GsBaseActivity<*>, intent: Intent) {
        if (!isWriteNow.get()) {
            val payload = readNdef(intent)
            onRead(payload)
        }
    }

    /* остальной код — чтение, запись, шифрование */
}
  • Плагин полностью управляет NfcAdapter, не нагружая Activity.

  • В onRead передаёт данные обратно в экран (VCI) или State.

Activity-плагины позволяют подключать новые аппаратные возможности так же легко, как dependency в Gradle. Всё управление происходит в одном месте, жизненный цикл синхронизирован, а бизнес-код экранов остаётся чистым.