diff --git a/README.md b/README.md
index b2880e41..d0dc9629 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# Debug-panel
-Библиотека для отладки приложений.
+Библиотека предоставляет встроенную отладочную панель для Android-приложений, предназначенную для использования в debug и QA-сборках.
@@ -8,21 +8,21 @@
[][license]
[](#)
-**[Changelog][changelog]** | **[Миграция на новые версии][migration-guide]**
+**[Changelog][changelog]** | **[Миграция на новые версии][migration-guide]** | **[Документация по разработке плагинов][plugin-development-doc]**
-Тебе надоело пересобирать приложение для того чтобы поменять сервер в настройках или переключить feature toggle? Эта библиотека разрабатывается с идеей решить эти и другие проблемы, и сделать процесс отладки приложения более удобным.
+Панель позволяет управлять состоянием приложения в runtime без внесения изменений в основной код.
-В данный момент библиотека предоставляет следующий функционал:
+Основные возможности:
-1. **Добавление, редактирование и выбор сервера.**
-2. **Управление feature-toggles и remote config на основе Konfeature.**
-3. **Отображение информации о приложении.**
+1. Добавление, редактирование и выбор сервера.
+2. Управление feature-toggles и remote config на основе Konfeature.
+3. Отображение информации о приложении.
-Библиотека разрабатывается используя подход работы с плагинами, когда каждый функционал подключается отдельным модулем в зависимостях.
+Каждая функциональность подключается отдельным плагином.
## Подключение библиотеки
-Для работы с библиотекой необходимо:
+Для работы с библиотекой необходимо выполнить следующие шаги:
1. Подключить `Core` модуль для работы самой панели:
@@ -54,7 +54,7 @@ dependencies {
```
-3. Для того чтобы библиотека не попала в релизную сборку необходимо подключить `no-op` версию библиотеки
+3. Для того, чтобы библиотека не попала в релизную сборку, необходимо подключить `no-op` версию библиотеки
```kotlin
releaseImplementation("com.redmadrobot.debug:panel-no-op:${debug_panel_version}")
@@ -82,7 +82,7 @@ class App : Application() {
}
```
-Для того чтобы открыть DebugPanel, нужно вызвать в коде:
+Для открытия DebugPanel необходимо вызвать:
```kotlin
fun openDebugPanel() {
@@ -90,7 +90,7 @@ fun openDebugPanel() {
}
```
-Так же в панель можно войти через уведомление которое появляется при запуске приложения использующее библиотеку. Через это же уведомление можно перейти в ручную настройку панели. Для этого нужно нажать кнопку `SETTINGS` в раскрытом уведомлении.
+Также панель доступна через уведомление, которое появляется при запуске приложения, использующего библиотеку. Через это уведомление можно перейти к ручной настройке панели, нажав кнопку `SETTINGS` в раскрытом уведомлении.

@@ -99,7 +99,7 @@ fun openDebugPanel() {
### ServersPlugin
Используется для работы с тестовыми серверами
-Можно задать список предустановленных серверов
+Доступна возможность задать список предустановленных серверов
```kotlin
ServersPlugin(
@@ -113,7 +113,7 @@ ServersPlugin(
)
```
-И подписаться на событие смены сервера
+Подписка на событие смены сервера
```kotlin
DebugPanel.subscribeToEvents(lifecycleOwner = this) { event ->
@@ -126,14 +126,14 @@ DebugPanel.subscribeToEvents(lifecycleOwner = this) { event ->
}
```
-Для получения выбранного сервера или **default** сервера из кода:
+Получение выбранного сервера или сервера по умолчанию:
```kotlin
val selectedServer = ServersPlugin.getSelectedServer()
val defaultServer = ServersPlugin.getDefaultServer()
```
-Так же если вы используете `OkHttp` в своем сетевом стеке то можете использовать `DebugServerInterceptor` который будет автоматически подменять хост в запросах на выбранный вами.
+При использовании `OkHttp` в сетевом стеке можно применить `DebugServerInterceptor`, который автоматически подменяет хост в запросах на выбранный сервер.
```kotlin
OkHttpClient.Builder()
@@ -141,7 +141,7 @@ OkHttpClient.Builder()
.build()
```
-Если запросы должны еще как то модифицироваться, например добавляться Header'ы то это можно сделать используя метод `modifyRequest`
+Если запросы требуют дополнительной модификации, например добавления заголовков, можно воспользоваться методом `modifyRequest`
```kotlin
OkHttpClient.Builder()
@@ -158,7 +158,7 @@ OkHttpClient.Builder()
)
.build()
```
-Текущий выбранный сервер можно получить следующим образом
+Получение текущего выбранного сервера
```kotlin
val selectedServer = getPlugin().getSelectedServer()
@@ -170,18 +170,18 @@ val selectedServer = getPlugin().getSelectedServer()
В основе плагина лежит библиотека [Konfeature][konfeature], которая позволяет:
-- отображать конфигурации feature, которые используются в konfeature
-- видеть источник каждого элемента конфигурации (Default, Firebase, AppGallery и т.д.)
-- переопределять значение элементов конфигурации с типом Boolean, String, Long, Double
+- отображать конфигурации feature, используемые в Konfeature
+- просматривать источник каждого элемента конфигурации (Default, Firebase, AppGallery и др.)
+- переопределять значения элементов конфигурации с типами Boolean, String, Long, Double
-Для подключения плагина, необходимо передать в него объект класса `KonfeatureDebugPanelInterceptor` и `Konfeature`
+Для подключения плагина необходимо передать объект класса `KonfeatureDebugPanelInterceptor` и экземпляр `Konfeature`
```kotlin
val debugPanelInterceptor = KonfeatureDebugPanelInterceptor(context)
val konfeatureInstance = konfeature {
if (isDebug) {
- addIntercepot(debugPanelInterceptor)
+ addInterceptor(debugPanelInterceptor)
}
}
@@ -191,17 +191,17 @@ KonfeaturePlugin(
)
```
-В builder konfeture можно настроить следующее:
+В builder Konfeature доступны следующие настройки:
-- добавить config конкретной фичи - `register(FeatureConfigN())`
-- настроить работу с remote config через реализацию интерфейса `FeatureSource` - `addSource(featureSource)`
-- настроить логирование - `setLogger(logger)`
+- добавление конфигурации конкретной фичи — `register(FeatureConfigN())`
+- настройка работы с remote config через реализацию интерфейса `FeatureSource` — `addSource(featureSource)`
+- настройка логирования — `setLogger(logger)`
### AboutApp Plugin
-Используется для отображения информации о приложении: версии, номера билда и других произвольных данных.
+Предназначен для отображения информации о приложении: версии, номера сборки и других произвольных данных.
-Для подключения плагина необходимо передать список `AboutAppInfo`. Требуется хотя бы один элемент:
+Для подключения плагина необходимо передать список объектов `AboutAppInfo`, содержащий хотя бы один элемент:
```kotlin
AboutAppPlugin(
@@ -222,8 +222,9 @@ AboutAppPlugin(
- `title` — название поля (например, «Версия»)
- `value` — значение поля (например, «1.0.0»)
-# Безопасность!
-Для того чтобы тестовые данные не попали в релизные сборки рекомендуется не задавать их явно в Application классе, а использовать реализации DebugDataProvider, которые можно разнести по разным buildType. Для release версии следует сделать пустую реализацию.
+# Безопасность
+
+Для предотвращения попадания тестовых данных в релизные сборки рекомендуется не задавать их явно в классе Application, а использовать реализации `DebugDataProvider`, которые можно разнести по разным `buildType`. Для release-версии следует создать пустую реализацию.
**buildType** `debug`
@@ -247,7 +248,7 @@ class DebugServersProvider : DebugDataProvider> {
}
}
```
-Добавление в плагин
+Передача в плагин
```kotlin
ServersPlugin(
diff --git a/docs/plugin_development.md b/docs/plugin_development.md
index f448f58e..7287de6d 100644
--- a/docs/plugin_development.md
+++ b/docs/plugin_development.md
@@ -1,182 +1,195 @@
# Разработка новых плагинов
-**[!]Важно.**
-1. Библиотека находится в статусе разработки и миграции на более актуальные решения.
-Актуальность данного документа стоит уточнить у холдера библиотеки. В данный момент это **r.choryev@redmadrobot.com**
-2. В библиотеке млогут быть спорные решения, но она открыта для предложений.
-3. В текущих плагинах, для работы со списками используется [Groupie](https://github.com/lisawray/groupie).
-Т.к. эта библиотека требует использование `jcenter` и уже не кажется таким уж подходящим решением, поэтому она будет удаляться из библиотеки.
- Поэтому это стоит учесть при разработке ваших новых плагинов и использовать какое-то другое решение.
+## Общая структура
+Debug Panel построена на подходе с использованием плагинов — каждая функциональность реализуется в виде отдельного модуля-плагина.
-## Общая структура
-Debug panel разрабатывается опираясь на подход разработки функционала отдельными плагинами.
-В данный момент есть несколько модулей на которых основана работа самой панели и разработка и работа плагинов.
+Базовые модули, на которых основана работа панели:
-* **debug-panel-core** - Реализация самой панели и базовых классов для поддержки системы плагинов.
-* **debug-panel-common** - Модуль содержащий общие библиотеки, классы и ресурсы переиспользуемые в плагинах.
- Библиотеки пробрасываются как сквозные зависимости при помощи типа зависимости `api`.
- Список предоставляемых библиотек можно посмотреть в файле [build.gradle](../debug-panel-common/build.gradle.kts)
+* **panel-core** — реализация панели, базовые классы системы плагинов и событийная модель.
+* **panel-no-op** — пустые реализации публичных API для релизных сборок (исключает отладочный код из продакшена).
## Создание нового плагина
-Для добавления нового плагина необходимо сделать несколько шагов:
-1. Создать в дирректории **plugins** новый модуль для реализации своего плагина.
-
-```
-plugins
-| your-plugin
-```
-2. Объявить новый модуль в файле **settings.gradle** по примеру уже существующих плагинов.
-
-```
-include ':your-plugin'
-project(':your-plugin').projectDir = new File(rootDir, 'plugins/your-plugin')
+### 1. Создать модуль
+
+Создайте новый модуль в директории `plugins/`:
+
+```
+plugins/
+└── plugin-your-feature/
```
-3. Добавить в **build.gradle** файл вашего модуля следующие настройки:
-```groovy
-android {
- /*.......*/
-
- compileSdkVersion build_versions.compile_sdk
+### 2. Зарегистрировать модуль в settings.gradle.kts
- defaultConfig {
- minSdkVersion build_versions.min_sdk
- targetSdkVersion build_versions.target_sdk
+Добавьте модуль по аналогии с существующими плагинами:
- versionCode getVersionCodeFromProperties()
- versionName getVersionNameFromProperties()
- }
+```kotlin
+// Plugins
+include(
+ ":plugins:plugin-your-feature",
+)
+```
- /*.......*/
+### 3. Настроить build.gradle.kts
+Примените convention-плагин, который содержит всю необходимую конфигурацию (compileSdk, minSdk, `explicitApi()`, зависимость на `panel-core` и Compose):
- kotlinOptions {
- freeCompilerArgs += "-Xexplicit-api=strict"
- }
+```kotlin
+plugins {
+ id("convention.debug.panel.plugin")
}
-dependencies {
- implementation(
- project(path: ':debug-panel-core'),
- project(path: ':debug-panel-common'),
+description = "Plugin description"
- deps.kotlin.stdlib
- )
+android {
+ namespace = "com.redmadrobot.debug.plugin.yourfeature"
}
+dependencies {
+ // Только специфичные для плагина зависимости
+}
```
-**[!]Важно. Конфигурация будет меняться при дальнейшей миграции с Groovy на Kotlin**
-4. Создать в своем модуле класс-плагин который и будет отвечать за взаимодействие с DebugPanel.
- Для этого класс должен унаследоваться от класса `Plugin()` и реализовать необходимые методы.
- В качестве аргументов класса можно передать необходимые для инициализации плагина данные.\
- **(О методе `getPluginContainer()` и `PluginDependencyContainer()` можно будет почитать ниже)**
-
+### 4. Создать класс плагина
+
+Класс плагина — точка входа, отвечающая за взаимодействие с DebugPanel.
+Унаследуйтесь от `Plugin()` и реализуйте обязательные методы.
+Подробнее о `getPluginContainer()` и `PluginDependencyContainer` — в разделе ниже.
+
```kotlin
public class YourPlugin(
- /*some arguments*/
+ /* аргументы для инициализации */
) : Plugin() {
- internal companion object {
- const val NAME = "AWESOME PLUGIN"
- }
-
- override fun getName(): String = NAME
+ override fun getName(): String = "YOUR PLUGIN"
- /*Plugin dependency container initializing*/
override fun getPluginContainer(commonContainer: CommonContainer): PluginDependencyContainer {
- return YourPluginContainer(sharedPreferences)
+ return YourPluginContainer(commonContainer)
}
- /*Plugin Fragment initializing*/
- override fun getFragment(): Fragment? {
- return YourPluginFragment()
+ @Composable
+ override fun content() {
+ YourScreen()
+ }
+}
+```
+
+Если плагин поддерживает редактирование через экран настроек панели, реализуйте интерфейс `EditablePlugin`:
+
+```kotlin
+public class YourPlugin : Plugin(), EditablePlugin {
+ // ...
+
+ @Composable
+ override fun content() {
+ YourScreen(isEditMode = false)
}
- /*Plugin Setting Fragment initializing.*/
- override fun getSettingFragment(): Fragment { //Нужно только если есть отдельный экран для настройки плагина.
- return YourPluginSettingFragment()
+ @Composable
+ override fun settingsContent() {
+ YourScreen(isEditMode = true)
}
}
```
-5. Создать **Fragment** экрана плагина(если он нужен) и унаследовать его от `PluginFragment()`.
-К фрагменту создать **ViewModel** и унаследовать от `PluginViewModel()`.
- В этой связке (Fragment+ViewModel), реализовывать пользовательское взаимодействие пользователя с плагином.
-
-## PluginDependencyContainer
-В библиотеке не используются библиотеки для реализации DI, т.к.:
-1. Не хочется тащить их зависимости в библиотеку.
-2. Библиотека не такая большая чтобы реализовывать полноценный DI.
+### 5. Создать UI на Jetpack Compose
-Вместо этого, в библиотеке используется подход с **Service Locator**.
-Для этого необходимо создать свой класс-контейнер, унаследовать его от **PluginDependencyContainer** и внутри него инициировать необходимые зависимости.
-Если вам для этого понадобится **Context**, его можно получить из **CommonContainer** который прилетает в качестве аргумента в методе `getPluginContainer()` при инициализации плагина.
-Пример реализации можно [посмотреть тут](../plugins/accounts-plugin/src/main/kotlin/com/redmadrobot/account/plugin/AccountsPluginContainer.kt)
+UI плагина реализуется с помощью Composable-функций. Для инъекции ViewModel используется хелпер `provideViewModel`:
-## Работа с классом плагина
+```kotlin
+@Composable
+internal fun YourScreen(
+ viewModel: YourViewModel = provideViewModel {
+ getPlugin()
+ .getContainer()
+ .createYourViewModel()
+ },
+) {
+ val state by viewModel.state.collectAsState()
+ // UI
+}
+```
+
+## PluginDependencyContainer
+
+В библиотеке не используются DI-фреймворки, чтобы не добавлять лишних зависимостей. Вместо этого применяется подход **Service Locator**.
-Класс-плагин, описание которого было в пункте **4**, является точкой инициализации вашего плагина.
-Поэтому доступ к данным и различным экземплярам классов нужно реализовывать через него.
-Чтобу получить доступ к самому плагину, нужно использовать метод `getPlugin()`.
-Например для получения контейнера зависимостей плагина, нужно вызвать:
+Для этого создайте класс-контейнер, реализующий интерфейс `PluginDependencyContainer`, и инициализируйте в нём необходимые зависимости.
+`Context` доступен через `CommonContainer`, который передаётся в метод `getPluginContainer()` при инициализации плагина.
```kotlin
-getPlugin()
- .getContainer()
+internal class YourPluginContainer(
+ private val container: CommonContainer,
+) : PluginDependencyContainer {
+
+ private val dataStore by lazy { YourDataStore(container.context) }
+
+ val repository by lazy { YourRepository(dataStore) }
+
+ fun createYourViewModel(): YourViewModel {
+ return YourViewModel(repository)
+ }
+}
```
-**Пример использования плагина для получения ViewModel во Fragment:**
+Пример реализации: [ServersPluginContainer](../plugins/plugin-servers/src/main/kotlin/com/redmadrobot/debug/plugin/servers/ServersPluginContainer.kt)
+
+## Работа с классом плагина
+
+Класс плагина является точкой доступа к данным и зависимостям.
+Для получения экземпляра плагина используйте `getPlugin()`:
```kotlin
- private val viewModel by lazy {
- obtainShareViewModel {
- getPlugin()
- .getContainer()
- .createServersViewModel()
- }
- }
+getPlugin()
+ .getContainer()
```
## Области видимости
-Все внутренние классы используемые только для работы плагина и не требующиеся для работы клиентского приложения, должны иметь область видимости **inner**
+Все модули используют `explicitApi()` — модификаторы видимости обязательны для всех объявлений.
+Внутренние классы, не предназначенные для использования в клиентском приложении, должны иметь модификатор `internal`.
## Тестирование
-Для тестирования плагина, необходимо:
-1. Подключить его как зависимость в модуль `sample`.
+Для тестирования плагина:
+
+1. Подключите его как зависимость в модуль `sample`:
-```groovy
- debugImplementation(project(path: ':your-plugin'))
+```kotlin
+debugImplementation(project(":plugins:plugin-your-feature"))
```
-2. Инициировать плагин в **App** классе **sample** приложения.
+2. Инициализируйте плагин в классе `App` sample-приложения:
```kotlin
- DebugPanel.initialize(
- application = this,
- plugins = listOf(YourPluggin())
+DebugPanel.initialize(
+ application = this,
+ plugins = listOf(
+ YourPlugin(/* ... */)
+ )
)
```
-3. Запустить **sample** проект
+3. Запустите sample-проект.
-## No-op зависимости
+## No-op реализации
-Для того чтобы в релизную сборку не попадали реализации публичных классов вашего модуля, необходимо добавить их в модуль no-op зависимостей.
-([Подробнее в статье](https://medium.com/@orhanobut/no-op-versions-for-dev-tools-b0a865934398)). \
-Для этого создайте пакет с именем вашего плагина в модуле **debug-panel-no-op** и скопируйте ваши публичные классы доступные пользователю в этот пакет.
+Чтобы отладочный код не попадал в релизную сборку, для каждого плагина необходимо создать no-op реализацию в модуле **panel-no-op**.
-[!]Важно. Поле **package** должно остаться оригинальным.
-Таким, каким оно было в вашем модуле.
+Создайте пакет с публичными классами плагина, доступными пользователю, и предоставьте пустые реализации.
-## Публикация
+Важно: **package** должен совпадать с оригинальным пакетом вашего модуля.
+
+В sample-приложении подключение выглядит так:
-Публикация новых плагнинов в основном репозитории должна проходить через создание **Merge Request** в ветку **develop**.
+```kotlin
+debugImplementation(project(":plugins:plugin-your-feature"))
+releaseImplementation(project(":panel-no-op"))
+```
+
+Подробнее о подходе: [No-op versions for dev tools](https://medium.com/@orhanobut/no-op-versions-for-dev-tools-b0a865934398)
+
+## Публикация
-Публикация на внутренний Maven пока делается вручную.
-За публикацией обращаться к r.choryev@redmadrobot.com.
-В ближайшее время есть планы пересмотреть этот подход.
+Публикация новых плагинов проходит через создание **Pull Request** в ветку **main**.