Merge pull request 'eagle-weighingScale' (#1) from eagle-weighingScale into main
Some checks failed
CI / lint (push) Has been cancelled
CI / test (push) Has been cancelled
CI / build-library (push) Has been cancelled
CI / build-android (push) Has been cancelled
CI / build-ios (push) Has been cancelled

Reviewed-on: #1
This commit was merged in pull request #1.
This commit is contained in:
2025-12-16 16:41:11 +05:30
4 changed files with 231 additions and 42 deletions

View File

@@ -1,2 +1,14 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
</manifest> </manifest>

View File

@@ -1,23 +1,193 @@
package com.weightscalebridge package com.weightscalebridge
import com.facebook.react.bridge.ReactApplicationContext import android.Manifest
import com.facebook.react.module.annotations.ReactModule import android.bluetooth.BluetoothAdapter
import android.bluetooth.BluetoothManager
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.CountDownTimer
import androidx.core.content.ContextCompat
import com.facebook.react.bridge.*
import com.facebook.react.modules.core.DeviceEventManagerModule
import kotlin.math.pow
import kotlin.math.round
@ReactModule(name = WeightScaleBridgeModule.NAME) class WeightScaleModule(
class WeightScaleBridgeModule(reactContext: ReactApplicationContext) : private val reactContext: ReactApplicationContext
NativeWeightScaleBridgeSpec(reactContext) { ) : ReactContextBaseJavaModule(reactContext) {
override fun getName(): String { private var bluetoothAdapter: BluetoothAdapter? = null
return NAME private var scanner: BluetoothLeScanner? = null
private var isScanning = false
private var timer: CountDownTimer? = null
private var currentWeight = 0.0
override fun getName(): String = "WeightScaleBridge"
init {
val manager =
reactContext.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager
bluetoothAdapter = manager.adapter
scanner = bluetoothAdapter?.bluetoothLeScanner
} }
// Example method private fun sendEvent(name: String, params: WritableMap?) {
// See https://reactnative.dev/docs/native-modules-android reactContext
override fun multiply(a: Double, b: Double): Double { .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java)
return a * b .emit(name, params)
} }
companion object { @ReactMethod
const val NAME = "WeightScaleBridge" fun isBluetoothEnabled(promise: Promise) {
try {
val enabled = bluetoothAdapter?.isEnabled ?: false
promise.resolve(enabled)
} catch (e: Exception) {
promise.reject("BLUETOOTH_CHECK_ERROR", e.message, e)
}
}
@ReactMethod
fun startScanning(promise: Promise) {
try {
if (!hasPermissions()) {
promise.reject("PERMISSION", "Bluetooth permissions not granted")
return
}
if (bluetoothAdapter?.isEnabled != true) {
promise.reject("BLUETOOTH", "Bluetooth disabled")
return
}
if (isScanning) {
promise.resolve(true)
return
}
isScanning = true
currentWeight = 0.0
scanner?.startScan(scanCallback)
sendEvent(
"onWeightScaleStatus",
Arguments.createMap().apply { putString("status", "scanning") }
)
timer = object : CountDownTimer(20000, 1000) {
override fun onTick(ms: Long) {}
override fun onFinish() {
stopScanning(null)
}
}.start()
promise.resolve(true)
} catch (e: Exception) {
promise.reject("SCAN_ERROR", e.message, e)
}
}
@ReactMethod
fun stopScanning(promise: Promise?) {
try {
scanner?.stopScan(scanCallback)
} catch (_: Exception) {}
timer?.cancel()
isScanning = false
sendEvent(
"onWeightScaleStatus",
Arguments.createMap().apply { putString("status", "stopped") }
)
promise?.resolve(true)
}
private val scanCallback = object : ScanCallback() {
override fun onScanResult(type: Int, result: ScanResult) {
val record = result.scanRecord ?: return
val data = record.manufacturerSpecificData
if (data.size() == 0) return
val raw = data.valueAt(0)
if (raw.size < 2) return
val weight = (((raw[0].toInt() and 0xff) shl 8) or
(raw[1].toInt() and 0xff)) / 10.0
currentWeight = weight
sendEvent(
"onWeightData",
Arguments.createMap().apply {
putDouble("weight", weight)
putBoolean("isStable", true)
}
)
sendEvent(
"onWeightScaleStatus",
Arguments.createMap().apply {
putString("status", "complete")
putString("message", "$weight kg")
}
)
stopScanning(null)
}
override fun onScanFailed(errorCode: Int) {
super.onScanFailed(errorCode)
sendEvent(
"onWeightScaleStatus",
Arguments.createMap().apply {
putString("status", "error")
putString("message", "Scan failed with code: $errorCode")
}
)
isScanning = false
}
}
@ReactMethod
fun calculateBMI(weight: Double, heightCm: Double, promise: Promise) {
try {
if (heightCm <= 0) {
promise.reject("INVALID_HEIGHT", "Height must be greater than 0")
return
}
val bmi = weight / (heightCm / 100).pow(2)
promise.resolve(round(bmi * 10) / 10)
} catch (e: Exception) {
promise.reject("BMI_ERROR", e.message, e)
}
}
private fun hasPermissions(): Boolean {
val perms =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
arrayOf(
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT
)
else
arrayOf(
Manifest.permission.ACCESS_FINE_LOCATION
)
return perms.all {
ContextCompat.checkSelfPermission(reactContext, it) ==
PackageManager.PERMISSION_GRANTED
}
} }
} }

