feat: add bluetooth service and viewmodel

This commit is contained in:
Fritz Heiden 2024-12-11 16:48:53 +01:00
parent a7184ece91
commit 421b1719be
6 changed files with 109 additions and 19 deletions

View File

@ -1,6 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:allowBackup="true"

View File

@ -0,0 +1,29 @@
package com.example.tvcontroller
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.content.Context
import android.content.Intent
import android.app.Activity
import androidx.activity.result.ActivityResultLauncher
import androidx.core.app.ComponentActivity
import androidx.core.content.ContextCompat.getSystemService
object BluetoothService {
private lateinit var bluetoothManager: BluetoothManager;
private var bluetoothAdapter: BluetoothAdapter? = null;
fun init(context: Context) {
bluetoothManager = getSystemService(context, BluetoothManager::class.java)!!
bluetoothAdapter = bluetoothManager.adapter
if (bluetoothAdapter == null) {
throw Exception("Bluetooth not supported on this device")
}
}
fun isEnabled(): Boolean {
return bluetoothAdapter?.isEnabled?: false
}
}

View File

@ -1,20 +1,23 @@
package com.example.tvcontroller
import android.content.ContentValues.TAG
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.Button
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Modifier
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
@ -23,12 +26,16 @@ import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
import com.example.tvcontroller.ui.theme.TVControllerTheme
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.res.painterResource
import androidx.lifecycle.viewmodel.compose.viewModel
import com.example.tvcontroller.ui.AppViewModel
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
BluetoothService.init(this)
enableEdgeToEdge()
setContent {
TVControllerTheme {
@ -40,17 +47,19 @@ class MainActivity : ComponentActivity() {
@Composable
fun TvControllerApp(
navController: NavHostController = rememberNavController()
navController: NavHostController = rememberNavController(),
appViewModel: AppViewModel = viewModel()
) {
val appUiState by appViewModel.uiState.collectAsState();
val backStackEntry by navController.currentBackStackEntryAsState()
val currentScreen = Screens.valueOf(backStackEntry?.destination?.route ?: Screens.Camera.name)
val currentScreen = Screen.valueOf(backStackEntry?.destination?.route ?: Screen.Camera.name)
val baselineCamera24 = painterResource(R.drawable.baseline_camera_24)
val baselineRemote24 = painterResource(R.drawable.baseline_settings_remote_24)
val baselineSettings24 = painterResource(R.drawable.baseline_settings_24)
Scaffold(modifier = Modifier.fillMaxSize(), bottomBar = {
NavigationBar {
NavigationBarItem(
onClick = { navController.navigate(Screens.Camera.name) },
onClick = { navController.navigate(Screen.Camera.name) },
icon = {
Icon(
baselineCamera24,
@ -58,10 +67,10 @@ fun TvControllerApp(
)
},
label = { Text("Camera") },
selected = currentScreen == Screens.Camera
selected = currentScreen == Screen.Camera
)
NavigationBarItem(
onClick = { navController.navigate(Screens.Remote.name) },
onClick = { navController.navigate(Screen.Remote.name) },
icon = {
Icon(
baselineRemote24,
@ -69,10 +78,10 @@ fun TvControllerApp(
)
},
label = { Text("Remote") },
selected = currentScreen == Screens.Remote
selected = currentScreen == Screen.Remote
)
NavigationBarItem(
onClick = { navController.navigate(Screens.Settings.name) },
onClick = { navController.navigate(Screen.Settings.name) },
icon = {
Icon(
baselineSettings24,
@ -80,23 +89,23 @@ fun TvControllerApp(
)
},
label = { Text("Settings") },
selected = currentScreen == Screens.Settings
selected = currentScreen == Screen.Settings
)
}
}) { innerPadding ->
Column {
NavHost(
navController = navController,
startDestination = Screens.Camera.name,
startDestination = Screen.Camera.name,
modifier = Modifier.padding(innerPadding)
) {
composable(route = Screens.Camera.name) {
composable(route = Screen.Camera.name) {
CameraScreen()
}
composable(route = Screens.Remote.name) {
composable(route = Screen.Remote.name) {
RemoteScreen()
}
composable(route = Screens.Settings.name) {
composable(route = Screen.Settings.name) {
SettingsScreen()
}
}
@ -106,15 +115,39 @@ fun TvControllerApp(
@Composable
fun CameraScreen(modifier: Modifier = Modifier) {
Text(text = "Camera Screen", modifier = modifier)
Column(
modifier = modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Camera Screen", modifier = modifier)
}
}
@Composable
fun RemoteScreen(modifier: Modifier = Modifier) {
Text(text = "Remote Screen", modifier = modifier)
Column(
modifier = modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Remote Screen", modifier = modifier)
Button(onClick = { Log.i(TAG, "RemoteScreen: Button clicked") }) {
Text(text = "Button")
}
}
}
@Composable
fun SettingsScreen(modifier: Modifier = Modifier) {
Text(text = "Settings Screen", modifier = modifier)
Column(
modifier = modifier
.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center
) {
Text(text = "Settings Screen", modifier = modifier)
}
}

View File

@ -1,7 +1,7 @@
package com.example.tvcontroller
enum class Screens {
enum class Screen {
Camera,
Remote,
Settings
}
}

View File

@ -0,0 +1,7 @@
package com.example.tvcontroller.ui
import com.example.tvcontroller.Screen
data class AppUiState(
val currentScreen: Screen = Screen.Camera,
)

View File

@ -0,0 +1,19 @@
package com.example.tvcontroller.ui
import androidx.lifecycle.ViewModel
import com.example.tvcontroller.Screen
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.update
class AppViewModel : ViewModel() {
private val _uiState = MutableStateFlow(AppUiState())
val uiState: StateFlow<AppUiState> = _uiState.asStateFlow()
fun updateScreen(screen: Screen) {
if (uiState.value.currentScreen == screen) return
val newUiState = uiState.value.copy(currentScreen = screen)
_uiState.update { newUiState }
}
}