From d3d8e19715958eed58b3c5c73fb72ce8824f1446 Mon Sep 17 00:00:00 2001 From: Nedifinita Date: Mon, 14 Jul 2025 23:15:01 +0800 Subject: [PATCH] fix: various issues --- .github/workflows/android-release.yml | 114 ++++++++++++++ .gitignore | 18 +-- .idea/.name | 2 +- LICENSE | 4 +- README.md | 14 +- app/.gitignore | 1 - app/build.gradle.kts | 6 +- .../receiver/lbj/ExampleInstrumentedTest.kt | 20 --- app/src/main/AndroidManifest.xml | 7 +- .../noxylva/lbjconsole}/BLEClient.kt | 90 +++++++++-- .../noxylva/lbjconsole}/MainActivity.kt | 147 +++++++++++++----- .../noxylva/lbjconsole}/model/TrainRecord.kt | 5 +- .../lbjconsole}/model/TrainRecordManager.kt | 2 +- .../ui/components/TrainDetailDialog.kt | 4 +- .../ui/components/TrainInfoCard.kt | 4 +- .../ui/components/TrainRecordsList.kt | 5 +- .../lbjconsole}/ui/screens/HistoryScreen.kt | 18 +-- .../lbjconsole}/ui/screens/MapScreen.kt | 121 +++++++------- .../lbjconsole}/ui/screens/MonitorScreen.kt | 8 +- .../lbjconsole/ui/screens/SettingsScreen.kt | 57 +++++++ .../noxylva/lbjconsole}/ui/theme/Color.kt | 2 +- .../noxylva/lbjconsole}/ui/theme/Theme.kt | 4 +- .../noxylva/lbjconsole}/ui/theme/Type.kt | 2 +- .../noxylva/lbjconsole}/util/LocationUtils.kt | 3 +- .../noxylva/lbjconsole}/util/LocoInfoUtil.kt | 2 +- .../receiver/lbj/ui/screens/SettingsScreen.kt | 38 ----- app/src/main/res/values/strings.xml | 2 +- .../test/java/receiver/lbj/ExampleUnitTest.kt | 13 -- 28 files changed, 462 insertions(+), 251 deletions(-) create mode 100644 .github/workflows/android-release.yml delete mode 100644 app/.gitignore delete mode 100644 app/src/androidTest/java/receiver/lbj/ExampleInstrumentedTest.kt rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/BLEClient.kt (82%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/MainActivity.kt (83%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/model/TrainRecord.kt (98%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/model/TrainRecordManager.kt (99%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/components/TrainDetailDialog.kt (98%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/components/TrainInfoCard.kt (98%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/components/TrainRecordsList.kt (99%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/screens/HistoryScreen.kt (97%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/screens/MapScreen.kt (85%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/screens/MonitorScreen.kt (97%) create mode 100644 app/src/main/java/org/noxylva/lbjconsole/ui/screens/SettingsScreen.kt rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/theme/Color.kt (75%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/theme/Theme.kt (91%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/ui/theme/Type.kt (91%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/util/LocationUtils.kt (97%) rename app/src/main/java/{receiver/lbj => org/noxylva/lbjconsole}/util/LocoInfoUtil.kt (99%) delete mode 100644 app/src/main/java/receiver/lbj/ui/screens/SettingsScreen.kt delete mode 100644 app/src/test/java/receiver/lbj/ExampleUnitTest.kt 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