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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions src/hooks/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,15 @@ export const PROFILE = {
SET_CONFIG_PROFILE: 'com.symbol.datawedge.api.SET_CONFIG'
};

export const PROFILE_CONFIG = {
PROFILE_NAME: 'OPENBOXES',
// DataWedge reads every value of a profile config bundle as a String, so all of the
// values below are quoted (including the booleans and the intent delivery mode).
// See sendDataWedgeConfig in useScanListener for how they reach DataWedge intact.

// The profile only becomes active for the app it is associated with, so the package
// name has to be the one the app is actually running under - it is not always
// com.openboxes.android (branded and experimental builds append a suffix).
export const getProfileConfig = (packageName: string) => ({
PROFILE_NAME: PROFILE.NAME,
PROFILE_ENABLED: 'true',
CONFIG_MODE: 'UPDATE',
PLUGIN_CONFIG: {
Expand All @@ -45,13 +52,14 @@ export const PROFILE_CONFIG = {
},
APP_LIST: [
{
PACKAGE_NAME: 'com.openboxes.android',
PACKAGE_NAME: packageName,
ACTIVITY_LIST: ['*']
}
]
};
export const PROFILE_CONFIG2 = {
PROFILE_NAME: 'OPENBOXES',
});

export const INTENT_OUTPUT_CONFIG = {
PROFILE_NAME: PROFILE.NAME,
PROFILE_ENABLED: 'true',
CONFIG_MODE: 'UPDATE',
PLUGIN_CONFIG: {
Expand All @@ -60,6 +68,7 @@ export const PROFILE_CONFIG2 = {
PARAM_LIST: {
intent_output_enabled: 'true',
intent_action: 'com.openboxes.android.ACTION',
// 2 = broadcast intent, which is what registerBroadcastReceiver listens for.
intent_delivery: '2'
}
}
Expand All @@ -71,7 +80,7 @@ export const PROFILE_CONFIG2 = {
// typed into the focused field as keystrokes), and re-enabled otherwise so plain
// inputs elsewhere can still be populated by scanning.
export const getKeystrokeOutputConfig = (enabled: boolean) => ({
PROFILE_NAME: 'OPENBOXES',
PROFILE_NAME: PROFILE.NAME,
PROFILE_ENABLED: 'true',
CONFIG_MODE: 'UPDATE',
PLUGIN_CONFIG: {
Expand Down
26 changes: 19 additions & 7 deletions src/hooks/useScanListener.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { useEffect, useRef } from 'react';
import { DeviceEventEmitter, Platform } from 'react-native';
import DataWedgeIntents from 'react-native-datawedge-intents';
import DeviceInfo from 'react-native-device-info';

import {
ACTION,
FILTER_ACTIONS,
FILTER_CATEGORY,
getKeystrokeOutputConfig,
getProfileConfig,
INTENT_OUTPUT_CONFIG,
LISTENER,
PROFILE,
PROFILE_CONFIG,
PROFILE_CONFIG2
PROFILE
} from './constant';

export type ScanResult = {
Expand All @@ -24,25 +25,36 @@ let profileConfigured = false;
// muted while this is > 0 (see below).
let activeConsumerCount = 0;

function sendDataWedgeCommand(extraName: string, extraValue: unknown): void {
function sendDataWedgeCommand(extraName: string, extraValue: string): void {
DataWedgeIntents.sendBroadcastWithExtras({
action: ACTION.API_ACTION,
extras: { [extraName]: extraValue, SEND_RESULT: 'false' }
});
}

// A profile config is a nested bundle, and the native bridge builds it by calling
// String.valueOf() on whatever it is given and re-parsing that as JSON. Handing it a
// plain object therefore serialises a Java Map ("{PROFILE_ENABLED=true, ...}"), whose
// unquoted values parse back as booleans and numbers - DataWedge then rejects them
// ("Key PROFILE_ENABLED expected String but value was a java.lang.Boolean"), leaves the
// profile disabled and never sends the scan broadcast. Passing real JSON keeps every
// value the String that DataWedge expects.
function sendDataWedgeConfig(config: Record<string, unknown>): void {
sendDataWedgeCommand(PROFILE.SET_CONFIG_PROFILE, JSON.stringify(config));
}

function configureProfileOnce(): void {
if (profileConfigured) {
return;
}
profileConfigured = true;
sendDataWedgeCommand(PROFILE.CREATE_PROFILE, PROFILE.NAME);
sendDataWedgeCommand(PROFILE.SET_CONFIG_PROFILE, PROFILE_CONFIG);
sendDataWedgeCommand(PROFILE.SET_CONFIG_PROFILE, PROFILE_CONFIG2);
sendDataWedgeConfig(getProfileConfig(DeviceInfo.getBundleId()));
sendDataWedgeConfig(INTENT_OUTPUT_CONFIG);
}

function setKeystrokeOutput(enabled: boolean): void {
sendDataWedgeCommand(PROFILE.SET_CONFIG_PROFILE, getKeystrokeOutputConfig(enabled));
sendDataWedgeConfig(getKeystrokeOutputConfig(enabled));
}

function extractScan(intent: Record<string, any>): ScanResult | null {
Expand Down
15 changes: 8 additions & 7 deletions src/screens/SortationPutaway/PutawayTaskListScreen.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { useFocusEffect, useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import React, { useCallback, useMemo, useState } from 'react';
import React, { useCallback, useMemo, useRef, useState } from 'react';
import { Alert, ScrollView, Text, TouchableOpacity, View } from 'react-native';
import { Button, Chip, Divider, Paragraph } from 'react-native-paper';
import { useDispatch, useSelector } from 'react-redux';
Expand Down Expand Up @@ -96,11 +96,12 @@ export default function PutawayTaskListScreen({ route }: PutawayTaskListScreenPr
const putawayTasks = useSelector((state: RootState) => state.putawayReducer.putawayTasks) as SortationTask[];
const [searchTerm, setSearchTerm] = useState('');
const [expandedZones, setExpandedZones] = useState<{ [key: string]: boolean }>({});
const [enteredManually, setEnteredManually] = useState(true);
// A hardware scan fires onChange and onSubmit in the same tick, so state would still read its previous value here.
const enteredManually = useRef(true);

const resetFilters = () => {
setSearchTerm('');
setEnteredManually(true);
enteredManually.current = true;
};

const navigateToTask = (task: SortationTask, requiresValidationScan: boolean) => {
Expand All @@ -116,7 +117,7 @@ export default function PutawayTaskListScreen({ route }: PutawayTaskListScreenPr

const handleScanChange = (text: string) => {
setSearchTerm(text);
setEnteredManually(false);
enteredManually.current = false;
};

const handleScan = (code: string) => {
Expand All @@ -127,7 +128,7 @@ export default function PutawayTaskListScreen({ route }: PutawayTaskListScreenPr
const matches = (putawayTasks ?? []).filter((task) => isProductBarcodeValid(trimmed, task.inventoryItem?.product));

if (matches.length === 1) {
navigateToTask(matches[0], enteredManually);
navigateToTask(matches[0], enteredManually.current);
return;
}
if (matches.length > 1) {
Expand All @@ -140,7 +141,7 @@ export default function PutawayTaskListScreen({ route }: PutawayTaskListScreenPr

const handleSearchSelect = (productCode: string) => {
const matches = (putawayTasks ?? []).filter((task) => task.inventoryItem?.product?.productCode === productCode);
setEnteredManually(true);
enteredManually.current = true;
if (matches.length === 1) {
navigateToTask(matches[0], true);
} else {
Expand Down Expand Up @@ -218,7 +219,7 @@ export default function PutawayTaskListScreen({ route }: PutawayTaskListScreenPr

const selectedTask = zoneTasks.tasks[taskIndex];

navigateToTask(selectedTask, enteredManually);
navigateToTask(selectedTask, enteredManually.current);
};

const handleClearSearch = () => {
Expand Down