Skip to content

Connect to localhost server dialog #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/android_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ jobs:
test-feature:
runs-on: ubuntu-latest

needs: build

steps:
- name: Checkout code
uses: actions/checkout@v4.1.0
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ User interface of the app is translated for languages listed in this table:
| Ukrainian | 0.1.0 | `Translated` |
| Turkish | 0.4.1 | `Translated` |
| Russian | 0.5.5 | `Translated` |
| Chinese (Simplified) | 0.6.2 | `Translated` |

Any contributions to the translations are welcome.

Expand Down
4 changes: 2 additions & 2 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
namespace 'com.shifthackz.aisdv1.app'
defaultConfig {
applicationId "com.shifthackz.aisdv1.app"
versionName "0.6.1"
versionCode 180
versionName "0.6.2"
versionCode 182

buildConfigField "String", "IMAGE_CDN_URL", "\"https://random.imagecdn.app/\""
buildConfigField "String", "HUGGING_FACE_URL", "\"https://huggingface.co/\""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ interface UrlValidator {
sealed interface Error {
data object Empty : Error
data object BadScheme : Error
data object BadPort : Error
data object Invalid : Error
data object Localhost : Error
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.shifthackz.aisdv1.core.validation.url
import android.util.Patterns
import android.webkit.URLUtil
import com.shifthackz.aisdv1.core.validation.ValidationResult
import java.net.URI

internal class UrlValidatorImpl : UrlValidator {

Expand All @@ -19,7 +20,11 @@ internal class UrlValidatorImpl : UrlValidator {
isValid = false,
validationError = UrlValidator.Error.BadScheme,
)
input.contains(LOCALHOST_IPV4) -> ValidationResult(
!isPortValid(input) -> ValidationResult(
isValid = false,
validationError = UrlValidator.Error.BadPort,
)
isLocalhostUrl(input) -> ValidationResult(
isValid = false,
validationError = UrlValidator.Error.Localhost,
)
Expand All @@ -34,9 +39,29 @@ internal class UrlValidatorImpl : UrlValidator {
else -> ValidationResult(isValid = true)
}

private fun isPortValid(url: String): Boolean = try {
val uri = URI(url)
val port = uri.port
port in 1..65535 || port == -1
} catch (e: Exception) {
false
}

private fun isLocalhostUrl(url: String): Boolean = try {
val uri = URI(url)
val host = uri.host
host.equals(LOCALHOST_ALIAS, true)
|| host.equals(LOCALHOST_IPV4, true)
|| host.equals(LOCALHOST_IPV6, true)
} catch (e: Exception) {
false
}

companion object {
private const val SCHEME_HTTPS = "https://"
private const val SCHEME_HTTP = "http://"
private const val LOCALHOST_ALIAS = "localhost"
private const val LOCALHOST_IPV4 = "127.0.0.1"
private const val LOCALHOST_IPV6 = "[::1]"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ fun ModalRenderer(
onDismissRequest = dismiss,
)

Modal.ExportInProgress -> ProgressDialog(
Modal.ExportInProgress -> ProgressDialog(
titleResId = R.string.exporting_progress_title,
subTitleResId = R.string.exporting_progress_sub_title,
canDismiss = false,
Expand Down Expand Up @@ -242,5 +242,14 @@ fun ModalRenderer(
onDismissRequest = dismiss,
onResult = { processIntent(ImageToImageIntent.UpdateImage(it)) }
)

Modal.ConnectLocalHost -> DecisionInteractiveDialog(
title = R.string.interaction_warning_title.asUiText(),
text = R.string.interaction_warning_localhost_sub_title.asUiText(),
confirmActionResId = R.string.action_connect,
dismissActionResId = R.string.cancel,
onConfirmAction = { processIntent(ServerSetupIntent.ConnectToLocalHost) },
onDismissRequest = dismiss,
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ sealed interface Modal {

data object ExportInProgress : Modal

data object ConnectLocalHost : Modal


@Immutable
data class SelectSdModel(val models: List<String>, val selected: String) : Modal
Expand Down Expand Up @@ -64,13 +66,13 @@ sealed interface Modal {
sealed interface Image : Modal {

@Immutable
data class Single(val result: AiGenerationResult, val autoSaveEnabled: Boolean): Image
data class Single(val result: AiGenerationResult, val autoSaveEnabled: Boolean) : Image

@Immutable
data class Batch(val results: List<AiGenerationResult>, val autoSaveEnabled: Boolean): Image
data class Batch(val results: List<AiGenerationResult>, val autoSaveEnabled: Boolean) : Image

@Immutable
data class Crop(val bitmap: Bitmap): Image
data class Crop(val bitmap: Bitmap) : Image

companion object {
fun create(list: List<AiGenerationResult>, autoSaveEnabled: Boolean): Image =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ sealed interface ServerSetupIntent : MviIntent {

data object LaunchManageStoragePermission : ServerSetupIntent

data object ConnectToLocalHost : ServerSetupIntent

sealed class LaunchUrl : ServerSetupIntent, KoinComponent {

protected val linksProvider: LinksProvider by inject()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ class ServerSetupViewModel(
it.copy(step = ServerSetupState.Step.CONFIGURE)
}

ServerSetupState.Step.CONFIGURE -> connectToServer()
ServerSetupState.Step.CONFIGURE -> validateAndConnectToServer()
}

is ServerSetupIntent.UpdateAuthType -> updateState {
Expand Down Expand Up @@ -200,10 +200,16 @@ class ServerSetupViewModel(
is ServerSetupIntent.UpdateStabilityAiApiKey -> updateState {
it.copy(stabilityAiApiKey = intent.key)
}

ServerSetupIntent.ConnectToLocalHost -> connectToServer()
}

private fun connectToServer() {
private fun validateAndConnectToServer() {
if (!validate()) return
connectToServer()
}

private fun connectToServer() {
emitEffect(ServerSetupEffect.HideKeyboard)
!when (currentState.mode) {
ServerSource.HORDE -> connectToHorde()
Expand All @@ -212,8 +218,10 @@ class ServerSetupViewModel(
ServerSource.HUGGING_FACE -> connectToHuggingFace()
ServerSource.OPEN_AI -> connectToOpenAi()
ServerSource.STABILITY_AI -> connectToStabilityAi()
}.doOnSubscribe { setScreenModal(Modal.Communicating(canCancel = false)) }
.subscribeOnMainThread(schedulersProvider).subscribeBy(::errorLog) { result ->
}
.doOnSubscribe { setScreenModal(Modal.Communicating(canCancel = false)) }
.subscribeOnMainThread(schedulersProvider)
.subscribeBy(::errorLog) { result ->
result.fold(
onSuccess = { onSetupComplete() },
onFailure = { t ->
Expand All @@ -230,8 +238,8 @@ class ServerSetupViewModel(
else {
val serverUrlValidation = urlValidator(currentState.serverUrl)
var isValid = serverUrlValidation.isValid
updateState {
var newState = it.copy(
updateState { state ->
var newState = state.copy(
serverUrlValidationError = serverUrlValidation.mapToUi()
)
if (currentState.authType == ServerSetupState.AuthType.HTTP_BASIC) {
Expand All @@ -243,6 +251,12 @@ class ServerSetupViewModel(
)
isValid = isValid && loginValidation.isValid && passwordValidation.isValid
}
if (serverUrlValidation.validationError is UrlValidator.Error.Localhost
&& newState.loginValidationError == null
&& newState.passwordValidationError == null
) {
newState = newState.copy(screenModal = Modal.ConnectLocalHost)
}
newState
}
isValid
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,11 +66,12 @@ fun Automatic1111Form(
supportingText = state.serverUrlValidationError
?.takeIf { !state.demoMode }
?.let { { Text(it.asString(), color = MaterialTheme.colorScheme.error) } },
maxLines = 1,
)
if (!state.demoMode) {
DropdownTextField(
modifier = fieldModifier,
label = "Authorization".asUiText(),
label = R.string.auth_title.asUiText(),
items = ServerSetupState.AuthType.entries,
value = state.authType,
onItemSelected = {
Expand All @@ -85,7 +86,6 @@ fun Automatic1111Form(
)
when (state.authType) {
ServerSetupState.AuthType.HTTP_BASIC -> {

TextField(
modifier = fieldModifier,
value = state.login,
Expand All @@ -97,6 +97,7 @@ fun Automatic1111Form(
supportingText = state.loginValidationError?.let {
{ Text(it.asString(), color = MaterialTheme.colorScheme.error) }
},
maxLines = 1,
)
TextField(
modifier = fieldModifier,
Expand All @@ -107,8 +108,11 @@ fun Automatic1111Form(
label = { Text(stringResource(id = R.string.hint_password)) },
isError = state.passwordValidationError != null,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Password),
visualTransformation = if (state.passwordVisible) VisualTransformation.None
else PasswordVisualTransformation(),
visualTransformation = if (state.passwordVisible) {
VisualTransformation.None
} else {
PasswordVisualTransformation()
},
supportingText = state.passwordValidationError?.let {
{ Text(it.asString(), color = MaterialTheme.colorScheme.error) }
},
Expand All @@ -126,7 +130,8 @@ fun Automatic1111Form(
},
content = { Icon(image, description) },
)
}
},
maxLines = 1,
)
}
else -> Unit
Expand Down Expand Up @@ -171,4 +176,4 @@ fun Automatic1111Form(
style = MaterialTheme.typography.bodyMedium,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ fun ValidationResult<UrlValidator.Error>.mapToUi(): UiText? {
if (this.isValid) return null
return when (validationError as UrlValidator.Error) {
UrlValidator.Error.BadScheme -> R.string.error_invalid_scheme
UrlValidator.Error.BadPort -> R.string.error_invalid_port
UrlValidator.Error.Empty -> R.string.error_empty_url
UrlValidator.Error.Invalid -> R.string.error_invalid_url
UrlValidator.Error.Localhost -> R.string.error_localhost_url
}.asUiText()
UrlValidator.Error.Localhost -> null
}?.asUiText()
}
5 changes: 5 additions & 0 deletions presentation/src/main/res/values-ru/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<string name="hint_in_paint_area">Область Inpaint</string>
<string name="hint_autodetect">Авто</string>

<string name="auth_title">Авторизация</string>
<string name="auth_anonymous">Анонимно</string>
<string name="auth_http_basic">HTTP Базовая</string>

Expand Down Expand Up @@ -208,6 +209,9 @@
<string name="interaction_export_sub_title">Эта функция экспортирует все изображения галереи в архив *.zip. Этот процесс может длиться долго, если у вас много изображений. Хотите продолжить?</string>
<string name="interaction_cache_sub_title">Это приведет к сбросу настроек программы и удалению всех созданных изображений. Вы хотите продолжить?</string>

<string name="interaction_warning_title">Предупреждение</string>
<string name="interaction_warning_localhost_sub_title">Вы пытаетесь подключиться к локальному серверу localhost (127.0.0.1).\n\nЭто может не сработать, если на вашем Android-устройстве не настроено туннелирование ssh или другой механизм переадресации портов.</string>

<string name="interaction_delete_generation_title">Удалить изображение</string>
<string name="interaction_delete_generation_sub_title">Вы уверены, что хотите окончательно удалить это изображение?</string>

Expand All @@ -230,6 +234,7 @@
<string name="error_empty_field">Поле не может быть пустым</string>
<string name="error_invalid">Неверные данные</string>
<string name="error_invalid_scheme">URL должно начинаться с <b>http://</b> или <b>https://</b></string>
<string name="error_invalid_port">Порт должен быть в диапазоне от 0 до 65535</string>
<string name="error_invalid_url">Недействительный URL-адрес сервера</string>
<string name="error_localhost_url">Вы не можете использовать localhost (127.0.0.1) URL-адрес для подключения к серверу.</string>
<string name="error_min_size">Минимальный размер %1$s</string>
Expand Down
9 changes: 7 additions & 2 deletions presentation/src/main/res/values-tr/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,13 +89,14 @@
<string name="hint_in_paint_area">Inpaint alan</string>
<string name="hint_autodetect">Otomatik olarak algıla</string>

<string name="auth_title">Yetki</string>
<string name="auth_anonymous">Anonim</string>
<string name="auth_http_basic">HTTP Temel</string>

<string name="hint_server_setup_title">Lütfen Stable Diffusion WebUI(AUTOMATIC1111) URL adresinizi yazın.</string>
<string name="hint_valid_urls">Bazı sunucu örnekleri:\n• http://192.168.0.2:7860\n• http://alanadiniz.com:7860\n• https://alanadiniz.com</string>
<string name="hint_demo_mode">This mode allows you to test the application behavior, even if you don\'t have Stable Diffusion WebUI server.\n\nIn demo mode app ignores user prompt, does not use AI server, and returns some mock images.</string>
<string name="hint_args_warning" tools:ignore="TypographyDashes">Before connecting ensure that:\n• you are running AUTOMATIC1111 WebUI with flags --api --listen\n• your firewall is not blocking 7860 port\n• phone is on the same WiFi with your PC</string>
<string name="hint_args_warning" tools:ignore="TypographyDashes,Typos">Before connecting ensure that:\n• you are running AUTOMATIC1111 WebUI with flags --api --listen\n• your firewall is not blocking 7860 port\n• phone is on the same WiFi with your PC</string>

<string name="hint_server_horde_title">Horde AI bulutuna bağlanın</string>
<string name="hint_server_horde_sub_title">Horde AI, Görüntü oluşturma çalışanları ve metin oluşturma çalışanlarından oluşan kitle kaynaklı dağıtılmış bir kümedir.</string>
Expand Down Expand Up @@ -128,7 +129,7 @@
<string name="home_tab_settings">Ayarlar</string>

<string name="gallery_tab_image">Resim</string>
<string name="gallery_tab_original">Orjinal</string>
<string name="gallery_tab_original">Orijinal</string>
<string name="gallery_tab_info">Bilgi</string>

<string name="title_text_to_image">Yazıdan Resime</string>
Expand Down Expand Up @@ -208,6 +209,9 @@
<string name="interaction_export_sub_title">Bu işlem bütün galerideki resimleri tek bir .zip arşivi dosyası olarak dışa aktaracaktır. Galerinizin boyutuna göre bu işlem uzun bir zaman alabailir. Devam etmek istiyor musunuz?</string>
<string name="interaction_cache_sub_title">Bu işlem bütün uygulama ayarlarını ve oluşturulan resimleri silecektir. Devam etmek istiyor musunuz?</string>

<string name="interaction_warning_title">Uyarı</string>
<string name="interaction_warning_localhost_sub_title">Localhost (127.0.0.1) sunucusuna bağlanmaya çalışıyorsunuz.\n\nAndroid cihazınızda ssh tüneli veya başka bir bağlantı noktası yönlendirme mekanizması kurulu olmadığı sürece çalışmayabilir.</string>

<string name="interaction_delete_generation_title">Resmi sil</string>
<string name="interaction_delete_generation_sub_title">Kalıcı olarak bu resmi silmek istediğinize emin misiniz?</string>

Expand All @@ -230,6 +234,7 @@
<string name="error_empty_field">Alan boş olmamalıdır</string>
<string name="error_invalid">Geçerli olmayan bilgi girişi</string>
<string name="error_invalid_scheme">Sunucu adresi <b>http://</b> veya <b>https://</b> ile başlamalıdır.</string>
<string name="error_invalid_port">Port 0 ile 65535 aralığında olmalıdır</string>
<string name="error_invalid_url">Geçersiz sunucu adresi.</string>
<string name="error_localhost_url">Sunucuya bağlanmak için localhost (127.0.0.1) URL adresi kullanamazsınız.</string>
<string name="error_min_size">Asgari boyut %1$s</string>
Expand Down
5 changes: 5 additions & 0 deletions presentation/src/main/res/values-uk/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@
<string name="hint_in_paint_area">Область Inpaint</string>
<string name="hint_autodetect">Авто</string>

<string name="auth_title">Авторизація</string>
<string name="auth_anonymous">Анонімна</string>
<string name="auth_http_basic">HTTP Базова</string>

Expand Down Expand Up @@ -208,6 +209,9 @@
<string name="interaction_export_sub_title">Ця функція експортує всі зображення галереї у архів *.zip. Цей процес може тривати довго, якщо у вас багато зображень. Бажаєте продовжити?</string>
<string name="interaction_cache_sub_title">Це призведе до скидання налаштувань програми та видалення всіх створених зображень. Ви бажаєте продовжити?</string>

<string name="interaction_warning_title">Попередження</string>
<string name="interaction_warning_localhost_sub_title">Ви намагаєтеся підключитися до локального сервера localhost (127.0.0.1).\n\nЦе може не працювати, якщо на вашому пристрої Android не налаштовано SSH-тунелювання або інший механізм переадресації портів.</string>

<string name="interaction_delete_generation_title">Видалити зображення</string>
<string name="interaction_delete_generation_sub_title">Ви впевнені, що хочете остаточно видалити це зображення?</string>

Expand All @@ -230,6 +234,7 @@
<string name="error_empty_field">Поле не може бути пустим</string>
<string name="error_invalid">Неправильні дані</string>
<string name="error_invalid_scheme">URL має починатися з <b>http://</b> або <b>https://</b></string>
<string name="error_invalid_port">Порт має бути в діапазоні від 0 до 65535</string>
<string name="error_invalid_url">Недійсна URL-адреса сервера</string>
<string name="error_localhost_url">Ви не можете використовувати localhost (127.0.0.1) URL-адресу для підключення до сервера.</string>
<string name="error_min_size">Мінімальний розмір %1$s</string>
Expand Down
Loading
Loading