diff --git a/App.js b/App.js
index 2980ba1..c7fc148 100644
--- a/App.js
+++ b/App.js
@@ -6,7 +6,7 @@ import * as Font from 'expo-font';
// Redux + Storage
import swimtrackerReducer from './state/Reducer';
import { createStore } from 'redux';
-import { DeviceReduxCoupling } from './state/DeviceReduxCoupling';
+import { ConnState, WifiState, DeviceReduxCoupling } from './state/DeviceReduxCoupling';
import { Provider } from 'react-redux';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { persistStore, persistReducer } from 'redux-persist'
@@ -43,17 +43,32 @@ export default class App extends React.Component {
super(props);
this.state = {
isReady: false,
+ disconnected: true,
+ isProvisioning: false,
};
+ this.unsubscribe = undefined;
}
- async componentDidMount() {
- /*await Font.loadAsync({
- Roboto: require('native-base/Fonts/Roboto.ttf'),
- Roboto_medium: require('native-base/Fonts/Roboto_medium.ttf'),
- ...Ionicons.font,
- });*/
+ componentDidMount() {
this.setState({ isReady: true });
this.device = new DeviceReduxCoupling(store);
+
+ console.log("subscribing");
+ let theApp = this;
+ this.unsubscribe = store.subscribe(() => {
+ const state = store.getState();
+ theApp.setState({
+ disconnected: state.deviceState.connState == ConnState.DISCONNECTED,
+ isProvisioning: state.deviceState.wifiState == WifiState.AP_PROVISIONING || state.deviceState.wifiState == WifiState.UNKNOWN,
+ });
+ });
+ }
+
+ componentWillUnmount() {
+ if (this.unsubscribe) {
+ this.unsubscribe();
+ console.log("unsubscribe");
+ }
}
render() {
@@ -64,61 +79,77 @@ export default class App extends React.Component {
headerShown: false,
};
+ let disconnectedView = (
+ <>
+
+ >
+ );
+
+ let provisioningView = (
+ <>
+
+ {props => }
+
+
+
+ >
+ );
+
+ let normalView = (
+ <>
+
+
+
+
+ >
+ );
+
+ let activeView;
+ if (this.state.disconnected)
+ activeView = disconnectedView;
+ else if (this.state.isProvisioning)
+ activeView = provisioningView;
+ else
+ activeView = normalView;
return (
} persistor={persistor}>
-
-
- {props => }
-
-
-
+
+ {activeView}
);
- /*
- return (
-
- } persistor={persistor}>
-
-
-
-
-
-
-
-
-
-
- );
- */
+
}
}
diff --git a/app.json b/app.json
index 5cf5487..eb6b338 100644
--- a/app.json
+++ b/app.json
@@ -25,6 +25,9 @@
"ios": {
"supportsTablet": true
},
- "description": ""
+ "description": "",
+ "android": {
+ "package": "tech.bauer.swimtracker"
+ }
}
}
diff --git a/components/SetupView.js b/components/SetupView.js
index c0f4c4d..b704ff8 100644
--- a/components/SetupView.js
+++ b/components/SetupView.js
@@ -14,12 +14,12 @@ function AdditionalOptionsBottomBar(props) {
return (
{ props.leftText ?
-
+
{props.leftText}
:
}
{props.rightText &&
-
+
{props.rightText}
}
@@ -35,6 +35,9 @@ const bottomBarStyles = StyleSheet.create({
},
text: {
color: "rgba(255,255,255,0.5)",
+ },
+ button: {
+ borderStyle: "dotted"
}
})
@@ -55,7 +58,7 @@ function SetupView(props) {
{props.backButton &&
-
+ props.navigation.goBack()}>
}
@@ -63,7 +66,9 @@ function SetupView(props) {
{props.headerText}
- {props.children}
+
+ {props.children}
+
{
+ conn._wifiScanPromises.push({ resolve: resolve, reject: reject });
+ });
- let conn = this;
- return new Promise((resolve, reject) => {
- conn._wifiScanPromise = { resolve: resolve, reject: reject };
- });
+ }
+
+ wifiResetToProvisioning() {
+ this._sendMsg(OpCodes.WIFI_STATE_SET, {
+ "reset_to_provisioning": true,
+ });
+
+ }
+
+ wifiSetModeAP(password) {
+ this._sendMsg(OpCodes.WIFI_STATE_SET, {
+ "ap_password": password,
+ });
+ }
+
+ wifiSetModeSTA(ssid, password) {
+ this._sendMsg(OpCodes.WIFI_STATE_SET, {
+ "sta_ssid": ssid,
+ "sta_password": password,
+ });
+ }
+
+ _sendMsg(code, data) {
+ let msg = undefined;
+ if (data) {
+ const serializedData = msgpack.encode(data);
+ msg = new Uint8Array([code, ...serializedData]);
+ } else {
+ msg = new Uint8Array(1);
+ msg[0] = OpCodes.WIFI_TRIGGER_SCAN;
}
+ this.ws.send(msg);
}
_onMessage = (e) => {
const dv = new DataView(e.data);
const opCode = dv.getInt8(0);
+ const payload = new Uint8Array(e.data).slice(1);
if (opCode === OpCodes.INITIAL_INFO) {
const headerSize = 6;
@@ -116,14 +137,15 @@ export default class SwimTrackerWebsocketConnection {
const data = new Uint16Array(e.data.slice(1));
this.onData(data);
} else if (opCode === OpCodes.WIFI_SCAN_RESPONSE) {
- console.log("got data", e.data);
- const scanResult = msgpack.decode(new Uint8Array(e.data).slice(1), { codec: this.msgpackCodec });
- if (this._wifiScanPromise !== null) {
- this._wifiScanPromise.resolve(scanResult);
- this._wifiScanPromise = null;
- } else {
- console.log("Got unexpected WiFi scan result", scanResult);
+ const scanResult = msgpack.decode(payload, { codec: this.msgpackCodec });
+
+ for (let i = 0; i < this._wifiScanPromises.length; ++i) {
+ this._wifiScanPromises[i].resolve(scanResult);
}
+ this._wifiScanPromises.length = 0;
+ } else if (opCode === OpCodes.WIFI_STATE_RESPONSE) {
+ const wifiInfo = msgpack.decode(payload, { codec: this.msgpackCodec });
+ this.onWifiStateInfo(wifiInfo);
}
}
diff --git a/state/DeviceReduxCoupling.js b/state/DeviceReduxCoupling.js
index c52cb8e..2e5a2d2 100644
--- a/state/DeviceReduxCoupling.js
+++ b/state/DeviceReduxCoupling.js
@@ -87,6 +87,7 @@ export class DeviceReduxCoupling {
this._onNewData,
(sessionId) => this.reduxStore.dispatch(reportSessionStarted(sessionId)),
() => this.reduxStore.dispatch(reportSessionStopped()),
+ (response) => this.reduxStore.dispatch(reportNewWifiState(response["state"])),
() => this.reduxStore.dispatch(reportDeviceConnect()),
() => this.reduxStore.dispatch(reportDeviceDisconnect())
);
@@ -152,15 +153,14 @@ export const deviceStateReducer = (state = INITIAL_DEVICE_STATE, action) => {
return state;
return { ...INITIAL_DEVICE_STATE, connState: ConnState.CONNECTED_STOPPING };
case WIFI_SET_STATE:
- console.log("here");
let wifState = WifiState.UNKNOWN;
- if (action.data === "STATION_MODE") { wifState = WifiState.STA; }
- else if (action.data === "AP_PROVISIONING") { wifState = WifiState.AP_PROVISIONING; }
- else if (action.data === "AP_SECURE") { wifState = WifiState.AP_SECURE; }
+ if (action.newStateStr === "STATION_MODE") { wifState = WifiState.STA; }
+ else if (action.newStateStr === "AP_PROVISIONING") { wifState = WifiState.AP_PROVISIONING; }
+ else if (action.newStateStr === "AP_SECURE") { wifState = WifiState.AP_SECURE; }
return { ...state, wifiState: wifState };
default:
- console.log("Unhandled state in deviceStateReducer", action, action.type);
- return state
+ //console.log("Unhandled state in deviceStateReducer", action, action.type, "state", state);
+ return state;
}
};
diff --git a/views/MainMenuView.js b/views/MainMenuView.js
index d59f43b..b3d4114 100644
--- a/views/MainMenuView.js
+++ b/views/MainMenuView.js
@@ -10,6 +10,11 @@ import {
import themeColors from '../components/themeColors';
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
import EntypoIcon from "react-native-vector-icons/Entypo";
+import FeatherIcon from "react-native-vector-icons/Feather";
+
+import { MaterialCommunityIcons } from '@expo/vector-icons';
+
+
import { ConnState, startSession } from '../state/DeviceReduxCoupling';
import { connect } from 'react-redux';
@@ -83,7 +88,7 @@ function ButtonGrid(props) {
style={[{ backgroundColor: themeColors["MIDNIGHT BLUE"] }, buttonGridStyles.button]}
activeOpacity={0.6}
>
-
+
EINSTELLUNGEN
diff --git a/views/WifiPasswordView.js b/views/WifiPasswordView.js
index 66e34b2..b2ad7d7 100644
--- a/views/WifiPasswordView.js
+++ b/views/WifiPasswordView.js
@@ -1,33 +1,69 @@
-import React from "react";
+import React, { useState, useEffect } from "react";
import {
StyleSheet,
Text,
View,
TouchableOpacity,
TextInput,
+ Keyboard
} from "react-native";
import SetupView from '../components/SetupView';
import EvilIcon from "react-native-vector-icons/EvilIcons";
import MaterialIcon from "react-native-vector-icons/MaterialCommunityIcons";
import themeColors from '../components/themeColors';
-
function WifiPasswordView(props) {
- props = {...props, ...props.route.params};
+ props = { ...props, ...props.route.params };
+
+ useEffect(() => {
+ Keyboard.addListener("keyboardDidShow", _keyboardDidShow);
+ Keyboard.addListener("keyboardDidHide", _keyboardDidHide);
+
+ // cleanup function
+ return () => {
+ Keyboard.removeListener("keyboardDidShow", _keyboardDidShow);
+ Keyboard.removeListener("keyboardDidHide", _keyboardDidHide);
+ };
+ }, []);
+
+ const [keyboardStatus, setKeyboardStatus] = useState(undefined);
+ const [password1, setPassword1] = useState("");
+ const [password2, setPassword2] = useState("");
+ const [errorMsg, setErrorMsg] = useState("");
+
+ const _keyboardDidShow = () => setKeyboardStatus(true);
+ const _keyboardDidHide = () => setKeyboardStatus(false);
let iconName = "wifi-strength-" + props.strength;
if (props.lock) {
iconName += "-lock";
}
+ const onSubmit = () => {
+
+ if (props.confirmPwInput && password1 != password2)
+ setErrorMsg("Passwords don't match");
+ else if (password1.length < 8)
+ setErrorMsg("Password has to be at least 8 characters long")
+ else if (password1.length > 128)
+ setErrorMsg("Password too long");
+ else
+ setErrorMsg("");
+ };
+
return (
-
- {props.subText}
-
+
+ {!keyboardStatus &&
+
+ {props.subText}
+
+ }
@@ -38,6 +74,7 @@ function WifiPasswordView(props) {
}
-
+ {errorMsg.length > 0 &&
+
+ {errorMsg}
+
+ }
+
+
{props.buttonText}
-
@@ -91,9 +134,10 @@ const styles = StyleSheet.create({
subtext: {
color: "rgba(255,255,255,1)",
textAlign: "left",
- fontSize: 18,
+ fontSize: 16,
lineHeight: 25,
width: "80%",
+ paddingBottom: 30,
},
formContainer: {
},
diff --git a/views/WifiSelectionView.js b/views/WifiSelectionView.js
index 31f0ed3..8d89e93 100644
--- a/views/WifiSelectionView.js
+++ b/views/WifiSelectionView.js
@@ -56,6 +56,7 @@ class WifiSelectionView extends React.Component {
constructor() {
super();
this.state = { wifiInfo: [] };
+ this.mounted = false;
}
processDeviceResponse(response) {
@@ -91,39 +92,56 @@ class WifiSelectionView extends React.Component {
}
componentDidMount() {
+ let component = this;
+ component.mounted = true;
this.props.device.conn.scanWifiNetworks().then(
(result) => {
- this.setState({ wifiInfo: this.processDeviceResponse(result) })
+ if(component.mounted) {
+ this.setState({ wifiInfo: this.processDeviceResponse(result) })
+ }
}
);
}
+ componentWillUnmount() {
+ this.mounted = false;
+ }
+
render() {
let inner;
if (this.state.wifiInfo.length > 0) {
inner = (
-
- {this.state.wifiInfo.map(e => (
- { this.props.navigation.navigate("WifiPasswordView", {
- ssid: e.ssid,
- lock: e.locked,
- strength: e.strength,
- buttonText: "Set Password",
- subText: "Please enter password for your home WiFi",
- }); }}>
- )
- )}
- )
+
+
+ {this.state.wifiInfo.map(e => (
+ {
+ this.props.navigation.navigate("WifiPasswordView", {
+ ssid: e.ssid,
+ lock: e.locked,
+ strength: e.strength,
+ confirmPwInput: false,
+ buttonText: "OK",
+ subText: "Please enter the password for your home WiFi",
+ });
+ }}>
+ )
+ )}
+
+
+ )
}
else {
- inner = (
-
+ inner = (
+
+ Scanning WiFi networks
+
+
)
}
@@ -131,11 +149,19 @@ class WifiSelectionView extends React.Component {
{
+ this.props.navigation.navigate("WifiPasswordView", {
+ ssid: "swimtracker-E2842S", // todo real id here
+ lock: true,
+ strength: 4,
+ confirmPwInput: true,
+ buttonText: "Set Password",
+ subText: "Use this option only if you're home WiFi doesn't reach the SwimTracker. The SwimTracker creates its own WiFi with the password you set here.",
+ });
+ }}
lowerRightButtonText="Need help?"
>
-
- {inner}
-
+ {inner}
)
}
@@ -145,7 +171,7 @@ class WifiSelectionView extends React.Component {
const styles = StyleSheet.create({
listContainer: {
height: "75%",
- //backgroundColor: "red",
+ flex: 1,
}
});