Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

☰♭ Adding a Bluetooth Scanner to dev options #1128

Merged
merged 38 commits into from
Mar 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
04df66f
Added button for Bluetooth Scan Page
the-bay-kay Feb 9, 2024
9ee0cc0
Added barebones BluetoothScanPage
the-bay-kay Feb 9, 2024
b15148c
Ran Prettier
the-bay-kay Feb 9, 2024
602a3b4
Merge branch 'e-mission:master' into bluetooth_scanner
louisg1337 Feb 10, 2024
4dc111f
Added in the new bluetooth plugin so that it will automatically downl…
louisg1337 Feb 10, 2024
2cf7882
Roughly implemented the bluetooth scanner into the bluetooth page. Cr…
louisg1337 Feb 10, 2024
7fe3958
Updated Scanner UI
the-bay-kay Feb 12, 2024
07e4e17
Added translation support to Bluetooth Scanner
the-bay-kay Feb 13, 2024
86957d9
Changed "Scan once" button to "Toggle Scanning"
the-bay-kay Feb 13, 2024
6023c7f
Refactored the permissions for the bluetooth scanner so that there is…
louisg1337 Feb 13, 2024
b2f4281
Merge branch 'bluetooth_scanner' of https://github.com/the-bay-kay/e-…
louisg1337 Feb 13, 2024
4a5d853
Got rid of unused permissions button now
louisg1337 Feb 13, 2024
9215f69
Removed erroneous i18n files
the-bay-kay Feb 14, 2024
b4cafd4
Fixed scan button, ran prettier
the-bay-kay Feb 14, 2024
2b366eb
Fixed weird react hook error by passing in the translation hook into …
louisg1337 Feb 19, 2024
157cfde
Updated bluetooth plugin, ran `prettier`
the-bay-kay Feb 19, 2024
43bfb4e
Updated plugin call to use new plugin
the-bay-kay Feb 20, 2024
4b5a7e7
Fixed rendering issues with BluetoothScanPage
the-bay-kay Feb 21, 2024
4788641
Merge branch 'master' into bluetooth_scanner
the-bay-kay Feb 21, 2024
0a71d52
Reverted button to "Scan Once"
the-bay-kay Feb 21, 2024
5fcab70
Fixed miscalculation with device name slicing
the-bay-kay Feb 22, 2024
f690d84
Bumped up number of expected plugins
louisg1337 Feb 23, 2024
bb16e6d
Forked our own version of the bluetooth plugin so that we can change …
louisg1337 Feb 27, 2024
00b8fdc
Added in functionality to prevent iOS users from using the bluetooth …
louisg1337 Feb 27, 2024
72f499f
Updated Bluetooth Scan Coverage
the-bay-kay Feb 28, 2024
e35aaff
Updated second API call
the-bay-kay Feb 29, 2024
1b772e2
Incorporated iOS functionality to scanner
louisg1337 Mar 1, 2024
1b3e946
Got rid of the ability for iOS users to use the scanner due to weird …
louisg1337 Mar 6, 2024
f8a6a8e
Ran prettier
louisg1337 Mar 6, 2024
1e47df3
Cleaned up PR: Fixed comments, cleaned JSX
the-bay-kay Mar 7, 2024
def2b24
Addressed review changes, updated permissions
the-bay-kay Mar 16, 2024
3939488
Merge branch 'master' into bluetooth_scanner
the-bay-kay Mar 16, 2024
1c1c2ba
Refractored Scanner Promises
the-bay-kay Mar 16, 2024
b76ce00
Updated how logs are handled, added Pairing Status
the-bay-kay Mar 17, 2024
c990c93
Fixed iOS Check
the-bay-kay Mar 17, 2024
b270036
Fixed permission function variable
the-bay-kay Mar 18, 2024
23e8de1
Merge branch 'master' into bluetooth_scanner
the-bay-kay Mar 19, 2024
295b96f
Refactored Bluetooth logs, fixed race conditions
the-bay-kay Mar 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions package.cordovabuild.json
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@
"cordova-plugin-androidx-adapter": {},
"phonegap-plugin-barcodescanner": {
"ANDROID_SUPPORT_V4_VERSION": "27.+"
}
},
"cordova-plugin-bluetooth-classic-serial-port": {},
"cordova-custom-config": {}
shankari marked this conversation as resolved.
Show resolved Hide resolved
}
},
"dependencies": {
Expand All @@ -118,7 +120,7 @@
"cordova-plugin-app-version": "0.1.14",
"cordova-plugin-customurlscheme": "5.0.2",
"cordova-plugin-device": "2.1.0",
"cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.8.2",
"cordova-plugin-em-datacollection": "git+https://github.com/e-mission/e-mission-data-collection.git#v1.8.3",
"cordova-plugin-em-opcodeauth": "git+https://github.com/e-mission/cordova-jwt-auth.git#v1.7.2",
"cordova-plugin-em-server-communication": "git+https://github.com/e-mission/cordova-server-communication.git#v1.2.6",
"cordova-plugin-em-serversync": "git+https://github.com/e-mission/cordova-server-sync.git#v1.3.2",
Expand All @@ -132,6 +134,8 @@
"cordova-plugin-ionic-webview": "5.0.0",
"cordova-plugin-local-notification-12": "github:e-mission/cordova-plugin-local-notification-12#v0.1.4-fix-android-action",
"cordova-plugin-x-socialsharing": "6.0.4",
"cordova-plugin-bluetooth-classic-serial-port": "git+https://github.com/louisg1337/cordova-plugin-bluetooth-classic-serial-port.git",
"cordova-custom-config": "^5.1.1",
"core-js": "^2.5.7",
"enketo-core": "^6.1.7",
"enketo-transformer": "^4.0.0",
Expand Down
1 change: 1 addition & 0 deletions setup/autoreload/macos-index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
const os = require('os');

const nameMap = new Map([
[23, ['Sonoma', '14.3.1']],
[22, ['Ventura', '13']],
[21, ['Monterey', '12']],
[20, ['Big Sur', '11']],
Expand Down
2 changes: 1 addition & 1 deletion setup/setup_shared_native.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ sed -i -e "s|/usr/bin/env node|/usr/bin/env node --unhandled-rejections=strict|"

npx cordova prepare

EXPECTED_COUNT=23
EXPECTED_COUNT=25
INSTALLED_COUNT=`npx cordova plugin list | wc -l`
echo "Found $INSTALLED_COUNT plugins, expected $EXPECTED_COUNT"
if [ $INSTALLED_COUNT -lt $EXPECTED_COUNT ];
Expand Down
14 changes: 13 additions & 1 deletion www/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"user-data": "User data",
"erase-data": "Erase data",
"dev-zone": "Developer zone",
"bluetooth-scan": "Scan for Bluetooth",
"refresh": "Refresh",
"end-trip-sync": "End trip + sync",
"check-consent": "Check consent",
Expand Down Expand Up @@ -230,6 +231,16 @@
"list-datepicker-close": "Close",
"list-datepicker-set": "Set",

"bluetooth": {
"scan-debug-title": "Bluetooth Scanner",
"scan-for-bluetooth": "Scan for Devices",
"is-scanning": "Scanning...",
"device-info": {
"id": "ID",
"name": "Name"
}
},

"service": {
"reading-server": "Reading from server...",
"reading-unprocessed-data": "Reading unprocessed data..."
Expand Down Expand Up @@ -409,7 +420,8 @@
"while-repopulating-entry": "While repopulating timeline entry: ",
"while-loading-metrics": "While loading metrics: ",
"while-log-messages": "While getting messages from the log ",
"while-max-index": "While getting max index "
"while-max-index": "While getting max index ",
"while-scanning-bluetooth": "While scanning for Bluetooth Devices: "
},
"consent-text": {
"title": "NREL OPENPATH PRIVACY POLICY/TERMS OF USE",
Expand Down
31 changes: 31 additions & 0 deletions www/js/bluetooth/BluetoothCard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react';
import { Card, List } from 'react-native-paper';
import { StyleSheet } from 'react-native';

type Props = any;
const BluetoothCard = ({ device }: Props) => {
return (
<Card style={cardStyles.card}>
<Card.Title
title={`Name: ${device.name}`}
titleVariant="titleLarge"
subtitle={`ID: ${device.id}`}
left={() => <List.Icon icon={device.is_paired ? 'bluetooth' : 'bluetooth-off'} />}
/>
</Card>
);
};

export const cardStyles = StyleSheet.create({
card: {
position: 'relative',
alignSelf: 'center',
marginVertical: 10,
},
cardContent: {
flex: 1,
width: '100%',
},
});

export default BluetoothCard;
111 changes: 111 additions & 0 deletions www/js/bluetooth/BluetoothScanPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { StyleSheet, Modal, ScrollView, SafeAreaView, View } from 'react-native';
import gatherBluetoothData from './bluetoothScanner';
import { logWarn, displayError, displayErrorMsg } from '../plugin/logger';
import BluetoothCard from './BluetoothCard';
import { Appbar, useTheme, Button } from 'react-native-paper';

/**
* The implementation of this scanner page follows the design of
* `www/js/survey/enketo/EnketoModal.tsx`!
*
* Future work may include refractoring these files to be implementations of a
* single base "pop-up page" component
*/

const BluetoothScanPage = ({ ...props }: any) => {
const { t } = useTranslation();
const [logs, setLogs] = useState<string[]>([]);
const [isScanning, setIsScanning] = useState(false);
const { colors } = useTheme();

// Function to run Bluetooth Classic test and update logs
const runBluetoothTest = async () => {
// Classic not currently supported on iOS
if (window['cordova'].platformId == 'ios') {
displayErrorMsg('Sorry, iOS is not supported!', 'OSError');
return;
}

try {
let response = await window['cordova'].plugins.BEMDataCollection.bluetoothScanPermissions();
if (response != 'OK') {
displayErrorMsg('Please Enable Bluetooth!', 'Insufficient Permissions');
return;
}
} catch (e) {
displayError(e, 'Insufficient Permissions');
return;
}

try {
setIsScanning(true);
const newLogs = await gatherBluetoothData(t);
setLogs(newLogs);
} catch (error) {
logWarn(error);
} finally {
setIsScanning(false);
}
};

const BluetoothCardList = ({ devices }) => (
<div>
{devices.map((device) => {
if (device) {
return <BluetoothCard device={device} />;
}
return null;
})}
</div>
);

const BlueScanContent = () => (
<div style={{ height: '100%' }}>
<Appbar.Header
statusBarHeight={0}
elevated={true}
style={{ height: 46, backgroundColor: colors.surface }}>
<Appbar.BackAction
onPress={() => {
props.onDismiss?.();
}}
/>
<Appbar.Content title={t('bluetooth.scan-debug-title')} titleStyle={{ fontSize: 17 }} />
</Appbar.Header>
<View style={s.btnContainer}>
<Button mode="elevated" onPress={runBluetoothTest} textColor={colors.primary} style={s.btn}>
{isScanning ? t('bluetooth.is-scanning') : t('bluetooth.scan-for-bluetooth')}
</Button>
</View>
<BluetoothCardList devices={logs} />
</div>
);

return (
<>
<Modal {...props} animationType="slide">
<SafeAreaView style={{ flex: 1 }}>
<ScrollView style={{ flex: 1 }} contentContainerStyle={{ flex: 1 }}>
<BlueScanContent />
</ScrollView>
</SafeAreaView>
</Modal>
</>
);
};

const s = StyleSheet.create({
btnContainer: {
padding: 16,
justifyContent: 'center',
},
btn: {
height: 38,
fontSize: 11,
margin: 4,
},
});

export default BluetoothScanPage;
54 changes: 54 additions & 0 deletions www/js/bluetooth/bluetoothScanner.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { logDebug, displayError } from '../plugin/logger';
import { BluetoothClassicDevice } from '../types/bluetoothTypes';

/**
* gatherBluetoothData scans for viewable Bluetooth Classic Devices
* @param t is the i18next translation function
* @returns an array of strings containing device data, formatted ['ID: id Name: name']
*/
export default function gatherBluetoothData(t): Promise<string[]> {
return new Promise((resolve, reject) => {
logDebug('Running bluetooth discovery test!');

// Device List "I/O"
function updatePairingStatus(pairingType: boolean, devices: Array<BluetoothClassicDevice>) {
devices.forEach((device) => {
device.is_paired = pairingType;
});
return devices;
}

// Plugin Calls
const unpairedDevicesPromise = new Promise((res, rej) => {
window['bluetoothClassicSerial'].discoverUnpaired(
(devices: Array<BluetoothClassicDevice>) => {
res(updatePairingStatus(false, devices));
},
(e: Error) => {
displayError(e, 'Error');
rej(e);
},
);
});

const pairedDevicesPromise = new Promise((res, rej) => {
window['bluetoothClassicSerial'].list(
(devices: Array<BluetoothClassicDevice>) => {
res(updatePairingStatus(true, devices));
},
(e: Error) => {
displayError(e, 'Error');
rej(e);
},
);
});

Promise.all([unpairedDevicesPromise, pairedDevicesPromise])
.then((logs: Array<any>) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, future fix: can't this be:

Suggested change
.then((logs: Array<any>) => {
.then((logs: Array<BluetoothClassicDevice>) => {

resolve(logs.flat());
})
.catch((e) => {
reject(e);
});
});
}
23 changes: 23 additions & 0 deletions www/js/control/BluetoothScanSettingRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { useState } from 'react';
import SettingRow from './SettingRow';
import BluetoothScanPage from '../bluetooth/BluetoothScanPage';

const BluetoothScanSettingRow = ({}) => {
const [bluePageVisible, setBluePageVisible] = useState<boolean>(false);

async function openPopover() {
setBluePageVisible(true);
}

return (
<>
<SettingRow
textKey="control.bluetooth-scan"
iconName="bluetooth-settings"
action={openPopover}></SettingRow>
<BluetoothScanPage visible={bluePageVisible} onDismiss={() => setBluePageVisible(false)} />
</>
);
};

export default BluetoothScanSettingRow;
3 changes: 2 additions & 1 deletion www/js/control/ProfileSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import ExpansionSection from './ExpandMenu';
import SettingRow from './SettingRow';
import ControlDataTable from './ControlDataTable';
import DemographicsSettingRow from './DemographicsSettingRow';
import BluetoothScanSettingRow from './BluetoothScanSettingRow';
import PopOpCode from './PopOpCode';
import ReminderTime from './ReminderTime';
import useAppConfig from '../useAppConfig';
Expand Down Expand Up @@ -433,8 +434,8 @@ const ProfileSettings = () => {
textKey="control.email-log"
iconName="email"
action={() => sendEmail('loggerDB')}></SettingRow>

<ExpansionSection sectionTitle="control.dev-zone">
<BluetoothScanSettingRow />
<SettingRow
textKey="control.refresh"
iconName="refresh"
Expand Down
8 changes: 8 additions & 0 deletions www/js/types/bluetoothTypes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Device data, as defined in BluetoothClassicSerial's docs
export type BluetoothClassicDevice = {
class: number;
id: string;
address: string;
name: string;
is_paired?: boolean; // We keep track of this, BCS doesn't
};
Loading