Skip to content
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
5 changes: 4 additions & 1 deletion .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
{
"permissions": {
"allow": [
"Bash(grep -r \"\\\\.FLATPAK\\\\|Flatpak\\\\|flatpak\" D:/development/Github-Store --include=*.kt)"
"Bash(grep -r \"\\\\.FLATPAK\\\\|Flatpak\\\\|flatpak\" . --include=*.kt)",
"Bash(./gradlew :composeApp:assembleDebug)",
"Bash(./gradlew :composeApp:jvmJar)",
"Bash(./gradlew :composeApp:assembleDebug :composeApp:jvmJar)"
]
}
}
Binary file modified composeApp/release/baselineProfiles/0/composeApp-release.dm
Binary file not shown.
Binary file modified composeApp/release/baselineProfiles/1/composeApp-release.dm
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import java.net.Authenticator
import java.net.InetSocketAddress
import java.net.PasswordAuthentication
import java.net.Proxy
import java.net.ProxySelector

actual fun createPlatformHttpClient(proxyConfig: ProxyConfig): HttpClient {
Authenticator.setDefault(null)
Expand All @@ -21,9 +20,11 @@ actual fun createPlatformHttpClient(proxyConfig: ProxyConfig): HttpClient {
}

is ProxyConfig.System -> {
config {
proxySelector(ProxySelector.getDefault())
}
// java.net.ProxySelector.getDefault() does not read Android's
// per-network HTTP proxy. Android publishes the active proxy
// through standard system properties instead, which we resolve
// explicitly here so traffic actually flows through it.
proxy = resolveAndroidSystemProxy()
}

is ProxyConfig.Http -> {
Expand Down Expand Up @@ -78,3 +79,22 @@ actual fun createPlatformHttpClient(proxyConfig: ProxyConfig): HttpClient {
}
}
}

internal fun resolveAndroidSystemProxy(): Proxy {
// System properties are user/OS-supplied, so guard against malformed
// values: InetSocketAddress(String, Int) throws IllegalArgumentException
// for ports outside 0..65535.
val httpsHost = System.getProperty("https.proxyHost")?.takeIf { it.isNotBlank() }
val httpsPort = System.getProperty("https.proxyPort")?.toIntOrNull()?.takeIf { it in 1..65535 }
if (httpsHost != null && httpsPort != null) {
return Proxy(Proxy.Type.HTTP, InetSocketAddress(httpsHost, httpsPort))
}

val httpHost = System.getProperty("http.proxyHost")?.takeIf { it.isNotBlank() }
val httpPort = System.getProperty("http.proxyPort")?.toIntOrNull()?.takeIf { it in 1..65535 }
if (httpHost != null && httpPort != null) {
return Proxy(Proxy.Type.HTTP, InetSocketAddress(httpHost, httpPort))
}

return Proxy.NO_PROXY
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import okhttp3.Credentials
import okhttp3.OkHttpClient
import okhttp3.Request
import zed.rainxch.core.data.network.ProxyManager
import zed.rainxch.core.data.network.resolveAndroidSystemProxy
import zed.rainxch.core.domain.model.DownloadProgress
import zed.rainxch.core.domain.model.ProxyConfig
import zed.rainxch.core.domain.network.Downloader
Expand Down Expand Up @@ -44,7 +45,12 @@ class AndroidDownloader(
proxy(Proxy.NO_PROXY)
}

is ProxyConfig.System -> {}
is ProxyConfig.System -> {
// ProxySelector.getDefault() does not honor Android's
// per-network HTTP proxy; resolve it explicitly so
// downloads also flow through the device proxy.
proxy(resolveAndroidSystemProxy())
}

is ProxyConfig.Http -> {
proxy(Proxy(Proxy.Type.HTTP, InetSocketAddress(config.host, config.port)))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import zed.rainxch.core.data.local.db.dao.UpdateHistoryDao
import zed.rainxch.core.data.logging.KermitLogger
import zed.rainxch.core.data.network.GitHubClientProvider
import zed.rainxch.core.data.network.ProxyManager
import zed.rainxch.core.data.network.ProxyTesterImpl
import zed.rainxch.core.data.network.createGitHubHttpClient
import zed.rainxch.core.data.repository.AuthenticationStateImpl
import zed.rainxch.core.data.repository.FavouritesRepositoryImpl
Expand All @@ -36,6 +37,7 @@ import zed.rainxch.core.domain.getPlatform
import zed.rainxch.core.domain.logging.GitHubStoreLogger
import zed.rainxch.core.domain.model.Platform
import zed.rainxch.core.domain.model.ProxyConfig
import zed.rainxch.core.domain.network.ProxyTester
import zed.rainxch.core.domain.repository.AuthenticationState
import zed.rainxch.core.domain.repository.FavouritesRepository
import zed.rainxch.core.domain.repository.InstalledAppsRepository
Expand Down Expand Up @@ -117,6 +119,10 @@ val coreModule =
)
}

