Log in or register to post comments

Help With Android Google Maps V2

August 12, 2013 - 2:14pm #1

Hi All, I could really use some help.  I'm struggling through tutorials to make a new Android Google Maps v2 plugin that's compatible with Vuforia (I am already Using GlobeKit for iOS and I need it to be similar in function).  I've only been working in Android for a few weeks and have had a lot of success but this part has me stumped.  Unity 4.2 Vuforia 2.6.7, Target 4.1.2.  I'm relatively certain my manifest is setup correctly.

I have tried the following.  In mymap.xml

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
          android:id="@+id/map"
          android:layout_width="match_parent"
          android:layout_height="match_parent"
          class="com.google.android.gms.maps.SupportMapFragment"/>
package ca.futurestories.landslideMapTest;

import java.util.Timer;
import java.util.TimerTask;

import android.app.FragmentManager;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
//import android.support.v4.app.FragmentManager;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout.LayoutParams;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;


import com.qualcomm.QCARUnityPlayer.QCARPlayerActivity;
import com.qualcomm.QCARUnityPlayer.QCARUnityPlayer;
import com.qualcomm.QCAR.*;


public class QCARJavaActivity extends QCARPlayerActivity {
	   
    private QCARUnityPlayer mQCARView = null;
    private Timer mViewFinderTimer = null;
    
    public SupportMapFragment mMapFragment;
    
    private GoogleMap googleMap;
    private int mapType = GoogleMap.MAP_TYPE_HYBRID;
    
      
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
     
    }
   
    @Override
    public void onResume() {
       super.onResume();
      
       if (mQCARView == null) {
              //search the QCAR view
              mViewFinderTimer = new Timer();
              mViewFinderTimer.scheduleAtFixedRate(new QCARViewFinderTask(), 1000, 1000);
       }
    }
   
    @Override
    public void onPause() {
       super.onPause();
      
       if (mViewFinderTimer != null) {
              mViewFinderTimer.cancel();
              mViewFinderTimer = null;
       }
    }
   
    class QCARViewFinderTask extends TimerTask {
      
      public void run() {
        QCARJavaActivity.this.runOnUiThread(new Runnable() {
           public void run() {
              if (!QCAR.isInitialized()) return; //wait for QCAR init
                          
              if (mQCARView != null) return;//already found, no need to search
                                 
              //else search
              View rootView = QCARJavaActivity.this.findViewById(android.R.id.content);
              QCARUnityPlayer qcarView = findQCARView(rootView);
                           
              //if QCAR view has been found, add some android view/widget on top
              if (qcarView != null) {
                  ViewGroup qcarParentView = (ViewGroup)(qcarView.getParent());
                 
                  View myView = getLayoutInflater().inflate(R.layout.mymap, null);
                  qcarParentView.addView(myView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
                           
                  mQCARView = qcarView;
                }
              }
         });
       }
   
       private QCARUnityPlayer findQCARView(View view) {
              if (view instanceof QCARUnityPlayer) {
                 return (QCARUnityPlayer)view;
              }
             
              if (view instanceof ViewGroup) {
                     ViewGroup vg = (ViewGroup)view;
                     for (int i = 0; i < vg.getChildCount(); ++i) {
                           QCARUnityPlayer foundView = findQCARView(vg.getChildAt(i));
                           if (foundView != null)
                                  return foundView;
                     }
              }
             
              return null;
        }
    }
}

But this just gives me the runtime crash: 

 

