Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

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

Discussion in 'Android' started by TimcAim, Apr 9, 2013.

  1. TimcAim

    TimcAim

    Joined:
    Apr 2, 2013
    Posts:
    6
    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 https://developers.google.com/maps/documentation/android/).

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

    FredT4

    Joined:
    Apr 10, 2013
    Posts:
    1
    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.
     
  3. JFo

    JFo

    Joined:
    Dec 9, 2007
    Posts:
    217
    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"...
     
  4. TimcAim

    TimcAim

    Joined:
    Apr 2, 2013
    Posts:
    6
    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
    HTML:
    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
    Code (csharp):
    1.  
    2.     public void showAndroidView()
    3.     {
    4.         Log.d("MyMessages", "in showAndroidView");
    5.         UnityPlayer.currentActivity.runOnUiThread(new Runnable()
    6.         {
    7.             public void run()
    8.             {
    9.                 Log.d("MyMessages", "Running showAndroidView");
    10.                 Intent intent = new Intent(UnityPlayer.currentActivity.getApplicationContext(), ViewActivity.class);
    11.                 UnityPlayer.currentActivity.startActivity(intent);
    12.             }
    13.         });
    14.     }
    15.  
    - In the normal Android activity, created the map (and entire UI) from code instead of from layout fragment
    Code (csharp):
    1.  
    2.     @Override
    3.     protected void onCreate(Bundle savedInstanceState)
    4.     {
    5.         super.onCreate(savedInstanceState);
    6.         Log.d("MyMessages", "in ViewActivity.onCreate");
    7.        
    8.         RelativeLayout mainLayout = new RelativeLayout(this);
    9.         mainLayout.setId(123);
    10.         setContentView(mainLayout);
    11.  
    12.         Log.d("MyMessages", "ViewActivity Before newInstance");
    13.         MapFragment frag = MapFragment.newInstance(); //This is where it used to crash with it's damn classnotfound exception
    14.         Log.d("MyMessages", "in ViewActivity After newInstance");
    15.         FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
    16.         fragmentTransaction.add(mainLayout.getId(), frag);
    17.         fragmentTransaction.commit();
    18.  
    19.     }
    20.  
    - Added the new activity to the AndroidManifest.xml file (as sub-element of <application>)
    Code (csharp):
    1.  
    2.         <activity android:name="my.android.game.ViewActivityUnity" android:screenOrientation="portrait" >
    3.         </activity>
    4.  
    - 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
    Code (csharp):
    1.  
    2. <?xml version="1.0" encoding="utf-8"?>
    3.  
    4. <manifest xmlns:android="http://schemas.android.com/apk/res/android" android:installLocation="preferExternal" package="com.platoevolved.mobclixtutorial" android:versionName="1.0" android:versionCode="1">
    5.   <supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
    6.   <application android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="false">
    7.    
    8.     <uses-library android:name="com.google.android.maps"/>
    9.  
    10.     <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">
    11.       <meta-data android:name="com.google.android.maps.v2.API_KEY"
    12.       android:value="YOUR OWN KEY HERE"/>
    13.       <intent-filter>
    14.         <action android:name="android.intent.action.MAIN" />
    15.         <category android:name="android.intent.category.LAUNCHER" />
    16.       </intent-filter>
    17.     </activity>
    18.     <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">
    19.     </activity>
    20.    
    21.    
    22.     <meta-data
    23.       android:name="com.google.android.maps.v2.API_KEY"
    24.       android:value="YOUR OWN KEY HERE"/>
    25.    
    26.     <activity android:name="my.android.game.ViewActivity"/>
    27.  
    28.   </application>
    29.   <uses-feature android:glEsVersion="0x00020000" />
    30.   <uses-sdk android:minSdkVersion="15" android:targetSdkVersion="15" />
    31.   <uses-permission android:name="android.permission.INTERNET" />
    32.   <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    33.   <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    34.   <permission
    35.     android:name="my.android.game.permission.MAPS_RECEIVE"
    36.     android:protectionLevel="signature"/>
    37.   <uses-permission android:name="my.android.game.permission.MAPS_RECEIVE"/>
    38.  
    39. </manifest>
    40.  
    - 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
    Code (csharp):
    1.  
    2. using UnityEngine;
    3. using System.Collections;
    4.  
    5. public class Gui : MonoBehaviour
    6. {
    7.  
    8.     public static AndroidJavaClass ViewJavaClass;
    9.  
    10.  
    11.  
    12.     // Use this for initialization
    13.     void Start ()
    14.     {
    15.         if (Application.platform == RuntimePlatform.Android)
    16.         {
    17.             // Initialize Android View
    18.             ViewJavaClass = new AndroidJavaClass("my.android.game.ViewActivityUnity");
    19.  
    20.             //AndroidJavaClass mapsclass = new AndroidJavaClass("com.google.android.gms.maps.MapFragment");
    21.             //mapsclass.CallStatic("newInstance");
    22.         }
    23.    
    24.     }
    25.    
    26.     // Update is called once per frame
    27.     void Update () {
    28.    
    29.     }
    30.  
    31.     void OnGUI()
    32.     {
    33.        
    34.         if (Application.platform == RuntimePlatform.Android)
    35.         {
    36.             if (GUI.Button(new Rect(10, 300, 150, 120), "Show android Screen"))
    37.             {
    38.                 AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
    39.                 AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
    40.                 activity.Call("showAndroidView");
    41.                
    42.                
    43.                 //ViewJavaClass.CallStatic("showAndroidView");
    44.             }
    45.         }
    46.         else
    47.         {
    48.             if (GUI.Button(new Rect(10, 300, 150, 120), "Show non-android Screen"))
    49.             {
    50.  
    51.             }
    52.         }
    53.     }
    54. }
    55.  
    - 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
    HTML:
    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
    Code (csharp):
    1.  
    2. <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES"/>
    3.  
    to the AndroidManifest.xml in Unity
     
    Last edited: Apr 11, 2013
  5. andrew-fray

    andrew-fray

    Joined:
    Jul 19, 2012
    Posts:
    155
    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.
     
  6. cap10subtext

    cap10subtext

    Joined:
    Jul 22, 2011
    Posts:
    27
    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.

    Code (csharp):
    1.  
    2. public class GoogleMapsv2FragmentManager extends FragmentActivity {
    3.  
    4.     public GoogleMap mMap;
    5.     private UnityPlayer m_UnityPlayer;
    6.  
    7.     @Override
    8.     public void onCreate(Bundle savedInstanceState) {
    9.         super.onCreate(savedInstanceState);
    10.         setContentView(R.layout.main);
    11.        
    12.         int _mapFragment = getResources().getIdentifier("map", "id", getPackageName());
    13.         mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(_mapFragment)).getMap();
    14.         mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
    15.         mMap.setMyLocationEnabled(true);
    16.          
    17.        
    18.         m_UnityPlayer = new UnityPlayer(this);
    19.          int glesMode = m_UnityPlayer.getSettings().getInt("gles_mode", 1);
    20.          m_UnityPlayer.init(glesMode, false);
    21.          int unityframe = getResources().getIdentifier("unity", "id", getPackageName());
    22.          FrameLayout layout = (FrameLayout) findViewById(unityframe);
    23.          LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
    24.          View mView = m_UnityPlayer.getView();
    25.          layout.addView(mView, 0, lp);
    26.     }
    27.      @Override
    28.       public void onResume() {
    29.         super.onResume();
    30.        
    31.       }  
    32.      
    33.       @Override
    34.       public void onPause() {
    35.         super.onPause();
    36.        
    37.       }
    38. }
    Code (csharp):
    1.  
    2. <?xml version="1.0" encoding="utf-8"?>
    3. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    4.     xmlns:map="http://schemas.android.com/apk/res-auto"
    5.     android:baselineAligned="false"
    6.     android:layout_width="match_parent"
    7.     android:layout_height="match_parent"
    8.     android:orientation="vertical" >
    9.  
    10.       <fragment
    11.        
    12.         android:id="@+id/map"
    13.         android:name="com.google.android.gms.maps.SupportMapFragment"
    14.         android:layout_width="match_parent"
    15.         android:layout_height="match_parent"
    16.         android:layout_marginBottom="200dp"
    17.          />
    18.  
    19.    
    20.     <FrameLayout
    21.         android:id="@+id/unity"
    22.         android:layout_width="match_parent"
    23.         android:layout_height="match_parent"
    24.         />
    25.  
    26. </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??? :confused:
    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
    Code (csharp):
    1.     int _mapFragment = getResources().getIdentifier("map", "id", getPackageName());
    2.         mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(_mapFragment)).getMap();
    3.         mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
    4.         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:
    http://www.youtube.com/watch?v=zxn2CuQszvw

    But the "instance" way of doing it doesn't seem compatible with using:

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

    3. 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.
     
    Last edited: Aug 11, 2013
  7. 8volution

    8volution

    Joined:
    Nov 14, 2012
    Posts:
    88
  8. shahroume

    shahroume

    Joined:
    Jul 16, 2014
    Posts:
    1
    any luck cap10subtext ??
     
  9. Hobby-Game-Developer

    Hobby-Game-Developer

    Joined:
    Aug 27, 2017
    Posts:
    14

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