Initial commit: LCK Control Android app
Multi-module Android app (app/shared/sdk) with backend-driven auth, Quest Platform SDK login, YouTube/Twitch OAuth linking, stream management via AIDL service. Compose UI with Hilt DI.
This commit is contained in:
23
sdk/build.gradle.kts
Normal file
23
sdk/build.gradle.kts
Normal file
@@ -0,0 +1,23 @@
|
||||
plugins {
|
||||
alias(libs.plugins.android.library)
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "com.omixlab.lckcontrol.sdk"
|
||||
compileSdk = 36
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 33
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
api(project(":shared"))
|
||||
implementation(libs.androidx.core.ktx)
|
||||
implementation(libs.kotlinx.coroutines.android)
|
||||
}
|
||||
2
sdk/src/main/AndroidManifest.xml
Normal file
2
sdk/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,2 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest />
|
||||
122
sdk/src/main/java/com/omixlab/lckcontrol/sdk/LckControlClient.kt
Normal file
122
sdk/src/main/java/com/omixlab/lckcontrol/sdk/LckControlClient.kt
Normal file
@@ -0,0 +1,122 @@
|
||||
package com.omixlab.lckcontrol.sdk
|
||||
|
||||
import android.content.ComponentName
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.ServiceConnection
|
||||
import android.os.IBinder
|
||||
import com.omixlab.lckcontrol.shared.ILckControlCallback
|
||||
import com.omixlab.lckcontrol.shared.ILckControlService
|
||||
import com.omixlab.lckcontrol.shared.LinkedAccount
|
||||
import com.omixlab.lckcontrol.shared.StreamPlan
|
||||
import com.omixlab.lckcontrol.shared.StreamPlanConfig
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
class LckControlClient(private val context: Context) {
|
||||
|
||||
companion object {
|
||||
private const val SERVICE_PACKAGE = "com.omixlab.lckcontrol"
|
||||
private const val SERVICE_CLASS = "$SERVICE_PACKAGE.service.LckControlService"
|
||||
private const val PERMISSION = "$SERVICE_PACKAGE.permission.USE_LCK_CONTROL"
|
||||
}
|
||||
|
||||
private var service: ILckControlService? = null
|
||||
private var clientId: String? = null
|
||||
|
||||
private val _connected = MutableStateFlow(false)
|
||||
val connected: StateFlow<Boolean> = _connected.asStateFlow()
|
||||
|
||||
private val _streamPlans = MutableStateFlow<List<StreamPlan>>(emptyList())
|
||||
val streamPlans: StateFlow<List<StreamPlan>> = _streamPlans.asStateFlow()
|
||||
|
||||
private val callback = object : ILckControlCallback.Stub() {
|
||||
override fun onStreamPlansChanged(plans: List<StreamPlan>) {
|
||||
_streamPlans.value = plans
|
||||
}
|
||||
|
||||
override fun onStreamPlanUpdated(plan: StreamPlan) {
|
||||
_streamPlans.value = _streamPlans.value.map {
|
||||
if (it.planId == plan.planId) plan else it
|
||||
}
|
||||
}
|
||||
|
||||
override fun onClientRegistered(id: String) {}
|
||||
override fun onClientUnregistered(id: String) {}
|
||||
}
|
||||
|
||||
private val connection = object : ServiceConnection {
|
||||
override fun onServiceConnected(name: ComponentName?, binder: IBinder?) {
|
||||
service = ILckControlService.Stub.asInterface(binder)
|
||||
service?.registerCallback(callback)
|
||||
_connected.value = true
|
||||
}
|
||||
|
||||
override fun onServiceDisconnected(name: ComponentName?) {
|
||||
service = null
|
||||
clientId = null
|
||||
_connected.value = false
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(): Boolean {
|
||||
val intent = Intent().apply {
|
||||
component = ComponentName(SERVICE_PACKAGE, SERVICE_CLASS)
|
||||
}
|
||||
return context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
|
||||
}
|
||||
|
||||
fun unbind() {
|
||||
service?.let { svc ->
|
||||
clientId?.let { svc.unregisterClient(it) }
|
||||
svc.unregisterCallback(callback)
|
||||
}
|
||||
try {
|
||||
context.unbindService(connection)
|
||||
} catch (_: IllegalArgumentException) {
|
||||
// Already unbound
|
||||
}
|
||||
service = null
|
||||
clientId = null
|
||||
_connected.value = false
|
||||
}
|
||||
|
||||
fun registerAsClient(clientName: String, packageName: String): String? {
|
||||
val id = service?.registerClient(clientName, packageName)
|
||||
clientId = id
|
||||
return id
|
||||
}
|
||||
|
||||
fun getLinkedAccounts(): List<LinkedAccount> {
|
||||
return service?.linkedAccounts ?: emptyList()
|
||||
}
|
||||
|
||||
fun getStreamPlans(): List<StreamPlan> {
|
||||
return service?.streamPlans ?: emptyList()
|
||||
}
|
||||
|
||||
fun getStreamPlan(planId: String): StreamPlan? {
|
||||
return service?.getStreamPlan(planId)
|
||||
}
|
||||
|
||||
fun createStreamPlan(config: StreamPlanConfig): StreamPlan? {
|
||||
return service?.createStreamPlan(config)
|
||||
}
|
||||
|
||||
fun prepareStreamPlan(planId: String): StreamPlan? {
|
||||
return service?.prepareStreamPlan(planId)
|
||||
}
|
||||
|
||||
fun startStreamPlan(planId: String): Boolean {
|
||||
return service?.startStreamPlan(planId) ?: false
|
||||
}
|
||||
|
||||
fun endStreamPlan(planId: String): Boolean {
|
||||
return service?.endStreamPlan(planId) ?: false
|
||||
}
|
||||
|
||||
fun setClientActivePlan(planId: String) {
|
||||
clientId?.let { service?.setClientActivePlan(it, planId) }
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user