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
20 changes: 18 additions & 2 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiDroidApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import com.ichi2.utils.ExceptionUtil
import com.ichi2.utils.LanguageUtil
import com.ichi2.utils.measureTime
import com.ichi2.utils.setWebContentsDebuggingEnabled
import com.ichi2.utils.startUpWebView
import com.ichi2.widget.DayRolloverAlarm
import com.ichi2.widget.WidgetNotificationScheduler
import com.ichi2.widget.cardanalysis.CardAnalysisWidget
Expand Down Expand Up @@ -172,8 +173,6 @@ open class AnkiDroidApp :
showThemedToast(this.applicationContext, getString(R.string.user_is_a_robot), false)
}

setWebContentsDebuggingEnabled(Prefs.isWebDebugEnabled)

setupContextMenus()

setup("makeBackendUsable") { makeBackendUsable(this) }
Expand All @@ -185,6 +184,8 @@ open class AnkiDroidApp :
if (!checkWebViewAvailable()) {
return
}
// after the probe: startUpWebView throws on its executor if the WebView provider is missing (5794)
setupWebView()

// Forget the last deck that was used in the CardBrowser
CardBrowser.clearLastDeckId()
Expand Down Expand Up @@ -447,6 +448,21 @@ open class AnkiDroidApp :
}
}

/**
* Starts asynchronously loading the WebView on a background thread.
*
* TODO: This only handles a subset of WebView init, and will not produce a performance
* improvement until this pattern is used for all WebView init.
*/
private fun setupWebView() =
setup("setupWebView") {
startUpWebView(
context = this,
onSuccess = { setWebContentsDebuggingEnabled(Prefs.isWebDebugEnabled) },
onFailure = { e -> Timber.w(e, "startUpWebView") },
)
}

/**
* @return the app version, OS version and device model, provided when syncing.
*/
Expand Down
36 changes: 36 additions & 0 deletions AnkiDroid/src/main/java/com/ichi2/utils/WebViewUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,17 @@ import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
import androidx.core.content.pm.PackageInfoCompat
import androidx.webkit.WebViewCompat
import androidx.webkit.WebViewOutcomeReceiver
import androidx.webkit.WebViewStartUpConfig
import androidx.webkit.WebViewStartUpResult
import androidx.webkit.WebViewStartupException
import com.ichi2.anki.R
import com.ichi2.anki.common.crashreporting.CrashReportService
import com.ichi2.anki.utils.openUrl
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.concurrent.Executors

@JvmInline
value class WebViewVersion(
Expand Down Expand Up @@ -231,3 +236,34 @@ fun setWebContentsDebuggingEnabled(enabled: Boolean) =
// android.util.AndroidRuntimeException: android.webkit.WebViewFactory$MissingWebViewPackageException: Failed to load WebView provider: No WebView installed
Timber.w(e, "setWebContentsDebuggingEnabled")
}

/**
* Starts the WebView on a background thread, so later WebView usage is faster.
*
* Exactly one of [onSuccess] or [onFailure] is called, on the main thread, once startup completes.
*
* Background work runs on a dedicated thread, released once startup completes.
*
* @see WebViewCompat.startUpWebView
*/
fun startUpWebView(
context: Context,
onSuccess: () -> Unit,
onFailure: (Throwable) -> Unit,
) {
val executor = Executors.newSingleThreadExecutor()
val config = WebViewStartUpConfig.Builder(executor).build()
val callback =
object : WebViewOutcomeReceiver<WebViewStartUpResult, WebViewStartupException> {
override fun onResult(result: WebViewStartUpResult?) {
executor.shutdown()
onSuccess()
}

override fun onError(e: WebViewStartupException) {
executor.shutdown()
onFailure(e)
}
}
WebViewCompat.startUpWebView(context, config, callback)
}
Loading