View File

@@ -1,33 +1,19 @@
package com.weightscalebridge package com.weightscalebridge
import com.facebook.react.BaseReactPackage import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.model.ReactModuleInfo import com.facebook.react.uimanager.ViewManager
import com.facebook.react.module.model.ReactModuleInfoProvider
import java.util.HashMap
class WeightScaleBridgePackage : BaseReactPackage() { class WeightScaleBridgePackage : ReactPackage {
override fun getModule(name: String, reactContext: ReactApplicationContext): NativeModule? {
return if (name == WeightScaleBridgeModule.NAME) { override fun createNativeModules(
WeightScaleBridgeModule(reactContext) reactContext: ReactApplicationContext
} else { ): List<NativeModule> {
null return listOf(WeightScaleModule(reactContext))
}
} }
override fun getReactModuleInfoProvider(): ReactModuleInfoProvider { override fun createViewManagers(
return ReactModuleInfoProvider { reactContext: ReactApplicationContext
val moduleInfos: MutableMap<String, ReactModuleInfo> = HashMap() ): List<ViewManager<*, *>> = emptyList()
moduleInfos[WeightScaleBridgeModule.NAME] = ReactModuleInfo(
WeightScaleBridgeModule.NAME,
WeightScaleBridgeModule.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
false, // isCxxModule
true // isTurboModule
)
moduleInfos
}
}
} }

View File

@@ -1,5 +1,26 @@
import WeightScaleBridge from './NativeWeightScaleBridge'; import { NativeModules, Platform } from 'react-native';
export function multiply(a: number, b: number): number { const LINKING_ERROR =
return WeightScaleBridge.multiply(a, b); `The package 'react-native-weight-scale-bridge' doesn't seem to be linked. Make sure:\n\n` +
Platform.select({ ios: "- You have run 'pod install'\n", default: '' }) +
'- You rebuilt the app after installing the package\n' +
'- You are not using Expo Go\n';
// Changed to match the native module name
const WeightScaleBridge = NativeModules.WeightScaleBridge;
if (!WeightScaleBridge) {
throw new Error(LINKING_ERROR);
} }
export const startScanning = () =>
WeightScaleBridge.startScanning();
export const stopScanning = () =>
WeightScaleBridge.stopScanning();
export const calculateBMI = (weight: number, heightCm: number) =>
WeightScaleBridge.calculateBMI(weight, heightCm);
export const isBluetoothEnabled = () =>
WeightScaleBridge.isBluetoothEnabled();