Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • nfb21181/carapp
1 result
Show changes
Commits on Source (3)
Showing
with 796 additions and 0 deletions
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/build
/captures
.externalNativeBuild
.cxx
local.properties
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<bytecodeTargetLevel target="21" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
<option name="testRunner" value="CHOOSE_PER_TEST" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveExternalAnnotations" value="false" />
</GradleProjectSettings>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="KotlinJpsPluginSettings">
<option name="version" value="1.9.24" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectMigrations">
<option name="MigrateToGradleLocalJavaHome">
<set>
<option value="$PROJECT_DIR$" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ExternalStorageConfigurationManager" enabled="true" />
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/build/classes" />
</component>
<component name="ProjectType">
<option name="id" value="Android" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
<option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
<option value="com.intellij.execution.junit.PatternConfigurationProducer" />
<option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
<option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
<option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
<option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
</set>
</option>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" />
</component>
</project>
\ No newline at end of file
/build
\ No newline at end of file
import java.util.regex.Pattern.compile
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
}
android {
namespace = "com.kedda.carApp"
compileSdk = 35
defaultConfig {
applicationId = "com.kedda.carApp"
minSdk = 24
targetSdk = 33
versionCode = 1
versionName = "1.0"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_11
targetCompatibility = JavaVersion.VERSION_11
}
kotlinOptions {
jvmTarget = "11"
}
}
dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.appcompat)
implementation(libs.material)
implementation(libs.androidx.activity)
implementation(libs.androidx.constraintlayout)
implementation(libs.androidx.material3.android)
implementation(libs.androidx.runtime.android)
implementation(libs.androidx.activity.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
implementation(platform(libs.androidx.compose.bom))
}
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
\ No newline at end of file
<?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"/>
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/carApp"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.ImDying"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name = ".ControlActivity"/>
<activity android:name=".VoiceControlActivity"
/>
</application>
</manifest>
\ No newline at end of file
app/src/main/ic_launcher-playstore.png

22.5 KiB

