Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Determine whether hardware buttons are drawn on screen in React Native

I draw a button at bottom: 0 in my React Native app (RN 0.42). This works pretty well on iOS and the most Android devices. But on Android devices with no physical hardware buttons, the button bar is drawn on the screen right above my button.

So: Is there a way to detect whether the hardware buttons are physical or software driven to adjust my layout in this case? Or is this just a bug in React Native since this only happens in modal dialogs?

Nexus (the green button has the style: bottom: 45 and the hardware buttons are rendered on the screen): enter image description here

Galaxy S5: (this device has real hardware buttons, so the bottom: 45 is too much): enter image description here

like image 569
Thomas Kekeisen Avatar asked Jun 21 '17 12:06

Thomas Kekeisen


1 Answers

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
like image 117
Pat Needham Avatar answered Sep 25 '22 03:09

Pat Needham