MapsInitializer.initialize no longer throws GooglePlayServicesNotAvailableException with the new google play services lib ver 15

I just updated the google play services lib to ver 15 to find out that MapsInitializer.initialize method no longer throws GooglePlayServicesNotAvailableException.

In the description of this method it now says:

Initializes the Google Maps Android API so that its classes are ready for use. If you are using MapFragment or MapView and have already obtained a (non-null) GoogleMap by calling getMap() on either of these classes, then it is not necessary to call this.

So, I guess if you have already called getMap and you have gotten a map object back, there is no need to call this any more.

Friday, February 14th, 2014 General 1 Comment

How to detect a user pan/touch/drag on Android Map v2

If you are impatient, you can skip directly to the solution.

The new implementation of the Android Map (v2) is great in many ways and their support for map fragments is awesome, but they fall a bit short in a couple of minor but very necessary implementations. That makes the maps API a bit incomplete.

I am referring in particular to the ability to distinguish between a user dragging (panning) the map, verses a programmatic move of the map.

That looks like a very basic need that a lot of Android programmers would face when dealing with the new map implementation. I have a number of apps, where I would like to update the map after the user has panned the map to a new location and I want to do that ONLY after they had stopped moving the map. I don’t want to trigger multiple updates while they are in the process of panning.

As you can deduce so far, there is no straight up way to implement this very basic behavior… or at least I could not find one. So, if you have found one, please let me know… I am only human and I might have missed something!

There are a couple of listeners implemented that kind of give you something to work with at a first glance, but they are both incomplete.

We have onCameraChangeListener, which gets called every time the map moves, zooms in/out or the camera angle changes, but it is completely useless in helping us implement the above needed behavior. It does not tell you in any way if the map was moved due to a user interaction or due to some other event triggering the map’s “camera change”. Furthermore, if you implement this listener you will notice that it gets triggered multiple times during a map panning. So, this makes it completely inefficient, especially when you are fetching data over the network all the time. The implementation of this listener could have been a bit more complete, if the only added a boolean value to the GoogleMap.OnCameraChangeListener indicating if the action was done by a human or not. Just like they implemented the SeekBar.OnSeekBarChangeListener.

Furthermore, you might think that this situation can be remedied with the map’s setOnMapClickListener, but you would be wrong, as you cannot use it in any way to distinguish between a touch down and a touch up events.

That finally leads us down the path of implementing this basic map behavioral need ourselves! I would like to think that Google does this to us, because they know we are better programmers than those stinking iOS dudes, who are used to getting anything they need handed on a platter of APIs:-) But may be I am wrong again…

So, a basic search led me to a couple of stackoverflow posts that try to tackle this issue:

How to handle onTouch event for map in Google Map API v2?
Google maps android api v2 – detect touch on map

But that sill did not completely solve my problem due to a couple of reasons. One is that they use an ugly static boolean variable to communicate back to the map activity if the map was touched or not. Furthermore they use that boolean in the setOnCameraChangeListener to decide whether to update the map or not, which flat out does not work. And the reason it does not work is because in most of the cases the setOnCameraChangeListener listener is triggered before the boolean value was updated, resulting in a failure of the code after that to execute.

So I took all this and made a few small but significant changes, namely using a custom interface to trigger the map to update at the right time. The TouchableWrapper class declares a UpdateMapAfterUserInterection interface, which is implemented by the Map Activity. That gives us a very fine control over the map events and what we want to do with them. Now a complete disclaimer… This all works very well, but it is a hacky way of doing things. Extending the map fragment, then putting a frame layout on top of if to intercept user interaction and then passing it over to the map activity just feels (and is) hacky. But it is the best solution I could find or think of for now. Please share if you have a better solution!

Here is the complete implementation:

UpdateMapAfterUserInterection class


public  class TouchableWrapper extends FrameLayout {

	private long lastTouched = 0;
	private static final long SCROLL_TIME = 200L; // 200 Milliseconds, but you can adjust that to your liking
	private UpdateMapAfterUserInterection updateMapAfterUserInterection;

	public TouchableWrapper(Context context) {
		super(context);
		// Force the host activity to implement the UpdateMapAfterUserInterection Interface
		try {
			updateMapAfterUserInterection = (ActivityMapv2) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement UpdateMapAfterUserInterection");
        }
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		switch (ev.getAction()) {
		case MotionEvent.ACTION_DOWN:
			lastTouched = SystemClock.uptimeMillis();
			break;
		case MotionEvent.ACTION_UP:
			final long now = SystemClock.uptimeMillis();
			if (now - lastTouched > SCROLL_TIME) {
				// Update the map
				updateMapAfterUserInterection.onUpdateMapAfterUserInterection();
			}
			break;
		}
		return super.dispatchTouchEvent(ev);
	}

	// Map Activity must implement this interface
    public interface UpdateMapAfterUserInterection {
        public void onUpdateMapAfterUserInterection();
    }
}

MySupportMapFragment class


public class MySupportMapFragment extends SupportMapFragment{
	public View mOriginalContentView;
	public TouchableWrapper mTouchView;

	@Override
	public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
		mOriginalContentView = super.onCreateView(inflater, parent, savedInstanceState);
		mTouchView = new TouchableWrapper(getActivity());
		mTouchView.addView(mOriginalContentView);
		return mTouchView;
	}

	@Override
	public View getView() {
		return mOriginalContentView;
	}
}

The layout for the map activity


<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/mapFragment"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_below="@+id/buttonBar"
    class="com.myFactory.myApp.MySupportMapFragment"
    />

And finally the Map Activity


public class ActivityMapv2 extends FragmentActivity implements UpdateMapAfterUserInterection {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
  }

// Implement the interface method
public void onUpdateMapAfterUserInterection() {
		// TODO Update the map now
        }
 }
Tuesday, April 9th, 2013 Android, Eclipse, Java, Programming 15 Comments

Implementing Android GCM in a different package other than the main application package

There is something you need to be aware of if you are placing your GCM (Google Cloud Messaging, aka push notifications) service (the one extending from GCMBaseIntentService) in a package other than the main application package. The default implementation of the GCMBroadcastReceiver class, whcih is part of the GCM library assumes that your service is going to be in the main application package. If the service is in a different package though, then it will not be able to be started by the broadcast receiver and if you are debugging your app it will look like your app requests a Registration ID from the Google servers, but then everything dies off and you are not getting a response back.

What you need to do in this case is extend the GCMBroadcastReceiver class and override the getGCMIntentServiceClassName method to return the package name of the service and the service class name. So for example, if your service was called GCMIntentService in package com.mycompany.mainpackage.somepackage, you would return: “com.mycompany.mainpackage.somepackage.GCMIntentService”. Here is an example of the extanded GCMBroadcastReceiver class:


public class GCMMyBoadcastReceiver extends GCMBroadcastReceiver {
	protected String getGCMIntentServiceClassName(Context contest) {
		return "com.mycompany.mainpackage.somepackage.GCMIntentService";
	}
}

Also, do not forget to update your manifest file to make sure that the new extended broadcast receiver is used:


<receiver android:name="com.mycompany.mainpackage.somepackage.GCMMyBoadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
    <intent-filter>
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
	<action android:name="com.google.android.c2dm.intent.REGISTRATION" />
        <category android:name="com.mycompany.mainpackage" />
   </intent-filter>
</receiver>

This little detail wasted a coule of hours of my time trying to figure out what I did wrong. Unfortunatly this detial is not well documented (not even mentioned) in their GCM implementation tutorial.

Monday, November 26th, 2012 Android, Eclipse, Java, Programming 1 Comment

Quick and easy way to remove all single line (//) comments in Eclipse

I had to quickly remove single line comments from an Android project in Eclipse. I could not find any built in feature in Eclipse to do that. So I resorted to a simple regular expression:

(//[^\n]*)

This simple regex selects all the strings that begin with “//” and end with a new line. So all you have to do is use it in the find and replace option for a file or to apply to the whole project or multiple files do Search -> File. Also, make sure to select the “regular expression” check box.

This is something I did very quickly… so be careful since it might pick up some urls as well… like anything after the “//” part in “http://mysite.com/page.php”.

Wednesday, November 7th, 2012 Android, C++, Eclipse, Java, Programming 1 Comment

A small bug in the Android In-App Billing demo app causes “Item Not Found”

Important: This is not a bug in the API! It is only a bug in the demo Dungeons application that Google provides.

Several months ago (May, 2012) Google released the new version (v2) of the Android In-App Billing API that supports auto-renewing subscriptions. That was a huge step forward! Thank you Google!
I started implementing this in my apps soon after but then discovered one small annoying bug in their demo application (Dungeons). It causes the the incorrect response of “item not found”.
I published the solution a couple of months ago to stackoverflow and the official android developers forum.

The bug is in the onClick for the Purchase button in the Dungeons class of the sample application.

The supplied method has a bug in the if {} else if {} statement where it causes the mBillingService.requestPurchase to be called twice, when the selected item is not a subscription item (mManagedType != Managed.SUBSCRIPTION). So the same item will be requested twice, once with an item type of “inapp” (which is the valid request) and immediately after that with an item type of “subs” (which is incorrect and it shows “item not found”).

Here is the buggy code:

if (mManagedType != Managed.SUBSCRIPTION &&
                    !mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
                showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
            } else if (!mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_SUBSCRIPTION, mPayloadContents)) {
                // Note: mManagedType == Managed.SUBSCRIPTION
                showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
}

To fix this, add mManagedType == Managed.SUBSCRIPTION to the else if above.

Here is how the function should look:

public void onClick(View v) {
        if (v == mBuyButton) {
            if (Consts.DEBUG) {
                Log.d(TAG, "buying: " + mItemName + " sku: " + mSku);
            }

            if (mManagedType != Managed.SUBSCRIPTION &&
                    !mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_INAPP, mPayloadContents)) {
                showDialog(DIALOG_BILLING_NOT_SUPPORTED_ID);
            } else if (mManagedType == Managed.SUBSCRIPTION && !mBillingService.requestPurchase(mSku, Consts.ITEM_TYPE_SUBSCRIPTION, mPayloadContents)) {
                // Note: mManagedType == Managed.SUBSCRIPTION
                showDialog(DIALOG_SUBSCRIPTIONS_NOT_SUPPORTED_ID);
            }
        } else if (v == mEditPayloadButton) {
            showPayloadEditDialog();
        } else if (v == mEditSubscriptionsButton) {
            editSubscriptions();
        }
    }
Wednesday, August 22nd, 2012 Android, Eclipse, General, Java, Programming No Comments

Search

 

Archive

April 2014
M T W T F S S
« Feb    
 123456
78910111213
14151617181920
21222324252627
282930  

Other