Jul 25

Android: Map tiles not loading – and possible fixes

I have encountered two reasons for the Google Maps tiles not loading. One is rather common, and the other one only seem to affect some phones:

  • Missing/wrong Google Maps API key
  • Permissions placed after application tag in the AndroidManifest.xml

Missing/wrong Google Maps API key

Usage of the Google Maps API key is properly documented many places, like in tutorials, the official Android developer site and the Google API project pages.

Wrong placement of permissions in the AndroidManifest.xml

The part about wrong placement of the permissions in the manifest file bit me when trying to get my map based app working on the Sony Ericsson Xperia X10 mini pro. The app was working like a charm in every configuration I tried, except on this little beauty. The configurations I tried included:

  • HTC Desire running 2.1
  • T-Mobile G1 running 1.6
  • Various emulators running 1.5, 1.6, 2.1 and 2.2 with various screen sizes. Also one that matched the x10 mini pro screen size.
  • Of course the X10 mini pro running 1.6

I had defined my permissions below the application definition, and after a while I moved the tags above the tag, and this fixed it.

So this is not OK on the X10 mini pro (AndroidManifest.xml):

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test.myapp" android:versionCode="1" android:versionName="1.0">
    <application android:label="@string/app_name" android:icon="@drawable/launcher_icon">
    	<uses-library android:name="com.google.android.maps" />
        <activity android:label="@string/app_name"
                  android:alwaysRetainTaskState="true" android:name=".views.Map">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

   <uses-permission android:name="android.permission.INTERNET"></uses>      
</manifest> 

While this works:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      package="com.test.myapp" android:versionCode="1" android:versionName="1.0">

    <uses-permission android:name="android.permission.INTERNET"></uses>

    <application android:label="@string/app_name" android:icon="@drawable/launcher_icon">
    	<uses-library android:name="com.google.android.maps" />
        <activity android:label="@string/app_name"
                  android:alwaysRetainTaskState="true" android:name=".views.Map">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest> 

If someone can tell me why defining permissions below the application tag works on most set-ups, but not on the X10 mini, please enlighten me.

Jul 15

Flattr: Mikrobetaling (mikrodonasjon) for nettinnhold

Flattr er et ferskt system (i beta fra mars 2010) for mikrodonasjoner, som tar sikte på å gjøre det mye enklere å donere småpenger til innholdsprodusenter. Kort fortalt fungerer det slik:

  1. Du setter inn en fast sum hver måned
  2. I løpet av måneden klikker du på flattr-knappene til de tingene du mener har gjort seg fortjent til en økonomisk påskjønnelse
  3. Ved månedens utløp blir din sum delt likt mellom alle tingene du har flattret i løpet av måneden

Foreløpig bærer Flattr preg av at det fortsatt befinner seg på betastadiet; brukermassen er ikke er all verden og utvalget av ting som kan flattres/blir flattret er deretter. Det bedrer seg dog fortløpende. Er dette måten å tjene penger på nettinhold i fremtiden?

Flattr er i åpen beta, men er du utålmodig (og tidlig ute med å lese denne posten), kan du forsøke en av disse invitasjonskodene:

288a1a706ac7a139b
0dd1624666273b5c3
66a27863d0a49c785

Jun 25

Handling longpress/longclick in MapActivity

You might be interested to know that I have written a new post on this same topic, with a solution that is cleaner and works better to boot. You can find it here.

While working on my MapActivity based Android app, I wanted to be able to show a context menu when the user longpressed any point on the map (not necessarily a marker in an overlay) and perform some action related to that point. I wrongly assumed that there would be some simple method to override for this, like onLongPress(). Therefore I set out to find another solution, and here it is. It’s not perfect, but it works really well in my app, and I hope others might find this useful as well.

Stuff that didn’t work

  • Simply overriding onCreateContextMenu() in the MapActivity. This would not yield a context menu upon longpress on the map.
  • setOnLongClickListener() in the MapView to catch a longpress/longclick, and then manually call MapView.showContextMenu() to display the menu. The listener never caught an event.

I also briefly looked at the mapview-overlay-manager library, which might work for you.  It will, among many other things, give you an onLongPress() event. I figured this to be overkill for solving my problem.

