diff --git a/alerts/.gitignore b/alerts/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/alerts/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/alerts/README.md b/alerts/README.md
new file mode 100644
index 0000000..b4d5aa5
--- /dev/null
+++ b/alerts/README.md
@@ -0,0 +1,55 @@
+alerts
+=====
+
+### Общее описание
+
+Модуль содержит:
+`ViewableAlertDialog` - служит для демонстрации AlertDialog с использованием View, необходимо вызвать метод `showAlertDialog`, который
+в качестве агруметов может принимать:
+* title - Заголовок диалога
+* message - дополнительное сообщение
+* positiveButtonText - текст правой кнопки (по умолчанию "ОК")
+* onPositiveAction - колбэк при нажатии на правую кнопку
+* negativeBtnTitle - текст левой кнопки (по умолчаннию null - в этом случаи не отображается)
+* onNegativeAction - колбэк при нажатии на левую кнопку,
+* dialogLayout - id кастомного layout (по умолчанию R.layout.dialog_alert)
+
+---
+`ComposableAlertDialog` - служит для демонстрации AlertDialog с использованием Jetpack Compose, необходимо вызвать метод `ShowAlertDialog`, который
+в качестве агруметов может принимать:
+* isDialogOpen - индикатор состояния диалога
+* title - Заголовок диалога
+* message - дополнительное сообщение
+* positiveButtonText - текст правой кнопки
+* onPositiveAction - колбэк при нажатии на правую кнопку
+* negativeBtnTitle - текст левой кнопки (по умолчаннию null - в этом случаи не отображается)
+* onNegativeAction - колбэк при нажатии на левую кнопку,
+
+Кастомизация Compose версии происходит по средствам инициализации полей: customTitle, customMessage, customConfirmBtn, customNegativeBtn
+
+### Примеры
+
+View версия (ViewableAlertDialog):
+```kotlin
+ViewableAlertDialog.showAlertDialog(
+ activity,
+ title = "Ой, что-то пошло не так",
+ message = "Попробуйте ещё раз",
+ positiveButtonText = "Ещё раз",
+ onPositiveAction = { retryConnection() },
+ negativeBtnTitle = "Отмена"
+ )
+```
+
+Compose версия (ComposableAlertDialog):
+```kotlin
+val isDialogOpen = remember { mutableStateOf(false)}
+....
+//Создание диалога
+ComposableAlertDialog
+ .apply { customTitle = { Text(text = "Ой, что-то пошло не так", color = Color.Blue) } }
+ .ShowAlertDialog(isDialogOpen, message = "Проблемы с сетью", positiveButtonText = "ОК")
+....
+//Отображение диалога
+isDialogOpen.value = true
+```
\ No newline at end of file
diff --git a/alerts/build.gradle b/alerts/build.gradle
new file mode 100644
index 0000000..dba3f27
--- /dev/null
+++ b/alerts/build.gradle
@@ -0,0 +1,40 @@
+apply from: "../android-configs/lib-config.gradle"
+
+ext {
+ composeVersion = '1.0.5'
+}
+
+android {
+ buildFeatures {
+ viewBinding true
+ }
+}
+
+dependencies {
+ implementation("androidx.core:core-ktx")
+ implementation("androidx.constraintlayout:constraintlayout")
+ implementation project(":kotlin-extensions")
+
+ implementation "androidx.compose.runtime:runtime:1.1.1"
+ implementation "androidx.compose.ui:ui:1.1.1"
+ implementation "androidx.compose.foundation:foundation:1.1.1"
+ implementation "androidx.compose.foundation:foundation-layout:1.1.1"
+ implementation "androidx.compose.material:material:1.1.1"
+ implementation "androidx.compose.runtime:runtime-livedata:1.1.1"
+ implementation "androidx.compose.ui:ui-tooling:1.1.1"
+ implementation "com.google.android.material:compose-theme-adapter:1.1.9"
+
+ constraints {
+ implementation("androidx.core:core-ktx") {
+ version {
+ require '1.0.0'
+ }
+ }
+
+ implementation("androidx.constraintlayout:constraintlayout") {
+ version {
+ require '2.2.0-alpha03'
+ }
+ }
+ }
+}
diff --git a/alerts/src/main/AndroidManifest.xml b/alerts/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..80ea08a
--- /dev/null
+++ b/alerts/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/alerts/src/main/java/ru/touchin/roboswag/composable dialog/ComposableAlertDialog.kt b/alerts/src/main/java/ru/touchin/roboswag/composable dialog/ComposableAlertDialog.kt
new file mode 100644
index 0000000..7ae6011
--- /dev/null
+++ b/alerts/src/main/java/ru/touchin/roboswag/composable dialog/ComposableAlertDialog.kt
@@ -0,0 +1,63 @@
+package com.ptut.statehandlingcompose
+
+import androidx.compose.material.AlertDialog
+import androidx.compose.material.Text
+import androidx.compose.material.TextButton
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.MutableState
+
+object ComposableAlertDialog {
+ var customTitle: @Composable (() -> Unit)? = null
+ var customMessage: @Composable (() -> Unit)? = null
+ var customConfirmBtn: @Composable (() -> Unit)? = null
+ var customNegativeBtn: @Composable (() -> Unit)? = null
+
+ @Composable
+ fun ShowAlertDialog(
+ isDialogOpen: MutableState,
+ title: String? = null,
+ message: String? = null,
+ positiveButtonText: String? = null,
+ onPositiveAction: (() -> Unit)? = null,
+ negativeBtnTitle: String? = null,
+ onNegativeAction: (() -> Unit)? = null
+ ) {
+ if (isDialogOpen.value) {
+ AlertDialog(
+ onDismissRequest = { isDialogOpen.value = false },
+
+ title = customTitle ?: { Text(title.orEmpty()) },
+ text = customMessage ?: { Text(message.orEmpty()) },
+
+ confirmButton = customConfirmBtn ?: {
+ TextButton(
+ onClick = {
+ onPositiveAction?.invoke()
+ isDialogOpen.value = false
+ }
+ ) {
+ Text(positiveButtonText.orEmpty())
+ }
+ },
+
+ dismissButton = when {
+ customNegativeBtn != null -> customNegativeBtn
+ else -> {
+ negativeBtnTitle?.let { positiveText ->
+ {
+ TextButton(
+ onClick = {
+ onNegativeAction?.invoke()
+ isDialogOpen.value = false
+ }
+ ) {
+ Text(positiveText)
+ }
+ }
+ }
+ }
+ }
+ )
+ }
+ }
+}
diff --git a/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/AlertDialogUtils.kt b/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/AlertDialogUtils.kt
new file mode 100644
index 0000000..6664c1a
--- /dev/null
+++ b/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/AlertDialogUtils.kt
@@ -0,0 +1,24 @@
+package ru.touchin.roboswag.alerts.dialog_view
+
+import android.app.AlertDialog
+import android.widget.TextView
+import androidx.core.view.isVisible
+import ru.touchin.extensions.setOnRippleClickListener
+
+fun setupButton(alertDialog: AlertDialog, buttonView: TextView, text: String?, onButtonClick: (() -> Unit)?) {
+ buttonView.setTextOrGone(text)
+ buttonView.setOnRippleClickListener {
+ onButtonClick?.invoke()
+ alertDialog.dismiss()
+ }
+}
+
+fun TextView.setTextOrGone(text: CharSequence?) {
+ if (!text.isNullOrEmpty()) {
+ isVisible = true
+ setText(text)
+ } else {
+ isVisible = false
+ setText(null)
+ }
+}
diff --git a/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/ViewableAlertDialog.kt b/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/ViewableAlertDialog.kt
new file mode 100644
index 0000000..0ff2d76
--- /dev/null
+++ b/alerts/src/main/java/ru/touchin/roboswag/viewable_dialog/ViewableAlertDialog.kt
@@ -0,0 +1,63 @@
+package ru.touchin.roboswag.alerts
+
+import android.app.AlertDialog
+import android.content.Context
+import android.view.LayoutInflater
+import android.widget.TextView
+import ru.touchin.roboswag.alerts.dialog_view.setTextOrGone
+import ru.touchin.roboswag.alerts.dialog_view.setupButton
+
+object ViewableAlertDialog {
+
+ fun showAlertDialog(
+ context: Context,
+ title: String? = null,
+ message: String? = null,
+ positiveButtonText: String = context.getString(R.string.positive_btn),
+ onPositiveAction: (() -> Unit)? = null,
+ negativeBtnTitle: String? = null,
+ onNegativeAction: (() -> Unit)? = null,
+ dialogLayout: Int = R.layout.dialog_alert,
+ cancelable: Boolean = true,
+ onCancelAction: () -> Unit = {}
+ ) {
+ AlertDialog.Builder(context)
+ .setView(LayoutInflater.from(context).inflate(dialogLayout, null))
+ .show()
+ .apply {
+ setupAlertDialog(
+ dialog = this,
+ title = title,
+ message = message,
+ positiveButtonText = positiveButtonText,
+ onPositiveClick = onPositiveAction,
+ negativeButtonText = negativeBtnTitle,
+ onNegativeClick = onNegativeAction,
+ cancelable = cancelable,
+ onCancelAction = onCancelAction)
+ }
+ }
+
+ private fun setupAlertDialog(
+ dialog: AlertDialog,
+ title: String? = null,
+ message: String? = null,
+ positiveButtonText: String,
+ onPositiveClick: (() -> Unit)? = null,
+ negativeButtonText: String? = null,
+ onNegativeClick: (() -> Unit)? = null,
+ cancelable: Boolean = true,
+ onCancelAction: () -> Unit = {}
+ ) {
+ dialog.setCancelable(cancelable)
+ dialog.setOnDismissListener { onCancelAction() }
+ dialog.findViewById(R.id.alert_title)?.setTextOrGone(title)
+ dialog.findViewById(R.id.alert_message)?.setTextOrGone(message)
+ dialog.findViewById(R.id.alert_positive_button)?.let { buttonView ->
+ setupButton(dialog, buttonView, positiveButtonText, onPositiveClick)
+ }
+ dialog.findViewById(R.id.alert_negative_button)?.let { buttonView ->
+ setupButton(dialog, buttonView, negativeButtonText, onNegativeClick)
+ }
+ }
+}
diff --git a/alerts/src/main/res/layout/dialog_alert.xml b/alerts/src/main/res/layout/dialog_alert.xml
new file mode 100644
index 0000000..2d8797e
--- /dev/null
+++ b/alerts/src/main/res/layout/dialog_alert.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/alerts/src/main/res/values/strings.xml b/alerts/src/main/res/values/strings.xml
new file mode 100644
index 0000000..319d46c
--- /dev/null
+++ b/alerts/src/main/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ OK
+ Cancel
+
diff --git a/alerts/src/main/res/values/styles.xml b/alerts/src/main/res/values/styles.xml
new file mode 100644
index 0000000..6999012
--- /dev/null
+++ b/alerts/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+