Native splash held while connecting; offline shows the same picture with a connecting line; web splash hidden inside the app
Inside the app the web splash is invisible with the new APK (the system splash is still on top) and icon-only for old APKs, so there is no second picture to switch to. Offline or a 5xx on the main frame: system splash released onto the same icon (288dp drawable) with 'در حال اتصال به اینترنت…', retried every 5s. A slow-but-online load shows nothing extra. Debug build compiles; not run on a device. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ package com.tadrisino.app
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
@@ -11,7 +12,14 @@ import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.os.VibratorManager
|
||||
import android.util.Log
|
||||
import android.util.TypedValue
|
||||
import android.view.Gravity
|
||||
import android.view.View
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import android.webkit.JavascriptInterface
|
||||
import android.webkit.WebResourceResponse
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
@@ -46,11 +54,60 @@ class MainActivity : AppCompatActivity() {
|
||||
private lateinit var webView: WebView
|
||||
|
||||
/**
|
||||
* اسپلشِ سیستم تا وقتی true نشده روی صفحه میماند. فرانت وقتی اولین صفحهی واقعی (ورود/خانه) کشیده شد
|
||||
* `TadrisinoAndroid.appReady()` را صدا میزند. اگر هرگز نرسید (سایت بالا نیامد، JS خطا داد) بعد از
|
||||
* SPLASH_MAX_MS رها میشود تا کاربر بهجای اسپلشِ بیانتها صفحهی خطای WebView را ببیند، نه چیزِ دیگر.
|
||||
* اسپلشِ سیستم تا وقتی true نشده روی صفحه میماند، هرچقدر اتصال کند باشد (خواستِ مالک): فرانت وقتی
|
||||
* اولین صفحهی واقعی (ورود/خانه) کشیده شد `TadrisinoAndroid.appReady()` را صدا میزند.
|
||||
* فقط دو حالت زودتر رهایش میکند: صفحهی اصلی بالا نیامد (آفلاین/خطای سرور → [showOffline]، با همان
|
||||
* تصویر و یک جمله)، یا صفحه لود شد ولی تا [READY_WATCHDOG_MS] appReady نیامد (خطای JS؛ لاگ میشود).
|
||||
*/
|
||||
@Volatile private var webReady = false
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private lateinit var offlineView: View
|
||||
private var offlineShown = false
|
||||
private val retryLoad = object : Runnable {
|
||||
override fun run() {
|
||||
if (!offlineShown) return
|
||||
webView.loadUrl(BuildConfig.WEB_URL)
|
||||
handler.postDelayed(this, RETRY_MS)
|
||||
}
|
||||
}
|
||||
|
||||
private fun dp(v: Int): Int = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, v.toFloat(), resources.displayMetrics).toInt()
|
||||
|
||||
/** همان تصویرِ اسپلشِ سیستم (۲۸۸dp، همان drawable) با یک جمله زیرش؛ جای آیکون با اسپلشِ سیستم یکی است. */
|
||||
private fun buildOfflineView(): View = FrameLayout(this).apply {
|
||||
setBackgroundColor(Color.WHITE)
|
||||
visibility = View.GONE
|
||||
isClickable = true
|
||||
addView(ImageView(context).apply { setImageResource(R.drawable.splash_icon) },
|
||||
FrameLayout.LayoutParams(dp(288), dp(288), Gravity.CENTER))
|
||||
addView(TextView(context).apply {
|
||||
text = "در حال اتصال به اینترنت…"
|
||||
setTextColor(Color.parseColor("#1a1a2e"))
|
||||
setTextSize(TypedValue.COMPLEX_UNIT_SP, 16f)
|
||||
layoutDirection = View.LAYOUT_DIRECTION_RTL
|
||||
gravity = Gravity.CENTER
|
||||
}, FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT, Gravity.CENTER)
|
||||
.apply { topMargin = dp(150) })
|
||||
}
|
||||
|
||||
private fun showOffline() {
|
||||
if (!offlineShown) {
|
||||
offlineShown = true
|
||||
webView.visibility = View.INVISIBLE // صفحهی خطای پیشفرضِ WebView دیده نشود
|
||||
offlineView.visibility = View.VISIBLE
|
||||
handler.postDelayed(retryLoad, RETRY_MS)
|
||||
}
|
||||
webReady = true
|
||||
}
|
||||
|
||||
private fun hideOffline() {
|
||||
if (!offlineShown) return
|
||||
offlineShown = false
|
||||
handler.removeCallbacks(retryLoad)
|
||||
offlineView.visibility = View.GONE
|
||||
webView.visibility = View.VISIBLE
|
||||
}
|
||||
private var payment: Payment? = null
|
||||
private var connection: Connection? = null
|
||||
private val webHost: String = Uri.parse(BuildConfig.WEB_URL).host ?: ""
|
||||
@@ -70,13 +127,13 @@ class MainActivity : AppCompatActivity() {
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
installSplashScreen().setKeepOnScreenCondition { !webReady }
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
if (!webReady) Log.w(TAG, "web app never reported ready; releasing the splash after ${SPLASH_MAX_MS}ms")
|
||||
webReady = true
|
||||
}, SPLASH_MAX_MS)
|
||||
super.onCreate(savedInstanceState)
|
||||
webView = WebView(this)
|
||||
setContentView(webView)
|
||||
offlineView = buildOfflineView()
|
||||
setContentView(FrameLayout(this).apply {
|
||||
addView(webView)
|
||||
addView(offlineView)
|
||||
})
|
||||
|
||||
webView.settings.javaScriptEnabled = true
|
||||
webView.settings.domStorageEnabled = true // توکنِ ورود در localStorage است
|
||||
@@ -86,9 +143,21 @@ class MainActivity : AppCompatActivity() {
|
||||
// صداهای کوتاهِ رابط (Web Audio) بعد از پاسخِ سرور پخش میشوند، نه درست در لحظهی لمس.
|
||||
webView.settings.mediaPlaybackRequiresUserGesture = false
|
||||
webView.webViewClient = object : WebViewClient() {
|
||||
// صفحهی اصلی بالا نیامد (نبودِ شبکه): اسپلش را رها کن تا خطای WebView دیده شود، نه ۱۰ ثانیه انتظار.
|
||||
// صفحهی اصلی بالا نیامد (بیاینترنت یا خطای سرور): همان تصویر با «در حال اتصال»، و هر چند ثانیه دوباره.
|
||||
override fun onReceivedError(view: WebView, request: WebResourceRequest, error: android.webkit.WebResourceError) {
|
||||
if (request.isForMainFrame) webReady = true
|
||||
if (request.isForMainFrame) showOffline()
|
||||
}
|
||||
|
||||
override fun onReceivedHttpError(view: WebView, request: WebResourceRequest, errorResponse: WebResourceResponse) {
|
||||
if (request.isForMainFrame && errorResponse.statusCode >= 500) showOffline()
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String?) {
|
||||
if (offlineShown) return // صفحهی خطا هم onPageFinished میزند؛ تا بارِ موفق همان تصویر بماند
|
||||
handler.postDelayed({
|
||||
if (!webReady) Log.e(TAG, "page loaded but appReady never came in ${READY_WATCHDOG_MS}ms; releasing the splash")
|
||||
webReady = true
|
||||
}, READY_WATCHDOG_MS)
|
||||
}
|
||||
|
||||
// هر آدرسی بیرونِ دامنهی خودمان در مرورگرِ گوشی باز میشود، نه داخلِ اپ؛ پس پل فقط
|
||||
@@ -229,6 +298,7 @@ class MainActivity : AppCompatActivity() {
|
||||
@JavascriptInterface
|
||||
fun appReady() {
|
||||
webReady = true
|
||||
runOnUiThread { hideOffline() }
|
||||
}
|
||||
|
||||
/** فرانت با این میفهمد درگاه داخلِ اپ باز میشود (isPaymentPage)، نه در مرورگرِ بیرونی. */
|
||||
@@ -251,6 +321,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "Tadrisino"
|
||||
private const val SPLASH_MAX_MS = 10_000L
|
||||
private const val RETRY_MS = 5_000L
|
||||
private const val READY_WATCHDOG_MS = 15_000L
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user