ti.modules.titanium.geolocation.TiLocation.java Source code

Java tutorial

Introduction

Here is the source code for ti.modules.titanium.geolocation.TiLocation.java

Source

/**
 * Appcelerator Titanium Mobile
 * Copyright (c) 2009-2012 by Appcelerator, Inc. All Rights Reserved.
 * Licensed under the terms of the Apache Public License
 * Please see the LICENSE included with this distribution for details.
 */
package ti.modules.titanium.geolocation;

import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;

import org.apache.http.client.HttpClient;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.BasicResponseHandler;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.appcelerator.kroll.KrollDict;
import org.appcelerator.kroll.common.Log;
import org.appcelerator.kroll.common.TiMessenger;
import org.appcelerator.titanium.TiApplication;
import org.appcelerator.titanium.TiC;
import org.appcelerator.titanium.analytics.TiAnalyticsEvent;
import org.appcelerator.titanium.analytics.TiAnalyticsEventFactory;
import org.appcelerator.titanium.util.TiPlatformHelper;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.location.Location;
import android.location.LocationManager;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;

public class TiLocation implements Handler.Callback {
    public static final int ERR_POSITION_UNAVAILABLE = 6;
    public static final int MSG_FIRST_ID = 100;
    public static final int MSG_LOOKUP = MSG_FIRST_ID + 1;
    public static final int MSG_LAST_ID = MSG_FIRST_ID + 2;

    public LocationManager locationManager;

    private static final String TAG = "TiLocation";
    private static final String BASE_GEO_URL = "http://api.appcelerator.net/p/v1/geo?";

    private String mobileId;
    private String appGuid;
    private String sessionId;
    private String countryCode;
    private long lastAnalyticsTimestamp = 0;
    private List<String> knownProviders;
    private Handler runtimeHandler;

    public interface GeocodeResponseHandler {
        public abstract void handleGeocodeResponse(KrollDict geocodeResponse);
    }

    public TiLocation() {
        locationManager = (LocationManager) TiApplication.getInstance().getSystemService(Context.LOCATION_SERVICE);
        knownProviders = locationManager.getAllProviders();
        mobileId = TiPlatformHelper.getMobileId();
        appGuid = TiApplication.getInstance().getAppInfo().getGUID();
        sessionId = TiPlatformHelper.getSessionId();
        countryCode = Locale.getDefault().getCountry();
        runtimeHandler = new Handler(TiMessenger.getRuntimeMessenger().getLooper(), this);
    }

    public boolean handleMessage(Message msg) {
        if (msg.what == MSG_LOOKUP) {
            String urlValue = msg.getData().getString(TiC.PROPERTY_URL);
            String directionValue = msg.getData().getString(TiC.PROPERTY_DIRECTION);

            AsyncTask<Object, Void, Integer> task = getLookUpTask();
            task.execute(urlValue, directionValue, msg.obj);

            return true;
        }

        return false;
    }

    public boolean isProvider(String name) {
        return knownProviders.contains(name);
    }

    public boolean getLocationServicesEnabled() {
        List<String> providerNames = locationManager.getProviders(true);

        if (Log.isDebugModeEnabled()) {
            Log.i(TAG, "Enabled location provider count: " + providerNames.size());

            for (String providerName : providerNames) {
                Log.i(TAG, providerName + " service available");
            }
        }

        // don't count the passive provider
        for (String name : providerNames) {
            if (name.equals(LocationManager.NETWORK_PROVIDER) || name.equals(LocationManager.GPS_PROVIDER)) {
                return true;
            }
        }

        return false;
    }

    public Location getLastKnownLocation() {
        Location latestKnownLocation = null;

        for (String provider : knownProviders) {
            Location lastKnownLocation = null;
            try {
                lastKnownLocation = locationManager.getLastKnownLocation(provider);

            } catch (IllegalArgumentException e) {
                Log.e(TAG, "Unable to get last know location for [" + provider + "], provider is null");

            } catch (SecurityException e) {
                Log.e(TAG, "Unable to get last know location for [" + provider + "], permission denied");
            }

            if (lastKnownLocation == null) {
                continue;
            }

            if ((latestKnownLocation == null) || (lastKnownLocation.getTime() > latestKnownLocation.getTime())) {
                latestKnownLocation = lastKnownLocation;
            }
        }

        return latestKnownLocation;
    }