import android.Manifest
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.bluetooth.BluetoothSocket
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.util.Log
import android.widget.Toast
import androidx.annotation.RequiresApi
import androidx.core.app.ActivityCompat
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.IOException
import java.util.*
object BluetoothManager {
var bluetoothSocket: BluetoothSocket? = null
var isConnected: Boolean = false
@RequiresApi(Build.VERSION_CODES.GINGERBREAD_MR1)
suspend fun connectToDevice(context: Context, device: BluetoothDevice, uuid: UUID): Boolean {
return withContext(Dispatchers.IO) {
try {
// Check for Bluetooth permissions (handle this in the calling Activity)
if (ActivityCompat.checkSelfPermission(
context,
Manifest.permission.BLUETOOTH_CONNECT
) != PackageManager.PERMISSION_GRANTED
) {
Log.e("BluetoothManager", "Missing BLUETOOTH_CONNECT permission")
return@withContext false // return false
}
if (bluetoothSocket == null || !isConnected) {
bluetoothSocket = device.createInsecureRfcommSocketToServiceRecord(uuid)
BluetoothAdapter.getDefaultAdapter()?.cancelDiscovery()
bluetoothSocket?.connect()
isConnected = true
Toast.makeText(context, "Connected to device", Toast.LENGTH_SHORT).show()
Log.e("BluetoothManager", "Connected to device: $device")
}
true
} catch (e: IOException) {
e.printStackTrace()
isConnected = false
Log.e("BluetoothManager", "Connection failed: ${e.message}")
false
}
}
}
suspend fun disconnect(): Boolean {
return withContext(Dispatchers.IO) {
try {
bluetoothSocket?.close()
bluetoothSocket = null
isConnected = false
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
}
suspend fun sendCommand(command: String): Boolean {
return withContext(Dispatchers.IO) {
try {
bluetoothSocket?.outputStream?.write(command.toByteArray())
true
} catch (e: IOException) {
e.printStackTrace()
false
}
}
}
}
package com.kedda.carApp
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import android.util.Log
import android.view.MotionEvent
import android.widget.Button
import android.widget.Switch
import android.widget.Toast
import androidx.appcompat.widget.AppCompatImageButton
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
import java.util.UUID
class ControlActivity : MainActivity() {
companion object {
var m_myUUID: UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB")
lateinit var m_bluetoothAdapter: BluetoothAdapter
lateinit var m_address: String
}
private var commandHandler: Handler? = null
private var commandRunnable: Runnable? = null
// Time formatter for precise timestamps
private val timeFormatter = SimpleDateFormat("HH:mm:ss.SSS", Locale.getDefault())
@SuppressLint("UseSwitchCompatOrMaterialCode", "WrongViewCast", "MissingInflatedId")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.control_layout)
m_address = intent.getStringExtra(EXTRA_ADDRESS).toString()
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val device: BluetoothDevice? = m_bluetoothAdapter.getRemoteDevice(m_address)
// Move Bluetooth connection to a coroutine
if (device != null) {
lifecycleScope.launch {
val success = BluetoothManager.connectToDevice(this@ControlActivity, device, m_myUUID)
if (!success) {
logWithTime("Connection failed in ControlActivity", isError = true)
val intent = Intent(this@ControlActivity, MainActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish()
}
}
} else {
logWithTime("Bluetooth device not found", isError = true)
Toast.makeText(this, "Bluetooth Device Not Found", Toast.LENGTH_SHORT).show()
}
val switchLayout = findViewById<Switch>(R.id.switch1)
switchLayout.isChecked = false
switchLayout.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
logWithTime("Voice mode switch activated")
lifecycleScope.launch {
BluetoothManager.sendCommand("VoiceModeAccessed\n")
}
val intent = Intent(this, VoiceControlActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish()
} else {
logWithTime("Button mode switch activated")
lifecycleScope.launch {
BluetoothManager.sendCommand("ButtonModeAccessed\n")
}
}
}
// Setting button movement commands
setupMovementButton(findViewById(R.id.forwardButton), "forwards\n")
setupMovementButton(findViewById(R.id.backwardsdButton), "backwards\n")
setupMovementButton(findViewById(R.id.leftButton), "left\n")
setupMovementButton(findViewById(R.id.rightButton), "right\n")
setupMovementButton(findViewById(R.id.findRocketButton), "FindRocket\n")
// Setting stop button
val controlStop = findViewById<AppCompatImageButton>(R.id.stopButton)
controlStop.setOnClickListener {
logWithTime("Stop button pressed")
lifecycleScope.launch {
BluetoothManager.sendCommand("motorstop\n")
}
}
// setting the gripper buttons
val grabButton = findViewById<Button>(R.id.graspButton)
grabButton.setOnClickListener {
logWithTime("Grab button pressed")
lifecycleScope.launch {
BluetoothManager.sendCommand("grabrocket\n")
}
}
val release = findViewById<Button>(R.id.releaseButton)
release.setOnClickListener {
logWithTime("Release button pressed")
lifecycleScope.launch {
BluetoothManager.sendCommand("releaserocket\n")
}
}
// Setting disconnect button
val disconnectButton = findViewById<Button>(R.id.disconnectButton)
disconnectButton.setOnClickListener {
logWithTime("Disconnect button pressed")
lifecycleScope.launch {
BluetoothManager.disconnect()
val intent = Intent(this@ControlActivity, MainActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
logWithTime("Disconnected from device")
finish()
}
}
}
// Helper function to log with timestamp
private fun logWithTime(message: String, isError: Boolean = false) {
val timestamp = timeFormatter.format(Date())
val logMessage = "[$timestamp] $message"
if (isError) {
Log.e("ControlActivity", logMessage)
} else {
Log.d("ControlActivity", logMessage)
}
}
// If button pressed send command continuously while pressed
@SuppressLint("ClickableViewAccessibility")
private fun setupMovementButton(button: AppCompatImageButton, command: String) {
button.setOnTouchListener { _, motionEvent ->
when (motionEvent.action) {
MotionEvent.ACTION_DOWN -> {
val pressTime = SystemClock.elapsedRealtimeNanos()
val pressTimestamp = timeFormatter.format(Date())
logWithTime("PRESSED - Button: ${button.contentDescription} - Command: $command - SystemNanos: $pressTime")
startSendingCommand(command)
true
}
MotionEvent.ACTION_UP -> {
val releaseTime = SystemClock.elapsedRealtimeNanos()
val releaseTimestamp = timeFormatter.format(Date())
logWithTime("RELEASED - Button: ${button.contentDescription} - Command: $command - SystemNanos: $releaseTime")
stopSendingCommand()
true
}
else -> false
}
}
}
// Start sending command every 100ms
private fun startSendingCommand(command: String) {
commandHandler = Handler(Looper.getMainLooper())
commandRunnable = object : Runnable {
override fun run() {
lifecycleScope.launch {
BluetoothManager.sendCommand(command)
logWithTime("SENDING - Command: $command")
}
commandHandler?.postDelayed(this, 1000) // Send command every 100ms
}
}
commandHandler?.post(commandRunnable!!)
}
// Stop sending command
private fun stopSendingCommand() {
logWithTime("STOPPED - Command sending")
commandHandler?.removeCallbacks(commandRunnable ?: return)
commandHandler = null
}
}
\ No newline at end of file
package com.kedda.carApp
import BluetoothManager
import android.app.Activity
import android.Manifest
import android.annotation.SuppressLint
import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothDevice
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.widget.Toast
import android.util.Log
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.ListView
import androidx.annotation.RequiresApi
import com.kedda.carApp.ControlActivity.Companion.m_address
import com.kedda.carApp.ControlActivity.Companion.m_myUUID
import androidx.appcompat.widget.AppCompatImageButton
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.launch
open class MainActivity : AppCompatActivity() {
private var m_bluetoothAdapter: BluetoothAdapter? = null
private lateinit var m_pairedDevices: Set<BluetoothDevice>
private val REQUEST_ENABLE_BLUETOOTH = 1
private val REQUEST_PERMISSIONS = 2
companion object {
const val EXTRA_ADDRESS: String = "ESP32-BT-CARRR"
}
@RequiresApi(Build.VERSION_CODES.S)
@SuppressLint("MissingInflatedId", "MissingPermission")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContentView(R.layout.activity_main)
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
if (m_bluetoothAdapter == null) {
//Toast.makeText(this, "Device does not support Bluetooth", Toast.LENGTH_SHORT).show()
val intent = Intent(this, MainActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish()
return
}
checkPermissions()
if (!m_bluetoothAdapter!!.isEnabled) {
val enableBluetoothIntent = Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE)
startActivityForResult(enableBluetoothIntent, REQUEST_ENABLE_BLUETOOTH)
} else {
pairedDeviceList()
}
val selectDeviceRefresh = findViewById<Button>(R.id.select_device_refresh)
selectDeviceRefresh.setOnClickListener { pairedDeviceList() }
}
@RequiresApi(Build.VERSION_CODES.S)
private fun checkPermissions() {
val permissionsNeeded = mutableListOf(
Manifest.permission.BLUETOOTH,
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_FINE_LOCATION,
Manifest.permission.ACCESS_COARSE_LOCATION
)
val permissionsToRequest = permissionsNeeded.filter {
ActivityCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED
}
if (permissionsToRequest.isNotEmpty()) {
ActivityCompat.requestPermissions(this, permissionsToRequest.toTypedArray(), REQUEST_PERMISSIONS)
} else {
pairedDeviceList()
//Toast.makeText(this, "Permissions already granted", Toast.LENGTH_SHORT).show()
}
}
// Inside pairedDeviceList function
private fun pairedDeviceList() {
m_pairedDevices = m_bluetoothAdapter!!.bondedDevices
val list = ArrayList<String>()
if (m_pairedDevices.isNotEmpty()) {
for (device in m_pairedDevices) {
if (ActivityCompat.checkSelfPermission(
this,
Manifest.permission.BLUETOOTH_CONNECT
) != PackageManager.PERMISSION_GRANTED
) {
return
}
list.add("${device.name} - ${device.address}")
Log.i("device", "Device found: ${device.name} - ${device.address}")
}
} else {
// Toast.makeText(this, "No paired devices", Toast.LENGTH_SHORT).show()
}
val adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, list)
val deviceListView = findViewById<ListView>(R.id.select_device_list)
deviceListView.adapter = adapter
// Set an on item click listener for the list
deviceListView.setOnItemClickListener { _, _, position, _ ->
val selectedDevice = m_pairedDevices.elementAt(position) // Get BluetoothDevice object
m_address = selectedDevice.address // Initialize m_address with the selected device's address
lifecycleScope.launch {
BluetoothManager.connectToDevice(this@MainActivity, selectedDevice, m_myUUID)
if(!BluetoothManager.isConnected) {
Log.e("MainActivity", "Connection failed in MainActivity")
Toast.makeText(this@MainActivity, "Bluetooth Connection Failed", Toast.LENGTH_SHORT).show()
val intent = Intent(this@MainActivity, MainActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish()
}
val intent = Intent(this@MainActivity, ControlActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish()
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_ENABLE_BLUETOOTH) {
if (resultCode == Activity.RESULT_OK) {
//.makeText(this, "Bluetooth has been enabled", Toast.LENGTH_SHORT).show()
pairedDeviceList() // Just refresh instead of restarting activity
} else {
//Toast.makeText(this, "Bluetooth enabling has been cancelled", Toast.LENGTH_SHORT).show()
}
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == REQUEST_PERMISSIONS) {
if (grantResults.isNotEmpty() && grantResults.all { it == PackageManager.PERMISSION_GRANTED }) {
pairedDeviceList()
} else {
//Toast.makeText(this, "Permissions denied", Toast.LENGTH_SHORT).show()
}
}
}
}
package com.kedda.carApp
import android.annotation.SuppressLint
import android.app.Activity
import android.bluetooth.BluetoothAdapter
import android.content.Intent
import android.os.Bundle
import android.speech.RecognizerIntent
import android.util.Log
import android.widget.ImageButton
import android.widget.Switch
import android.widget.TextView
import android.widget.Toast
import java.io.IOException
import java.util.Locale
import android.os.Build
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.lifecycle.lifecycleScope
import com.kedda.carApp.ControlActivity.Companion.m_bluetoothAdapter
import kotlinx.coroutines.launch
class VoiceControlActivity : MainActivity() {
companion object {
lateinit var m_address: String
}
private lateinit var speechRecognitionLauncher: ActivityResultLauncher<Intent>
@RequiresApi(Build.VERSION_CODES.ECLAIR)
@SuppressLint("UseSwitchCompatOrMaterialCode")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.voice_recognizer)
m_address = intent.getStringExtra(EXTRA_ADDRESS).toString()
m_bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
val switch_layout = findViewById<Switch>(R.id.switch1)
switch_layout.isChecked = true
// Switch to ControlActivity when switched off
// Handle switch toggle
switch_layout.setOnCheckedChangeListener { _, isChecked ->
if (!isChecked) {
// Switch to button control mode
lifecycleScope.launch {
BluetoothManager.sendCommand("ButtonModeAccessed\n")
}
// Toast.makeText(this, "Switching to voice Button Mode", Toast.LENGTH_SHORT).show()
if (!isFinishing) { // Check if activity is not finishing
val intent = Intent(this, ControlActivity::class.java)
intent.putExtra(EXTRA_ADDRESS, m_address)
startActivity(intent)
finish() // Close the current activity
}
}
}
// Handle voice button click event
val voiceButton = findViewById<ImageButton>(R.id.voiceButton)
voiceButton.setOnClickListener {
speak();
}
// Initialize speech recognition launcher and send the commands to the device
speechRecognitionLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
if (result.resultCode == Activity.RESULT_OK) {
try{
val resultData = result.data?.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS)
resultData?.get(0)?.let { recognizedText ->
val speechText = findViewById<TextView>(R.id.instructionText)
speechText.text = recognizedText
// Send the recognized text as a command
lifecycleScope.launch {
BluetoothManager.sendCommand(recognizedText)
}
}
}catch (e: IOException){
Log.e("voice command line 91", "command not sent :(", e)
}
}
}
}
// speak function to start speech recognition launcher and send the commands to the device
private fun speak() {
val mIntent = Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH)
mIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM)
mIntent.putExtra(RecognizerIntent.EXTRA_LANGUAGE,
Locale.getDefault())
mIntent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Hi, speak something")
speechRecognitionLauncher.launch(mIntent)
}
}
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>
\ No newline at end of file
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:tint="#636462" android:viewportHeight="24" android:viewportWidth="24" android:width="24dp">
<path android:fillColor="@android:color/white" android:pathData="M12,4c4.41,0 8,3.59 8,8s-3.59,8 -8,8s-8,-3.59 -8,-8S7.59,4 12,4M12,2C6.48,2 2,6.48 2,12c0,5.52 4.48,10 10,10c5.52,0 10,-4.48 10,-10C22,6.48 17.52,2 12,2L12,2zM13,12l0,-4h-2l0,4H8l4,4l4,-4H13z"/>
</vector>