そんなにBlog updateするなら俺だってLocationManager - User Location情報取得

前回Postの予告通り、今回はLocationManagerを実装したいと思います。LocationManagerを使用し、User Location情報を取得しGoogle Map等と連携する事が出来ます。下記のScreen shotのような情報を取得出来ます


LocationManagerとは

LocationManagerを使用するとSystemが提供するLocation serviceにaccessすることが出来ます。Location serviceにより定期的にLocation情報を取得したり、ある特定の場所に近づくと通知を受け取ったりすることが出来ます(原文、ここ)

実装の要点は下記です

  1. AndroidManifest.xmlandroid.permission.ACCESS_FINE_LOCATIONを追加
  2. LocationManagerを取得
  3. Intentを作成
  4. 上記IntentからPendingIntentを作成
  5. LocationManagerにPendingIntentを登録
  6. 該当のIntentに反応するようにReceiverにIntentFilterを登録
  7. BroadcastReceiverを拡張したclassでLocationManagerから発行されたIntentを受信した際の処理を記載
  8. LocationManagerに登録していたIntentをキャンセル(キャンセルしないとBatteryの消耗量がとてつもないことになります)

気づいた点をまとめます

  • Geocoder#getFromLocation()はNetworkにaccessするので本来ならUI threadとは別threadで処理をした方がよい

んで実装です

AndroidManifest.xml

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

    <application android:icon="@drawable/icon" android:label="@string/app_name">
        <activity android:name=".LocationManagerActivity"
                  android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
	<receiver android:name="com.android.practice.LocationUpdateReceiver"/>
    </application>
    <!-- AndroidManifest.xmlにandroid.permission.ACCESS_FINE_LOCATIONを追加 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
</manifest>

Activity, BroadcastReceiver

package com.android.practice;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;

import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.location.Address;
import android.location.Geocoder;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class LocationManagerActivity extends Activity {
    
	private static final boolean DEBUG = true;
	private static final String TAG = "LocationManagerActivity";
	
	private static final String ACTION_LOCATION_UPDATE = "com.android.practice.ACTION_LOCATION_UPDATE";
	
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        setContentView(R.layout.main);
        setOnClickListeners();
        setIntentFilterToReceiver();
    }
    
    private void setOnClickListeners(){
    	
    	final Button requestLocationBtn = (Button)findViewById(R.id.request_location);
    	final Button removeUpdateBtn = (Button)findViewById(R.id.remove_update);
    	
   		requestLocationBtn.setOnClickListener(new View.OnClickListener() {
   			@Override
   			public void onClick(View v) {
   				requestLocationUpdates();
   			}
   		});
    	
   		removeUpdateBtn.setOnClickListener(new View.OnClickListener() {
   			@Override
   			public void onClick(View v) {
   				removeUpdates();
   			}
   		});
    }
    
    private void requestLocationUpdates(){
		final Context context = LocationManagerActivity.this;
		
    	final PendingIntent requestLocation = getRequestLocationIntent(context);
    	//LocationManagerを取得する
		LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
		
		for(String providerName: lm.getAllProviders()){
			if(lm.isProviderEnabled(providerName)){
				if(DEBUG)Log.d(TAG, "Provider: " + providerName);
		    	//LocationManagerにPendingIntentを登録
				lm.requestLocationUpdates(providerName, 0, 0, requestLocation);
			}
		}
		
    	if(DEBUG){
    		Toast.makeText(context, "Request Location", Toast.LENGTH_SHORT).show();
    	}
    }
    
    private void removeUpdates(){
    	final Context context = LocationManagerActivity.this;
    	
    	final PendingIntent requestLocation = getRequestLocationIntent(context);
		LocationManager lm = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
    	//LocationManagerに登録していたIntentをキャンセル
		lm.removeUpdates(requestLocation);
		
		if(DEBUG){
    		Toast.makeText(context, "Remove update", Toast.LENGTH_SHORT).show();    	
    	}
    }
    
    private PendingIntent getRequestLocationIntent(Context context){
    	//Intentを作成
    	final Intent intent = new Intent();
    	intent.setAction(ACTION_LOCATION_UPDATE);
    	//PendingIntentを作成
    	return PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    }
    
    private void setIntentFilterToReceiver(){
    	//該当のIntentに反応するようにReceiverにIntentFilterを登録
    	final IntentFilter filter = new IntentFilter();
    	filter.addAction(ACTION_LOCATION_UPDATE);
    	registerReceiver(new LocationUpdateReceiver(), filter);
    }
    
    public class LocationUpdateReceiver extends BroadcastReceiver{
    	
    	//BroadcastReceiverを拡張したclassでLocationManagerから発行されたIntentを受信した際の処理を記載
		@Override
		public void onReceive(Context context, Intent intent) {
			if(DEBUG)Log.d(TAG, "onReceive");
			String action = intent.getAction();
			if(action != null && action.equals(ACTION_LOCATION_UPDATE)){
				final Location location = (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
				if(location == null){
					return;
				}
				updateView(location);
				if(DEBUG)Toast.makeText(context, "Location update", Toast.LENGTH_SHORT).show();
			}
		}
		
		private void updateView(Location location){
			final Activity activity = LocationManagerActivity.this;
			((TextView)activity.findViewById(R.id.accuracy)).setText("Accuracy: " + String.valueOf(location.getAccuracy()));
			((TextView)activity.findViewById(R.id.altitude)).setText("Altitude: " + String.valueOf(location.getAltitude()));
			((TextView)activity.findViewById(R.id.bearing)).setText("Bearing: " + String.valueOf(location.getBearing()));
			((TextView)activity.findViewById(R.id.latitude)).setText("latitude: " + String.valueOf(location.getLatitude()));
			((TextView)activity.findViewById(R.id.longitude)).setText("longitude: " + String.valueOf(location.getLongitude()));
			((TextView)activity.findViewById(R.id.provider)).setText("Provider: " + String.valueOf(location.getProvider()));
			((TextView)activity.findViewById(R.id.speed)).setText("Speed: " + String.valueOf(location.getSpeed()));
			((TextView)activity.findViewById(R.id.time)).setText("Time: " + String.valueOf(location.getTime()));
			((TextView)activity.findViewById(R.id.address)).setText("Address: " + getAddress((Context)activity, location.getLatitude(), location.getLongitude()));
		}
		
		private String getAddress(Context context, double latitude, double longitude){
			String addressString = null;
			Geocoder gCoder = new Geocoder(context, Locale.getDefault()); 
			List<Address> addresses = new ArrayList<Address>();
			
			try {
				//Geocoder#getFromLocation()はNetworkにaccessするので本来ならUI threadとは別threadで処理をした方がよい
				addresses = gCoder.getFromLocation(latitude, longitude, 1);
			} catch (IOException e) {
				e.printStackTrace();
			}
			
			for(Address address: addresses){
				address.setLatitude(latitude);
				address.setLongitude(longitude);
				final int maxIndex = address.getMaxAddressLineIndex();
				for(int i = 0; i < maxIndex; i++){
					final String element = address.getAddressLine(i);
					if(element != null){
						if(addressString != null){
							addressString += element;
						}else{
							addressString = element;
						}
					}
				}
			}
			
			return addressString;
		}
    }
}

と、まあ、こんな感じです。前回のAlarmManagerと使用方法は一緒ですね。
Google Map等と連携して手軽にUser Location情報を利用したapplicationを作成出来ますね。

HashTag #Java, #Android, #LocationManager, #BroadcastReceiver, #Geocoder, #Address