08-12 20:02:55.002: E/AndroidRuntime(655): FATAL EXCEPTION: main
08-12 20:02:55.002: E/AndroidRuntime(655): java.lang.Error: FATAL EXCEPTION [main]
08-12 20:02:55.002: E/AndroidRuntime(655): Unity version     : 4.2.0f4
08-12 20:02:55.002: E/AndroidRuntime(655): Device model      : asus Transformer Prime TF201
08-12 20:02:55.002: E/AndroidRuntime(655): Device fingerprint: asus/US_epad/TF201:4.1.1/JRO03C/US_epad-10.4.2.18-20121122:user/release-keys
08-12 20:02:55.002: E/AndroidRuntime(655): Caused by: android.view.InflateException: Binary XML file line #2: Error inflating class fragment
08-12 20:02:55.002: E/AndroidRuntime(655): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:704)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.view.LayoutInflater.inflate(LayoutInflater.java:466)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.view.LayoutInflater.inflate(LayoutInflater.java:396)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.view.LayoutInflater.inflate(LayoutInflater.java:352)
08-12 20:02:55.002: E/AndroidRuntime(655): at ca.futurestories.landslideMapTest.QCARJavaActivity$QCARViewFinderTask$1.run(QCARJavaActivity.java:125)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.os.Handler.handleCallback(Handler.java:615)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.os.Handler.dispatchMessage(Handler.java:92)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.os.Looper.loop(Looper.java:137)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.app.ActivityThread.main(ActivityThread.java:4745)
08-12 20:02:55.002: E/AndroidRuntime(655): at java.lang.reflect.Method.invokeNative(Native Method)
08-12 20:02:55.002: E/AndroidRuntime(655): at java.lang.reflect.Method.invoke(Method.java:511)
08-12 20:02:55.002: E/AndroidRuntime(655): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:786)
08-12 20:02:55.002: E/AndroidRuntime(655): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
08-12 20:02:55.002: E/AndroidRuntime(655): at dalvik.system.NativeStart.main(Native Method)
08-12 20:02:55.002: E/AndroidRuntime(655): Caused by: java.lang.ClassCastException: com.google.android.gms.maps.SupportMapFragment cannot be cast to android.app.Fragment
08-12 20:02:55.002: E/AndroidRuntime(655): at android.app.Fragment.instantiate(Fragment.java:577)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.app.Fragment.instantiate(Fragment.java:552)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.app.Activity.onCreateView(Activity.java:4656)
08-12 20:02:55.002: E/AndroidRuntime(655): at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:680)
08-12 20:02:55.002: E/AndroidRuntime(655): ... 13 more
 
After trying this I discovered that this is the wrong way to go about this.  The "correct way to go about it seems to be to use FragmentActivity, which I can't seem to wrap my head around because I need to be extending QCARPlayerActivity.
 
So I thought I was on the right track with this:
 
  //if QCAR view has been found, add some android view/widget on top
              if (qcarView != null) {
                  ViewGroup qcarParentView = (ViewGroup)(qcarView.getParent());
                           
                FragmentManager fragmentManager = getFragmentManager();
                  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                  MapFragment fragment = new MapFragment();
                  fragmentTransaction.add(<?????????>, fragment);
                  fragmentTransaction.commit();
                 
  mQCARView = qcarView; 

                } 

But I can't seem to figure out what goes <?????>  here.  If i was creating the activity view programmatically I would use R.id.MyMapView, or something of the sort, I'd imagine.  But Since I'm getting the ViewGroup of QCAR's parent, I can't figure out what the Fragment Container is supposed to be.

Can someone please help?  Either conceptually to tell me what design model to follow, or specifically if you know how this should be accomplished.

I should also mention that all other attempts to create the SupportMapFragment using the XML files work, but aren't compatible with Vuforia.  So I'm 99% sure I have my eclipse environment setup correctly, as long as I'm not accessing the fragment then the Vuforia code builds and runs.  So really it's just about overlaying a MapFragment in a way that's compatible with Vuforia.

Thanks in advance.

Help With Android Google Maps V2

August 14, 2013 - 9:03am #13

Okay I have a work around but it's not pretty.  This looks like the right way to do it but it destroys the map each time...  so there's no way to just control the visibility with a map loaded with markers and polygons.

 if (fragment == null){
        	fragment = new MapFragment();
        	
        	 GoogleMapOptions options = new GoogleMapOptions();
        	 options.zOrderOnTop(true).mapType(GoogleMap.MAP_TYPE_HYBRID);

     		// Define new bundle with options
     		Bundle arguments = new Bundle();
     		arguments.putParcelable(MAP_OPTIONS, options);
      
     		// Set arguments to this instance
     		fragment.setArguments(arguments);
     		googleMap = fragment.getMap();

        }
...

    	  _savedCameraPosition = fragment.getMap().getCameraPosition();
    	  FragmentManager fragmentManager = getFragmentManager();
    	  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
          fragmentTransaction.remove(fragmentManager.findFragmentByTag("MAP"));
          fragmentTransaction.commit();
          fragmentManager.executePendingTransactions();

 

I have to remove the mapFragment every time which wipes all the markers... so I have to re-add them and re-load the map every time.  But it seems to work well enough for now and I can even control if the map goes on top of the camera feed.

Thanks for your help!  It's always appreciated!

Help With Android Google Maps V2

August 13, 2013 - 2:13pm #12

The child GLSurfaceView seems to deactivate the Unity Layer as well.

This is rough.  I'm seriously getting stressed here...

Is the Background Texture Access sample performed on the java side of things?  I'm getting a sinking feeling that this might not acutally be possible the way I'm trying to do it.  But I'm not sure how else to use a unity interface to switch between the Vuforia camera and Google Maps v2.

Help With Android Google Maps V2

August 13, 2013 - 12:06pm #11

The QCARView is an instance of QCARUnityPlayer, so if you remove that view, you will loose the Unity view (and the buttons with it);

if you search among the children, you could check whether you find an instance of GLSurfaceView.

 

Help With Android Google Maps V2

August 13, 2013 - 11:57am #10

Okay, just because this could be up and running in one line of code (rather than redoing it all from scratch), I've tried the following:

qcarParentView.removeView(qcarView);

But the Unity Buttons also disappear.  Is the camera view a specific child of the qcarView I might be able to add and remove?

Help With Android Google Maps V2

August 13, 2013 - 11:31am #9

I believe the Unity GUI buttons are rendered on top, not on the same GL surface view;

perhaps you may want to try the Background Texture Access sample, which uses a different method to render the camera view.

 

Help With Android Google Maps V2

August 13, 2013 - 11:17am #8

Yes, but it doesn't work as expected.  The map UI controls appear and are clickable (meaning the view is likely already in the front) but the conflict seems to be with the GL rendering on the surface view.  Is there a way to access the Vuforia surfaceview directly to toggle the camera feed on or off without losing the Unity GUI buttons?  Something along those lines would probably be sufficient for what I'm trying to do.

Maps doesn't allow for ZOrder to be set.

I'm resigned to not using the Vuforia and GoogleMaps SurfaceViews at the same time but are the Unity GUI buttons also being rendered on that surfaceview?

Help With Android Google Maps V2

August 13, 2013 - 11:01am #7

Have you tried using the method bringToFront() on the map view ?

Another possiblity is to try using setZOrderOnTop(true) on one of the SurfaceViews;

Note that mixing SurfaceViews in Android is known to be problematic in general.

 

Help With Android Google Maps V2

August 13, 2013 - 10:00am #6

Within spitting distance of my goal but the glitch with the map and camera not showing up exactly how I need them to has lead me to discover this:

http://stackoverflow.com/questions/15370624/mapfragment-and-camera-seem-to-interfere-with-each-other-in-the-same-activity

Apparently it's a conflict between 2 surfaceviews.

Now I have to learn how to incorporate two activities in the same app.  

Two weeks of work and nothing to show for it...  I think my head is going to explode.  

Help With Android Google Maps V2

August 12, 2013 - 3:45pm #5

What happens if you deliberately background and then foreground the app? - same behavior?

Yes, and infact I can see the map flash for a brief second before the app enters the background.  Then when it's in the foreground the map appears.

Maybe I'm going about this incorrectly... I do need to be sure that the map is Overlapping the camera feed/Unity in the background otherwise I'm not able to set the margins correctly/dynamically.  I'm not currently working with a layout but should I be using a layout as an intermediary?  Should a Layout be added to the QCAR parent view and then add the map over that to ensure there can still be some of buttons from Unity appearing at the bottom edge of the app?  Sorry for all the questions.  I'm so maddeningly close to cracking this...

Help With Android Google Maps V2

August 12, 2013 - 3:32pm #4

What happens if you deliberately background and then foreground the app? - same behavior?

Let's see what Alessandro has to say when he gets back online. But it's good to see that you've gotten the map to render.

Help With Android Google Maps V2

August 12, 2013 - 2:27pm #3

Yes, I've followed them to a tee, and I just figured out what i was missing.

  //if QCAR view has been found, add some android view/widget on top
              if (qcarView != null) {
                  ViewGroup qcarParentView = (ViewGroup)(qcarView.getParent());
                  FragmentManager fragmentManager = getFragmentManager();
                  FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
                  MapFragment fragment = new MapFragment();
                  fragmentTransaction.add(qcarParentView.getId(), fragment);
                  fragmentTransaction.commit();
                  
                  //  View myView = getLayoutInflater().inflate(R.layout.mymap, null);
                 // qcarParentView.addView(myView, new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
                           
                  mQCARView = qcarView;
                }

Because getLayoutInflater().Inflate seems to explode when handling a map fragment.

However, a new issue has presented itself.  When the app first launches, all I see is the vuforia app and the +/- scale buttons for the map, but when the app goes to sleep and wakes up, the app appears correctly (or at least at this point it appears Above the Vuforia camera feed).

Any thoughts on what could be causing that?

Log in or register to post comments