Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 22 additions & 11 deletions .idea/other.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

212 changes: 113 additions & 99 deletions app/src/main/java/com/example/bluetoothscanner/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,58 +6,60 @@ import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
import android.bluetooth.le.ScanSettings
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.os.Bundle
import android.provider.Settings
import android.util.Log
import android.widget.EditText
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.core.content.FileProvider
import androidx.compose.runtime.*
import androidx.core.content.ContextCompat
import com.example.bluetoothscanner.ui.theme.BLEScannerScreen
import okhttp3.*
import java.io.File
import java.io.FileWriter
import java.io.IOException
import java.text.SimpleDateFormat
import java.util.*
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Callback
import okhttp3.Call
import okhttp3.Response
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import android.provider.Settings
import android.content.Intent
import android.net.Uri
import androidx.core.content.FileProvider

import java.util.Date
import java.util.Locale
import java.util.TimeZone

class MainActivity : ComponentActivity() {

private lateinit var bluetoothAdapter: BluetoothAdapter
private lateinit var sensorManager: SensorManager
private lateinit var rotationVectorSensor: Sensor

private var scanning by mutableStateOf(false)
private var scanResults by mutableStateOf(listOf<ScanResult>())
private var azimuthValues by mutableStateOf(listOf<Float>())

private val targetMacAddresses = setOf(
"60:98:66:33:42:D4", "60:98:66:32:8E:28", "60:98:66:32:BC:AC", "60:98:66:30:A9:6E",
"60:98:66:32:CA:74", "60:98:66:2F:CF:9F", "60:98:66:32:B8:EF", "60:98:66:32:CA:59",
"60:98:66:33:35:4C", "60:98:66:32:AF:B6", "60:98:66:33:0E:8C", "60:98:66:32:C8:E9",
"60:98:66:32:9F:67", "60:98:66:33:24:44", "60:98:66:32:BB:CB", "60:98:66:32:AA:F8",
"A0:6C:65:99:DB:7C", "60:98:66:32:98:58"
"60:98:66:32:98:58", "60:98:66:32:8E:28", "60:98:66:32:BC:AC", "60:98:66:30:A9:6E", "60:98:66:32:CA:74"
)

private var azimuth = 0f

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

setContent {
BLEScannerScreen(
scanResults = scanResults,
azimuthValues = azimuthValues,
scanning = scanning,
onStartScan = { startBleScan() },
onStopScan = { promptFileNameAndSave() }
Expand All @@ -67,28 +69,27 @@ class MainActivity : ComponentActivity() {
val bluetoothManager = getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = bluetoothManager.adapter

// 테스트 메시지 서버로 전송
//sendTestDataToServer()
// SensorManager 초기화 및 TYPE_ROTATION_VECTOR 센서 등록
sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
rotationVectorSensor = sensorManager.getDefaultSensor(Sensor.TYPE_ROTATION_VECTOR)!!
sensorManager.registerListener(sensorEventListener, rotationVectorSensor, SensorManager.SENSOR_DELAY_UI)

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
requestBluetoothPermissions()
} else {
startBleScan()
}


}

private fun requestBluetoothPermissions() {
val requestMultiplePermissions =
registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val allGranted = permissions.entries.all { it.value }
if (allGranted) {
startBleScan()
} else {
Log.e("MainActivity", "Permission denied")
}
val requestMultiplePermissions = registerForActivityResult(ActivityResultContracts.RequestMultiplePermissions()) { permissions ->
val allGranted = permissions.entries.all { it.value }
if (allGranted) {
startBleScan()
} else {
Log.e("MainActivity", "Permission denied")
}
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
requestMultiplePermissions.launch(
Expand All @@ -99,27 +100,22 @@ class MainActivity : ComponentActivity() {
)
)
} else {
requestMultiplePermissions.launch(
arrayOf(Manifest.permission.ACCESS_FINE_LOCATION)
)
requestMultiplePermissions.launch(arrayOf(Manifest.permission.ACCESS_FINE_LOCATION))
}
}

private fun startBleScan() {
try {
// 스캔 시작 전에 기존 데이터를 초기화
scanResults = listOf()

if (ContextCompat.checkSelfPermission(
this,
Manifest.permission.BLUETOOTH_SCAN
) == PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION
) == PackageManager.PERMISSION_GRANTED
) {
bluetoothAdapter.bluetoothLeScanner.startScan(bleScanCallback)
if (ContextCompat.checkSelfPermission(this, Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED ||
ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {

val scanSettings = ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.build()

bluetoothAdapter.bluetoothLeScanner.startScan(null, scanSettings, bleScanCallback)
scanning = true
Log.i("MainActivity", "Scanning started...")
} else {
Expand All @@ -135,7 +131,7 @@ class MainActivity : ComponentActivity() {
bluetoothAdapter.bluetoothLeScanner.stopScan(bleScanCallback)
scanning = false
Log.i("MainActivity", "Scanning stopped.")
saveAndShareCsv(fileName) // CSV 파일 저장 및 공유
saveAndShareCsv(fileName)
} catch (e: SecurityException) {
Log.e("MainActivity", "SecurityException: ${e.message}")
}
Expand All @@ -148,20 +144,14 @@ class MainActivity : ComponentActivity() {

if (macAddress in targetMacAddresses) {
scanResults = scanResults + result
azimuthValues = azimuthValues + azimuth // 현재 azimuth 값을 추가합니다.

val rssi = result.rssi
val timestamp = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(Date())
val number = scanResults.size // 스캔된 데이터의 개수

Log.i("BLE Scan", "Device: $macAddress, RSSI: $rssi")

// 서버로 데이터 전송
sendDataToServer(macAddress, rssi, number)
sendDataToServer(macAddress, rssi, azimuth)
}
}
}


private fun promptFileNameAndSave() {
val editText = EditText(this)
editText.hint = "Enter file name"
Expand All @@ -184,20 +174,82 @@ class MainActivity : ComponentActivity() {
.show()
}

private fun sendDataToServer(macAddress: String, rssi: Int, azimuth: Float) {
val client = OkHttpClient()
val androidId: String = Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
val json = """
{
"macAddress": "$macAddress",
"rssi": $rssi,
"deviceId": "$androidId",
"azimuth": $azimuth
}
"""

val mediaType = "application/json; charset=utf-8".toMediaType()
val body = json.toRequestBody(mediaType)
val request = Request.Builder()
.url("https://49fe-117-16-196-162.ngrok-free.app/api/current_rssi") // 서버 URL 변경
.post(body)
.build()

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("MainActivity", "Failed to send data to server", e)
}

override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
Log.i("MainActivity", "Data sent successfully")
} else {
Log.e("MainActivity", "Server error: ${response.code}")
}
}
})
}

private val sensorEventListener = object : SensorEventListener {
override fun onSensorChanged(event: SensorEvent) {
if (event.sensor == rotationVectorSensor) {
val rotationMatrix = FloatArray(9)
SensorManager.getRotationMatrixFromVector(rotationMatrix, event.values)
val orientation = FloatArray(3)
SensorManager.getOrientation(rotationMatrix, orientation)

// azimuth 값을 0~359 범위의 Float으로 변환
azimuth = ((Math.toDegrees(orientation[0].toDouble()).toInt() + 360) % 360).toFloat()

Log.i("Direction", "Azimuth: $azimuth")
}
}

override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}



override fun onResume() {
super.onResume()
sensorManager.registerListener(sensorEventListener, rotationVectorSensor, SensorManager.SENSOR_DELAY_UI)
}

override fun onPause() {
super.onPause()
sensorManager.unregisterListener(sensorEventListener)
}

private fun saveAndShareCsv(fileName: String) {
val file = File(getExternalFilesDir(null), "$fileName.csv")

try {
// 새 파일에만 데이터를 기록
FileWriter(file, false).use { writer ->
// CSV 파일에 데이터를 씁니다.
writer.append("No.,TimeStamp,MAC Address,RSSI\n")
// CSV 파일의 헤더 작성
writer.append("No.,TimeStamp,MAC Address,RSSI,Azimuth\n")
scanResults.forEachIndexed { index, result ->
val timestamp = SimpleDateFormat(
"HH:mm:ss",
Locale.getDefault()
).format(result.timestampNanos / 1000000)
writer.append("${index + 1},$timestamp,${result.device.address},${result.rssi}\n")
val timestamp = SimpleDateFormat("HH:mm:ss", Locale.getDefault()).format(result.timestampNanos / 1000000)
val azimuth = azimuthValues.getOrNull(index) ?: 0f // 방향 데이터 추가
writer.append("${index + 1},$timestamp,${result.device.address},${result.rssi},$azimuth\n")
}
}

Expand All @@ -208,6 +260,7 @@ class MainActivity : ComponentActivity() {
}
}


private fun shareCsvFile(file: File) {
val uri: Uri = FileProvider.getUriForFile(
this,
Expand All @@ -223,43 +276,4 @@ class MainActivity : ComponentActivity() {
}
startActivity(Intent.createChooser(shareIntent, "Share CSV via"))
}

private fun sendDataToServer(macAddress: String, rssi: Int, number: Int) {
val client = OkHttpClient()

// Android ID 가져오기
val androidId: String =
Settings.Secure.getString(contentResolver, Settings.Secure.ANDROID_ID)
Log.i("androidID", "안드로이드아이디: ${androidId}")
val json = """
{
"macAddress": "$macAddress",
"rssi": $rssi,
"deviceId": "$androidId",
"number": $number
}
"""

val mediaType = "application/json; charset=utf-8".toMediaType()
val body = json.toRequestBody(mediaType)
val request = Request.Builder()
.url("https://b2c7-117-16-195-2.ngrok-free.app/api/current_rssi") // 서버의 URL 여기에
.post(body)
.build()

client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
Log.e("MainActivity", "Failed to send data to server", e)
}

override fun onResponse(call: Call, response: Response) {
if (response.isSuccessful) {
Log.i("MainActivity", "Data sent successfully")
} else {
Log.e("MainActivity", "Server error: ${response.code}")
}
}
})
}
}

Loading