Skip to content
This repository was archived by the owner on Feb 4, 2025. It is now read-only.

Commit 94d25d3

Browse files
Merge pull request #3067 from wordpress-mobile/fix-useragent-anr
[ANR fix] Move UserAgent initialization to a background thread
2 parents 17272cf + fbd4ece commit 94d25d3

File tree

3 files changed

+52
-14
lines changed

3 files changed

+52
-14
lines changed

example/src/main/java/org/wordpress/android/fluxc/example/di/AppConfigModule.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515

1616
import java.lang.reflect.Field;
1717

18+
import javax.inject.Singleton;
19+
1820
import dagger.Module;
1921
import dagger.Provides;
2022

@@ -41,6 +43,7 @@ public AppSecrets provideAppSecrets() {
4143
}
4244

4345
@Provides
46+
@Singleton
4447
public UserAgent provideUserAgent(Context appContext) {
4548
return new UserAgent(appContext, "fluxc-example-android");
4649
}

fluxc/src/main/java/org/wordpress/android/fluxc/network/UserAgent.kt

Lines changed: 44 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,45 @@ package org.wordpress.android.fluxc.network
22

33
import android.content.Context
44
import android.webkit.WebSettings
5+
import kotlinx.coroutines.CoroutineDispatcher
6+
import kotlinx.coroutines.CoroutineScope
7+
import kotlinx.coroutines.Dispatchers
8+
import kotlinx.coroutines.launch
9+
import org.wordpress.android.util.AppLog
510
import org.wordpress.android.util.PackageUtils
611

7-
@SuppressWarnings("SwallowedException", "TooGenericExceptionCaught", "MemberNameEqualsClassName")
8-
class UserAgent(appContext: Context?, appName: String) {
9-
val userAgent: String
12+
@Suppress("MemberNameEqualsClassName")
13+
class UserAgent @JvmOverloads constructor(
14+
private val appContext: Context?,
15+
private val appName: String,
16+
bgDispatcher: CoroutineDispatcher = Dispatchers.Default
17+
) {
18+
/**
19+
* User-Agent string when making HTTP connections, for both API traffic and WebViews.
20+
* Appends "[appName]/version" to WebView's default User-Agent string for the webservers
21+
* to get the full feature list of the browser and serve content accordingly, e.g.:
22+
* "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
23+
* AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36
24+
* wp-android/4.7"
25+
*/
26+
var userAgent: String = getAppNameVersion()
27+
private set
28+
29+
private val coroutineScope = CoroutineScope(bgDispatcher)
1030

1131
init {
32+
coroutineScope.launch {
33+
initUserAgent()
34+
}
35+
}
36+
37+
/**
38+
* Initializes the User-Agent string.
39+
* This method will be called asynchronously to avoid blocking the main thread,
40+
* because `WebSettings.getDefaultUserAgent()` can be slow.
41+
*/
42+
@Suppress("TooGenericExceptionCaught", "SwallowedException")
43+
private fun initUserAgent() {
1244
// Device's default User-Agent string.
1345
// E.g.:
1446
// "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
@@ -18,17 +50,17 @@ class UserAgent(appContext: Context?, appName: String) {
1850
} catch (e: RuntimeException) {
1951
// `getDefaultUserAgent()` can throw an Exception
2052
// see: https://github.com/wordpress-mobile/WordPress-Android/issues/20147#issuecomment-1961238187
21-
""
53+
AppLog.e(
54+
AppLog.T.UTILS,
55+
"Error getting the user's default User-Agent, ${e.stackTraceToString()}"
56+
)
57+
return
2258
}
23-
// User-Agent string when making HTTP connections, for both API traffic and WebViews.
24-
// Appends "wp-android/version" to WebView's default User-Agent string for the webservers
25-
// to get the full feature list of the browser and serve content accordingly, e.g.:
26-
// "Mozilla/5.0 (Linux; Android 6.0; Android SDK built for x86_64 Build/MASTER; wv)
27-
// AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36
28-
// wp-android/4.7"
29-
val appWithVersion = "$appName/${PackageUtils.getVersionName(appContext)}"
30-
userAgent = if (defaultUserAgent.isNotEmpty()) "$defaultUserAgent $appWithVersion" else appWithVersion
59+
60+
userAgent = "$defaultUserAgent ${getAppNameVersion()}"
3161
}
3262

63+
private fun getAppNameVersion() = "$appName/${PackageUtils.getVersionName(appContext)}"
64+
3365
override fun toString(): String = userAgent
3466
}

fluxc/src/test/java/org/wordpress/android/fluxc/network/UserAgentTest.kt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package org.wordpress.android.fluxc.network
22

33
import android.webkit.WebSettings
4+
import kotlinx.coroutines.Dispatchers
45
import org.junit.Test
56
import org.junit.runner.RunWith
67
import org.mockito.Mockito.mockStatic
@@ -22,7 +23,8 @@ class UserAgentTest {
2223
fun testUserAgent() = withMockedPackageUtils {
2324
mockStatic(WebSettings::class.java).use {
2425
whenever(WebSettings.getDefaultUserAgent(context)).thenReturn(USER_AGENT)
25-
val result = UserAgent(context, APP_NAME)
26+
// Use the Unconfined dispatcher to allow the test to run synchronously
27+
val result = UserAgent(context, APP_NAME, bgDispatcher = Dispatchers.Unconfined)
2628
assertEquals("$USER_AGENT $APP_NAME/$APP_VERSION", result.toString())
2729
}
2830
}
@@ -31,7 +33,8 @@ class UserAgentTest {
3133
fun testDefaultUserAgentFailure() = withMockedPackageUtils {
3234
mockStatic(WebSettings::class.java).use {
3335
whenever(WebSettings.getDefaultUserAgent(context)).thenThrow(RuntimeException(""))
34-
val result = UserAgent(context, APP_NAME)
36+
// Use the Unconfined dispatcher to allow the test to run synchronously
37+
val result = UserAgent(context, APP_NAME, bgDispatcher = Dispatchers.Unconfined)
3538
assertEquals("$APP_NAME/$APP_VERSION", result.toString())
3639
}
3740
}

0 commit comments

Comments
 (0)