Skip to content

Commit

Permalink
Merge pull request #3067 from wordpress-mobile/fix-useragent-anr
Browse files Browse the repository at this point in the history
[ANR fix] Move UserAgent initialization to a background thread
  • Loading branch information
hichamboushaba authored Jul 26, 2024
2 parents 17272cf + fbd4ece commit 94d25d3
Show file tree
Hide file tree
Showing 3 changed files with 52 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

import java.lang.reflect.Field;

import javax.inject.Singleton;

import dagger.Module;
import dagger.Provides;

Expand All @@ -41,6 +43,7 @@ public AppSecrets provideAppSecrets() {
}

@Provides
@Singleton
public UserAgent provideUserAgent(Context appContext) {
return new UserAgent(appContext, "fluxc-example-android");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,45 @@ package org.wordpress.android.fluxc.network

import android.content.Context
import android.webkit.WebSettings
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.wordpress.android.util.AppLog
import org.wordpress.android.util.PackageUtils

@SuppressWarnings("SwallowedException", "TooGenericExceptionCaught", "MemberNameEqualsClassName")
class UserAgent(appContext: Context?, appName: String) {
val userAgent: String
@Suppress("MemberNameEqualsClassName")
class UserAgent @JvmOverloads constructor(
private val appContext: Context?,
private val appName: String,
bgDispatcher: CoroutineDispatcher = Dispatchers.Default
) {
/**
* User-Agent string when making HTTP connections, for both API traffic and WebViews.
* Appends "[appName]/version" to WebView's default User-Agent string for the webservers
* to get the full feature list of the browser and serve content accordingly, e.g.:
* "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
* AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36
* wp-android/4.7"
*/
var userAgent: String = getAppNameVersion()
private set

private val coroutineScope = CoroutineScope(bgDispatcher)

init {
coroutineScope.launch {
initUserAgent()
}
}

/**
* Initializes the User-Agent string.
* This method will be called asynchronously to avoid blocking the main thread,
* because `WebSettings.getDefaultUserAgent()` can be slow.
*/
@Suppress("TooGenericExceptionCaught", "SwallowedException")
private fun initUserAgent() {
// Device's default User-Agent string.
// E.g.:
// "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
Expand All @@ -18,17 +50,17 @@ class UserAgent(appContext: Context?, appName: String) {
} catch (e: RuntimeException) {
// `getDefaultUserAgent()` can throw an Exception
// see: https://github.com/wordpress-mobile/WordPress-Android/issues/20147#issuecomment-1961238187
""
AppLog.e(
AppLog.T.UTILS,
"Error getting the user's default User-Agent, ${e.stackTraceToString()}"
)
return
}
// User-Agent string when making HTTP connections, for both API traffic and WebViews.
// Appends "wp-android/version" to WebView's default User-Agent string for the webservers
// to get the full feature list of the browser and serve content accordingly, e.g.:
// "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
// AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36
// wp-android/4.7"
val appWithVersion = "$appName/${PackageUtils.getVersionName(appContext)}"
userAgent = if (defaultUserAgent.isNotEmpty()) "$defaultUserAgent $appWithVersion" else appWithVersion

userAgent = "$defaultUserAgent ${getAppNameVersion()}"
}

private fun getAppNameVersion() = "$appName/${PackageUtils.getVersionName(appContext)}"

override fun toString(): String = userAgent
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.wordpress.android.fluxc.network

import android.webkit.WebSettings
import kotlinx.coroutines.Dispatchers
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mockStatic
Expand All @@ -22,7 +23,8 @@ class UserAgentTest {
fun testUserAgent() = withMockedPackageUtils {
mockStatic(WebSettings::class.java).use {
whenever(WebSettings.getDefaultUserAgent(context)).thenReturn(USER_AGENT)
val result = UserAgent(context, APP_NAME)
// Use the Unconfined dispatcher to allow the test to run synchronously
val result = UserAgent(context, APP_NAME, bgDispatcher = Dispatchers.Unconfined)
assertEquals("$USER_AGENT $APP_NAME/$APP_VERSION", result.toString())
}
}
Expand All @@ -31,7 +33,8 @@ class UserAgentTest {
fun testDefaultUserAgentFailure() = withMockedPackageUtils {
mockStatic(WebSettings::class.java).use {
whenever(WebSettings.getDefaultUserAgent(context)).thenThrow(RuntimeException(""))
val result = UserAgent(context, APP_NAME)
// Use the Unconfined dispatcher to allow the test to run synchronously
val result = UserAgent(context, APP_NAME, bgDispatcher = Dispatchers.Unconfined)
assertEquals("$APP_NAME/$APP_VERSION", result.toString())
}
}
Expand Down

0 comments on commit 94d25d3

Please sign in to comment.