My solution

I ended up overriding MapActivity.dispatchTouchEvent(MotionEvent event), which processes all touch events to the activity. When overriding this method, we need to take care of the following elements to handle longpresses:

  1. If the finger has just touched the screen, we start checking how long it is held. We need to do this in a separate thread, or else the UI will lock up. If the checks in the following point do not kick in within a given threshold, we have a longpress and call the desired code, In my case this was to show the context menu.
  2. If some motion event that can not be part of a longpress has been performed, cancel the check in pt.2. These events are:
    • Finger has been removed from screen
    • Finger has moved more than a given threshold since the
      previous event

    Here is the complete MapActivity, handling all this:

    The code

    Although I admit not being a very experienced Java programmer, this is not my actual implementation, but a simplified example to show which elements needs to be in place for this to work. Horizontal scrollbar at the bottom.

    import android.os.Bundle;
    import android.os.Looper;
    import android.view.MotionEvent;
    
    import com.google.android.maps.MapActivity;
    
    public class Map extends MapActivity {
    	private boolean isPotentialLongPress;
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		setContentView(R.layout.main);
    	}
    
    	@Override
    	public boolean dispatchTouchEvent(MotionEvent event) {
    		handleLongPress(event);
    		return super.dispatchTouchEvent(event);
    	}
    
    	private void handleLongPress(MotionEvent event) {
    		if (event.getAction() == MotionEvent.ACTION_DOWN) {
    			// A new touch has been detected
    
    			new Thread(new Runnable() {
    				public void run() {
    					Looper.prepare();
    					if (isLongPressDetected()) {
    						// We have a longpress! Perform your action here
    					}
    				}
    			}).start();
    		} else if (event.getAction() == MotionEvent.ACTION_MOVE) {
    			/*
    			 * Only MotionEvent.ACTION_MOVE could potentially be regarded as
    			 * part of a longpress, as this event is trigged by the finger
    			 * moving slightly on the device screen. Any other events causes us
    			 * to cancel this events status as a potential longpress.
    			 */
    			if (event.getHistorySize() < 1)
    				return; // First call, no history
    
    			// Get difference in position since previous move event
    			float diffX = event.getX()
    					- event.getHistoricalX(event.getHistorySize() - 1);
    			float diffY = event.getY()
    					- event.getHistoricalY(event.getHistorySize() - 1);
    
    			/* If position has moved substatially, this is not a long press but
    			   probably a drag action */
    			if (Math.abs(diffX) > 0.5f || Math.abs(diffY) > 0.5f) {
    				isPotentialLongPress = false;
    			}
    		} else {
    			// This motion is something else, and thus not part of a longpress
    			isPotentialLongPress = false;
    		}
    	}
    
    	/**
    	 * Loops for an amount of time while checking if the state of the
    	 * isPotentialLongPress variable has changed. If it has, this is regarded as
    	 * if the longpress has been canceled. Else it is regarded as a longpress.
    	 */
    	public boolean isLongPressDetected() {
    		isPotentialLongPress = true;
    		try {
    			for (int i = 0; i < (50); i++) {
    				Thread.sleep(10);
    				if (!isPotentialLongPress) {
    					return false;
    				}
    			}
    			return true;
    		} catch (InterruptedException e) {
    			return false;
    		} finally {
    			isPotentialLongPress = false;
    		}
    	}
    
    	@Override
    	protected boolean isRouteDisplayed() {
    		return false;
    	}
    
    }
    

    If you have a better way of doing this, I’d love to hear from you.

May 16

Bysyklist Oslo – en bysykkel-app for Android

I frustrasjon over å ofte bruke mer tid på å finne parkering til bysykkelen enn jeg brukte på å faktisk komme meg fra A til B, utviklet jeg i fjor en bysykkel-app for Android-baserte telefoner. I år har jeg pusset litt på denne og lagt den ut på Android Market i håp om at den kan være til nytte for andre enn meg. Appen heter “Bysyklist Oslo“.

Fri programvare

Koden er frigitt under GPL-lisens, og befinner seg på Gitorious.

» Newer posts