single<ProxyTester> {
ProxyTesterImpl()
}

single<SyncInstalledAppsUseCase> {
SyncInstalledAppsUseCase(
packageMonitor = get(),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package zed.rainxch.core.data.network

import io.ktor.client.plugins.HttpRequestTimeoutException
import io.ktor.client.plugins.HttpTimeout
import io.ktor.client.request.get
import io.ktor.client.statement.HttpResponse
import io.ktor.util.network.UnresolvedAddressException
import zed.rainxch.core.domain.model.ProxyConfig
import zed.rainxch.core.domain.network.ProxyTestOutcome
import zed.rainxch.core.domain.network.ProxyTester
import java.io.IOException
import java.net.ConnectException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
Comment on lines +11 to +14
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Declared Kotlin targets =="
rg -n --glob '**/*.gradle.kts' --glob '**/*.gradle' 'ios\(|macos|linuxX64|mingwX64|js\(|wasm|androidTarget|jvm\('

echo
echo "== JVM imports inside commonMain =="
rg -n --glob '**/src/commonMain/**/*.kt' '^import java\.'

Repository: OpenHub-Store/GitHub-Store

Length of output: 99


🏁 Script executed:

# Confirm the file exists and view its imports
cat -n "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt" | head -20

Repository: OpenHub-Store/GitHub-Store

Length of output: 995


🏁 Script executed:

# Search for Kotlin Multiplatform configuration
find . -name "build.gradle.kts" -o -name "build.gradle" -o -name "settings.gradle.kts" | head -10

Repository: OpenHub-Store/GitHub-Store

Length of output: 412


🏁 Script executed:

# Check for kotlin block in any gradle files more broadly
rg -l "kotlin\s*\{" --glob "**/*.gradle.kts" --glob "**/*.gradle"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1434


🏁 Script executed:

# Read the core/data/build.gradle.kts to check KMP target configuration
cat "core/data/build.gradle.kts"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1285


🏁 Script executed:

# Check the convention plugin definition for KMP library
cat "build-logic/convention/build.gradle.kts"

Repository: OpenHub-Store/GitHub-Store

Length of output: 2739


🏁 Script executed:

# Also search for where the convention plugin targets are defined
find "build-logic" -name "*.gradle.kts" -o -name "*.kt" | grep -i kmp | head -5

Repository: OpenHub-Store/GitHub-Store

Length of output: 138


🏁 Script executed:

# Read the KMP Library convention plugin
cat "build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 1325


🏁 Script executed:

# Search for the configureKotlinMultiplatform function
rg -n "fun configureKotlinMultiplatform" "build-logic/convention/src/main/kotlin/"

Repository: OpenHub-Store/GitHub-Store

Length of output: 52


🏁 Script executed:

# Look for convention files that define multiplatform config
fd "\.kt$" "build-logic/convention/src/main/kotlin/" | xargs grep -l "kotlin\s*\{" | head -10

Repository: OpenHub-Store/GitHub-Store

Length of output: 88


🏁 Script executed:

# Search for configureKotlinMultiplatform more broadly
rg "configureKotlinMultiplatform" "build-logic/convention/src/main/kotlin/"

Repository: OpenHub-Store/GitHub-Store

Length of output: 643


🏁 Script executed:

# List all .kt files in the convention directory
fd "\.kt$" "build-logic/convention/src/main/kotlin/" | sort

Repository: OpenHub-Store/GitHub-Store

Length of output: 1352


🏁 Script executed:

# Read the KotlinMultiplatform.kt file
cat "build-logic/convention/src/main/kotlin/zed/rainxch/githubstore/convention/KotlinMultiplatform.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 900


🏁 Script executed:

# Read the full ProxyTesterImpl.kt to see how java.* exceptions are used
cat "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 2699


🏁 Script executed:

# Search for other ProxyTester implementations
rg -n "class.*ProxyTester" --glob "**/*.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 191


🏁 Script executed:

# Check if these java.* exceptions are actually needed or if Ktor exceptions would suffice
rg -n "SocketTimeoutException|UnknownHostException|ConnectException|IOException" "core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt"

Repository: OpenHub-Store/GitHub-Store

Length of output: 394


Move JVM-specific exception handling out of commonMain.

Lines 11-14 import java.* exceptions in commonMain, violating the platform API guideline. These imports are actively used in catch blocks (lines 49, 53, 55, 57). Move JVM-only exception mapping to androidMain and jvmMain via expect/actual pattern, or replace with Ktor-native exception handling where available.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt`
around lines 11 - 14, ProxyTesterImpl currently imports JVM-only exceptions
(IOException, ConnectException, SocketTimeoutException, UnknownHostException) in
commonMain and uses them in its catch blocks; remove those java.* imports and
the JVM-specific catches from
core/data/src/commonMain/kotlin/zed/rainxch/core/data/network/ProxyTesterImpl.kt,
create an expect/actual mapping or platform-specific helper (e.g., expect fun
mapNetworkException(e: Throwable): ProxyTestResult or expect class
NetworkExceptionMapper with actual implementations in androidMain/jvmMain) and
replace the direct exception checks in the ProxyTesterImpl test method with a
call to that platform mapper (or use Ktor-native exception types if available)
so that platform-specific exception handling lives in actual implementations
while commonMain only depends on the abstract mapping API.

import kotlin.coroutines.cancellation.CancellationException
import kotlin.time.TimeSource

class ProxyTesterImpl : ProxyTester {
override suspend fun test(config: ProxyConfig): ProxyTestOutcome {
val client =
createPlatformHttpClient(config).config {
install(HttpTimeout) {
requestTimeoutMillis = TEST_TIMEOUT_MS
connectTimeoutMillis = TEST_TIMEOUT_MS
socketTimeoutMillis = TEST_TIMEOUT_MS
}
expectSuccess = false
}

return try {
val started = TimeSource.Monotonic.markNow()
val response: HttpResponse = client.get(TEST_URL)
val elapsed = started.elapsedNow().inWholeMilliseconds

when {
response.status.value == 407 ->
ProxyTestOutcome.Failure.ProxyAuthRequired

response.status.value in 200..299 ->
ProxyTestOutcome.Success(latencyMs = elapsed)

else ->
ProxyTestOutcome.Failure.UnexpectedResponse(response.status.value)
}
} catch (e: CancellationException) {
throw e
} catch (e: HttpRequestTimeoutException) {
ProxyTestOutcome.Failure.Timeout
} catch (e: SocketTimeoutException) {
ProxyTestOutcome.Failure.Timeout
} catch (e: UnresolvedAddressException) {
ProxyTestOutcome.Failure.DnsFailure
} catch (e: UnknownHostException) {
ProxyTestOutcome.Failure.DnsFailure
} catch (e: ConnectException) {
ProxyTestOutcome.Failure.ProxyUnreachable
} catch (e: IOException) {
ProxyTestOutcome.Failure.Unknown(e.message)
} catch (e: Exception) {
ProxyTestOutcome.Failure.Unknown(e.message)
} finally {
client.close()
}
}

private companion object {
const val TEST_URL = "https://api.github.com/zen"
const val TEST_TIMEOUT_MS = 8_000L
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package zed.rainxch.core.domain.network

import zed.rainxch.core.domain.model.ProxyConfig

/**
* Verifies that a [ProxyConfig] can actually reach the GitHub API. Implementations
* should issue a single lightweight request through a throwaway HTTP client built
* with the supplied config so the test exercises the same engine code path the
* real client uses.
*/
interface ProxyTester {
suspend fun test(config: ProxyConfig): ProxyTestOutcome
}

sealed interface ProxyTestOutcome {
/** Connection succeeded. [latencyMs] is the round-trip time of the test request. */
data class Success(
val latencyMs: Long,
) : ProxyTestOutcome

sealed interface Failure : ProxyTestOutcome {
/** Could not resolve a hostname (DNS failure or unresolved proxy host). */
data object DnsFailure : Failure

/** Reached the network but could not connect to the proxy itself. */
data object ProxyUnreachable : Failure

/** Connection or socket timed out. */
data object Timeout : Failure

/** Proxy returned 407 / requested authentication. */
data object ProxyAuthRequired : Failure

/** Proxy or upstream returned a non-2xx HTTP status. */
data class UnexpectedResponse(
val statusCode: Int,
) : Failure

/** Anything else (TLS errors, malformed config, etc.). */
data class Unknown(
val message: String?,
) : Failure
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,15 @@
<string name="invalid_proxy_port">منفذ الوكيل غير صالح</string>
<string name="proxy_show_password">إظهار كلمة المرور</string>
<string name="proxy_hide_password">إخفاء كلمة المرور</string>
<string name="proxy_test">اختبار</string>
<string name="proxy_test_in_progress">جارٍ الاختبار…</string>
<string name="proxy_test_success">الاتصال ناجح (%1$d مللي ثانية)</string>
<string name="proxy_test_error_dns">تعذر تحليل المضيف. تحقق من عنوان الوكيل.</string>
<string name="proxy_test_error_unreachable">تعذر الوصول إلى خادم الوكيل.</string>
<string name="proxy_test_error_timeout">انتهت مهلة الاتصال.</string>
<string name="proxy_test_error_auth_required">يلزم التحقق من الوكيل.</string>
<string name="proxy_test_error_status">استجابة غير متوقعة: HTTP %1$d</string>
<string name="proxy_test_error_unknown">فشل اختبار الاتصال</string>

<!-- Snackbar -->
<string name="logout_success">تم تسجيل الخروج بنجاح، جارٍ إعادة التوجيه...</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,15 @@
<string name="invalid_proxy_port">অবৈধ প্রক্সি পোর্ট</string>
<string name="proxy_show_password">পাসওয়ার্ড দেখান</string>
<string name="proxy_hide_password">পাসওয়ার্ড লুকান</string>
<string name="proxy_test">পরীক্ষা</string>
<string name="proxy_test_in_progress">পরীক্ষা চলছে…</string>
<string name="proxy_test_success">সংযোগ ঠিক আছে (%1$d ms)</string>
<string name="proxy_test_error_dns">হোস্ট সমাধান করা যায়নি। প্রক্সি ঠিকানা যাচাই করুন।</string>
<string name="proxy_test_error_unreachable">প্রক্সি সার্ভারে পৌঁছানো যায়নি।</string>
<string name="proxy_test_error_timeout">সংযোগের সময় শেষ হয়ে গেছে।</string>
<string name="proxy_test_error_auth_required">প্রক্সি প্রমাণীকরণ প্রয়োজন।</string>
<string name="proxy_test_error_status">অপ্রত্যাশিত প্রতিক্রিয়া: HTTP %1$d</string>
<string name="proxy_test_error_unknown">সংযোগ পরীক্ষা ব্যর্থ</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@
<string name="invalid_proxy_port">Puerto de proxy no válido</string>
<string name="proxy_show_password">Mostrar contraseña</string>
<string name="proxy_hide_password">Ocultar contraseña</string>
<string name="proxy_test">Probar</string>
<string name="proxy_test_in_progress">Probando…</string>
<string name="proxy_test_success">Conexión correcta (%1$d ms)</string>
<string name="proxy_test_error_dns">No se pudo resolver el host. Verifica la dirección del proxy.</string>
<string name="proxy_test_error_unreachable">No se pudo conectar con el servidor proxy.</string>
<string name="proxy_test_error_timeout">Se agotó el tiempo de conexión.</string>
<string name="proxy_test_error_auth_required">Se requiere autenticación del proxy.</string>
<string name="proxy_test_error_status">Respuesta inesperada: HTTP %1$d</string>
<string name="proxy_test_error_unknown">La prueba de conexión falló</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@
<string name="invalid_proxy_port">Port proxy invalide</string>
<string name="proxy_show_password">Afficher le mot de passe</string>
<string name="proxy_hide_password">Masquer le mot de passe</string>
<string name="proxy_test">Tester</string>
<string name="proxy_test_in_progress">Test en cours…</string>
<string name="proxy_test_success">Connexion OK (%1$d ms)</string>
<string name="proxy_test_error_dns">Impossible de résoudre l\'hôte. Vérifiez l\'adresse du proxy.</string>
<string name="proxy_test_error_unreachable">Impossible de joindre le serveur proxy.</string>
<string name="proxy_test_error_timeout">Délai de connexion dépassé.</string>
<string name="proxy_test_error_auth_required">Authentification proxy requise.</string>
<string name="proxy_test_error_status">Réponse inattendue : HTTP %1$d</string>
<string name="proxy_test_error_unknown">Échec du test de connexion</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,15 @@
<string name="invalid_proxy_port">अमान्य प्रॉक्सी पोर्ट</string>
<string name="proxy_show_password">पासवर्ड दिखाएँ</string>
<string name="proxy_hide_password">पासवर्ड छुपाएँ</string>
<string name="proxy_test">परीक्षण</string>
<string name="proxy_test_in_progress">परीक्षण हो रहा है…</string>
<string name="proxy_test_success">कनेक्शन ठीक है (%1$d ms)</string>
<string name="proxy_test_error_dns">होस्ट हल नहीं हो सका। प्रॉक्सी पता जाँचें।</string>
<string name="proxy_test_error_unreachable">प्रॉक्सी सर्वर तक नहीं पहुँचा जा सका।</string>
<string name="proxy_test_error_timeout">कनेक्शन समय समाप्त हो गया।</string>
<string name="proxy_test_error_auth_required">प्रॉक्सी प्रमाणीकरण आवश्यक है।</string>
<string name="proxy_test_error_status">अप्रत्याशित प्रतिक्रिया: HTTP %1$d</string>
<string name="proxy_test_error_unknown">कनेक्शन परीक्षण विफल</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,15 @@
<string name="invalid_proxy_port">Porta proxy non valida</string>
<string name="proxy_show_password">Mostra password</string>
<string name="proxy_hide_password">Nascondi password</string>
<string name="proxy_test">Verifica</string>
<string name="proxy_test_in_progress">Verifica in corso…</string>
<string name="proxy_test_success">Connessione OK (%1$d ms)</string>
<string name="proxy_test_error_dns">Impossibile risolvere l\'host. Controlla l\'indirizzo del proxy.</string>
<string name="proxy_test_error_unreachable">Impossibile raggiungere il server proxy.</string>
<string name="proxy_test_error_timeout">Timeout della connessione.</string>
<string name="proxy_test_error_auth_required">Autenticazione proxy richiesta.</string>
<string name="proxy_test_error_status">Risposta inattesa: HTTP %1$d</string>
<string name="proxy_test_error_unknown">Verifica connessione non riuscita</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -375,6 +375,15 @@
<string name="invalid_proxy_port">無効なプロキシポート</string>
<string name="proxy_show_password">パスワードを表示</string>
<string name="proxy_hide_password">パスワードを非表示</string>
<string name="proxy_test">テスト</string>
<string name="proxy_test_in_progress">テスト中…</string>
<string name="proxy_test_success">接続OK (%1$d ms)</string>
<string name="proxy_test_error_dns">ホストを解決できません。プロキシアドレスを確認してください。</string>
<string name="proxy_test_error_unreachable">プロキシサーバーに接続できません。</string>
<string name="proxy_test_error_timeout">接続がタイムアウトしました。</string>
<string name="proxy_test_error_auth_required">プロキシ認証が必要です。</string>
<string name="proxy_test_error_status">予期しない応答:HTTP %1$d</string>
<string name="proxy_test_error_unknown">接続テストに失敗しました</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -408,6 +408,15 @@
<string name="invalid_proxy_port">잘못된 프록시 포트</string>
<string name="proxy_show_password">비밀번호 표시</string>
<string name="proxy_hide_password">비밀번호 숨기기</string>
<string name="proxy_test">테스트</string>
<string name="proxy_test_in_progress">테스트 중…</string>
<string name="proxy_test_success">연결 성공 (%1$d ms)</string>
<string name="proxy_test_error_dns">호스트를 확인할 수 없습니다. 프록시 주소를 확인하세요.</string>
<string name="proxy_test_error_unreachable">프록시 서버에 연결할 수 없습니다.</string>
<string name="proxy_test_error_timeout">연결 시간이 초과되었습니다.</string>
<string name="proxy_test_error_auth_required">프록시 인증이 필요합니다.</string>
<string name="proxy_test_error_status">예기치 않은 응답: HTTP %1$d</string>
<string name="proxy_test_error_unknown">연결 테스트 실패</string>


<!-- Track app feature -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,15 @@
<string name="invalid_proxy_port">Nieprawidłowy port proxy</string>
<string name="proxy_show_password">Pokaż hasło</string>
<string name="proxy_hide_password">Ukryj hasło</string>
<string name="proxy_test">Testuj</string>
<string name="proxy_test_in_progress">Testowanie…</string>
<string name="proxy_test_success">Połączenie OK (%1$d ms)</string>
<string name="proxy_test_error_dns">Nie można rozwiązać hosta. Sprawdź adres proxy.</string>
<string name="proxy_test_error_unreachable">Nie można połączyć się z serwerem proxy.</string>
<string name="proxy_test_error_timeout">Upłynął limit czasu połączenia.</string>
<string name="proxy_test_error_auth_required">Wymagane uwierzytelnienie proxy.</string>
<string name="proxy_test_error_status">Nieoczekiwana odpowiedź: HTTP %1$d</string>
<string name="proxy_test_error_unknown">Test połączenia nie powiódł się</string>


<!-- Track app feature -->
Expand Down
Loading