Skip to content

Commit

Permalink
Updated with changes made by Woo Android team
Browse files Browse the repository at this point in the history
  • Loading branch information
nbradbury committed Feb 14, 2025
1 parent 1a15115 commit 60db7e6
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 30 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,65 @@ 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

@Suppress("TooGenericExceptionCaught", "SwallowedException", "MemberNameEqualsClassName")
class UserAgent(appContext: Context, appName: String) {
var 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 {
// default to using just the app name and version
val appWithVersion = "$appName/${PackageUtils.getVersionName(appContext)}"
userAgent = appWithVersion

// then get the default user agent on a separate thread - this can cause an ANR on the main thread
// see: https://github.com/wordpress-mobile/WordPress-Android/issues/21679#issuecomment-2654510229
CoroutineScope(Dispatchers.Default).launch {
try {
val defaultUserAgent = WebSettings.getDefaultUserAgent(appContext)
if (defaultUserAgent.isNotEmpty()) {
userAgent = "$defaultUserAgent $appWithVersion"
}
} catch (e: RuntimeException) {
// `getDefaultUserAgent()` can throw an Exception
// see: https://github.com/wordpress-mobile/WordPress-Android/issues/20147#issuecomment-1961238187
}
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)
// AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/44.0.2403.119 Mobile Safari/537.36"
val defaultUserAgent = try {
WebSettings.getDefaultUserAgent(appContext)
} 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
}

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,10 +1,7 @@
package org.wordpress.android.fluxc.network

import android.webkit.WebSettings
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mockito.mockStatic
Expand All @@ -25,21 +22,19 @@ class UserAgentTest {
@Test
fun testUserAgent() = withMockedPackageUtils {
mockStatic(WebSettings::class.java).use {
CoroutineScope(Dispatchers.Default).launch {
whenever(WebSettings.getDefaultUserAgent(context)).thenReturn(USER_AGENT)
// we need to delay here to give `getDefaultUserAgent()` time since it runs on a separate thread
delay(500)
val result = UserAgent(context, APP_NAME)
assertEquals("$USER_AGENT $APP_NAME/$APP_VERSION", result.toString())
}
whenever(WebSettings.getDefaultUserAgent(context)).thenReturn(USER_AGENT)
// 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())
}
}

@Test
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 60db7e6

Please sign in to comment.