diff --git a/.github/workflows/android-release.yml b/.github/workflows/android-release.yml
new file mode 100644
index 0000000..0f2686e
--- /dev/null
+++ b/.github/workflows/android-release.yml
@@ -0,0 +1,114 @@
+name: Android Release Build
+
+on:
+ push:
+ tags:
+ - 'v*'
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Build Debug APK
+ run: ./gradlew assembleDebug
+
+ - name: Build Release APK
+ run: ./gradlew assembleRelease
+ env:
+ KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PASSWORD }}
+ KEY_ALIAS: ${{ secrets.KEY_ALIAS }}
+ KEY_PASSWORD: ${{ secrets.KEY_PASSWORD }}
+
+ - name: Upload Debug APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: debug-apk
+ path: app/build/outputs/apk/debug/*.apk
+
+ - name: Upload Release APK
+ uses: actions/upload-artifact@v4
+ with:
+ name: release-apk
+ path: app/build/outputs/apk/release/*.apk
+
+ - name: Create Release
+ if: startsWith(github.ref, 'refs/tags/')
+ uses: softprops/action-gh-release@v1
+ with:
+ files: |
+ app/build/outputs/apk/debug/*.apk
+ app/build/outputs/apk/release/*.apk
+ generate_release_notes: true
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ test:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+
+ - name: Set up JDK 17
+ uses: actions/setup-java@v4
+ with:
+ distribution: 'temurin'
+ java-version: '17'
+
+ - name: Setup Android SDK
+ uses: android-actions/setup-android@v3
+
+ - name: Cache Gradle packages
+ uses: actions/cache@v3
+ with:
+ path: |
+ ~/.gradle/caches
+ ~/.gradle/wrapper
+ key: ${{ runner.os }}-gradle-${{ hashFiles('**/*.gradle*', '**/gradle-wrapper.properties') }}
+ restore-keys: |
+ ${{ runner.os }}-gradle-
+
+ - name: Grant execute permission for gradlew
+ run: chmod +x gradlew
+
+ - name: Run tests
+ run: ./gradlew test
+
+ - name: Run lint
+ run: ./gradlew lint
+
+ - name: Upload test results
+ uses: actions/upload-artifact@v4
+ if: always()
+ with:
+ name: test-results
+ path: |
+ app/build/reports/tests/
+ app/build/reports/lint-results.html
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index aa724b7..7ca217f 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,15 +1,15 @@
*.iml
.gradle
-/local.properties
-/.idea/caches
-/.idea/libraries
-/.idea/modules.xml
-/.idea/workspace.xml
-/.idea/navEditor.xml
-/.idea/assetWizardSettings.xml
+.idea/caches
+.idea/libraries
+.idea/modules.xml
+.idea/workspace.xml
+.idea/navEditor.xml
+.idea/assetWizardSettings.xml
.DS_Store
-/build
-/captures
+build
+captures
.externalNativeBuild
.cxx
local.properties
+local.properties
diff --git a/.idea/.name b/.idea/.name
index cf9db12..d07af4f 100644
--- a/.idea/.name
+++ b/.idea/.name
@@ -1 +1 @@
-LBJ_Console
\ No newline at end of file
+LBJ Receiver
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index 3a79638..cf3b3dc 100644
--- a/LICENSE
+++ b/LICENSE
@@ -631,7 +631,7 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
- LBJ_Console is an Android app designed to receive and display LBJ (Locomotive Bell Jingling) messages via BLE from the SX1276_Receive_LBJ device.
+ LBJ Console is an Android app designed to receive and display LBJ (Locomotive Bell Jingling) messages via BLE from the SX1276_Receive_LBJ device.
Copyright (C) 2025 undef-i
This program is free software: you can redistribute it and/or modify
@@ -652,7 +652,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
- LBJ_Console Copyright (C) 2025 undef-i
+ LBJ Console Copyright (C) 2025 undef-i
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
diff --git a/README.md b/README.md
index a8d09be..66f4eb3 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,15 @@
-# LBJ_Console
+# LBJ Console
-LBJ_Console is an Android app designed to receive and display LBJ messages via BLE from the [SX1276_Receive_LBJ](https://github.com/undef-i/SX1276_Receive_LBJ) device.
+LBJ Console is an Android app designed to receive and display LBJ messages via BLE from the [SX1276_Receive_LBJ](https://github.com/undef-i/SX1276_Receive_LBJ) device.
+
+## Roadmap
+- Tab state persistence
+- Record filtering (train number, time range)
+- Record management page optimization
+- Optional train merge by locomotive/number
+- Offline data storage
+- Add record timestamps
# License
-This project is licensed under the GNU General Public License v3.0 (GPLv3). This license ensures that the software remains free and open source, requiring that any modifications or derivative works must also be released under the same license terms.
\ No newline at end of file
+This project is licensed under the GNU General Public License v3.0 (GPLv3). This license ensures that the software remains free and open source, requiring that any modifications or derivative works must also be released under the same license terms.
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
deleted file mode 100644
index 42afabf..0000000
--- a/app/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 2a6bd47..ea828a2 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -5,13 +5,13 @@ plugins {
}
android {
- namespace = "receiver.lbj"
+ namespace = "org.noxylva.lbjconsole"
compileSdk = 35
defaultConfig {
- applicationId = "receiver.lbj"
+ applicationId = "org.noxylva.lbjconsole"
minSdk = 29
- targetSdk = 34
+ targetSdk = 35
versionCode = 1
versionName = "1.0"
diff --git a/app/src/androidTest/java/receiver/lbj/ExampleInstrumentedTest.kt b/app/src/androidTest/java/receiver/lbj/ExampleInstrumentedTest.kt
deleted file mode 100644
index 1355497..0000000
--- a/app/src/androidTest/java/receiver/lbj/ExampleInstrumentedTest.kt
+++ /dev/null
@@ -1,20 +0,0 @@
-package receiver.lbj
-
-import androidx.test.platform.app.InstrumentationRegistry
-import androidx.test.ext.junit.runners.AndroidJUnit4
-
-import org.junit.Test
-import org.junit.runner.RunWith
-
-import org.junit.Assert.*
-
-
-@RunWith(AndroidJUnit4::class)
-class ExampleInstrumentedTest {
- @Test
- fun useAppContext() {
-
- val appContext = InstrumentationRegistry.getInstrumentation().targetContext
- assertEquals("receiver.lbj", appContext.packageName)
- }
-}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 035d73d..8e5c4a7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -4,12 +4,11 @@
-
-
+
+
+
-
-
diff --git a/app/src/main/java/receiver/lbj/BLEClient.kt b/app/src/main/java/org/noxylva/lbjconsole/BLEClient.kt
similarity index 82%
rename from app/src/main/java/receiver/lbj/BLEClient.kt
rename to app/src/main/java/org/noxylva/lbjconsole/BLEClient.kt
index 3d8505d..ce1da98 100644
--- a/app/src/main/java/receiver/lbj/BLEClient.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/BLEClient.kt
@@ -1,17 +1,22 @@
-package receiver.lbj
+package org.noxylva.lbjconsole
import android.annotation.SuppressLint
import android.bluetooth.*
+import android.bluetooth.le.BluetoothLeScanner
+import android.bluetooth.le.ScanCallback
+import android.bluetooth.le.ScanResult
import android.content.Context
+import android.content.pm.PackageManager
+import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
+import androidx.core.content.ContextCompat
import java.nio.charset.StandardCharsets
import org.json.JSONObject
import java.util.*
-class BLEClient(private val context: Context) : BluetoothGattCallback(),
- BluetoothAdapter.LeScanCallback {
+class BLEClient(private val context: Context) : BluetoothGattCallback() {
companion object {
const val TAG = "LBJ_BT"
const val SCAN_PERIOD = 10000L
@@ -35,6 +40,24 @@ class BLEClient(private val context: Context) : BluetoothGattCallback(),
private var trainInfoCallback: ((JSONObject) -> Unit)? = null
private var handler = Handler(Looper.getMainLooper())
private var targetDeviceName: String? = null
+ private var bluetoothLeScanner: BluetoothLeScanner? = null
+
+ private val leScanCallback = object : ScanCallback() {
+ override fun onScanResult(callbackType: Int, result: ScanResult) {
+ val device = result.device
+ val deviceName = device.name
+ if (targetDeviceName != null) {
+ if (deviceName == null || !deviceName.equals(targetDeviceName, ignoreCase = true)) {
+ return
+ }
+ }
+ scanCallback?.invoke(device)
+ }
+
+ override fun onScanFailed(errorCode: Int) {
+ Log.e(TAG, "BLE scan failed code=$errorCode")
+ }
+ }
fun setTrainInfoCallback(callback: (JSONObject) -> Unit) {
@@ -42,8 +65,28 @@ class BLEClient(private val context: Context) : BluetoothGattCallback(),
}
+ private fun hasBluetoothPermissions(): Boolean {
+ val bluetoothPermissions = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_SCAN) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_CONNECT) == PackageManager.PERMISSION_GRANTED
+ } else {
+ ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH) == PackageManager.PERMISSION_GRANTED &&
+ ContextCompat.checkSelfPermission(context, android.Manifest.permission.BLUETOOTH_ADMIN) == PackageManager.PERMISSION_GRANTED
+ }
+
+ val locationPermissions = ContextCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED ||
+ ContextCompat.checkSelfPermission(context, android.Manifest.permission.ACCESS_COARSE_LOCATION) == PackageManager.PERMISSION_GRANTED
+
+ return bluetoothPermissions && locationPermissions
+ }
+
@SuppressLint("MissingPermission")
fun scanDevices(targetDeviceName: String? = null, callback: (BluetoothDevice) -> Unit) {
+ if (!hasBluetoothPermissions()) {
+ Log.e(TAG, "Bluetooth permissions not granted")
+ return
+ }
+
try {
scanCallback = callback
this.targetDeviceName = targetDeviceName
@@ -57,13 +100,19 @@ class BLEClient(private val context: Context) : BluetoothGattCallback(),
return
}
+ bluetoothLeScanner = bluetoothAdapter.bluetoothLeScanner
+ if (bluetoothLeScanner == null) {
+ Log.e(TAG, "BluetoothLeScanner unavailable")
+ return
+ }
+
handler.postDelayed({
stopScan()
}, SCAN_PERIOD)
isScanning = true
Log.d(TAG, "Starting BLE scan target=${targetDeviceName ?: "Any"}")
- bluetoothAdapter.startLeScan(this)
+ bluetoothLeScanner?.startScan(leScanCallback)
} catch (e: SecurityException) {
Log.e(TAG, "Scan security error: ${e.message}")
} catch (e: Exception) {
@@ -75,28 +124,25 @@ class BLEClient(private val context: Context) : BluetoothGattCallback(),
@SuppressLint("MissingPermission")
fun stopScan() {
if (isScanning) {
- val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
- bluetoothAdapter.stopLeScan(this)
+ bluetoothLeScanner?.stopScan(leScanCallback)
isScanning = false
}
}
- override fun onLeScan(device: BluetoothDevice, rssi: Int, scanRecord: ByteArray) {
- val deviceName = device.name
- if (targetDeviceName != null) {
-
- if (deviceName == null || !deviceName.equals(targetDeviceName, ignoreCase = true)) {
- return
- }
- }
- scanCallback?.invoke(device)
- }
@SuppressLint("MissingPermission")
fun connect(address: String, onConnectionStateChange: ((Boolean) -> Unit)? = null): Boolean {
+ Log.d(TAG, "Attempting to connect to device: $address")
+
+ if (!hasBluetoothPermissions()) {
+ Log.e(TAG, "Bluetooth permissions not granted")
+ handler.post { onConnectionStateChange?.invoke(false) }
+ return false
+ }
+
if (address.isBlank()) {
Log.e(TAG, "Connection failed empty address")
handler.post { onConnectionStateChange?.invoke(false) }
@@ -109,6 +155,18 @@ class BLEClient(private val context: Context) : BluetoothGattCallback(),
handler.post { onConnectionStateChange?.invoke(false) }
return false
}
+
+ if (!bluetoothAdapter.isEnabled) {
+ Log.e(TAG, "Bluetooth adapter is disabled")
+ handler.post { onConnectionStateChange?.invoke(false) }
+ return false
+ }
+
+ if (isConnected) {
+ Log.w(TAG, "Already connected to device")
+ handler.post { onConnectionStateChange?.invoke(true) }
+ return true
+ }
bluetoothGatt?.close()
diff --git a/app/src/main/java/receiver/lbj/MainActivity.kt b/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt
similarity index 83%
rename from app/src/main/java/receiver/lbj/MainActivity.kt
rename to app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt
index 755e6df..c8cd3b8 100644
--- a/app/src/main/java/receiver/lbj/MainActivity.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/MainActivity.kt
@@ -1,4 +1,4 @@
-package receiver.lbj
+package org.noxylva.lbjconsole
import android.Manifest
import android.bluetooth.BluetoothAdapter
@@ -6,7 +6,7 @@ import android.bluetooth.BluetoothDevice
import android.content.Context
import android.content.Intent
import java.io.File
-import android.net.Uri
+import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
@@ -19,36 +19,30 @@ import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.filled.LocationOn
import androidx.compose.material3.*
import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.unit.sp
import androidx.core.content.FileProvider
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.json.JSONObject
import org.osmdroid.config.Configuration
-import receiver.lbj.model.TrainRecord
-import receiver.lbj.model.TrainRecordManager
-import receiver.lbj.ui.screens.HistoryScreen
-import receiver.lbj.ui.screens.MapScreen
-import receiver.lbj.ui.screens.SettingsScreen
-import receiver.lbj.ui.screens.MapScreen
-import receiver.lbj.ui.theme.LBJReceiverTheme
-import receiver.lbj.util.LocoInfoUtil
+import org.noxylva.lbjconsole.model.TrainRecord
+import org.noxylva.lbjconsole.model.TrainRecordManager
+import org.noxylva.lbjconsole.ui.screens.HistoryScreen
+import org.noxylva.lbjconsole.ui.screens.MapScreen
+import org.noxylva.lbjconsole.ui.screens.SettingsScreen
+import org.noxylva.lbjconsole.ui.theme.LBJReceiverTheme
+import org.noxylva.lbjconsole.util.LocoInfoUtil
import java.util.*
import androidx.lifecycle.lifecycleScope
-import androidx.lifecycle.viewModelScope
import android.bluetooth.le.ScanCallback
import android.bluetooth.le.ScanResult
@@ -80,7 +74,10 @@ class MainActivity : ComponentActivity() {
private var temporaryStatusMessage by mutableStateOf(null)
- private var targetDeviceName = "LBJReceiver"
+ private var targetDeviceName = "LBJReceiver"
+
+
+ private val settingsPrefs by lazy { getSharedPreferences("app_settings", Context.MODE_PRIVATE) }
private val requestPermissions = registerForActivityResult(
@@ -133,17 +130,31 @@ class MainActivity : ComponentActivity() {
super.onCreate(savedInstanceState)
- requestPermissions.launch(arrayOf(
- Manifest.permission.BLUETOOTH,
- Manifest.permission.BLUETOOTH_ADMIN,
- Manifest.permission.BLUETOOTH_CONNECT,
- Manifest.permission.BLUETOOTH_SCAN,
+ loadSettings()
+
+
+ val permissions = mutableListOf()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ permissions.addAll(arrayOf(
+ Manifest.permission.BLUETOOTH_CONNECT,
+ Manifest.permission.BLUETOOTH_SCAN,
+ Manifest.permission.BLUETOOTH_ADVERTISE
+ ))
+ } else {
+ permissions.addAll(arrayOf(
+ Manifest.permission.BLUETOOTH,
+ Manifest.permission.BLUETOOTH_ADMIN
+ ))
+ }
+
+ permissions.addAll(arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION,
- Manifest.permission.ACCESS_COARSE_LOCATION,
- Manifest.permission.WRITE_EXTERNAL_STORAGE,
- Manifest.permission.READ_EXTERNAL_STORAGE
+ Manifest.permission.ACCESS_COARSE_LOCATION
))
+ requestPermissions.launch(permissions.toTypedArray())
+
bleClient.setTrainInfoCallback { jsonData ->
handleTrainInfo(jsonData)
@@ -263,17 +274,36 @@ class MainActivity : ComponentActivity() {
deviceName = settingsDeviceName,
onDeviceNameChange = { newName -> settingsDeviceName = newName },
onApplySettings = {
-
-
+ saveSettings()
+ targetDeviceName = settingsDeviceName
Toast.makeText(this, "设备名称 '${settingsDeviceName}' 已保存,下次连接时生效", Toast.LENGTH_LONG).show()
Log.d(TAG, "Applied settings deviceName=${settingsDeviceName}")
},
locoInfoUtil = locoInfoUtil
)
-
-
-
+ // 显示连接对话框
+ if (showConnectionDialog) {
+ ConnectionDialog(
+ isScanning = isScanning,
+ devices = foundDevices,
+ onDismiss = {
+ showConnectionDialog = false
+ stopScan()
+ },
+ onScan = {
+ if (isScanning) {
+ stopScan()
+ } else {
+ startScan()
+ }
+ },
+ onConnect = { device ->
+ showConnectionDialog = false
+ connectToDevice(device)
+ }
+ )
+ }
}
}
}
@@ -284,13 +314,23 @@ class MainActivity : ComponentActivity() {
deviceStatus = "正在连接..."
Log.d(TAG, "Connecting to device name=${device.name ?: "Unknown"} address=${device.address}")
+ // 检查蓝牙适配器状态
+ val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
+ if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled) {
+ deviceStatus = "蓝牙未启用"
+ Log.e(TAG, "Bluetooth adapter unavailable or disabled")
+ return
+ }
+
bleClient.connect(device.address) { connected ->
- if (connected) {
- deviceStatus = "已连接"
- Log.d(TAG, "Connected to device name=${device.name ?: "Unknown"}")
- } else {
- deviceStatus = "连接失败或已断开连接"
- Log.e(TAG, "Connection failed name=${device.name ?: "Unknown"}")
+ runOnUiThread {
+ if (connected) {
+ deviceStatus = "已连接"
+ Log.d(TAG, "Connected to device name=${device.name ?: "Unknown"}")
+ } else {
+ deviceStatus = "连接失败或已断开连接"
+ Log.e(TAG, "Connection failed name=${device.name ?: "Unknown"}")
+ }
}
}
@@ -382,6 +422,19 @@ class MainActivity : ComponentActivity() {
private fun startScan() {
+ val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter()
+ if (bluetoothAdapter == null) {
+ Log.e(TAG, "Bluetooth adapter unavailable")
+ deviceStatus = "设备不支持蓝牙"
+ return
+ }
+
+ if (!bluetoothAdapter.isEnabled) {
+ Log.e(TAG, "Bluetooth adapter disabled")
+ deviceStatus = "请启用蓝牙"
+ return
+ }
+
isScanning = true
foundDevices = emptyList()
val targetDeviceName = settingsDeviceName.ifBlank { null }
@@ -396,8 +449,11 @@ class MainActivity : ComponentActivity() {
Log.d(TAG, "Found target=$targetDeviceName, connecting")
stopScan()
connectToDevice(device)
- } else if (!foundDevices.any { it.address == device.address }) {
- showConnectionDialog = true
+ } else {
+ // 如果没有指定目标设备名称,或者找到的设备不是目标设备,显示连接对话框
+ if (targetDeviceName == null) {
+ showConnectionDialog = true
+ }
}
}
}
@@ -414,6 +470,21 @@ class MainActivity : ComponentActivity() {
private fun updateDeviceList() {
foundDevices = scanResults.map { it.device }
}
+
+
+ private fun loadSettings() {
+ settingsDeviceName = settingsPrefs.getString("device_name", "LBJReceiver") ?: "LBJReceiver"
+ targetDeviceName = settingsDeviceName
+ Log.d(TAG, "Loaded settings deviceName=${settingsDeviceName}")
+ }
+
+
+ private fun saveSettings() {
+ settingsPrefs.edit()
+ .putString("device_name", settingsDeviceName)
+ .apply()
+ Log.d(TAG, "Saved settings deviceName=${settingsDeviceName}")
+ }
}
@OptIn(ExperimentalMaterial3Api::class)
@@ -478,7 +549,7 @@ fun MainContent(
Scaffold(
topBar = {
TopAppBar(
- title = { Text("LBJReceiver") },
+ title = { Text("LBJ Console") },
actions = {
timeSinceLastUpdate.value?.let { time ->
diff --git a/app/src/main/java/receiver/lbj/model/TrainRecord.kt b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt
similarity index 98%
rename from app/src/main/java/receiver/lbj/model/TrainRecord.kt
rename to app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt
index 3edf3a8..b7dae58 100644
--- a/app/src/main/java/receiver/lbj/model/TrainRecord.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecord.kt
@@ -1,11 +1,10 @@
-package receiver.lbj.model
+package org.noxylva.lbjconsole.model
import android.util.Log
import org.json.JSONObject
-import java.text.SimpleDateFormat
import java.util.*
import org.osmdroid.util.GeoPoint
-import receiver.lbj.util.LocationUtils
+import org.noxylva.lbjconsole.util.LocationUtils
class TrainRecord(jsonData: JSONObject? = null) {
companion object {
diff --git a/app/src/main/java/receiver/lbj/model/TrainRecordManager.kt b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecordManager.kt
similarity index 99%
rename from app/src/main/java/receiver/lbj/model/TrainRecordManager.kt
rename to app/src/main/java/org/noxylva/lbjconsole/model/TrainRecordManager.kt
index 3acdbc8..8884d1d 100644
--- a/app/src/main/java/receiver/lbj/model/TrainRecordManager.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/model/TrainRecordManager.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.model
+package org.noxylva.lbjconsole.model
import android.content.Context
import android.content.SharedPreferences
diff --git a/app/src/main/java/receiver/lbj/ui/components/TrainDetailDialog.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainDetailDialog.kt
similarity index 98%
rename from app/src/main/java/receiver/lbj/ui/components/TrainDetailDialog.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainDetailDialog.kt
index 8038569..6334217 100644
--- a/app/src/main/java/receiver/lbj/ui/components/TrainDetailDialog.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainDetailDialog.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.ui.components
+package org.noxylva.lbjconsole.ui.components
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
@@ -16,7 +16,7 @@ import androidx.compose.ui.window.DialogProperties
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.Marker
-import receiver.lbj.model.TrainRecord
+import org.noxylva.lbjconsole.model.TrainRecord
@Composable
fun TrainDetailDialog(
diff --git a/app/src/main/java/receiver/lbj/ui/components/TrainInfoCard.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainInfoCard.kt
similarity index 98%
rename from app/src/main/java/receiver/lbj/ui/components/TrainInfoCard.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainInfoCard.kt
index 2b138de..cf9b2f9 100644
--- a/app/src/main/java/receiver/lbj/ui/components/TrainInfoCard.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainInfoCard.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.ui.components
+package org.noxylva.lbjconsole.ui.components
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
@@ -10,7 +10,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.material3.HorizontalDivider
-import receiver.lbj.model.TrainRecord
+import org.noxylva.lbjconsole.model.TrainRecord
@Composable
fun TrainInfoCard(
diff --git a/app/src/main/java/receiver/lbj/ui/components/TrainRecordsList.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainRecordsList.kt
similarity index 99%
rename from app/src/main/java/receiver/lbj/ui/components/TrainRecordsList.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainRecordsList.kt
index 06cbb5b..5bf7781 100644
--- a/app/src/main/java/receiver/lbj/ui/components/TrainRecordsList.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/components/TrainRecordsList.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.ui.components
+package org.noxylva.lbjconsole.ui.components
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
@@ -9,7 +9,6 @@ import androidx.compose.material.icons.filled.Clear
import androidx.compose.material.icons.filled.FilterList
import androidx.compose.material.icons.filled.Share
import androidx.compose.material3.*
-import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
import androidx.compose.runtime.*
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.ui.Alignment
@@ -18,7 +17,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
-import receiver.lbj.model.TrainRecord
+import org.noxylva.lbjconsole.model.TrainRecord
import java.text.SimpleDateFormat
import java.util.*
diff --git a/app/src/main/java/receiver/lbj/ui/screens/HistoryScreen.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/HistoryScreen.kt
similarity index 97%
rename from app/src/main/java/receiver/lbj/ui/screens/HistoryScreen.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/screens/HistoryScreen.kt
index d719349..a07f2c4 100644
--- a/app/src/main/java/receiver/lbj/ui/screens/HistoryScreen.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/HistoryScreen.kt
@@ -1,10 +1,5 @@
-package receiver.lbj.ui.screens
+package org.noxylva.lbjconsole.ui.screens
-import androidx.compose.animation.AnimatedVisibility
-import androidx.compose.animation.fadeIn
-import androidx.compose.animation.fadeOut
-import androidx.compose.animation.slideInVertically
-import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.background
@@ -17,18 +12,11 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
-import androidx.compose.material.icons.filled.FilterList
-import androidx.compose.material.icons.filled.SignalCellular4Bar
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.input.pointer.pointerInput
-import androidx.compose.ui.input.pointer.positionChange
-import androidx.compose.ui.geometry.Offset
-import androidx.compose.foundation.gestures.detectTransformGestures
-import androidx.compose.ui.input.pointer.util.VelocityTracker
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@@ -42,8 +30,8 @@ import org.osmdroid.views.overlay.Marker
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
import org.osmdroid.views.overlay.TilesOverlay
-import receiver.lbj.model.TrainRecord
-import receiver.lbj.util.LocoInfoUtil
+import org.noxylva.lbjconsole.model.TrainRecord
+import org.noxylva.lbjconsole.util.LocoInfoUtil
import java.text.SimpleDateFormat
import java.util.*
diff --git a/app/src/main/java/receiver/lbj/ui/screens/MapScreen.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/MapScreen.kt
similarity index 85%
rename from app/src/main/java/receiver/lbj/ui/screens/MapScreen.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/screens/MapScreen.kt
index 377e463..d9429fb 100644
--- a/app/src/main/java/receiver/lbj/ui/screens/MapScreen.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/MapScreen.kt
@@ -1,25 +1,15 @@
-package receiver.lbj.ui.screens
+package org.noxylva.lbjconsole.ui.screens
-import android.Manifest
import android.content.Context
-import receiver.lbj.R
-import android.graphics.Bitmap
-import android.graphics.Canvas
import android.graphics.Color
import android.graphics.drawable.Drawable
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
-import android.location.Location
-import android.location.LocationListener
import android.util.Log
-import android.location.LocationManager
-import android.view.View
import android.view.ViewGroup
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Refresh
-import androidx.compose.material.icons.filled.Add
-import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.MyLocation
import androidx.compose.material.icons.filled.Layers
import androidx.compose.material3.*
@@ -36,19 +26,15 @@ import androidx.lifecycle.LifecycleEventObserver
import kotlinx.coroutines.launch
import org.osmdroid.config.Configuration
import org.osmdroid.tileprovider.MapTileProviderBasic
-import org.osmdroid.tileprovider.tilesource.OnlineTileSourceBase
import org.osmdroid.tileprovider.tilesource.TileSourceFactory
import org.osmdroid.tileprovider.tilesource.XYTileSource
import org.osmdroid.util.GeoPoint
import org.osmdroid.views.MapView
import org.osmdroid.views.overlay.*
-import org.osmdroid.views.overlay.compass.CompassOverlay
-import org.osmdroid.views.overlay.compass.InternalCompassOrientationProvider
import org.osmdroid.views.overlay.mylocation.GpsMyLocationProvider
import org.osmdroid.views.overlay.mylocation.MyLocationNewOverlay
-import receiver.lbj.model.TrainRecord
+import org.noxylva.lbjconsole.model.TrainRecord
import java.io.File
-import java.util.*
@Composable
@@ -184,13 +170,7 @@ fun MapScreen(
mapView.invalidate()
- if (!isMapInitialized && validRecords.isNotEmpty()) {
- validRecords.firstOrNull()?.getCoordinates()?.let { point ->
- mapView.controller.setZoom(12.0)
- mapView.controller.setCenter(point)
- isMapInitialized = true
- }
- }
+
}
@@ -258,7 +238,7 @@ fun MapScreen(
val railwayTileSource = XYTileSource(
"OpenRailwayMap",
- 0, 24, // 穷尽所有可能的缩放级别
+ 0, 24,
256,
".png",
arrayOf(
@@ -297,7 +277,15 @@ fun MapScreen(
}
- controller.setZoom(10.0)
+ if (validRecords.isNotEmpty()) {
+ validRecords.lastOrNull()?.getCoordinates()?.let { lastPoint ->
+ controller.setCenter(lastPoint)
+ controller.setZoom(12.0)
+ }
+ } else {
+ controller.setCenter(defaultPosition)
+ controller.setZoom(10.0)
+ }
try {
@@ -307,28 +295,8 @@ fun MapScreen(
locationUpdateMinTime = 1000
}
+
val myLocationOverlay = MyLocationNewOverlay(locationProvider, this).apply {
- // 使用我们的自定义人形图标
- // 使用原生位置/雷达图标
- val personDrawable = ctx.resources.getDrawable(android.R.drawable.ic_menu_mylocation, ctx.theme)
- // 设置为黑色
- personDrawable.setTint(Color.BLACK)
-
- val bitmap = Bitmap.createBitmap(
- personDrawable.intrinsicWidth,
- personDrawable.intrinsicHeight,
- Bitmap.Config.ARGB_8888
- )
- val canvas = Canvas(bitmap)
- personDrawable.setBounds(0, 0, canvas.width, canvas.height)
- personDrawable.draw(canvas)
-
- setPersonIcon(bitmap)
- // 设置中心对齐
- setPersonAnchor(0.5f, 0.5f)
-
- isDrawAccuracyEnabled = false
-
enableMyLocation()
runOnFirstFix {
@@ -337,21 +305,40 @@ fun MapScreen(
currentLocation = GeoPoint(location.latitude, location.longitude)
if (!isMapInitialized) {
- controller.animateTo(location)
-
- isMapInitialized = true
- }
+ controller.setCenter(location)
+ controller.setZoom(15.0)
+ isMapInitialized = true
+ Log.d("MapScreen", "Map initialized with GPS position: $location")
+ }
} ?: run {
-
if (!isMapInitialized) {
- controller.animateTo(defaultPosition)
- }
+ if (validRecords.isNotEmpty()) {
+ validRecords.lastOrNull()?.getCoordinates()?.let { lastPoint ->
+ controller.setCenter(lastPoint)
+ controller.setZoom(12.0)
+ isMapInitialized = true
+ Log.d("MapScreen", "Map initialized with last record position: $lastPoint")
+ }
+ } else {
+ controller.setCenter(defaultPosition)
+ isMapInitialized = true
+ }
+ }
}
} catch (e: Exception) {
e.printStackTrace()
-
if (!isMapInitialized) {
- controller.animateTo(defaultPosition)
+ if (validRecords.isNotEmpty()) {
+ validRecords.lastOrNull()?.getCoordinates()?.let { lastPoint ->
+ controller.setCenter(lastPoint)
+ controller.setZoom(12.0)
+ isMapInitialized = true
+ Log.d("MapScreen", "Map fallback to last record position: $lastPoint")
+ }
+ } else {
+ controller.setCenter(defaultPosition)
+ isMapInitialized = true
+ }
}
}
}
@@ -425,7 +412,7 @@ fun MapScreen(
overlay.enableFollowLocation()
overlay.enableMyLocation()
overlay.myLocation?.let { location ->
- mapViewRef.value?.controller?.animateTo(location)
+ mapViewRef.value?.controller?.setCenter(location)
}
}
},
@@ -467,14 +454,22 @@ fun MapScreen(
FloatingActionButton(
onClick = {
mapViewRef.value?.let { mapView ->
- if (validRecords.isNotEmpty()) {
- validRecords.firstOrNull()?.getCoordinates()?.let { point ->
- mapView.controller.animateTo(point)
- mapView.controller.setZoom(12.0)
+ myLocationOverlayRef.value?.myLocation?.let { gpsLocation ->
+ mapView.controller.setCenter(gpsLocation)
+ mapView.controller.setZoom(15.0)
+ Log.d("MapScreen", "Refresh button: GPS position used")
+ } ?: run {
+ if (validRecords.isNotEmpty()) {
+ validRecords.lastOrNull()?.getCoordinates()?.let { point ->
+ mapView.controller.setCenter(point)
+ mapView.controller.setZoom(12.0)
+ Log.d("MapScreen", "Refresh button: last record position used")
+ }
+ } else {
+ mapView.controller.setCenter(defaultPosition)
+ mapView.controller.setZoom(10.0)
+ Log.d("MapScreen", "Refresh button: default position used")
}
- } else {
- mapView.controller.animateTo(defaultPosition)
- mapView.controller.setZoom(10.0)
}
}
onCenterMap()
diff --git a/app/src/main/java/receiver/lbj/ui/screens/MonitorScreen.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/MonitorScreen.kt
similarity index 97%
rename from app/src/main/java/receiver/lbj/ui/screens/MonitorScreen.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/screens/MonitorScreen.kt
index 35a4af1..de2322b 100644
--- a/app/src/main/java/receiver/lbj/ui/screens/MonitorScreen.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/MonitorScreen.kt
@@ -1,9 +1,7 @@
-package receiver.lbj.ui.screens
+package org.noxylva.lbjconsole.ui.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
-import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Clear
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
@@ -14,8 +12,8 @@ import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.coroutines.delay
-import receiver.lbj.model.TrainRecord
-import receiver.lbj.ui.components.TrainDetailDialog
+import org.noxylva.lbjconsole.model.TrainRecord
+import org.noxylva.lbjconsole.ui.components.TrainDetailDialog
import java.text.SimpleDateFormat
import java.util.*
diff --git a/app/src/main/java/org/noxylva/lbjconsole/ui/screens/SettingsScreen.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/SettingsScreen.kt
new file mode 100644
index 0000000..1cc5640
--- /dev/null
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/screens/SettingsScreen.kt
@@ -0,0 +1,57 @@
+package org.noxylva.lbjconsole.ui.screens
+
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.*
+import androidx.compose.material3.*
+import androidx.compose.runtime.*
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalUriHandler
+import androidx.compose.ui.unit.dp
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun SettingsScreen(
+ deviceName: String,
+ onDeviceNameChange: (String) -> Unit,
+ onApplySettings: () -> Unit
+) {
+ val uriHandler = LocalUriHandler.current
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(16.dp),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp)
+ ) {
+ OutlinedTextField(
+ value = deviceName,
+ onValueChange = onDeviceNameChange,
+ label = { Text("蓝牙设备名称") },
+ modifier = Modifier.fillMaxWidth(),
+ )
+
+ Button(
+ onClick = onApplySettings,
+ modifier = Modifier.fillMaxWidth()
+ ) {
+ Text("应用设置")
+ }
+ }
+
+ Spacer(modifier = Modifier.weight(1f))
+
+ Text(
+ text = "LBJ Console v0.0.1 by undef-i",
+ style = MaterialTheme.typography.bodySmall,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ modifier = Modifier.clickable {
+ uriHandler.openUri("https://github.com/undef-i")
+ }
+ )
+ }
+}
diff --git a/app/src/main/java/receiver/lbj/ui/theme/Color.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Color.kt
similarity index 75%
rename from app/src/main/java/receiver/lbj/ui/theme/Color.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/theme/Color.kt
index 0b750f6..1299e83 100644
--- a/app/src/main/java/receiver/lbj/ui/theme/Color.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Color.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.ui.theme
+package org.noxylva.lbjconsole.ui.theme
import androidx.compose.ui.graphics.Color
diff --git a/app/src/main/java/receiver/lbj/ui/theme/Theme.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Theme.kt
similarity index 91%
rename from app/src/main/java/receiver/lbj/ui/theme/Theme.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/theme/Theme.kt
index 7c804bb..63e93b2 100644
--- a/app/src/main/java/receiver/lbj/ui/theme/Theme.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Theme.kt
@@ -1,8 +1,6 @@
-package receiver.lbj.ui.theme
+package org.noxylva.lbjconsole.ui.theme
-import android.app.Activity
import android.os.Build
-import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
diff --git a/app/src/main/java/receiver/lbj/ui/theme/Type.kt b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Type.kt
similarity index 91%
rename from app/src/main/java/receiver/lbj/ui/theme/Type.kt
rename to app/src/main/java/org/noxylva/lbjconsole/ui/theme/Type.kt
index 6ffa1b1..01d918c 100644
--- a/app/src/main/java/receiver/lbj/ui/theme/Type.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/ui/theme/Type.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.ui.theme
+package org.noxylva.lbjconsole.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
diff --git a/app/src/main/java/receiver/lbj/util/LocationUtils.kt b/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt
similarity index 97%
rename from app/src/main/java/receiver/lbj/util/LocationUtils.kt
rename to app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt
index 34b0d6b..f62e5e0 100644
--- a/app/src/main/java/receiver/lbj/util/LocationUtils.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/util/LocationUtils.kt
@@ -1,8 +1,7 @@
-package receiver.lbj.util
+package org.noxylva.lbjconsole.util
import android.util.Log
import org.osmdroid.util.GeoPoint
-import kotlin.math.abs
object LocationUtils {
diff --git a/app/src/main/java/receiver/lbj/util/LocoInfoUtil.kt b/app/src/main/java/org/noxylva/lbjconsole/util/LocoInfoUtil.kt
similarity index 99%
rename from app/src/main/java/receiver/lbj/util/LocoInfoUtil.kt
rename to app/src/main/java/org/noxylva/lbjconsole/util/LocoInfoUtil.kt
index aae6301..44534ce 100644
--- a/app/src/main/java/receiver/lbj/util/LocoInfoUtil.kt
+++ b/app/src/main/java/org/noxylva/lbjconsole/util/LocoInfoUtil.kt
@@ -1,4 +1,4 @@
-package receiver.lbj.util
+package org.noxylva.lbjconsole.util
import android.content.Context
import android.util.Log
diff --git a/app/src/main/java/receiver/lbj/ui/screens/SettingsScreen.kt b/app/src/main/java/receiver/lbj/ui/screens/SettingsScreen.kt
deleted file mode 100644
index 2ae5c33..0000000
--- a/app/src/main/java/receiver/lbj/ui/screens/SettingsScreen.kt
+++ /dev/null
@@ -1,38 +0,0 @@
-package receiver.lbj.ui.screens
-
-import androidx.compose.foundation.layout.*
-import androidx.compose.material3.*
-import androidx.compose.runtime.*
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Modifier
-import androidx.compose.ui.unit.dp
-
-@OptIn(ExperimentalMaterial3Api::class)
-@Composable
-fun SettingsScreen(
- deviceName: String,
- onDeviceNameChange: (String) -> Unit,
- onApplySettings: () -> Unit
-) {
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(16.dp),
- horizontalAlignment = Alignment.CenterHorizontally,
- verticalArrangement = Arrangement.spacedBy(16.dp)
- ) {
- Text("设置", style = MaterialTheme.typography.headlineMedium)
-
- OutlinedTextField(
- value = deviceName,
- onValueChange = onDeviceNameChange,
- label = { Text("蓝牙设备名称") },
- modifier = Modifier.fillMaxWidth()
- )
-
- Button(onClick = onApplySettings, modifier = Modifier.fillMaxWidth()) {
- Text("应用设备名称")
- }
-
- }
-}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index ab831ab..5ff55f9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -1,3 +1,3 @@
- LBJ Receiver
+ LBJ Console
\ No newline at end of file
diff --git a/app/src/test/java/receiver/lbj/ExampleUnitTest.kt b/app/src/test/java/receiver/lbj/ExampleUnitTest.kt
deleted file mode 100644
index 3c8b179..0000000
--- a/app/src/test/java/receiver/lbj/ExampleUnitTest.kt
+++ /dev/null
@@ -1,13 +0,0 @@
-package receiver.lbj
-
-import org.junit.Test
-
-import org.junit.Assert.*
-
-
-class ExampleUnitTest {
- @Test
- fun addition_isCorrect() {
- assertEquals(4, 2 + 2)
- }
-}
\ No newline at end of file