Skip to content

Commit b7794de

Browse files
committed
Breaking API change: only allow apps to set a User-Agent prefix to remove error-prone situations where an application wasn't including the Hotwire substring in their custom user agnet.
1 parent 83528c7 commit b7794de

File tree

4 files changed

+64
-26
lines changed

4 files changed

+64
-26
lines changed

core/src/main/kotlin/dev/hotwire/core/config/HotwireConfig.kt

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -65,22 +65,27 @@ class HotwireConfig internal constructor() {
6565
}
6666

6767
/**
68-
* Provides a standard substring to be included in your WebView's user agent
69-
* to identify itself as a Hotwire Native app.
70-
*
71-
* Important: Ensure that you've registered your bridge components before
72-
* calling this so the bridge component names are included in your user agent.
68+
* Set a custom user agent application prefix for every WebView instance. The
69+
* library will automatically append a substring to your prefix which includes:
70+
* - "Hotwire Native Android; Turbo Native Android;"
71+
* - "bridge-components: [your bridge components];"
72+
* - The WebView's default Chromium user agent string
7373
*/
74-
fun userAgentSubstring(): String {
75-
val components = registeredBridgeComponentFactories.joinToString(" ") { it.name }
76-
return "Hotwire Native Android; Turbo Native Android; bridge-components: [$components];"
77-
}
74+
var applicationUserAgentPrefix: String? = null
7875

7976
/**
80-
* Set a custom user agent for every WebView instance.
81-
*
82-
* Important: Include `Hotwire.userAgentSubstring()` as part of your
83-
* custom user agent for compatibility with your server.
77+
* Gets the full user agent that is used for every WebView instance. This includes:
78+
* - Your (optional) custom `applicationUserAgentPrefix`
79+
* - "Hotwire Native Android; Turbo Native Android;"
80+
* - "bridge-components: [your bridge components];"
81+
* - The WebView's default Chromium user agent string
8482
*/
85-
var userAgent: String = userAgentSubstring()
83+
fun userAgent(context: Context): String {
84+
val components = registeredBridgeComponentFactories.joinToString(" ") { it.name }
85+
86+
return applicationUserAgentPrefix?.let { "$it " }.orEmpty() +
87+
"Hotwire Native Android; Turbo Native Android; " +
88+
"bridge-components: [$components]; " +
89+
Hotwire.webViewInfo(context).defaultUserAgent
90+
}
8691
}

core/src/main/kotlin/dev/hotwire/core/turbo/webview/HotwireWebView.kt

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import android.content.Context
55
import android.content.res.Configuration
66
import android.os.Build
77
import android.util.AttributeSet
8-
import android.view.View
98
import android.webkit.WebView
109
import android.widget.FrameLayout
1110
import android.widget.FrameLayout.LayoutParams.MATCH_PARENT
@@ -36,10 +35,10 @@ open class HotwireWebView @JvmOverloads constructor(
3635
internal set
3736

3837
init {
39-
id = View.generateViewId()
38+
id = generateViewId()
4039
settings.javaScriptEnabled = true
4140
settings.domStorageEnabled = true
42-
settings.userAgentString = "${Hotwire.config.userAgent} ${settings.userAgentString}"
41+
settings.userAgentString = Hotwire.config.userAgent(context)
4342
settings.setSupportMultipleWindows(true)
4443
layoutParams = FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)
4544
initDayNightTheming()
Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,58 @@
11
package dev.hotwire.core.bridge
22

3+
import android.content.Context
4+
import android.os.Build
5+
import androidx.test.core.app.ApplicationProvider
36
import dev.hotwire.core.config.Hotwire
7+
import dev.hotwire.core.turbo.BaseUnitTest
48
import org.junit.Assert.assertEquals
9+
import org.junit.Before
510
import org.junit.Test
11+
import org.junit.runner.RunWith
12+
import org.robolectric.RobolectricTestRunner
13+
import org.robolectric.annotation.Config
14+
15+
@RunWith(RobolectricTestRunner::class)
16+
@Config(sdk = [Build.VERSION_CODES.R])
17+
class UserAgentTest : BaseUnitTest() {
18+
private lateinit var context: Context
19+
20+
@Before
21+
override fun setup() {
22+
super.setup()
23+
context = ApplicationProvider.getApplicationContext()
24+
Hotwire.config.applicationUserAgentPrefix = null
25+
}
626

7-
class UserAgentTest {
827
@Test
9-
fun userAgentSubstring() {
28+
fun `user agent with no prefix`() {
1029
Hotwire.config.registeredBridgeComponentFactories = TestData.componentFactories
1130

12-
val userAgentSubstring = Hotwire.config.userAgentSubstring()
13-
assertEquals(userAgentSubstring, "Hotwire Native Android; Turbo Native Android; bridge-components: [one two];")
31+
val userAgent = Hotwire.config.userAgent(context)
32+
val expectedUserAgent =
33+
"Hotwire Native Android; Turbo Native Android; " +
34+
"bridge-components: [one two]; " +
35+
TEST_USER_AGENT
36+
37+
assertEquals(expectedUserAgent, userAgent)
1438
}
1539

1640
@Test
17-
fun userAgent() {
41+
fun `user agent with prefix`() {
42+
Hotwire.config.applicationUserAgentPrefix = "My Application Prefix;"
1843
Hotwire.config.registeredBridgeComponentFactories = TestData.componentFactories
19-
Hotwire.config.userAgent = "Test; ${Hotwire.config.userAgentSubstring()}"
2044

21-
val userAgent = Hotwire.config.userAgent
22-
assertEquals(userAgent, "Test; Hotwire Native Android; Turbo Native Android; bridge-components: [one two];")
45+
val userAgent = Hotwire.config.userAgent(context)
46+
val expectedUserAgent =
47+
"My Application Prefix; " +
48+
"Hotwire Native Android; Turbo Native Android; " +
49+
"bridge-components: [one two]; " +
50+
TEST_USER_AGENT
51+
52+
assertEquals(expectedUserAgent, userAgent)
53+
}
54+
55+
companion object {
56+
private const val TEST_USER_AGENT = "user"
2357
}
2458
}

demo/src/main/kotlin/dev/hotwire/demo/DemoApplication.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,6 @@ class DemoApplication : Application() {
6969
Hotwire.config.debugLoggingEnabled = BuildConfig.DEBUG
7070
Hotwire.config.webViewDebuggingEnabled = BuildConfig.DEBUG
7171
Hotwire.config.jsonConverter = KotlinXJsonConverter()
72-
Hotwire.config.userAgent = "Hotwire Demo; ${Hotwire.config.userAgentSubstring()}"
72+
Hotwire.config.applicationUserAgentPrefix = "Hotwire Demo;"
7373
}
7474
}

0 commit comments

Comments
 (0)