Skip to content
Open
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
174 changes: 170 additions & 4 deletions core/src/main/java/in/testpress/fragments/WebViewFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,31 +40,68 @@ class WebViewFragment : Fragment(), EmptyViewListener {
private var enableSwipeRefresh: Boolean = false
var session: TestpressSession? = null
var lockToLandscape: Boolean = false
private var webViewClient: CustomWebViewClient? = null
var loadUrlCalledTime: Long = 0

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 8: WebViewFragment.onCreate() STARTED")

parseArguments()

android.util.Log.d("AI_TIMING", "✅ STEP 8 DONE: WebViewFragment.onCreate() completed in ${System.currentTimeMillis() - startTime}ms")
Comment on lines +48 to +53

Choose a reason for hiding this comment

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

high

These timing logs appear to be for debugging purposes. They should be removed from the final code to keep the codebase clean and avoid unnecessary logging in production.

Suggested change
val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 8: WebViewFragment.onCreate() STARTED")
parseArguments()
android.util.Log.d("AI_TIMING", "✅ STEP 8 DONE: WebViewFragment.onCreate() completed in ${System.currentTimeMillis() - startTime}ms")
parseArguments()

}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 9: WebViewFragment.onCreateView() STARTED")

_layout = WebviewFragmentBinding.inflate(inflater, container, false)

android.util.Log.d("AI_TIMING", "✅ STEP 9 DONE: WebViewFragment.onCreateView() completed in ${System.currentTimeMillis() - startTime}ms")
Comment on lines +61 to +66

Choose a reason for hiding this comment

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

high

This debugging code should be removed before merging. It adds noise to the logs and is not necessary for production builds.

Suggested change
val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 9: WebViewFragment.onCreateView() STARTED")
_layout = WebviewFragmentBinding.inflate(inflater, container, false)
android.util.Log.d("AI_TIMING", "✅ STEP 9 DONE: WebViewFragment.onCreateView() completed in ${System.currentTimeMillis() - startTime}ms")
_layout = WebviewFragmentBinding.inflate(inflater, container, false)

return layout.root
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)

val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 10: WebViewFragment.onViewCreated() STARTED")

showLoading()
android.util.Log.d("AI_TIMING", " Showing loading spinner... (${System.currentTimeMillis() - startTime}ms)")

initializedSwipeRefresh()
android.util.Log.d("AI_TIMING", " Initialized swipe refresh (${System.currentTimeMillis() - startTime}ms)")

initializeEmptyViewFragment()
android.util.Log.d("AI_TIMING", " Initialized empty view fragment (${System.currentTimeMillis() - startTime}ms)")

webView = layout.webView
listener?.onWebViewInitializationSuccess()
android.util.Log.d("AI_TIMING", " WebView reference obtained (${System.currentTimeMillis() - startTime}ms)")

android.util.Log.d("AI_TIMING", "🟦 STEP 11: Calling setupWebView()...")
val setupStart = System.currentTimeMillis()
setupWebView()
android.util.Log.d("AI_TIMING", "✅ STEP 11 DONE: setupWebView() completed in ${System.currentTimeMillis() - setupStart}ms")

android.util.Log.d("AI_TIMING", "🟦 STEP 12: Calling populateInstituteSettings()...")
val settingsStart = System.currentTimeMillis()
populateInstituteSettings()
android.util.Log.d("AI_TIMING", "✅ STEP 12 DONE: populateInstituteSettings() completed in ${System.currentTimeMillis() - settingsStart}ms")

android.util.Log.d("AI_TIMING", "🟦 STEP 13: Calling loadContent() - NETWORK REQUEST STARTS HERE...")
val loadStart = System.currentTimeMillis()
loadContent()
android.util.Log.d("AI_TIMING", "✅ STEP 13 DONE: loadContent() initiated in ${System.currentTimeMillis() - loadStart}ms (network request continues in background)")

android.util.Log.d("AI_TIMING", "✅ STEP 10 DONE: WebViewFragment.onViewCreated() completed in ${System.currentTimeMillis() - startTime}ms total")
Comment on lines +73 to +104

Choose a reason for hiding this comment

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

high

The extensive timing logs in this method are useful for debugging but should not be part of the production code. Please remove them to improve readability and avoid log spam.

