Перейти к основному содержанию
`Ctrl`+`K`

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

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-примере, либо получить плагин через сессию.

## Работа с Navigator

В `onInitializeUi()` плагин может:

- показать диалог через `navigator.createDialog(…)`;

- запустить экран через `navigator.setBaseMenu()` или вставить `SmTrans` в текущий стек.

Главное — не обращаться к UI до этой фазы: до создания `Navigator` компоновка ещё не готова.

## Пример 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. Всё управление происходит в одном месте, жизненный цикл синхронизирован, а бизнес-код экранов остаётся чистым.