Using the Google Play Services library in an android plugin (Google Maps)

Hi everybody,

I’m currently trying to get a Google map from google’s “Google Maps Android API v2” into Unity. I have a java plugin that runs perfectly when I dedploy it to my android device but, it shows the required map and everything, when I use it in my unity project and deploy the unity project to my android device it crashes with a “java.lang.NoClassDefFoundError: com.google.android.gms.maps.MapFragment” error.

So I tried modifying my plugin to just display a Textview and not use the google api. When I ran that through my unity project unity displayed the view fine. Then I thought something could be wrong with using a non-jar library so I made a small test library and made my plugin use it, the same way the google services library gets used. This again worked perfectly.

So I’m at a loss, whenever I use the Google Play Services it crashes because it can’t find com.google.android.gms.maps.* .
Every example I find is of a billing solution that provideds a .jar, whereas the google play services apiI need for Google maps is in project-form (as described on เอกสารประกอบของ Google Maps Platform  |  Maps SDK for Android  |  Google for Developers).

Has anyone got this working before or is it just not possible ?

My understanding is that you don’t need the Google API as of version 2, but you will need fragments and the Google Play Services jar. The plugin you’re using may be for v1.

Still trying to master v2 so post the solution.

Got the maps v2 working when added BuildConfig.java and R.java from google-play-services lib project to my Assets\Plugins\Android\src\com\google\android\gms folder (as I compile unity plugin with Ant). It seems be the problem with finding the right resources. Haven’t found any “proper” way to do it so used this “hack”…

The plugin I’m using is very much v2, as I understand it the maps-v2-api is included in the google-play-services library on the device and to use it in your own project/plugin, you need to add a special non-jar library project from google .

You, sir, have done what I couldn’t do. It works now (at least the map shows but it doesn’t load the actual terrain but I imagine that’s something I can change now that the map is actually there).

For everyone trying to get Google Maps v2 in their unity application, here’s what I did :