Suggested change
val startTime = System.currentTimeMillis()
android.util.Log.d("AI_TIMING", "🟦 STEP 10: WebViewFragment.onViewCreated() STARTED")
showLoading()
android.util.Log.d("AI_TIMING", " Showing loading spinner... (${System.currentTimeMillis() - startTime}ms)")
initializedSwipeRefresh()
android.util.Log.d("AI_TIMING", " Initialized swipe refresh (${System.currentTimeMillis() - startTime}ms)")
initializeEmptyViewFragment()
android.util.Log.d("AI_TIMING", " Initialized empty view fragment (${System.currentTimeMillis() - startTime}ms)")
webView = layout.webView
listener?.onWebViewInitializationSuccess()
android.util.Log.d("AI_TIMING", " WebView reference obtained (${System.currentTimeMillis() - startTime}ms)")
android.util.Log.d("AI_TIMING", "🟦 STEP 11: Calling setupWebView()...")
val setupStart = System.currentTimeMillis()
setupWebView()
android.util.Log.d("AI_TIMING", "✅ STEP 11 DONE: setupWebView() completed in ${System.currentTimeMillis() - setupStart}ms")
android.util.Log.d("AI_TIMING", "🟦 STEP 12: Calling populateInstituteSettings()...")
val settingsStart = System.currentTimeMillis()
populateInstituteSettings()
android.util.Log.d("AI_TIMING", "✅ STEP 12 DONE: populateInstituteSettings() completed in ${System.currentTimeMillis() - settingsStart}ms")
android.util.Log.d("AI_TIMING", "🟦 STEP 13: Calling loadContent() - NETWORK REQUEST STARTS HERE...")
val loadStart = System.currentTimeMillis()
loadContent()
android.util.Log.d("AI_TIMING", "✅ STEP 13 DONE: loadContent() initiated in ${System.currentTimeMillis() - loadStart}ms (network request continues in background)")
android.util.Log.d("AI_TIMING", "✅ STEP 10 DONE: WebViewFragment.onViewCreated() completed in ${System.currentTimeMillis() - startTime}ms total")
showLoading()
initializedSwipeRefresh()
initializeEmptyViewFragment()
webView = layout.webView
listener?.onWebViewInitializationSuccess()
setupWebView()
populateInstituteSettings()
loadContent()

}

