Pedometer algorithm doesn't count steps properly using accelerometer data - android

I am using react-native and I want to count the steps of the user like Samsung Health step indicator.
I am using react-native-sensors library to access accelerometer data.
I followed this tutorial for pedometer algorithm, implemented in react-native.
import {View, Text} from 'react-native';
import {
accelerometer,
SensorTypes,
setUpdateIntervalForType,
} from 'react-native-sensors';
setUpdateIntervalForType(SensorTypes.accelerometer, 400);
const App = () => {
const [xAcceleration, setXAcceleration] = useState(0);
const [yAcceleration, setYAcceleration] = useState(0);
const [zAcceleration, setZAcceleration] = useState(0);
const [magnitudePrevious, setMagnitudePrevious] = useState(0);
const [steps, setSteps] = useState(0);
useEffect(() => {
const subscription = accelerometer
.pipe(data => data)
.subscribe(speed => {
setXAcceleration(speed.x);
setYAcceleration(speed.y);
setZAcceleration(speed.z);
});
return () => {
subscription.unsubscribe();
};
}, []);
useEffect(() => {
const magnitude = Math.sqrt(
Math.pow(xAcceleration, 2) +
Math.pow(yAcceleration, 2) +
Math.pow(zAcceleration, 2),
);
const magnitudeDelta = magnitude - magnitudePrevious;
setMagnitudePrevious(() => magnitude);
// I tried magnitudeDelta > 6, magnitudeDelta > 4,
// magnitudeDelta > 2, magnitudeDelta > 10 but didn't work properly
if (magnitudeDelta > 2) setSteps(prevSteps => prevSteps + 1);
}, [xAcceleration, yAcceleration, zAcceleration]);
return (
<View>
<Text>{steps}</Text>
</View>
)
}
If a shake my phone upwards or sideways it increments the steps but I think it's ok because samething happens in Samsung Health. But the main problem is it is not as accurate as Samsung Health when you walk.
For example: If a stepped 20 times it only counts 8 of them. I want it to be close to actual value.

Related

Re-creating touch-based gestures with Playwright to test mobile viewports

