feat: 集成服务端驱动 UI 演示
新增 Remote Compose 播放端与服务端生成逻辑,支持按页面拉取并渲染二进制 UI 文档。
This commit is contained in:
@@ -28,6 +28,7 @@
|
||||
</scm>
|
||||
<properties>
|
||||
<java.version>17</java.version>
|
||||
<kotlin.version>2.1.0</kotlin.version>
|
||||
</properties>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
@@ -40,6 +41,11 @@
|
||||
<artifactId>spring-boot-starter-webmvc</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.fasterxml.jackson.core</groupId>
|
||||
<artifactId>jackson-databind</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
@@ -49,17 +55,78 @@
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<scope>annotationProcessor</scope>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- Remote Compose:服务端生成二进制文档 -->
|
||||
<dependency>
|
||||
<groupId>androidx.compose.remote</groupId>
|
||||
<artifactId>remote-creation-jvm</artifactId>
|
||||
<version>1.0.0-alpha17</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-stdlib</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<repositories>
|
||||
<repository>
|
||||
<id>google</id>
|
||||
<url>https://maven.google.com</url>
|
||||
</repository>
|
||||
<repository>
|
||||
<id>central</id>
|
||||
<url>https://repo.maven.apache.org/maven2</url>
|
||||
</repository>
|
||||
</repositories>
|
||||
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>org.jetbrains.kotlin</groupId>
|
||||
<artifactId>kotlin-maven-plugin</artifactId>
|
||||
<version>${kotlin.version}</version>
|
||||
<configuration>
|
||||
<jvmTarget>17</jvmTarget>
|
||||
<args>
|
||||
<arg>-Xjsr305=strict</arg>
|
||||
</args>
|
||||
</configuration>
|
||||
<executions>
|
||||
<execution>
|
||||
<id>compile</id>
|
||||
<phase>process-sources</phase>
|
||||
<goals>
|
||||
<goal>compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>${project.basedir}/src/main/kotlin</sourceDir>
|
||||
<sourceDir>${project.basedir}/src/main/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
<execution>
|
||||
<id>test-compile</id>
|
||||
<phase>process-test-sources</phase>
|
||||
<goals>
|
||||
<goal>test-compile</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<sourceDirs>
|
||||
<sourceDir>${project.basedir}/src/test/kotlin</sourceDir>
|
||||
<sourceDir>${project.basedir}/src/test/java</sourceDir>
|
||||
</sourceDirs>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-maven-plugin</artifactId>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.ttstd.composeremote.server.controller;
|
||||
|
||||
import com.ttstd.composeremote.server.RemoteDocumentBuilderKt;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 服务端驱动 UI 接口。
|
||||
*
|
||||
* <p>一个页面一个接口:每个页面通过独立的路径暴露,例如
|
||||
* <ul>
|
||||
* <li>GET /api/remote-ui/home → 首页</li>
|
||||
* <li>GET /api/remote-ui/detail → 详情页</li>
|
||||
* </ul>
|
||||
* 返回 {@code application/octet-stream} 的 Remote Compose 二进制文档,
|
||||
* 由 Android 端 {@code RemoteComposePlayer} 原生渲染。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/remote-ui")
|
||||
public class RemoteUIController {
|
||||
|
||||
@GetMapping(
|
||||
value = {"/{page}", ""},
|
||||
produces = MediaType.APPLICATION_OCTET_STREAM_VALUE
|
||||
)
|
||||
public byte[] getPage(@PathVariable(name = "page", required = false) String page) {
|
||||
String target = (page == null || page.isBlank()) ? "home" : page;
|
||||
return RemoteDocumentBuilderKt.buildRemoteDocument(target);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
package com.ttstd.composeremote.server
|
||||
|
||||
import androidx.compose.remote.core.CoreDocument
|
||||
import androidx.compose.remote.creation.JvmRcPlatformServices
|
||||
import androidx.compose.remote.creation.RemoteComposeWriter
|
||||
import androidx.compose.remote.creation.dsl.RcColumnScope
|
||||
import androidx.compose.remote.creation.dsl.RcProfile
|
||||
import androidx.compose.remote.creation.dsl.RcSp
|
||||
import androidx.compose.remote.creation.dsl.createRcBuffer
|
||||
import androidx.compose.remote.creation.dsl.fillMaxSize
|
||||
import androidx.compose.remote.creation.dsl.fillMaxWidth
|
||||
import androidx.compose.remote.creation.dsl.padding
|
||||
import androidx.compose.remote.creation.dsl.background
|
||||
import androidx.compose.remote.creation.dsl.Modifier
|
||||
import androidx.compose.remote.creation.profile.Profile
|
||||
import androidx.compose.remote.creation.profile.RemoteComposeWriterFactory
|
||||
|
||||
fun buildRemoteDocument(page: String): ByteArray {
|
||||
val writerFactory = RemoteComposeWriterFactory { _, _, _ ->
|
||||
RemoteComposeWriter(1080, 1920, "remote-ui", JvmRcPlatformServices())
|
||||
}
|
||||
val profile = Profile(
|
||||
CoreDocument.DOCUMENT_API_LEVEL,
|
||||
CoreDocument.PROFILE,
|
||||
JvmRcPlatformServices(),
|
||||
writerFactory
|
||||
)
|
||||
val rcProfile = RcProfile(profile, false)
|
||||
return createRcBuffer(rcProfile) {
|
||||
Column(Modifier.fillMaxSize().background(0xFFFFFFFF.toInt()).padding(16f)) {
|
||||
Text(
|
||||
text = if (page == "home") "Home Page" else "Detail Page",
|
||||
fontSize = RcSp(36f),
|
||||
color = 0xFF0000FF.toInt() // Blue
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user