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
38 changes: 5 additions & 33 deletions .github/workflows/android-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -69,16 +69,6 @@ jobs:
- name: Checkout kit-android code
uses: actions/checkout@v4

- name: Checkout kit repository
uses: actions/checkout@v4
with:
repository: ton-connect/kit
ref: fix/android-dto # Branch with RequestError and nullable fixes
path: ton-repo
sparse-checkout: |
packages/walletkit
packages/walletkit-android-bridge

- name: Set up JDK 17
uses: actions/setup-java@v4
with:
Expand All @@ -89,35 +79,15 @@ jobs:
- name: Setup Android SDK
uses: android-actions/setup-android@v3

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'

- name: Set up pnpm
uses: pnpm/action-setup@v2
with:
version: 8

- name: Build TypeScript Bridge Bundle
run: |
cd ton-repo
pnpm install
pnpm turbo run build --filter=walletkit-android-bridge --force

- name: Copy Bridge Bundle to dist-android
- name: Populate dist-android from committed assets
run: |
mkdir -p dist-android
cp ton-repo/packages/walletkit-android-bridge/dist/* dist-android/
cp TONWalletKit-Android/impl/src/main/assets/walletkit/* dist-android/

- name: Grant execute permission for gradlew
working-directory: TONWalletKit-Android
run: chmod +x gradlew

- name: Sync WebView Assets
working-directory: TONWalletKit-Android
run: ./gradlew syncWalletKitWebViewAssets

- name: Run unit tests
working-directory: TONWalletKit-Android
run: ./gradlew :impl:testWebviewDebugUnitTest
Expand Down Expand Up @@ -288,7 +258,7 @@ jobs:
path: |
~/.android/avd/*
~/.android/adb*
key: avd-api-35-x86_64
key: avd-api-35-x86_64-pixel6

- name: Create AVD and generate snapshot
if: steps.avd-cache.outputs.cache-hit != 'true'
Expand All @@ -297,6 +267,7 @@ jobs:
api-level: 35
arch: x86_64
target: google_apis
profile: pixel_6
force-avd-creation: false
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
Expand Down Expand Up @@ -418,6 +389,7 @@ jobs:
api-level: 35
arch: x86_64
target: google_apis
profile: pixel_6
force-avd-creation: false
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
disable-animations: true
Expand Down
11 changes: 11 additions & 0 deletions Scripts/generate-api/generate-api-models.sh
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,17 @@ rm -rf "$DEST_DIR"
mkdir -p "$DEST_DIR"
cp -R "$MODELS_DIR/"* "$DEST_DIR/"

# Remove empty generated files (from x-skip-model suppression)
echo "🧹 Removing empty generated files..."
find "$DEST_DIR" -name '*.kt' -type f -empty -delete
find "$DEST_DIR" -name '*.kt' -type f | while read -r file; do
# Check if file contains only whitespace/blank lines/comments/package/suppress but no actual code
if ! grep -qE '^\s*(class |data class |sealed class |object |interface |typealias |enum class |fun |val |var |abstract )' "$file"; then
echo " Removing boilerplate-only file: $(basename "$file")"
rm "$file"
fi
done

# Clean up generated directory
echo "🧹 Cleaning up generated directory..."
rm -rf "$OUTPUT_DIR"
97 changes: 96 additions & 1 deletion Scripts/generate-api/templates/data_class.mustache
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
{{#vendorExtensions.x-skip-model}}
{{! Suppressed model (e.g., single-field variant inlined into parent union) }}
{{/vendorExtensions.x-skip-model}}
{{^vendorExtensions.x-skip-model}}
{{#vendorExtensions.x-type-alias}}
typealias {{{classname}}} = {{modelNamePrefix}}{{{vendorExtensions.x-alias-target}}}
{{/vendorExtensions.x-type-alias}}
{{^vendorExtensions.x-type-alias}}
{{#vendorExtensions.x-discriminated-union}}
{{>modelDiscriminatedUnion}}
{{/vendorExtensions.x-discriminated-union}}
{{^vendorExtensions.x-discriminated-union}}
{{#vendorExtensions.x-is-generic}}
{{>modelGeneric}}
{{/vendorExtensions.x-is-generic}}
{{^vendorExtensions.x-is-generic}}
{{^multiplatform}}
{{#kotlinx_serialization}}
import kotlinx.serialization.Serializable
Expand All @@ -16,6 +28,21 @@ import kotlinx.serialization.builtins.serializer
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
{{/enumUnknownDefaultCase}}
{{#vendorExtensions.x-inline-interface-unions}}
{{#-first}}
import kotlinx.serialization.KSerializer
import kotlinx.serialization.SerializationException
import kotlinx.serialization.descriptors.SerialDescriptor
import kotlinx.serialization.descriptors.buildClassSerialDescriptor
import kotlinx.serialization.encoding.Decoder
import kotlinx.serialization.encoding.Encoder
import kotlinx.serialization.json.JsonDecoder
import kotlinx.serialization.json.JsonEncoder
import kotlinx.serialization.json.jsonObject
import kotlinx.serialization.json.jsonPrimitive
import kotlinx.serialization.serializer
{{/-first}}
{{/vendorExtensions.x-inline-interface-unions}}
{{/kotlinx_serialization}}
{{#parcelizeModels}}
import android.os.Parcelable
Expand All @@ -40,12 +67,77 @@ import kotlinx.parcelize.Parcelize
{{#nonPublicApi}}internal {{/nonPublicApi}}{{#hasVars}}data {{/hasVars}}class {{classname}} (

{{#allVars}}
{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{^-last}},{{/-last}}
{{#vendorExtensions.x-frozen}}
@SerialName("{{{baseName}}}")
private val {{{name}}}: {{{dataType}}}{{^required}}? = null{{/required}}{{/vendorExtensions.x-frozen}}
{{^vendorExtensions.x-frozen}}
{{#vendorExtensions.x-interface-union}}
@SerialName("{{{baseName}}}")
val {{{name}}}: {{#lambda.titlecase}}{{{name}}}{{/lambda.titlecase}}{{^required}}? = null{{/required}}{{/vendorExtensions.x-interface-union}}
{{^vendorExtensions.x-interface-union}}
{{#required}}{{>data_class_req_var}}{{/required}}{{^required}}{{>data_class_opt_var}}{{/required}}{{/vendorExtensions.x-interface-union}}
{{/vendorExtensions.x-frozen}}
{{^-last}},{{/-last}}

{{/allVars}}
{{#vendorExtensions.x-constant-fields}}
{{#-first}}{{#hasVars}},{{/hasVars}}{{/-first}}
@SerialName("{{{name}}}")
val {{{name}}}: kotlin.String = "{{{value}}}"{{^-last}},{{/-last}}
{{/vendorExtensions.x-constant-fields}}
){{#parent}} : {{{parent}}}(){{/parent}} {

companion object
{{#vendorExtensions.x-inline-interface-unions}}

@Serializable(with = {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}.Serializer::class)
sealed class {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}} {
{{#cases}}

@Serializable
data class {{#lambda.titlecase}}{{{caseName}}}{{/lambda.titlecase}}(
val value: {{modelNamePrefix}}{{{typeName}}}
) : {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}()
{{/cases}}

internal object Serializer : KSerializer<{{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}> {
override val descriptor: SerialDescriptor = buildClassSerialDescriptor("{{classname}}.{{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}")

@Suppress("UNCHECKED_CAST")
override fun serialize(encoder: Encoder, value: {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}) {
val jsonEncoder = encoder as? JsonEncoder
?: throw SerializationException("{{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}} can only be serialized with JSON")

val jsonElement = when (value) {
{{#cases}}
is {{#lambda.titlecase}}{{{caseName}}}{{/lambda.titlecase}} ->
jsonEncoder.json.encodeToJsonElement(serializer<{{modelNamePrefix}}{{{typeName}}}>(), value.value)
{{/cases}}
}
jsonEncoder.encodeJsonElement(jsonElement)
}

override fun deserialize(decoder: Decoder): {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}} {
val jsonDecoder = decoder as? JsonDecoder
?: throw SerializationException("{{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}} can only be deserialized from JSON")

val jsonObject = jsonDecoder.decodeJsonElement().jsonObject
val discriminatorValue = jsonObject["{{{discriminatorField}}}"]?.jsonPrimitive?.content
?: throw SerializationException("Missing '{{{discriminatorField}}}' discriminator for {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}")

return when (discriminatorValue) {
{{#cases}}
"{{{rawValue}}}" ->
{{#lambda.titlecase}}{{{caseName}}}{{/lambda.titlecase}}(
jsonDecoder.json.decodeFromJsonElement(serializer<{{modelNamePrefix}}{{{typeName}}}>(), jsonObject)
)
{{/cases}}
else -> throw SerializationException("Unknown discriminator '$discriminatorValue' for {{#lambda.titlecase}}{{{propertyName}}}{{/lambda.titlecase}}")
}
}
}
}
{{/vendorExtensions.x-inline-interface-unions}}
{{#hasEnums}}
{{#vars}}
{{#isEnum}}
Expand All @@ -70,4 +162,7 @@ import kotlinx.parcelize.Parcelize
{{/vars}}
{{/hasEnums}}
}
{{/vendorExtensions.x-is-generic}}
{{/vendorExtensions.x-discriminated-union}}
{{/vendorExtensions.x-type-alias}}
{{/vendorExtensions.x-skip-model}}
Loading
Loading