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
11 changes: 9 additions & 2 deletions packages/sparkling-debug-tool/android/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@ android {
kotlinOptions {
jvmTarget = "11"
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}

dependencies {
Expand All @@ -45,6 +50,8 @@ dependencies {
implementation(libs.lynx.service.log)
implementation(libs.lynx.service.devtool)
implementation(libs.lynx.devtool)
testImplementation(libs.junit)
testImplementation("org.robolectric:robolectric:4.11.1")

val sparklingVersion =
(findProperty("SPARKLING_ANDROID_SDK_VERSION") as? String)
Expand All @@ -58,9 +65,9 @@ dependencies {
}
val localSparklingMethod = rootProject.findProject(":sparkling-method")
if (localSparklingMethod != null) {
compileOnly(localSparklingMethod)
implementation(localSparklingMethod)
} else {
compileOnly("com.tiktok.sparkling:sparkling-method:$sparklingVersion")
implementation("com.tiktok.sparkling:sparkling-method:$sparklingVersion")
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2026 TikTok Pte. Ltd.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
package com.tiktok.sparkling.debugtool

import java.net.URI

internal const val DEV_URL_ERROR_MESSAGE = "The url must be a valid http:// or https:// URL"

internal fun normalizedDevUrl(url: String?): String? {
val normalized = url?.trim().orEmpty()
if (normalized.isEmpty()) return null

val parsed = runCatching { URI(normalized) }.getOrNull() ?: return null
val scheme = parsed.scheme?.lowercase()
if (scheme != "http" && scheme != "https") return null
if (parsed.host.isNullOrEmpty()) return null

return normalized
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ import com.tiktok.sparkling.debugtool.inspect.MethodInvocation
import com.tiktok.sparkling.debugtool.inspect.MethodInvocationStore
import com.tiktok.sparkling.debugtool.inspect.SparklingDebugAutoWiring
import com.tiktok.sparkling.debugtool.inspector.SparklingInspectorFragment
import com.tiktok.sparkling.debugtool.getDevUrl.GetDevUrlMethod
import com.tiktok.sparkling.debugtool.setDevUrl.SetDevUrlMethod
import com.tiktok.sparkling.method.registry.core.SparklingBridgeManager

/**
* Entry point for the Sparkling debug tool. The host typically only needs to
Expand Down Expand Up @@ -93,6 +96,7 @@ object SparklingDebugTool {
if (!isDebuggableApp(application) && !config.enableInNonDebuggableApp) {
return
}
registerDevUrlMethods(application)
// Always apply the all-on debug flag set when running under the debug
// tool: switches are no longer user-visible. Hosts that need a custom
// subset can still call [setFlags] explicitly.
Expand Down Expand Up @@ -333,6 +337,21 @@ object SparklingDebugTool {

private fun prefs(context: Context) = context.applicationContext.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)

private fun registerDevUrlMethods(application: Application) {
SparklingBridgeManager.registerIDLMethod(
"debugtool.getDevUrl",
clazz = GetDevUrlMethod::class.java,
) {
GetDevUrlMethod(application)
}
SparklingBridgeManager.registerIDLMethod(
"debugtool.setDevUrl",
clazz = SetDevUrlMethod::class.java,
) {
SetDevUrlMethod(application)
}
}

private fun isDebuggableApp(context: Context): Boolean = (context.applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE) != 0

private fun resolveFlags(context: Context): DebugFlags {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright (c) 2026 TikTok Pte. Ltd.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
package com.tiktok.sparkling.debugtool.getDevUrl

import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodName
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodParamField
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodParamModel
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodResultModel
import com.tiktok.sparkling.method.registry.core.base.AbsSparklingIDLMethod
import com.tiktok.sparkling.method.registry.core.model.idl.IDLMethodBaseParamModel
import com.tiktok.sparkling.method.registry.core.model.idl.IDLMethodBaseResultModel

abstract class AbsGetDevUrlMethodIDL :
AbsSparklingIDLMethod<
AbsGetDevUrlMethodIDL.IDLMethodGetDevUrlParamModel,
AbsGetDevUrlMethodIDL.IDLMethodGetDevUrlResultModel,
>() {
@IDLMethodName(name = "debugtool.getDevUrl", results = ["url"])
final override val name: String = "debugtool.getDevUrl"

@IDLMethodParamModel
interface IDLMethodGetDevUrlParamModel : IDLMethodBaseParamModel

@IDLMethodResultModel
interface IDLMethodGetDevUrlResultModel : IDLMethodBaseResultModel {
@get:IDLMethodParamField(required = true, isGetter = true, keyPath = "url")
@set:IDLMethodParamField(required = true, isGetter = false, keyPath = "url")
var url: String
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) 2026 TikTok Pte. Ltd.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
package com.tiktok.sparkling.debugtool.getDevUrl

import android.content.Context
import com.tiktok.sparkling.debugtool.SparklingDebugTool
import com.tiktok.sparkling.method.registry.core.BridgePlatformType
import com.tiktok.sparkling.method.registry.core.model.idl.CompletionBlock
import com.tiktok.sparkling.method.registry.core.utils.createXModel

class GetDevUrlMethod(
private val context: Context,
) : AbsGetDevUrlMethodIDL() {
override fun handle(
params: IDLMethodGetDevUrlParamModel,
callback: CompletionBlock<IDLMethodGetDevUrlResultModel>,
type: BridgePlatformType,
) {
callback.onSuccess(
IDLMethodGetDevUrlResultModel::class.java.createXModel(getSDKContext()?.containerID).apply {
url = SparklingDebugTool.getDevUrl(context, "")
},
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// Copyright (c) 2026 TikTok Pte. Ltd.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
package com.tiktok.sparkling.debugtool.setDevUrl

import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodName
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodParamField
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodParamModel
import com.tiktok.sparkling.method.registry.core.annotation.IDLMethodResultModel
import com.tiktok.sparkling.method.registry.core.base.AbsSparklingIDLMethod
import com.tiktok.sparkling.method.registry.core.model.idl.IDLMethodBaseParamModel
import com.tiktok.sparkling.method.registry.core.model.idl.IDLMethodBaseResultModel

abstract class AbsSetDevUrlMethodIDL :
AbsSparklingIDLMethod<
AbsSetDevUrlMethodIDL.IDLMethodSetDevUrlParamModel,
AbsSetDevUrlMethodIDL.IDLMethodSetDevUrlResultModel,
>() {
@IDLMethodName(name = "debugtool.setDevUrl", params = ["url"])
final override val name: String = "debugtool.setDevUrl"

@IDLMethodParamModel
interface IDLMethodSetDevUrlParamModel : IDLMethodBaseParamModel {
@get:IDLMethodParamField(required = false, isGetter = true, keyPath = "url")
val url: String?
}

@IDLMethodResultModel
interface IDLMethodSetDevUrlResultModel : IDLMethodBaseResultModel
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Copyright (c) 2026 TikTok Pte. Ltd.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
package com.tiktok.sparkling.debugtool.setDevUrl

import android.content.Context
import com.tiktok.sparkling.debugtool.DEV_URL_ERROR_MESSAGE
import com.tiktok.sparkling.debugtool.SparklingDebugTool
import com.tiktok.sparkling.debugtool.normalizedDevUrl
import com.tiktok.sparkling.method.registry.core.BridgePlatformType
import com.tiktok.sparkling.method.registry.core.IDLBridgeMethod
import com.tiktok.sparkling.method.registry.core.model.idl.CompletionBlock
import com.tiktok.sparkling.method.registry.core.utils.createXModel

class SetDevUrlMethod(
private val context: Context,
) : AbsSetDevUrlMethodIDL() {
override fun handle(
params: IDLMethodSetDevUrlParamModel,
callback: CompletionBlock<IDLMethodSetDevUrlResultModel>,
type: BridgePlatformType,
) {
val url =
normalizedDevUrl(params.url)
?: return callback.onFailure(IDLBridgeMethod.INVALID_PARAM, DEV_URL_ERROR_MESSAGE)

SparklingDebugTool.setDevUrl(context, url)
callback.onSuccess(
IDLMethodSetDevUrlResultModel::class.java.createXModel(getSDKContext()?.containerID),
)
}
}
Loading
Loading