Can someone help me re-create touch-based gestures(swipe, zoom, pinch, etc.) in Playwright to test emulated mobile devices? It has only a tap method out of the box, which works perfectly, but I need a whole set of functionality to test the mobile viewports of our web application.
I have tried so far a couple of scripts that I found online, which are not failing my tests but rather don't work for me for some reason:
await page.evaluate(() => {
const target = document.querySelector("#abc");
target.addEventListener('touchstart', (e) => {
console.log('touch start');
});
function simulateTouchEvent(element, type, touches) {
const touchEvents = [];
touches.forEach((touch) => {
touchEvents.push(new Touch({
clientX: touch.x,
clientY: touch.y,
identifier: touch.id,
target: element,
}));
});
element.dispatchEvent(new TouchEvent(type, {
touches: touchEvents,
view: window,
cancelable: true,
bubbles: true,
}));
}
simulateTouchEvent(target, 'touchstart', [{
id: "123",
x: 10,
y: 10,
}]);
simulateTouchEvent(target, 'touchend', [{
id: "123",
x: 10,
y: 10,
}]);
})
also this
const el = await page.locator(
".selector"
);
const dataTransfer = await page.evaluateHandle(
() => new DataTransfer()
);
await el.dispatchEvent("touchstart", { dataTransfer, steps: 5 });
await el.dispatchEvent("touchend", { dataTransfer, steps: 5 });
and
async function dispatchTouchEvent(
playWright: Page,
type: 'touchstart' | 'touchend' | 'touchcancel' | 'touchmove',
selector: string,
page?: Position,
screen?: Position,
client?: Position,
options?: PageExtraTouchOptions,
) {
await playWright.$eval(
selector,
(el, options) => {
const rect = el.getBoundingClientRect();
const {
client = {},
page = {},
screen = {},
type,
options: touchOpt,
} = options;
const touchObj = new Touch({
clientX: client.x,
clientY: client.y,
identifier: Date.now(),
pageX:
page.x || (client.x !== undefined ? rect.left + client.x : undefined),
pageY:
page.y || (client.y !== undefined ? rect.top + client.y : undefined),
screenX: screen.x,
screenY: screen.y,
target: el,
});
const touchEvent = new TouchEvent(type, {
bubbles: true,
cancelable: true,
...touchOpt,
changedTouches: [touchObj],
targetTouches: [touchObj],
touches: [touchObj],
});
return el.dispatchEvent(touchEvent);
},
{ client, options, page, screen, type },
);
}
await dispatchTouchEvent(
page,
"touchstart",
".selector"
);
await dispatchTouchEvent(
page,
"touchend",
".selector"
);
I recently have been also looking into Playwrights experimental android emulation feature, in the hopes that it might help me to emulate at least an android device, but haven't even been able to even run it because of ECONNREFUSED error.
Would appreciate any help, because I`m completely stuck in here.
Playwright version: 1.23;
OS: Ubuntu Neon KDE 20.04;

"react-native-modal-datetime-picker" years list is not scrolling

I tried to find a solution on my own, or at least similar problems with other people, but I failed.
This problem appeared after updating the react-native-reanimated to version 2.x. I need it to work with other components, so the option to roll back is not suitable.
The problem occurs only on android. Does anyone know why this might be?
My component code is presented below:
import PropTypes from 'prop-types';
import React, { useCallback, useMemo, useState } from 'react';
import TextInput from './TextInput';
import { View, StyleSheet } from 'react-native';
import { FAB, TouchableRipple } from 'react-native-paper';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import moment from 'moment';
import { colors } from '../../../styles/colors';
import { CalendarLinear } from '../../../config/images';
import { formatDate } from '../../../helpers';
import { typographySizes } from '../../../styles/typography.style';
import { em } from '../../../styles/sizes';
const minDate = new Date('1900-01-01');
const maxDate = new Date('2038-01-01');
const iconSize = typographySizes.small.fontSize;
const CalendarLinearIcon = () => (
<CalendarLinear width={iconSize} height={iconSize} fill={colors.muted_dark} />
);
const TextInputDate = (props) => {
let { value } = props;
const {
onChangeText,
mode = 'date',
min = minDate,
max = maxDate,
locale = 'ru-RU',
icon = true,
...rest
} = props;
value = formatDate(value);
const [visible, setVisible] = useState(false);
const showPicker = useCallback(() => {
setVisible(true);
}, []);
const hidePicker = useCallback(() => {
setVisible(false);
}, []);
const confirmPicker = useCallback(
(date) => {
const value = new moment(date).format('YYYY-MM-DD');
setVisible(false);
onChangeText(value);
},
[onChangeText]
);
const trailingIcon = useMemo(
() =>
(icon && (
<FAB small style={styles.calendarButton} icon={CalendarLinearIcon} />
)) ||
undefined,
[icon]
);
return (
<>
<DateTimePickerModal
isVisible={visible}
value={new Date(value)}
mode={mode}
minimumDate={min}
maximumDate={max}
locale={locale}
onConfirm={confirmPicker}
onCancel={hidePicker}
/>
<TouchableRipple
onPress={showPicker}
style={{ borderTopLeftRadius: em / 2, borderTopRightRadius: em / 2 }}
borderless>
<View>
<TextInput
{...rest}
keyboardType={'numeric'}
// onChangeText={onChange}
type={'date'}
editable={false}
value={value}
onFocus={showPicker}
trailingIcon={trailingIcon}
/>
<View style={StyleSheet.absoluteFill} />
</View>
</TouchableRipple>
</>
);
};
TextInputDate.propTypes = {
value: PropTypes.any.isRequired,
onChangeText: PropTypes.func.isRequired,
mode: PropTypes.oneOf(['date', 'time', 'datetime', 'countdown']),
min: PropTypes.instanceOf(Date),
max: PropTypes.instanceOf(Date),
locale: PropTypes.string,
};
const styles = {
calendarButton: {
backgroundColor: 'transparent',
shadowOpacity: 0,
shadowRadius: 0,
elevation: 0,
height: iconSize * 2,
width: iconSize * 2,
},
};
export default TextInputDate;
UPD1:
I found this only occurs on small screens. Apparently, a nested scrollable view is formed or something like that.
UPD2:
I tried to create a reproducible example in codesandbox but I get an error. I think this is a flaw in the platform. But this code can help reproduce this problem on your PC.
UPD3:
The problem cannot be the minimum or maximum date. Moreover, I do not use the time mode.
UPD4:
Apparently the issue has nothing to do with react-native-reanimated, it just coincided. I have reproduced the issue separately, without this library.
I also reported about the issue to the developers.
UPD5:
Thanks to the developer's answer, I ran additional tests and it turned out that the real reason for this behavior is in #react-native-community/datetimepicker.
The standard example from the documentation reproduces this behavior.
I have also reported the issue to other developers.
Looking at the props in the documentation, it says "Min Date. Does not work with 'time' picker on Android". Same for "Max Date."
The developer said there was no point in solving the problem... I agree with him, because this does not occur on real devices.

How to stop React Native re-rendering?

I'm still learning to use React Native and runnig into an issue with the stack size being exceeded but I'm unsure why. Looking at other posts I see it must be that the screen is being rerendered too many times and is stuck in a loop but how can I prevent this happening?
RaceListScreen
export function RandomRaceScreen(this: any, {navigation: navigation}) {
const [raceList, setRaceList] = useState<RaceModel[]>([]);
useEffect(() => {
const fetchedRaces: RaceModel[] = getCoreRaceList();
setRaceList(fetchedRaces);
}, []);
//number of players must be less than max number of available races
const racePressed = (raceId: number) => {
console.log('Displaying info about Race, ', raceId);
navigation.navigate('RaceLoreListScreen', {raceId: raceId});
};
const renderRaces = (item: unknown) => {
return (
<RaceCard
race={item.item}
onClick={() => {
racePressed(item.item._groupId);
}}
/>
);
};
const width = Dimensions.get('window').width;
return (
<ImageBackground
source={require('../../assets/space_background_reduced_v1.png')}
style={globalStyles.background}>
<FlatList
data={raceList}
renderItem={renderRaces}
sliderWidth={width}
containerCustomStyle={style.carousel}
contentContainerCustomStyle={style.card}
itemWidth={width * 0.8}
layout="default"
removeClippedSubviews={false}
/>
</ImageBackground>
);
}
getCoreRaceList function:
import {RaceModel} from '../../models/RaceModel';
import races from '../../races/core_races.json';
export function getCoreRaceList(): RaceModel[] {
let raceList: RaceModel[] = [];
for (let i = 0; i < 5; i++) {
raceList.push(
new RaceModel(races[i], races[i].name, races[i].homeworld, false),
);
}
return raceList;
}

Detecting Angular Velocity in a React-Native mobile application

I'm hoping to make a feature in a mobile application which detects how fast the user is spinning their phone and which direction. I believe this is angular velocity, but please correct me if I'm wrong.
For what it's worth, I'm experimenting with this idea using react-native (create-react-native-app) with the help of Expo dev tools, and the Expo accelerometer API.
https://docs.expo.io/versions/v15.0.0/sdk/accelerometer.html
My question is perhaps more fundamental though.
Can I reliably detect the speed at which the phone is rotating, and which direction? And would any code solution that I write provide consistent values across different mobile devices, or vary wildly?
Then, if it is a plausible feat, how would I determine such a value? Would I compare value from millisecond to millisecond, and if so, which values?
Thanks for helping me get my head around this.
While I'm still not certain what the correct term is, I've managed to attain the value that I'm looking for by making use of the gyroscope API and monitoring the 'Z-value'. Here is my working example. (Needs to be run with expo)
import React from 'react';
import Expo, {
Gyroscope,
} from 'expo';
import { Text, TouchableOpacity, View } from 'react-native';
export default class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
gyroscopeData: {
x: 0,
y: 0,
z: 0
},
}
}
componentDidMount() {
this._toggle();
}
componentWillUnmount() {
this._unsubscribe();
}
_toggle = () => {
if (this._subscription) {
this._unsubscribe();
} else {
this._subscribe();
}
}
_slow = () => {
Gyroscope.setUpdateInterval(1000);
}
_fast = () => {
Gyroscope.setUpdateInterval(16);
}
_subscribe = () => {
this._subscription = Gyroscope.addListener((result) => {
this.setState({gyroscopeData: result});
});
}
_unsubscribe = () => {
this._subscription && this._subscription.remove();
this._subscription = null;
}
render() {
let { x, y, z } = this.state.gyroscopeData;
return (
<View>
{/*<Text> x: {round(x)}</Text>*/}
{/*<Text> y: {round(y)}</Text>*/}
<Text> z: {z}</Text>
<View>
<TouchableOpacity onPress={this._toggle}>
<Text>Toggle</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._slow}>
<Text>Slow</Text>
</TouchableOpacity>
<TouchableOpacity onPress={this._fast}>
<Text>Fast</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
function round(n) {
if (!n) {
return 0;
}
return Math.floor(n * 100) / 100;
}

React Navigation: StackNavigator transition for Android

I am using this library https://reactnavigation.org/docs/intro/ to build android by react-native. I can make the navigation happens on android device but how I can make the screen slide in from the right and fade in from the left. It seems that this behaviour happens on iOS device but not in Android. Is there any animation configuration for android app?
Please see below animation. This is recorded in iOS.
Starting from : "#react-navigation/native": "^5.5.1",
import {createStackNavigator, TransitionPresets} from '#react-navigation/stack';
const TransitionScreenOptions = {
...TransitionPresets.SlideFromRightIOS, // This is where the transition happens
};
const CreditStack = createStackNavigator();
function CreditStackScreen() {
return (
<CreditStack.Navigator screenOptions={TransitionScreenOptions}> // Don't forget the screen options
<CreditStack.Screen
name="Credit"
component={HomeScreen}
options={headerWithLogo}
/>
<HomeStack.Screen
name="WorkerDetails"
component={WorkerDetails}
options={headerWithLogoAndBackBtn}
/>
</CreditStack.Navigator>
);
}
You can watch this video to understand more:
https://www.youtube.com/watch?v=PvjV96CNPqM&ab_channel=UnsureProgrammer
You should use transitionConfig to override default screen transitions as written on this page.
Unfortunately there is no example provided how that function works but you can find some examples in this file: \react-navigation\lib\views\CardStackStyleInterpolator.js
So your code should look like this:
const navigator = StackNavigator(scenes, {
transitionConfig: () => ({
screenInterpolator: sceneProps => {
const { layout, position, scene } = sceneProps;
const { index } = scene;
const translateX = position.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [layout.initWidth, 0, 0]
});
const opacity = position.interpolate({
inputRange: [
index - 1,
index - 0.99,
index,
index + 0.99,
index + 1
],
outputRange: [0, 1, 1, 0.3, 0]
});
return { opacity, transform: [{ translateX }] };
}
})
});
For StackNavigatoin 6.x.x
Just import
import { TransitionPresets } from '#react-navigation/stack';
Then create a config:
const screenOptionStyle = {
// headerShown: false,
...TransitionPresets.SlideFromRightIOS,
};
And finally just assign them to the Stack Navigator Screen Options:
<Stack.Navigator
screenOptions={screenOptionStyle}
>
<Stack.Screen
...
...
All the above answers are correct, but the solutions work ONLY if you are using createStackNavigator, and not if you are using createNativeStackNavigator; unfortunatelly, if you are following the get started section from react-navigation's docs, you will end up using the latter.
Here you can find a SO question speaking about the differences between the two, but the most relevant one for this questions is that many of the options that your can pass to the former (such as transitionConfig), cannot be passed to the latter.
If you are using createNativeStackNavigator this is how you can do it:
import { createNativeStackNavigator } from '#react-navigation/native-stack'
const StackNavigator = createNativeStackNavigator()
const MyNativeStackNavigator = () =>{
return <StackNavigator.Navigation
screenOptions={{
animation: 'slide_from_right', //<-- this is what will do the trick
presentation: 'card',
}}
>
{routes}
</StackNavigator.Navigator>
}
you need to import StackViewTransitionConfigs from 'react-navigation-stack'
then, override the transitionConfing function.
const myStack = createStackNavigator({
Screen1,
Screen2,
Screen3
},{
transitionConfig: () => StackViewTransitionConfigs.SlideFromRightIOS
}
On #react-navigation/stack component version, the way to do a slide from the right animation is:
<Stack.Navigator
screenOptions={{
cardStyleInterpolator: ({index, current, next, layouts: {screen}}) => {
const translateX = current.progress.interpolate({
inputRange: [index - 1, index, index + 1],
outputRange: [screen.width, 0, 0],
});
const opacity = next?.progress.interpolate({
inputRange: [0, 1, 2],
outputRange: [1, 0, 0],
});
return {cardStyle: {opacity, transform: [{translateX}]}};
},
}}>
<Stack.Screen name="MainScreen" component={MainScreen} />
...
</Stack.Navigator>
Better you can use the react native navigation for this. You can configure your screen using configureScene method. Inside that method use Navigator.SceneConfigs for animating screen. It's work for both android and iOS.
You can get useful information from index.d.ts file, find the export interface TransitionConfig , then press 'Ctrl' & left_click on NavigationTransitionSpec and NavigationSceneRendererProps, then you can get everything you want.

Categories

Resources