!!! DISCLAIMER : This is still VERY rough and a lot of code/steps might not be needed, this is the result of almost a week’s worth of trying stuff out and fiddling about, and JFo fixing the last step for me. Also, I might have forgotten a step or 2 by now because I tried so.many.different.things. Still, I think this might help someone regardless.

  • Created an Android Application Project in eclipse
  • Followed the guide at https://developers.google.com/maps/documentation/android/start#installing_the_google_maps_android_v2_api to get the Android project working by itself (without anything Unity related)
  • Added Unity\Data\PlaybackEngines\androidplayer\bin\classes.jar to the project in eclipse, in \libs (where android-support-v4.jar should be too)
  • Added a new activity to the Android in eclipse, that extends UnityPlayerActivity
  • In that new activity, created a new method (which I’ll call from Unity’s C#) that starts the normal Android Activity
	public void showAndroidView() 
	{
		Log.d("MyMessages", "in showAndroidView");
		UnityPlayer.currentActivity.runOnUiThread(new Runnable() 
		{
			public void run()
			{
				Log.d("MyMessages", "Running showAndroidView");
				Intent intent = new Intent(UnityPlayer.currentActivity.getApplicationContext(), ViewActivity.class);
				UnityPlayer.currentActivity.startActivity(intent);
			}
		});
	}
  • In the normal Android activity, created the map (and entire UI) from code instead of from layout fragment
	@Override
	protected void onCreate(Bundle savedInstanceState) 
	{
		super.onCreate(savedInstanceState);
		Log.d("MyMessages", "in ViewActivity.onCreate");
		
		RelativeLayout mainLayout = new RelativeLayout(this);
		mainLayout.setId(123);
		setContentView(mainLayout);

		Log.d("MyMessages", "ViewActivity Before newInstance");
		MapFragment frag = MapFragment.newInstance(); //This is where it used to crash with it's damn classnotfound exception
		Log.d("MyMessages", "in ViewActivity After newInstance");
		FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
		fragmentTransaction.add(mainLayout.getId(), frag);
		fragmentTransaction.commit();

	}
  • Added the new activity to the AndroidManifest.xml file (as sub-element of )
        <activity android:name="my.android.game.ViewActivityUnity" android:screenOrientation="portrait" >
        </activity>
  • Right clicked in eclipse package explorer → export → jar file → in the top left selected my own project and the google-play-services_lib project
  • In Unity, created a folder in ‘Assets’ named ‘Plugins’
  • In the ‘Plugins’ folder, created a new one named ‘Android’
  • Dragged the jar I just exported into that new folder in Unity
  • Dragged google-play-services.jar and google-play-services.jar-properties from your eclipse project \google-play-services_lib\libs into the ‘Android’ folder in Unity aswell
  • Created a new xml file in the same ‘Android’ folder in Unity, named it ‘AndroidManifest.xml’ and wrote this in it
<?xml version="1.0" encoding="utf-8"?>

<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.platoevolved.mobclixtutorial" android:versionName="1.0" android:versionCode="1">
  <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
  <application android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false">
   
    <uses-library android:name="com.google.android.maps"/>

    <activity android:name=".ViewActivityUnity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait">
      <meta-data android:name="com.google.android.maps.v2.API_KEY"
      android:value="YOUR OWN KEY HERE"/>
      <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
      </intent-filter>
    </activity>
    <activity android:name="com.unity3d.player.VideoPlayer" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" android:screenOrientation="portrait">
    </activity>
    
    
    <meta-data
      android:name="com.google.android.maps.v2.API_KEY"
      android:value="YOUR OWN KEY HERE"/>
    
    <activity android:name="my.android.game.ViewActivity"/>

  </application>
  <uses-feature android:glEsVersion="0x00020000" />
  <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
  <uses-permission android:name="android.permission.INTERNET" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
  <permission
    android:name="my.android.game.permission.MAPS_RECEIVE"
    android:protectionLevel="signature"/>
  <uses-permission android:name="my.android.game.permission.MAPS_RECEIVE"/>

</manifest>
  • Created the folder tree src\com\google\android\gms in the ‘Android’ folder in Unity
  • Dragged BuildConfig.java and R.java into the new gms folder in Unity, from my eclipse project \google-play-services_lib\gen\com\google\android\gms
  • Created a C# Script in Unity, in the ‘Assets\Scripts’ folder to call to the Android UnityPlayerActivity activity’s method to show the Google Map
using UnityEngine;
using System.Collections;

public class Gui : MonoBehaviour 
{

    public static AndroidJavaClass ViewJavaClass;



	// Use this for initialization
	void Start () 
    {
        if (Application.platform == RuntimePlatform.Android)
        {
            // Initialize Android View
            ViewJavaClass = new AndroidJavaClass("my.android.game.ViewActivityUnity");

            //AndroidJavaClass mapsclass = new AndroidJavaClass("com.google.android.gms.maps.MapFragment");
            //mapsclass.CallStatic("newInstance");
        }
	
	}
	
	// Update is called once per frame
	void Update () {
	
	}

    void OnGUI()
    {
        
        if (Application.platform == RuntimePlatform.Android)
        {
            if (GUI.Button(new Rect(10, 300, 150, 120), "Show android Screen"))
            {
                AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
                AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
                activity.Call("showAndroidView");
                
                
                //ViewJavaClass.CallStatic("showAndroidView");
            }
        }
        else
        {
            if (GUI.Button(new Rect(10, 300, 150, 120), "Show non-android Screen"))
            {

            }
        }
    }
}
  • In Unity, dragged this script onto the Main Camera
  • In Unity Edit->Project Settings->Player ->Other Settings set the ‘Bundle Identifier’ to the namespace of my eclipse project (my.android.game in my case)
  • Also in the Player->Other Settings I set the ‘Minimum API Level’ to 4.0.3 ‘Ice Cream Sandwich’
  • I connected my physical Android Device to the pc (and installed the USB drivers for it) because google maps v2 doesn’t work on emulators yet
  • In Unity File → Build Run → Android Project → Build Run

And I’ve uploaded the projects for reference, disclaimer again : I provide no support and it’s provided as-is and such

http://www.sendspace.com/file/grjghb

I hope at least someone gets a use out of this ^^

EDIT : Fixed the map not getting filled up with actual terrain, I added

<uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>

to the AndroidManifest.xml in Unity

I’m doing a similar thing, but not with maps. I only want game services integration.

I made a jar out of google-play-services_lib and my custom android activity. In the jar export I chose not to include the manifests, .classpath, .project, either gen folder, and my res folder, because that caused name clashes.

I put gen/com/google/android/gms/* in src/com/google/android/gms in plugins/android, and made a build.

On start, it listed a load of problems with unresolved references from gms, such as:

06-06 18:26:17.960: W/dalvikvm(3084): VFY: unable to resolve static field 110 (common_google_play_services_enable_title) in Lcom/google/android/gms/R$string;

I can fix this by instead of exporting my jar with the google-play-services_lib included, I create an eclipse project from unity and link the resultant project to google-play-serivces_lib. I do it like this: right-click game eclipse project->android->library->add->google-play_services_lib. Now I get no resource errors, but I get a very awkward build process.

I’d really love to convince gms to pick up the google-play-services_lib resources without having to link through eclipse, so any help is appreciated.

I’m working towards an Android Google Maps v2 plugin now that Unity 4.2 supports library projects, it makes things easier, no hacks required it seems. I’m having a couple weird problems though which I’m sure because I’m new to Unity/Android programming.

public class GoogleMapsv2FragmentManager extends FragmentActivity {

	public GoogleMap mMap;
	private UnityPlayer m_UnityPlayer;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		
		int _mapFragment = getResources().getIdentifier("map", "id", getPackageName());
		mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(_mapFragment)).getMap();
		mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
		mMap.setMyLocationEnabled(true);
		 
		
		m_UnityPlayer = new UnityPlayer(this);
		 int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
		 m_UnityPlayer.init(glesMode, false);
		 int unityframe = getResources().getIdentifier("unity", "id", getPackageName());
		 FrameLayout layout = (FrameLayout) findViewById(unityframe);
		 LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
		 View mView = m_UnityPlayer.getView();
		 layout.addView(mView, 0, lp);
	}
	 @Override
	  public void onResume() {
	    super.onResume();
	    
	  }   
	  
	  @Override
	  public void onPause() {
	    super.onPause();
	   
	  } 
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:map="http://schemas.android.com/apk/res-auto"
    android:baselineAligned="false"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

      <fragment
       
        android:id="@+id/map"
        android:name="com.google.android.gms.maps.SupportMapFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_marginBottom="200dp"
         />

    
    <FrameLayout
        android:id="@+id/unity"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</LinearLayout>

So the problem is this: if I use LinearLayout the map is perfect, but the Unity window doesn’t show up at all. But if I use RelativeLayout, everything LOOKS okay at first, but the map runs off the sides of the screen making the zoom and centre buttons disappear. Worse, it’s no longer responding to touch events. I was trying to get this to work exactly like Globekit meaning that the map is just an overlay I can toggle on and off, but with the relative layout, I can’t seem to get both Unity and the map plugin to respond to touch events.

  1. How do I keep the functionality of both while making the map overlap the Unity view??? :face_with_spiral_eyes:
    EDIT:
    The order in which the fragment is added seems to make a difference but if I put the map fragment last, the buttons appear but the map doesn’t. Adding the following code to onResume
	int _mapFragment = getResources().getIdentifier("map", "id", getPackageName());
		mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(_mapFragment)).getMap();
		mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
		mMap.setMyLocationEnabled(true);

makes it work perfectly when the device wakes back up, which is just too weird. Because I can’t make it work manually no matter what I do. I think I’m just adding the Unity view incorrectly or failing to set something in the correct order… that’s all I can think of.

Also does anyone know which example would be appropriate to follow for getting the map to respond to OnGUI events? I’ve tried a couple including:

But the “instance” way of doing it doesn’t seem compatible with using:

@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
  1. What’s best, simplest way to call a function from Unity OnGUI to java when using @override onCreate?

  2. When the device goes to sleep the map frame disappears… Am I missing code from OnResume? It seems like OnCreate isn’t getting called again on awake. How do I ensure the map appears when waking or resuming the app?

I plan on releasing the Plugin whenever I can get it working properly… Sorry if the code is strange, I only started programming for Android about a week ago.

I just released this https://www.assetstore.unity3d.com/#/content/13153
A video demo here: http://ignaciopecino.com/mapnav/mapnav.html
It is a different approach though. I hope it helps!

any luck cap10subtext ??

For anyone still trying to get it work, try to add -keep class com.google.** { *; } to your proguard file.