集成 Timber 日志框架,降级 AGP/Gradle 适配 JDK 17,修复构建设置

- 新增 PosefitApplication 初始化 Timber DebugTree

- WebRtcSenderClient/MainActivity/SimpleSdpObserver 添加 Timber 日志

- AGP 9.2.1 → 8.7.3,Gradle 9.4.1 → 8.11.1 适配 JDK 17

- compileSdk/targetSdk 恢复为 34,minSdk 保持 29 支持 Android 10

- 添加 android.useAndroidX=true 修复构建检查

- 删除 gradle-daemon-jvm.properties (JDK 21 残留配置)

- 信令端口默认改为 8765
This commit is contained in:
2026-06-01 23:50:57 +08:00
parent 334df75155
commit a355919cdc
11 changed files with 81 additions and 25 deletions
+2
View File
@@ -29,6 +29,7 @@ android {
} }
buildFeatures { buildFeatures {
compose true compose true
buildConfig true
} }
} }
@@ -47,6 +48,7 @@ dependencies {
testImplementation libs.junit testImplementation libs.junit
androidTestImplementation libs.espresso.core androidTestImplementation libs.espresso.core
androidTestImplementation libs.ext.junit androidTestImplementation libs.ext.junit
implementation libs.timber
implementation "io.getstream:stream-webrtc-android:1.3.10" implementation "io.getstream:stream-webrtc-android:1.3.10"
implementation "com.squareup.okhttp3:okhttp:4.12.0" implementation "com.squareup.okhttp3:okhttp:4.12.0"
} }
+1
View File
@@ -19,6 +19,7 @@
android:required="true" /> android:required="true" />
<application <application
android:name=".PosefitApplication"
android:allowBackup="true" android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules" android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules" android:fullBackupContent="@xml/backup_rules"
@@ -13,6 +13,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.kimgo.posefit.sender.WebRtcSenderClient import com.kimgo.posefit.sender.WebRtcSenderClient
import timber.log.Timber
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
@@ -23,11 +24,14 @@ class MainActivity : ComponentActivity() {
if (granted) { if (granted) {
val url = getSavedSignalingUrl() val url = getSavedSignalingUrl()
startWebRtc(url) startWebRtc(url)
} else {
Timber.w("Camera permission denied")
} }
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
Timber.d("MainActivity onCreate")
setContent { setContent {
MaterialTheme { MaterialTheme {
@@ -87,7 +91,7 @@ class MainActivity : ComponentActivity() {
private fun getSavedSignalingUrl(): String { private fun getSavedSignalingUrl(): String {
val prefs = getSharedPreferences("posefit_prefs", Context.MODE_PRIVATE) val prefs = getSharedPreferences("posefit_prefs", Context.MODE_PRIVATE)
return prefs.getString("signaling_url", "ws://192.168.110.240:8080") ?: "ws://192.168.110.240:8080" return prefs.getString("signaling_url", "ws://192.168.2.10:8765") ?: "ws://192.168.2.10:8765"
} }
private fun saveSignalingUrl(url: String) { private fun saveSignalingUrl(url: String) {
@@ -96,6 +100,7 @@ class MainActivity : ComponentActivity() {
} }
private fun startWebRtc(url: String) { private fun startWebRtc(url: String) {
Timber.i("startWebRtc: %s", url)
webRtcClient?.release() webRtcClient?.release()
webRtcClient = WebRtcSenderClient( webRtcClient = WebRtcSenderClient(
context = this, context = this,
@@ -105,6 +110,7 @@ class MainActivity : ComponentActivity() {
} }
private fun stopWebRtc() { private fun stopWebRtc() {
Timber.i("stopWebRtc")
webRtcClient?.release() webRtcClient?.release()
webRtcClient = null webRtcClient = null
} }
@@ -112,5 +118,6 @@ class MainActivity : ComponentActivity() {
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
stopWebRtc() stopWebRtc()
Timber.d("MainActivity onDestroy")
} }
} }
@@ -0,0 +1,13 @@
package com.kimgo.posefit
import android.app.Application
import timber.log.Timber
class PosefitApplication : Application() {
override fun onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree())
}
}
}
@@ -2,11 +2,16 @@ package com.kimgo.posefit.sender
import org.webrtc.SdpObserver import org.webrtc.SdpObserver
import org.webrtc.SessionDescription import org.webrtc.SessionDescription
import timber.log.Timber
open class SimpleSdpObserver : SdpObserver { open class SimpleSdpObserver : SdpObserver {
override fun onCreateSuccess(desc: SessionDescription) {} override fun onCreateSuccess(desc: SessionDescription) {}
override fun onSetSuccess() {} override fun onSetSuccess() {}
override fun onCreateFailure(error: String) {} override fun onCreateFailure(error: String) {
override fun onSetFailure(error: String) {} Timber.e("SDP onCreateFailure: %s", error)
}
override fun onSetFailure(error: String) {
Timber.e("SDP onSetFailure: %s", error)
}
} }
@@ -4,6 +4,7 @@ import android.content.Context
import okhttp3.* import okhttp3.*
import org.json.JSONObject import org.json.JSONObject
import org.webrtc.* import org.webrtc.*
import timber.log.Timber
class WebRtcSenderClient( class WebRtcSenderClient(
private val context: Context, private val context: Context,
@@ -23,6 +24,7 @@ class WebRtcSenderClient(
private var surfaceTextureHelper: SurfaceTextureHelper? = null private var surfaceTextureHelper: SurfaceTextureHelper? = null
fun start() { fun start() {
Timber.i("WebRTC starting, signalingUrl=%s", signalingUrl)
initPeerConnectionFactory() initPeerConnectionFactory()
connectSignaling() connectSignaling()
} }
@@ -47,6 +49,8 @@ class WebRtcSenderClient(
.setVideoEncoderFactory(encoderFactory) .setVideoEncoderFactory(encoderFactory)
.setVideoDecoderFactory(decoderFactory) .setVideoDecoderFactory(decoderFactory)
.createPeerConnectionFactory() .createPeerConnectionFactory()
Timber.d("PeerConnectionFactory initialized")
} }
private fun connectSignaling() { private fun connectSignaling() {
@@ -57,6 +61,7 @@ class WebRtcSenderClient(
webSocket = okHttpClient.newWebSocket(request, object : WebSocketListener() { webSocket = okHttpClient.newWebSocket(request, object : WebSocketListener() {
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
Timber.d("Signaling WebSocket connected")
createPeerConnection() createPeerConnection()
startCamera() startCamera()
createOffer() createOffer()
@@ -65,6 +70,14 @@ class WebRtcSenderClient(
override fun onMessage(webSocket: WebSocket, text: String) { override fun onMessage(webSocket: WebSocket, text: String) {
handleSignalingMessage(text) handleSignalingMessage(text)
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
Timber.e(t, "Signaling WebSocket failure")
}
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
Timber.w("Signaling WebSocket closed, code=%d, reason=%s", code, reason)
}
}) })
} }
@@ -93,10 +106,16 @@ class WebRtcSenderClient(
webSocket.send(json.toString()) webSocket.send(json.toString())
} }
override fun onIceConnectionChange(state: PeerConnection.IceConnectionState) {
Timber.d("ICE connection state: %s", state)
}
override fun onIceGatheringChange(state: PeerConnection.IceGatheringState) {
Timber.d("ICE gathering state: %s", state)
}
override fun onSignalingChange(state: PeerConnection.SignalingState) {} override fun onSignalingChange(state: PeerConnection.SignalingState) {}
override fun onIceConnectionChange(state: PeerConnection.IceConnectionState) {}
override fun onIceConnectionReceivingChange(receiving: Boolean) {} override fun onIceConnectionReceivingChange(receiving: Boolean) {}
override fun onIceGatheringChange(state: PeerConnection.IceGatheringState) {}
override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>) {} override fun onIceCandidatesRemoved(candidates: Array<out IceCandidate>) {}
override fun onAddStream(stream: MediaStream) {} override fun onAddStream(stream: MediaStream) {}
override fun onRemoveStream(stream: MediaStream) {} override fun onRemoveStream(stream: MediaStream) {}
@@ -137,6 +156,8 @@ class WebRtcSenderClient(
videoTrack, videoTrack,
listOf("posefit_stream") listOf("posefit_stream")
) )
Timber.d("Camera started: %s, resolution=1280x720@30fps", cameraName)
} }
private fun createOffer() { private fun createOffer() {
@@ -160,17 +181,27 @@ class WebRtcSenderClient(
} }
webSocket.send(json.toString()) webSocket.send(json.toString())
Timber.d("Offer sent via signaling")
}
override fun onSetFailure(error: String) {
Timber.e("setLocalDescription failed: %s", error)
} }
override fun onSetFailure(error: String) {}
override fun onCreateSuccess(desc: SessionDescription) {} override fun onCreateSuccess(desc: SessionDescription) {}
override fun onCreateFailure(error: String) {} override fun onCreateFailure(error: String) {
Timber.e("createOffer failed: %s", error)
}
}, desc) }, desc)
} }
override fun onSetSuccess() {} override fun onSetSuccess() {}
override fun onCreateFailure(error: String) {} override fun onCreateFailure(error: String) {
override fun onSetFailure(error: String) {} Timber.e("createOffer failed: %s", error)
}
override fun onSetFailure(error: String) {
Timber.e("setLocalDescription failed: %s", error)
}
}, constraints) }, constraints)
} }
@@ -189,6 +220,7 @@ class WebRtcSenderClient(
SimpleSdpObserver(), SimpleSdpObserver(),
sdp sdp
) )
Timber.d("Answer received from server")
} }
"candidate" -> { "candidate" -> {
@@ -204,6 +236,8 @@ class WebRtcSenderClient(
} }
fun release() { fun release() {
Timber.i("WebRTC releasing")
try { try {
videoCapturer?.stopCapture() videoCapturer?.stopCapture()
} catch (_: Exception) { } catch (_: Exception) {
@@ -219,5 +253,7 @@ class WebRtcSenderClient(
eglBase.release() eglBase.release()
webSocket.close(1000, "close") webSocket.close(1000, "close")
okHttpClient.dispatcher.executorService.shutdown() okHttpClient.dispatcher.executorService.shutdown()
Timber.d("WebRTC released")
} }
} }
+1
View File
@@ -1,4 +1,5 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
alias(libs.plugins.android.application) apply false alias(libs.plugins.android.application) apply false
alias(libs.plugins.jetbrains.kotlin.android) apply false
} }
+2
View File
@@ -7,6 +7,8 @@
# Specifies the JVM arguments used for the daemon process. # Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings. # The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
org.gradle.java.home=C\:\\Program Files\\Java\\jdk-17
android.useAndroidX=true
# When configured, Gradle will run in incubating parallel mode. # When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. For more details, visit # This option should only be used with decoupled projects. For more details, visit
# https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects # https://developer.android.com/r/tools/gradle-multi-project-decoupled-projects
-12
View File
@@ -1,12 +0,0 @@
#This file is generated by updateDaemonJvm
toolchainUrl.FREE_BSD.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.FREE_BSD.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.LINUX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.LINUX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.MAC_OS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/73bcfb608d1fde9fb62e462f834a3299/redirect
toolchainUrl.MAC_OS.X86_64=https\://api.foojay.io/disco/v3.0/ids/846ee0d876d26a26f37aa1ce8de73224/redirect
toolchainUrl.UNIX.AARCH64=https\://api.foojay.io/disco/v3.0/ids/ec7520a1e057cd116f9544c42142a16b/redirect
toolchainUrl.UNIX.X86_64=https\://api.foojay.io/disco/v3.0/ids/4c4f879899012ff0a8b2e2117df03b0e/redirect
toolchainUrl.WINDOWS.AARCH64=https\://api.foojay.io/disco/v3.0/ids/9482ddec596298c84656d31d16652665/redirect
toolchainUrl.WINDOWS.X86_64=https\://api.foojay.io/disco/v3.0/ids/39701d92e1756bb2f141eb67cd4c660e/redirect
toolchainVersion=21
+3 -1
View File
@@ -1,5 +1,5 @@
[versions] [versions]
agp = "9.2.1" agp = "8.7.3"
kotlin = "2.0.0" kotlin = "2.0.0"
composeBom = "2024.02.00" composeBom = "2024.02.00"
composeCompiler = "1.5.8" composeCompiler = "1.5.8"
@@ -11,8 +11,10 @@ junitVersion = "1.1.5"
espressoCore = "3.5.1" espressoCore = "3.5.1"
appcompat = "1.6.1" appcompat = "1.6.1"
material = "1.10.0" material = "1.10.0"
timber = "5.0.1"
[libraries] [libraries]
timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" }
junit = { group = "junit", name = "junit", version.ref = "junit" } junit = { group = "junit", name = "junit", version.ref = "junit" }
ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } ext-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+2 -3
View File
@@ -1,9 +1,8 @@
#Mon Jun 01 17:35:27 CST 2026 #Mon Jun 01 17:35:27 CST 2026
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionSha256Sum=2ab2958f2a1e51120c326cad6f385153bb11ee93b3c216c5fccebfdfbb7ec6cb distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=false
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists