I have an App with two Views, blue view must be shown only when app is in active state (foreground) and the green view must be shown when app is put on background (e.g. clocking on hardware square button on Android).
The following picture shows what I currently get when I put the App in background state:
The following one is what I want to do when App is put on background (on iOS works, on Android not so well):
And this is the implementation.
Please, ignore for the moment that the component for iOS is almost identical to that for Android except for the style of the View. I was doing other tests with different components and for the moment it's okay for me to keep them like this. The difference between the two components (iOS / Android) lies in the style, because in iOS the zIndex
works, while in Android I have to use elevation
to overlap the views.
const showSecurityScreenFromAppState = appState =>
['background', 'inactive'].includes(appState);
const withSecurityScreenIOS = Wrapped => {
return class WithSecurityScreen extends React.Component {
constructor(props) {
super(props);
}
state = {
showSecurityScreen: showSecurityScreenFromAppState(AppState.currentState),
};
componentDidMount() {
AppState.addEventListener('change', this.onChangeAppState);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.onChangeAppState);
}
onChangeAppState = nextAppState => {
const showSecurityScreen = showSecurityScreenFromAppState(nextAppState);
this.setState({showSecurityScreen});
};
//this.state.showSecurityScreen
render() {
return (
<>
<View style={[styles.container, {zIndex: this.state.showSecurityScreen ? 1 : -1}]}>
<View style={{flex: 1, backgroundColor: globalStyles.colors.customerGreen}}>
<View style={styles.upperView}>
<Text style={styles.upperViewText}>MyApp</Text>
</View>
</View>
</View>
<Wrapped {...this.props}/>
</>
);
}
};
};
const withSecurityScreenAndroid = Wrapped => {
return class WithSecurityScreen extends React.Component {
constructor(props) {
super(props);
}
state = {
showSecurityScreen: showSecurityScreenFromAppState(AppState.currentState),
};
componentDidMount() {
AppState.addEventListener('change', this.onChangeAppState);
}
componentWillUnmount() {
AppState.removeEventListener('change', this.onChangeAppState);
}
onChangeAppState = nextAppState => {
const showSecurityScreen = showSecurityScreenFromAppState(nextAppState);
this.setState({showSecurityScreen});
};
//this.state.showSecurityScreen
render() {
return (
<>
<View style={[styles.container, {elevation: this.state.showSecurityScreen ? 1 : -1}]}>
<View style={{flex: 1, backgroundColor: globalStyles.colors.customerGreen}}>
<View style={styles.upperView}>
<Text style={styles.upperViewText}>MyApp</Text>
</View>
</View>
</View>
<Wrapped {...this.props}/>
</>
);
}
};
};
export const withSecurityScreen =
Platform.OS === 'ios' ? withSecurityScreenIOS : withSecurityScreenAndroid;
There is not problems in the code, it works very well in iOS, but on Android sometimes it works and sometimes it doesn't. I suspect the cause is that Android OS, when the square button is pressed, it takes a screenshot of the current view and uses it as a background image of the App. Sometimes, however, it happens that the change of layer (elevation) is faster than the Android screenshot and therefore I can see the green screen in the background, as Android is using it as an image.
Is there a way to synchronize these operations? That is: can I make sure that when I press the square button, Android waits for the view to change and then let it go in the background?
Note: I have tested the functioning of elevation when the app is in the foreground, simply inverting the values of elevation
when it is in the background or in the foreground, I assure that elevation
is working.