    public void doAnalytics(Location location) {
        long locationTime = location.getTime();
        if (locationTime - lastAnalyticsTimestamp > TiAnalyticsEventFactory.MAX_GEO_ANALYTICS_FREQUENCY) {
            TiAnalyticsEvent event = TiAnalyticsEventFactory.createAppGeoEvent(location);
            if (event != null) {
                TiApplication.getInstance().postAnalyticsEvent(event);
                lastAnalyticsTimestamp = locationTime;
            }
        }
    }

    public void forwardGeocode(String address, GeocodeResponseHandler responseHandler) {
        if (address != null) {
            String geocoderUrl = buildGeocoderURL(TiC.PROPERTY_FORWARD, mobileId, appGuid, sessionId, address,
                    countryCode);
            if (geocoderUrl != null) {
                Message message = runtimeHandler.obtainMessage(MSG_LOOKUP);
                message.getData().putString(TiC.PROPERTY_DIRECTION, TiC.PROPERTY_FORWARD);
                message.getData().putString(TiC.PROPERTY_URL, geocoderUrl);

                message.obj = responseHandler;
                message.sendToTarget();
            }

        } else {
            Log.e(TAG, "Unable to forward geocode, address is null");
        }
    }

    public void reverseGeocode(double latitude, double longitude, GeocodeResponseHandler responseHandler) {
        String geocoderUrl = buildGeocoderURL(TiC.PROPERTY_REVERSE, mobileId, appGuid, sessionId,
                latitude + "," + longitude, countryCode);
        if (geocoderUrl != null) {
            Message message = runtimeHandler.obtainMessage(MSG_LOOKUP);
            message.getData().putString(TiC.PROPERTY_DIRECTION, TiC.PROPERTY_REVERSE);
            message.getData().putString(TiC.PROPERTY_URL, geocoderUrl);

            message.obj = responseHandler;
            message.sendToTarget();

        } else {
            Log.e(TAG, "Unable to reverse geocode, geocoder url is null");
        }
    }

    private String buildGeocoderURL(String direction, String mid, String aguid, String sid, String query,
            String countryCode) {
        String url = null;

        try {
            StringBuilder sb = new StringBuilder();
            sb.append(BASE_GEO_URL).append("d=r").append("&mid=").append(mid).append("&aguid=").append(aguid)
                    .append("&sid=").append(sid).append("&q=").append(URLEncoder.encode(query, "utf-8"));

            url = sb.toString();

        } catch (UnsupportedEncodingException e) {
            Log.e(TAG, "Unable to encode query to utf-8: " + e.getMessage());
        }

        return url;
    }

