package com.safeluck.floatwindow
|
|
import android.content.ComponentName
|
import android.content.Context
|
import android.content.Intent
|
import android.content.ServiceConnection
|
import android.os.Bundle
|
import android.os.IBinder
|
import android.os.RemoteException
|
import androidx.activity.ComponentActivity
|
import androidx.activity.compose.setContent
|
import androidx.activity.enableEdgeToEdge
|
import androidx.compose.foundation.layout.*
|
import androidx.compose.material3.*
|
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.*
|
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.unit.dp
|
import com.safeluck.floatwindow.ui.theme.AnyunVideoTheme
|
import timber.log.Timber
|
|
class MainActivity : ComponentActivity() {
|
private var mediaAidlInterface: IMediaAidlInterface? = null
|
|
private val callback = object : IMyCallback.Stub() {
|
@Throws(RemoteException::class)
|
override fun onResult(re: ResponseVO?) {
|
re?.let {
|
Timber.d("Callback received: type=${it.type}, errCode=${it.errCode}, message=${it.message}")
|
}
|
}
|
}
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
super.onCreate(savedInstanceState)
|
enableEdgeToEdge()
|
|
var isServiceBoundState by mutableStateOf(false)
|
|
val serviceConnection = object : ServiceConnection {
|
override fun onServiceConnected(name: ComponentName?, service: IBinder?) {
|
mediaAidlInterface = IMediaAidlInterface.Stub.asInterface(service)
|
isServiceBoundState = true
|
Timber.d("FloatingService connected")
|
}
|
|
override fun onServiceDisconnected(name: ComponentName?) {
|
mediaAidlInterface = null
|
isServiceBoundState = false
|
Timber.d("FloatingService disconnected")
|
}
|
}
|
|
setContent {
|
AnyunVideoTheme {
|
Surface(
|
modifier = Modifier.fillMaxSize(),
|
color = MaterialTheme.colorScheme.background
|
) {
|
MainScreen(
|
isServiceBound = isServiceBoundState,
|
onBindService = {
|
if (!isServiceBoundState) {
|
val intent = Intent(this@MainActivity, FloatingService::class.java)
|
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE)
|
Timber.d("Binding FloatingService")
|
}
|
},
|
onUnbindService = {
|
if (isServiceBoundState) {
|
try {
|
mediaAidlInterface?.unregisterCallback(callback)
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error unregistering callback")
|
}
|
unbindService(serviceConnection)
|
isServiceBoundState = false
|
mediaAidlInterface = null
|
Timber.d("Unbinding FloatingService")
|
}
|
},
|
onStartAndroidRecord = { startAndroidRecord() },
|
onStopAndroidRecord = { stopAndroidRecord() },
|
onStartUsbRecord = { startUsbRecord() },
|
onStopUsbRecord = { stopUsbRecord() },
|
onStartUsbPush = { startUsbPush() },
|
onStopUsbPush = { stopUsbPush() }
|
)
|
}
|
}
|
}
|
}
|
|
private fun startAndroidRecord() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot start Android record")
|
return
|
}
|
|
try {
|
val mediaArgu = MediaArgu().apply {
|
isPush = false
|
isUsedOutCamera = false // Android 内置摄像头
|
codeRate = 0
|
frameRate = 0
|
m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
|
recordTime = 0
|
tfCardFlag = 0 // 内部存储
|
}
|
|
mediaAidlInterface?.registerCallback(callback)
|
mediaAidlInterface?.startMedia(mediaArgu)
|
Timber.d("Started Android camera record")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error starting Android record")
|
}
|
}
|
|
private fun stopAndroidRecord() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot stop Android record")
|
return
|
}
|
|
try {
|
mediaAidlInterface?.stopMedia()
|
Timber.d("Stopped Android camera record")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error stopping Android record")
|
}
|
}
|
|
private fun startUsbRecord() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot start USB record")
|
return
|
}
|
|
try {
|
val mediaArgu = MediaArgu().apply {
|
isPush = false
|
isUsedOutCamera = true // USB 摄像头
|
codeRate = 0
|
frameRate = 0
|
m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
|
recordTime = 0
|
tfCardFlag = 0 // 内部存储
|
}
|
|
mediaAidlInterface?.registerCallback(callback)
|
mediaAidlInterface?.startMedia(mediaArgu)
|
Timber.d("Started USB camera record")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error starting USB record")
|
}
|
}
|
|
private fun stopUsbRecord() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot stop USB record")
|
return
|
}
|
|
try {
|
mediaAidlInterface?.stopMedia()
|
Timber.d("Stopped USB camera record")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error stopping USB record")
|
}
|
}
|
|
private fun startUsbPush() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot start USB push")
|
return
|
}
|
|
try {
|
val mediaArgu = MediaArgu().apply {
|
isPush = true
|
isUsedOutCamera = true // USB 摄像头
|
codeRate = 0
|
frameRate = 0
|
m_screen = MediaArgu.ScreenSolution(640, 480) // 默认分辨率
|
url = "rtmp://your-push-url" // TODO: 需要设置实际的推流地址
|
userName = ""
|
pwd = ""
|
}
|
|
mediaAidlInterface?.registerCallback(callback)
|
mediaAidlInterface?.startMedia(mediaArgu)
|
Timber.d("Started USB camera push")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error starting USB push")
|
}
|
}
|
|
private fun stopUsbPush() {
|
if (mediaAidlInterface == null) {
|
Timber.w("Service not bound, cannot stop USB push")
|
return
|
}
|
|
try {
|
mediaAidlInterface?.stopMedia()
|
Timber.d("Stopped USB camera push")
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error stopping USB push")
|
}
|
}
|
|
override fun onDestroy() {
|
super.onDestroy()
|
if (mediaAidlInterface != null) {
|
try {
|
mediaAidlInterface?.unregisterCallback(callback)
|
} catch (e: RemoteException) {
|
Timber.e(e, "Error unregistering callback")
|
}
|
}
|
}
|
}
|
|
@Composable
|
fun MainScreen(
|
isServiceBound: Boolean,
|
onBindService: () -> Unit,
|
onUnbindService: () -> Unit,
|
onStartAndroidRecord: () -> Unit,
|
onStopAndroidRecord: () -> Unit,
|
onStartUsbRecord: () -> Unit,
|
onStopUsbRecord: () -> Unit,
|
onStartUsbPush: () -> Unit,
|
onStopUsbPush: () -> Unit
|
) {
|
Column(
|
modifier = Modifier
|
.fillMaxSize()
|
.padding(16.dp),
|
horizontalAlignment = Alignment.CenterHorizontally,
|
verticalArrangement = Arrangement.spacedBy(12.dp)
|
) {
|
Text(
|
text = "FloatingService 控制",
|
style = MaterialTheme.typography.headlineMedium,
|
modifier = Modifier.padding(bottom = 8.dp)
|
)
|
|
Text(
|
text = if (isServiceBound) "服务状态: 已绑定" else "服务状态: 未绑定",
|
style = MaterialTheme.typography.bodyMedium,
|
color = if (isServiceBound) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.error,
|
modifier = Modifier.padding(bottom = 16.dp)
|
)
|
|
// 按钮 1: 绑定服务
|
Button(
|
onClick = onBindService,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = !isServiceBound
|
) {
|
Text("1. 绑定 FloatingService")
|
}
|
|
// 按钮 2: 解绑服务
|
Button(
|
onClick = onUnbindService,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("2. 解绑 FloatingService")
|
}
|
|
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
|
// 按钮 3: 开始 Android 相机录像
|
Button(
|
onClick = onStartAndroidRecord,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("3. 开始 Android 相机录像")
|
}
|
|
// 按钮 4: 结束 Android 相机录像
|
Button(
|
onClick = onStopAndroidRecord,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("4. 结束 Android 相机录像")
|
}
|
|
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
|
// 按钮 5: 开始 USB 相机录像
|
Button(
|
onClick = onStartUsbRecord,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("5. 开始 USB 相机录像")
|
}
|
|
// 按钮 6: 结束 USB 相机录像
|
Button(
|
onClick = onStopUsbRecord,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("6. 结束 USB 相机录像")
|
}
|
|
Divider(modifier = Modifier.padding(vertical = 8.dp))
|
|
// 按钮 7: 开始 USB 推流
|
Button(
|
onClick = onStartUsbPush,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("7. 开始 USB 推流")
|
}
|
|
// 按钮 8: 结束 USB 推流
|
Button(
|
onClick = onStopUsbPush,
|
modifier = Modifier.fillMaxWidth(),
|
enabled = isServiceBound
|
) {
|
Text("8. 结束 USB 推流")
|
}
|
}
|
}
|