- Home /
How can I detect if the back button is virtual or physical? Hardware or Software Device buttons
I want to display a UI exit or back button if the android device has virtual menu buttons instead of physical ones. Many phones have physical hardware buttons, whereas most tablets have virtual buttons. I have had complaints with my games cause people don't know to swipe up to get the menu so they can back out.
Good question because it's something I hadn't actually thought about before and I hate it when apps don't take advantage of my phone having hardware buttons.
A quick search would suggest the keycodes are Escape
and $$anonymous$$enu
but one site mentions Home
so try that too I guess.
I can make the hardware buttons work just fine. That isn't the issue. I want to display a button on the screen when there is not a hardware button, so the player does not have to swipe up first just to press the back button.
Ah sorry I must have skim read the question. I did a bit of searching around as I'd be interested to know how to do this too.
Found a few similar questions but no solution. I thought maybe it would be possible to use the keycode to check if that button actually exists. I mean laptops don't have dedicated numpads and different keyboards do have extra or missing keys sometimes so it seems logical. Couldn't find any way of doing that though.
The only solutions I can think of would be to check what the device is on first run and compare it to a list of devices which says which buttons exist for each device and which don't. Would be far from ideal of course and it would take ages to write the list given the number of devices out there. Another solution would just be to have an options menu so the player can manually choose to use hardware buttons rather than on screen, again not ideal as some people will try and turn it off even if they have no buttons. Another option could be to have the user calibrate the device on first run by instructing them to press the menu and back buttons on their device and if the input is true turn off on screen buttons for that button. At least that way the user wouldn't be able to turn off on screen buttons if they don't have the hardware ones. I'm sure there must be an automatic and more intuitive way to do this though.
Isn't that last idea (the calibration one) a bit circular? How would you confirm that the button the user had pressed was a physical button?
I think you might need to access the Android API for this one. Ive not needed to do that myself, so far but perhaps you can use this:
in conjunction with this:
http://docs.unity3d.com/ScriptReference/AndroidJavaObject.Call.html
Information on creating plugins for Android and accessing them with AndroidJavaObject can be read here:
http://docs.unity3d.com/$$anonymous$$anual/PluginsForAndroid.html
On another note, looking at your comment:
Immersive mode is only included on later versions of Android and there are a number of devices that permanently display software buttons, so for this it is not an issue. For the rest, it seems that disabling Immersive mode is what can be done to simply display the buttons. Perhaps it can be included as a setting in the Options, of whether or not the user wants the bar displayed or not.
That is what I was thinking. I think I had found a way to do it but it was with the Android API. I was having trouble accessing it in Unity though.
$$anonymous$$y Android API knowledge is quite limited and as such the syntax and structure baffles me a little.
I found a post on SO which seems to have a good method without requiring the ND$$anonymous$$. It uses the Android Java plugin interface so you can use the AndroidJavaObject class to interact with it.
http://stackoverflow.com/questions/9121781/calling-android-ndk-function-from-unity-script
Just installing Android Studio so I can learn this.
Answer by meat5000 · Apr 04, 2016 at 12:15 PM
Solution for Android Only:
Ok I succeeded I do believe. Took 2 days of melting my brain trying to work through all the problems i encountered along the way.
This class interacts with the Plugin and calls its methods.
using UnityEngine;
using System.Collections;
using System.IO;
public class AccessButtonsType : MonoBehaviour{
#if UNITY_ANDROID
private AndroidJavaObject testobj = null;
private AndroidJavaObject playerActivityContext = null;
void Start() {
if (testobj == null) {
// First, obtain the current activity context
using (var actClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) {
playerActivityContext = actClass.GetStatic<AndroidJavaObject>("currentActivity");
}
// Pass the context to a newly instantiated UnityGetButtonsType object
using (var pluginClass = new AndroidJavaClass("com.tmg.getbuttons.UnityGetButtonsType")) {
if (pluginClass != null) {
testobj = pluginClass.CallStatic<AndroidJavaObject>("instance");
testobj.Call("setContext", playerActivityContext);
}
}
}
}
public static string GetButtonsT() {
string emptyString = null;
if( Application.platform != RuntimePlatform.Android )
return emptyString;
var pluginClass = new AndroidJavaClass("com.tmg.getbuttons.UnityGetButtonsType") ;
AndroidJavaObject plugin = pluginClass.CallStatic<AndroidJavaObject>("instance");
return plugin.Call<string>("GetButtonsType");
}
#endif
}
I threw this in to test and control the .cs class.
using UnityEngine;
using System.Collections;
public class AccessScript : MonoBehaviour {
public string isIt;
void Start ()
{
isIt = "Empty";
}
void Update ()
{
isIt = AccessButtonsType.GetButtonsT();
if(isIt == null)
isIt = "Empty";
Debug.Log(isIt);
}
void OnGUI()
{
GUI.Label(new Rect(10, 10, 100, 20), isIt + " Buttons");
}
}
This is the .java plugin I compiled in Android studio and dropped into the Plugins/Android folder in .jar format. I didnt need to target JDK 1.6. I didn't need any XML manifest in the plugins folder.
package com.tmg.getbuttons;
import android.content.ContentValues;
import android.content.Intent;
import android.content.Context;
import android.os.Environment;
import android.view.ViewConfiguration;
public class UnityGetButtonsType {
Context context;
private static UnityGetButtonsType m_instance;
public static UnityGetButtonsType instance() {
if(m_instance == null)
m_instance = new UnityGetButtonsType();
return m_instance;
}
public UnityGetButtonsType(){
m_instance = this;
}
public void setContext(Context ctx) {
this.context = ctx;
}
public String GetButtonsType() {
ViewConfiguration vConfig = ViewConfiguration.get(this.context);
if (vConfig.hasPermanentMenuKey()) {
return "hard";
} else {
return "soft";
}
}
}
You may need to change package names etc to match your project.
It does throw an error in the editor, which is supposed to be remedied by the #if Android tags, but this doesnt work as its in Android mode in the editor too. You should add in 'if not editor'.
On Device you will see "Soft Buttons" or "Hard Buttons" in the top left.
$$anonymous$$odify the code to fit your needs.
Oh yes, here's the resources I drew the information from.
http://stackoverflow.com/questions/9121781/calling-android-ndk-function-from-unity-script
http://www.thegamecontriver.com/2015/04/android-plugin-unity-android-studio.html
http://www.vortech.net/2013/03/accessing-the-android-activity-context-in-unity3d/
Wow, thanks. I had been busy working on another project and you took this and solved it. I will use this. Thank you very much.
Quite a bit of work went into this answer. In return how about taking some time to mark it as accepted?
You compile the .java file into a .jar file yourself using the links provided for direction.
Great work! Thanks. You've got a thump up from me. But I have a problem with the hasPermanent$$anonymous$$enu$$anonymous$$ey method. On Samsung Galaxy S7 the method returns false (-> "soft"). But the Galaxy S7 has a permanent menu key. What's wrong?
No idea. I'll look into it if I get a chance. I've been out of the loop for a few months.
I suspect the keys are software configurable so perhaps they make them contextually soft to make life easier for themselves.