override fun onDestroy() {
Expand Down Expand Up @@ -112,8 +149,15 @@ class WebViewFragment : Fragment(), EmptyViewListener {
webView.settings.builtInZoomControls = false
webView.settings.cacheMode = WebSettings.LOAD_CACHE_ELSE_NETWORK
webView.settings.setSupportZoom(allowZoomControl)
webView.webViewClient = CustomWebViewClient(this)

// Set WebView clients
val webViewClient = CustomWebViewClient(this)
webView.webViewClient = webViewClient
webView.webChromeClient = CustomWebChromeClient(this)

// Store reference to client for timing
this.webViewClient = webViewClient

webView.settings.userAgentString += CUSTOM_USER_AGENT
webView.apply {
clearCache(true)
Expand All @@ -139,21 +183,143 @@ class WebViewFragment : Fragment(), EmptyViewListener {
}

private fun loadContent() {
android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", "=== DETAILED loadContent() ANALYSIS ===")
val loadContentStart = System.currentTimeMillis()

if (url.isNotEmpty()) {
webView.loadUrl(url, generateHeadersMap())
android.util.Log.d("AI_TIMING", "📍 URL to load: $url")

// Generate headers
val headersStart = System.currentTimeMillis()
val headers = generateHeadersMap()
android.util.Log.d("AI_TIMING", "⏱️ generateHeadersMap() took: ${System.currentTimeMillis() - headersStart}ms")

// Log headers
android.util.Log.d("AI_TIMING", "📋 Headers being sent:")
headers.forEach { (key, value) ->
if (key == "Authorization") {
android.util.Log.d("AI_TIMING", " $key: JWT [token hidden for security]")
} else {
android.util.Log.d("AI_TIMING", " $key: $value")
}
}

// Actually load the URL
android.util.Log.d("AI_TIMING", "🌐 Calling webView.loadUrl()...")
android.util.Log.d("AI_TIMING", "")

val webViewLoadStart = System.currentTimeMillis()
loadUrlCalledTime = webViewLoadStart // Store for timing calculations
webViewClient?.setLoadUrlTime(webViewLoadStart) // Pass to client

// 🧪 TEST MODE: Load simple HTML instead of network URL
android.util.Log.d("AI_TIMING", "🧪 TEST MODE: Loading local HTML instead of network URL")
android.util.Log.d("AI_TIMING", " This will tell us if delay is from network or WebView initialization")
android.util.Log.d("AI_TIMING", "")

val testHtml = """
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Hello World Test</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.container {
text-align: center;
padding: 40px;
background: rgba(255, 255, 255, 0.1);
border-radius: 20px;
backdrop-filter: blur(10px);
}
h1 {
font-size: 48px;
margin: 0 0 20px 0;
}
p {
font-size: 18px;
opacity: 0.9;
}
.info {
margin-top: 30px;
font-size: 14px;
opacity: 0.7;
}
</style>
</head>
<body>
<div class="container">
<h1>🚀 Hello World!</h1>
<p>WebView loaded successfully</p>
<div class="info">
<p>✅ No network request</p>
<p>✅ Pure local HTML</p>
<p>✅ Instant loading test</p>
</div>
</div>
<script>
console.log('Hello World HTML loaded at:', new Date().toISOString());
</script>
</body>
</html>
""".trimIndent()

webView.loadDataWithBaseURL(
null, // baseUrl
testHtml, // data
"text/html", // mimeType
"UTF-8", // encoding
null // historyUrl
)

// Original network URL loading (commented out for test)
// webView.loadUrl(url, headers)

android.util.Log.d("AI_TIMING", "⏱️ webView.loadDataWithBaseURL() call returned in: ${System.currentTimeMillis() - webViewLoadStart}ms")
android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", "⚠️ IMPORTANT: loadDataWithBaseURL() returns immediately!")
android.util.Log.d("AI_TIMING", " Since this is local HTML, there's NO network delay")
android.util.Log.d("AI_TIMING", " Any delay you see is purely from WebView rendering")
android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", " ⏰ Waiting for onPageStarted() callback...")
android.util.Log.d("AI_TIMING", " (Should fire almost instantly with local HTML)")

} else if (data.isNotEmpty()) {
webView.loadData(data, "text/html", null)
} else {
// If both the URL and data are empty, pass an unexpected error
showErrorView(TestpressException.unexpectedError(Exception("URL not found and data not found.")))
}

android.util.Log.d("AI_TIMING", "⏱️ Total loadContent() execution: ${System.currentTimeMillis() - loadContentStart}ms")
android.util.Log.d("AI_TIMING", "=====================================")
android.util.Log.d("AI_TIMING", "")
}

private fun generateHeadersMap(): Map<String, String> {
val headersMap = mutableMapOf<String, String>()
if (isAuthenticationRequired){
headersMap["Authorization"] = "JWT ${session?.token}"
headersMap["User-Agent"] = UserAgentProvider.get(requireContext())
val tokenStart = System.currentTimeMillis()
val token = session?.token
android.util.Log.d("AI_TIMING", " Getting auth token took: ${System.currentTimeMillis() - tokenStart}ms")

val userAgentStart = System.currentTimeMillis()
val userAgent = UserAgentProvider.get(requireContext())
android.util.Log.d("AI_TIMING", " Getting user agent took: ${System.currentTimeMillis() - userAgentStart}ms")

headersMap["Authorization"] = "JWT $token"
headersMap["User-Agent"] = userAgent
}
return headersMap
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,11 @@ import android.webkit.*
class CustomWebViewClient(val fragment: WebViewFragment) : WebViewClient() {

private var errorList = linkedMapOf<WebResourceRequest?,WebResourceResponse?>()
private var pageLoadStartTime: Long = 0

Choose a reason for hiding this comment

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

high

This property is only used for debug logging. It should be removed along with the associated logging code.

private var loadUrlCalledTime: Long = 0
private var firstResourceRequestTime: Long = 0
private var resourceLoadTimes = mutableMapOf<String, Long>()
private var mainPageUrl: String? = null

override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
val url = request?.url.toString()
Expand All @@ -34,12 +39,103 @@ class CustomWebViewClient(val fragment: WebViewFragment) : WebViewClient() {
fragment.allowNonInstituteUrlInWebView
}
}

fun setLoadUrlTime(time: Long) {
loadUrlCalledTime = time
}

override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
pageLoadStartTime = System.currentTimeMillis()
mainPageUrl = url

// Calculate time from loadUrl() call to onPageStarted()
val networkSetupTime = if (loadUrlCalledTime > 0) {
pageLoadStartTime - loadUrlCalledTime
} else {
-1L
}

android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", "================================================")
android.util.Log.d("AI_TIMING", "🟦 STEP 14: WebView.onPageStarted() FIRED!")
android.util.Log.d("AI_TIMING", "================================================")
android.util.Log.d("AI_TIMING", " URL: $url")
android.util.Log.d("AI_TIMING", "")

if (networkSetupTime > 0) {
android.util.Log.d("AI_TIMING", "⏱️ TIME FROM loadUrl() TO onPageStarted(): ${networkSetupTime}ms")
android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", "📊 THIS ${networkSetupTime}ms INCLUDES ALL NETWORK SETUP:")
android.util.Log.d("AI_TIMING", " ✅ DNS lookup (resolve domain to IP)")
android.util.Log.d("AI_TIMING", " ✅ TCP connection (3-way handshake)")
android.util.Log.d("AI_TIMING", " ✅ SSL/TLS handshake (if HTTPS)")
android.util.Log.d("AI_TIMING", " ✅ HTTP request sent to server")
android.util.Log.d("AI_TIMING", " ✅ Server processing request")
android.util.Log.d("AI_TIMING", " ✅ Response headers received")
android.util.Log.d("AI_TIMING", "")

// Estimate breakdown (rough estimates)
android.util.Log.d("AI_TIMING", "📈 ESTIMATED BREAKDOWN (typical values):")
val dns = (networkSetupTime * 0.15).toLong()
val tcp = (networkSetupTime * 0.10).toLong()
val ssl = (networkSetupTime * 0.20).toLong()
val request = (networkSetupTime * 0.05).toLong()
val server = (networkSetupTime * 0.45).toLong()
val headers = (networkSetupTime * 0.05).toLong()

android.util.Log.d("AI_TIMING", " DNS Lookup: ~${dns}ms (~15% of total)")
android.util.Log.d("AI_TIMING", " TCP Connection: ~${tcp}ms (~10% of total)")
android.util.Log.d("AI_TIMING", " SSL Handshake: ~${ssl}ms (~20% of total)")
android.util.Log.d("AI_TIMING", " HTTP Request: ~${request}ms (~5% of total)")
android.util.Log.d("AI_TIMING", " Server Processing: ~${server}ms (~45% of total) 🔴 BIGGEST")
android.util.Log.d("AI_TIMING", " Response Headers: ~${headers}ms (~5% of total)")
android.util.Log.d("AI_TIMING", "")

if (server > 1000) {
android.util.Log.d("AI_TIMING", "⚠️ WARNING: Server processing took ~${server}ms (>1 second!)")
android.util.Log.d("AI_TIMING", " This is SLOW - backend team should optimize!")
}
}

android.util.Log.d("AI_TIMING", "🔄 Now downloading HTML content and resources...")
android.util.Log.d("AI_TIMING", "================================================")
android.util.Log.d("AI_TIMING", "")

if (fragment.showLoadingBetweenPages) fragment.showLoading()
}

override fun onLoadResource(view: WebView?, url: String?) {
val currentTime = System.currentTimeMillis()

// Track first resource load time
if (firstResourceRequestTime == 0L && url != null) {
firstResourceRequestTime = currentTime
android.util.Log.d("AI_TIMING", "📥 First resource request started:")
android.util.Log.d("AI_TIMING", " URL: $url")
}

// Log main page resources
if (url != null && url == mainPageUrl) {
android.util.Log.d("AI_TIMING", "📄 Loading main HTML: $url")
}

// Track resource start time
if (url != null) {
resourceLoadTimes[url] = currentTime
}
}

override fun onPageFinished(view: WebView?, url: String?) {
val totalLoadTime = System.currentTimeMillis() - pageLoadStartTime
android.util.Log.d("AI_TIMING", "✅ STEP 14 DONE: WebView.onPageFinished() - Page loaded!")
android.util.Log.d("AI_TIMING", " Loaded URL: $url")
android.util.Log.d("AI_TIMING", " ⏱️ TOTAL PAGE LOAD TIME: ${totalLoadTime}ms")
android.util.Log.d("AI_TIMING", "")
android.util.Log.d("AI_TIMING", "========================================")
android.util.Log.d("AI_TIMING", "🎉 AI WEBVIEW FULLY LOADED AND VISIBLE!")
android.util.Log.d("AI_TIMING", "⏱️ Total time from button click: ${totalLoadTime}ms")
android.util.Log.d("AI_TIMING", "========================================")

Comment on lines +129 to +138

Choose a reason for hiding this comment

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

high

This extensive block of debug logging should be removed before merging. It's not suitable for production code.

fragment.hideLoading()
fragment.hideEmptyViewShowWebView()
checkWebViewHasError()
Expand Down
Loading