Determine whether hardware buttons are drawn on screen in React Native
It appears to be easy using Java to determine whether an Android device has a physical button, so to access that in React Native I would make a Native Module wrapper to return the result of ViewConfiguration.get(context).hasPermanentMenuKey()
.
You can open the /android
directory of your React Native project in Android Studio, then within the /app/src/main/java/<com.companyname.appname>/
folder, at the same level where you see MainActivity
and MainApplication
files, create a new Android resource directory called detecthardware
(or any other appropriate name). Within that create DetectHardwareModule.java and include this:
package com.companyname.appname.detecthardware;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.ReactMethod;
import java.util.List;
public class DetectHardwareModule extends ReactContextBaseJavaModule {
public DetectHardwareModule(ReactApplicationContext reactContext) {
super(reactContext);
}
@Override
public String getName() {
return "DetectHardware";
}
@ReactMethod
public void hasHardwareButtons(final Callback callback) {
callback.invoke(ViewConfiguration.get(getReactApplicationContext()).hasPermanentMenuKey());
}
}
Then create DetectHardwarePackage.java and include this:
package com.companyname.appname.detecthardware;
import com.facebook.react.ReactPackage;
import com.facebook.react.bridge.JavaScriptModule;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.uimanager.ViewManager;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class DetectHardwarePackage implements ReactPackage {
@Override
public List<NativeModule> createNativeModules(ReactApplicationContext reactContext) {
List<NativeModule> modules = new ArrayList<>();
modules.add(new DetectHardwareModule(reactContext));
return modules;
}
@Override
public List<Class<? extends JavaScriptModule>> createJSModules() {
return Collections.emptyList();
}
@Override
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
return Collections.emptyList();
}
}
Inside your MainApplication.java you'll need to include a getPackages
method so the JavaScript code can access it:
@Override
protected List<ReactPackage> getPackages() {
return Arrays.<ReactPackage>asList(
new MainReactPackage(),
new DetectHardwarePackage()
);
}
Now within your React Native component, make sure you import NativeModules
from react-native
, as well as Platform
so that you only run the function when on Android. In componentDidMount
:
componentDidMount() {
if (Platform.OS === 'android') {
NativeModules.DetectHardware.hasHardwareButtons(function(result) {
this.setState({hasHardwareButtons: result})
}.bind(this));
}
}
And finally within your render
function, calculate the bottom
amount to be 0 if the platform is android and hasHardwareButtons is true, otherwise 45:
var bottom = (Platform.OS === 'android' && this.state.hasHardwareButtons) ? 0 : 45
I use RN Dimensions to do this:
import {Dimensions} from 'react-native';
let deviceHeight = Dimensions.get('screen').height;
let windowHeight = Dimensions.get('window').height;
let bottomNavBarHeight = deviceHeight - windowHeight;
if (bottomNavBarHeight > 0) {
// onscreen navbar
} else {
// not onscreen navbar
}