    private AsyncTask<Object, Void, Integer> getLookUpTask() {
        AsyncTask<Object, Void, Integer> task = new AsyncTask<Object, Void, Integer>() {
            @Override
            protected Integer doInBackground(Object... args) {
                GeocodeResponseHandler geocodeResponseHandler = null;
                KrollDict event = null;
                try {
                    String url = (String) args[0];
                    String direction = (String) args[1];
                    geocodeResponseHandler = (GeocodeResponseHandler) args[2];

                    Log.d(TAG, "GEO URL [" + url + "]", Log.DEBUG_MODE);
                    HttpGet httpGet = new HttpGet(url);

                    HttpParams httpParams = new BasicHttpParams();
                    HttpConnectionParams.setConnectionTimeout(httpParams, 5000);

                    HttpClient client = new DefaultHttpClient(httpParams);
                    client.getParams().setBooleanParameter("http.protocol.expect-continue", false);
                    ResponseHandler<String> responseHandler = new BasicResponseHandler();
                    String response = client.execute(httpGet, responseHandler);

                    Log.i(TAG, "received Geo [" + response + "]", Log.DEBUG_MODE);

                    if (response != null) {
                        try {
                            JSONObject jsonObject = new JSONObject(response);
                            if (jsonObject.getBoolean(TiC.PROPERTY_SUCCESS)) {
                                if (direction.equals("forward")) {
                                    event = buildForwardGeocodeResponse(jsonObject);

                                } else {
                                    event = buildReverseGeocodeResponse(jsonObject);
                                }
                                event.putCodeAndMessage(TiC.ERROR_CODE_NO_ERROR, null);

                            } else {
                                event = new KrollDict();
                                String errorCode = "Unable to resolve message: Code ("
                                        + jsonObject.getString(TiC.ERROR_PROPERTY_ERRORCODE) + ")";
                                event.putCodeAndMessage(TiC.ERROR_CODE_UNKNOWN, errorCode);
                            }

                        } catch (JSONException e) {
                            Log.e(TAG, "Error converting geo response to JSONObject [" + e.getMessage() + "]", e,
                                    Log.DEBUG_MODE);
                        }
                    }

                } catch (Throwable t) {
                    Log.e(TAG, "Error retrieving geocode information [" + t.getMessage() + "]", t, Log.DEBUG_MODE);
                }

                if (geocodeResponseHandler != null) {
                    if (event == null) {
                        event = new KrollDict();
                        event.putCodeAndMessage(TiC.ERROR_CODE_UNKNOWN, "Error obtaining geolocation");
                    }
                    geocodeResponseHandler.handleGeocodeResponse(event);
                }

                return -1;
            }
        };

        return task;
    }

    private KrollDict buildForwardGeocodeResponse(JSONObject jsonResponse) throws JSONException {
        KrollDict address = new KrollDict();

        JSONArray places = jsonResponse.getJSONArray(TiC.PROPERTY_PLACES);
        if (places.length() > 0) {
            address = buildAddress(places.getJSONObject(0));
        }
        return address;
    }

    private KrollDict buildReverseGeocodeResponse(JSONObject jsonResponse) throws JSONException {
        JSONArray places = jsonResponse.getJSONArray(TiC.PROPERTY_PLACES);
        ArrayList<KrollDict> addresses = new ArrayList<KrollDict>();

        int count = places.length();
        for (int i = 0; i < count; i++) {
            addresses.add(buildAddress(places.getJSONObject(i)));
        }

        KrollDict response = new KrollDict();
        response.put(TiC.PROPERTY_SUCCESS, true);
        response.put(TiC.PROPERTY_PLACES, addresses.toArray());

        return response;
    }

    private KrollDict buildAddress(JSONObject place) {
        KrollDict address = new KrollDict();
        address.put(TiC.PROPERTY_STREET1, place.optString(TiC.PROPERTY_STREET, ""));
        address.put(TiC.PROPERTY_STREET, place.optString(TiC.PROPERTY_STREET, ""));
        address.put(TiC.PROPERTY_CITY, place.optString(TiC.PROPERTY_CITY, ""));
        address.put(TiC.PROPERTY_REGION1, ""); // AdminArea
        address.put(TiC.PROPERTY_REGION2, ""); // SubAdminArea
        address.put(TiC.PROPERTY_POSTAL_CODE, place.optString("zipcode", ""));
        address.put(TiC.PROPERTY_COUNTRY, place.optString(TiC.PROPERTY_COUNTRY, ""));
        address.put("countryCode", place.optString(TiC.PROPERTY_COUNTRY_CODE, "")); // TIMOB-4478, remove this later, was old android name
        address.put(TiC.PROPERTY_COUNTRY_CODE, place.optString(TiC.PROPERTY_COUNTRY_CODE, ""));
        address.put(TiC.PROPERTY_LONGITUDE, place.optString(TiC.PROPERTY_LONGITUDE, ""));
        address.put(TiC.PROPERTY_LATITUDE, place.optString(TiC.PROPERTY_LATITUDE, ""));
        address.put(TiC.PROPERTY_DISPLAY_ADDRESS, place.optString(TiC.PROPERTY_ADDRESS));
        address.put(TiC.PROPERTY_ADDRESS, place.optString(TiC.PROPERTY_ADDRESS));

        return address;
    }
}