uk.ac.brighton.ci360.bigarrow.PlacesAPISearch.java Source code

Java tutorial

Introduction

Here is the source code for uk.ac.brighton.ci360.bigarrow.PlacesAPISearch.java

Source

package uk.ac.brighton.ci360.bigarrow;

/**
 * This class encapsulates calls to the Places API.
 * 
 * Copyright (c) 2013 The BigArrow authors (see the file AUTHORS).
 * See the file LICENSE for copying permission.
 * 
 * @author jb259
 */

import java.io.IOException;
import java.net.Socket;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Properties;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpUriRequest;
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
import org.apache.http.conn.scheme.SchemeRegistry;
import org.apache.http.conn.ssl.SSLSocketFactory;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;

import uk.ac.brighton.ci360.bigarrow.PlaceSearchRequester.SearchEstab;
import uk.ac.brighton.ci360.bigarrow.PlaceSearchRequester.SearchType;
import uk.ac.brighton.ci360.bigarrow.places.Place;
import uk.ac.brighton.ci360.bigarrow.places.Place.Photo;
import uk.ac.brighton.ci360.bigarrow.places.PlaceDetails;
import uk.ac.brighton.ci360.bigarrow.places.PlacesList;
import android.annotation.SuppressLint;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.os.AsyncTask;
import android.util.Log;

import com.google.api.client.googleapis.GoogleHeaders;
import com.google.api.client.http.GenericUrl;
import com.google.api.client.http.HttpRequest;
import com.google.api.client.http.HttpRequestFactory;
import com.google.api.client.http.HttpRequestInitializer;
import com.google.api.client.http.HttpTransport;
import com.google.api.client.http.apache.ApacheHttpTransport;
import com.google.api.client.http.json.JsonHttpParser;
import com.google.api.client.json.jackson.JacksonFactory;

@SuppressLint("DefaultLocale")
public class PlacesAPISearch {

    private Location location;
    private String estabType;
    private String detailsReference;
    private SearchType searchType;

    private static final String TAG = "PubSearch";
    private PlaceSearchRequester requester;
    private String apiKey;
    private String placesSearchURL;
    private String placesDetailsURL;
    private String placesPhotoURL;
    private ArrayList<Photo> photoRefs;
    //private ArrayList<Bitmap> photoResults;
    private CopyOnWriteArrayList<Bitmap> photoResults;

    private ExecutorService executorService;

    private Cache<PlacesList> placesListCache;
    private Cache<PlaceDetails> placeDetailsCache;

    public PlacesAPISearch(PlaceSearchRequester requester) {
        this.requester = requester;
        Properties prop = new Properties();

        placesListCache = new Cache<PlacesList>(new Formatter() {
            @Override
            public String formatKey(String queryString) {
                String location = queryString.substring(queryString.indexOf("location=") + 9);
                location = location.substring(0, location.indexOf("&"));
                String lat = location.substring(0, location.indexOf(","));
                String lng = location.substring(location.indexOf(",") + 1);

                lat = String.format("%.3f", Double.parseDouble(lat));
                lng = String.format("%.3f", Double.parseDouble(lng));

                String type = queryString.substring(queryString.indexOf("types=") + 6);
                // "type1,type2&latitude,longitude"
                return type + "&" + lat + "," + lng;
            }
        });

        placeDetailsCache = new Cache<PlaceDetails>(new Formatter() {
            @Override
            public String formatKey(String key) {
                return key.substring(key.indexOf("reference=") + 10, key.indexOf("&sensor"));
            }
        });

        try {
            prop.load(PlacesAPISearch.class.getClassLoader().getResourceAsStream("config.properties"));
            apiKey = prop.getProperty("apikey_places");
            placesSearchURL = prop.getProperty("places_endpoint_search");
            placesDetailsURL = prop.getProperty("places_endpoint_details");
            placesPhotoURL = prop.getProperty("places_endpoint_photo");
            Log.d(TAG, apiKey);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    }

    public void search(Location l, SearchEstab[] estabs, SearchType searchType) {
        this.location = l;
        StringBuffer estabsStr = new StringBuffer();
        int i = 0;
        for (; i < estabs.length - 1; i++) {
            estabsStr.append(estabs[i].label());
            estabsStr.append(",");
        }
        estabsStr.append(estabs[i]);
        this.estabType = estabsStr.toString();
        this.searchType = searchType;
        new PlaceSearchTask().execute();
    }

    public void getDetail(String reference) {
        this.detailsReference = reference;
        this.searchType = SearchType.DETAIL;
        new PlaceDetailsTask().execute();
    }

    class PlaceSearchTask extends AsyncTask<String, Void, PlacesList> {
        final SearchType st = searchType;

        protected PlacesList doInBackground(String... urls) {
            PlacesList places = new PlacesList();
            try {
                HttpTransport transport = new ApacheHttpTransport();
                HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
                HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(placesSearchURL));
                // request.getUrl().setPort(443);
                request.getUrl().put("key", apiKey);
                request.getUrl().put("location", location.getLatitude() + "," + location.getLongitude());
                request.getUrl().put("rankby", "distance"); // in meters
                request.getUrl().put("sensor", "true");
                if (estabType != null)
                    request.getUrl().put("types", estabType.toLowerCase());

                String query = request.getUrl().toString();
                Log.d(TAG, query);

                if (placesListCache.contains(query)) {
                    places = placesListCache.get(query);
                    Log.d(TAG, "retrieved from cache");
                } else {
                    places = request.execute().parseAs(PlacesList.class);
                    // we really should do some data validation before storing
                    placesListCache.store(query, places);
                }

                // Check log cat for places response status
                Log.d(TAG, "" + places.status);

                return places;
            } catch (HttpResponseException e) {
                Log.e("Error:", e.getMessage());
                returnNoResult();
                return null;
            } catch (IOException e) {
                returnNoResult();
                e.printStackTrace();
                return null;
            }
        }

        protected void onPostExecute(PlacesList places) {
            Log.d(TAG, "AsyncTask is done");
            Log.d(TAG, "SearchType: " + searchType);
            if (places != null && places.results != null) {
                if (places.results.size() > 0) {
                    if (this.st == SearchType.MANY) {
                        requester.updateNearestPlaces(places);
                    } else {
                        Place p = places.results.get(0);
                        Location l = new Location(p.name);
                        l.setLatitude(p.geometry.location.lat);
                        l.setLongitude(p.geometry.location.lng);
                        Log.d(TAG, "Nearest pub:" + p.name);
                        Log.d(TAG, "Distance:" + location.distanceTo(l));
                        requester.updateNearestPlace(p, l, location.distanceTo(l));
                    }
                } else {
                    returnNoResult();
                }
            } else {
                returnNoResult();
            }
        }
    }

    class PlaceDetailsTask extends AsyncTask<String, Void, PlaceDetails> {

        protected PlaceDetails doInBackground(String... urls) {
            PlaceDetails details = new PlaceDetails();
            try {
                HttpTransport transport = new ApacheHttpTransport();
                HttpRequestFactory httpRequestFactory = createRequestFactory(transport);
                HttpRequest request = httpRequestFactory.buildGetRequest(new GenericUrl(placesDetailsURL));
                // request.getUrl().setPort(443);
                request.getUrl().put("key", apiKey);
                request.getUrl().put("reference", detailsReference);
                request.getUrl().put("sensor", "true");

                String query = request.getUrl().toString();
                Log.d(TAG, query);

                if (placeDetailsCache.contains(query)) {
                    details = placeDetailsCache.get(query);
                    Log.d(TAG, "retrieved from cache");
                } else {
                    details = request.execute().parseAs(PlaceDetails.class);
                    // we really should do some data validation before storing
                    placeDetailsCache.store(query, details);
                }

                // Check log cat for places response status
                Log.d(TAG, "" + details.status);

                return details;
            } catch (HttpResponseException e) {
                Log.e("Error:", e.getMessage());
                returnNoResult();
                return null;
            } catch (IOException e) {
                returnNoResult();
                e.printStackTrace();
                return null;
            }
        }

        protected void onPostExecute(PlaceDetails details) {
            Log.d(TAG, "AsyncTask is done");
            Log.d(TAG, "places is null?: " + (details == null));
            if (details != null) {
                requester.updatePlaceDetails(details);
            } else {
                returnNoResult();
            }
        }
    }

    public void getPhotos(Photo[] photoRefsArr) {
        executorService = Executors.newFixedThreadPool(5);
        photoRefs = new ArrayList<Photo>(Arrays.asList(photoRefsArr));
        photoResults = new CopyOnWriteArrayList<Bitmap>();
        for (Photo p : photoRefs) {
            executorService.execute(new PhotoTask(p.photo_reference));
        }
    }

    class PhotoTask implements Runnable {

        private final String photoRef;

        PhotoTask(String photoRef) {
            this.photoRef = photoRef;
        }

        private Bitmap getBitmap() {
            try {
                StringBuilder url = new StringBuilder(placesPhotoURL);
                url.append("maxwidth=400");
                url.append("&photoreference=").append(photoRef);
                url.append("&sensor=true");
                url.append("&key=").append(apiKey);

                HttpUriRequest request = new HttpGet(url.toString());
                HttpClient httpClient = new DefaultHttpClient();
                HttpResponse response = httpClient.execute(request);

                StatusLine statusLine = response.getStatusLine();
                int statusCode = statusLine.getStatusCode();
                if (statusCode == 200) {
                    HttpEntity entity = response.getEntity();
                    byte[] bytes = EntityUtils.toByteArray(entity);

                    Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
                    return bitmap;
                } else {
                    throw new IOException("Download failed, HTTP response code " + statusCode + " - "
                            + statusLine.getReasonPhrase());
                }
            } catch (HttpResponseException e) {
                Log.e("Error:", e.getMessage());
                returnNoResult();
                return null;
            } catch (IOException e) {
                returnNoResult();
                e.printStackTrace();
                return null;
            }
        }

        @Override
        public void run() {
            photoResults.add(getBitmap());
            if (photoRefs.size() == photoResults.size()) {
                requester.updatePhotos(new ArrayList<Bitmap>(photoResults));
            }
        }

    }

    public void getPlaceDetails(String reference) throws Exception {
        detailsReference = reference;
        new PlaceDetailsTask().execute();
    }

    private void returnNoResult() {
        Log.d(TAG, "NO RESULT");
        if (searchType == SearchType.MANY) {
            requester.updateNearestPlaces(new PlacesList());
        } else {
            Place p = new Place();
            p.name = "No results";
            p.id = Place.NO_RESULT;
            requester.updateNearestPlace(p, null, 0f);
        }
    }

    private static HttpRequestFactory createRequestFactory(final HttpTransport transport) {
        return transport.createRequestFactory(new HttpRequestInitializer() {
            public void initialize(HttpRequest request) {
                GoogleHeaders headers = new GoogleHeaders();
                headers.setApplicationName("BigArrow");
                request.setHeaders(headers);
                JsonHttpParser parser = new JsonHttpParser(new JacksonFactory());
                request.addParser(parser);
            }
        });
    }

    @SuppressWarnings("unused")
    private HttpClient sslClient(HttpClient client) {
        try {
            X509TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] xcs, String string) throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };
            SSLContext ctx = SSLContext.getInstance("TLS");
            ctx.init(null, new TrustManager[] { tm }, null);
            SSLSocketFactory ssf = new MySSLSocketFactory(ctx);
            ssf.setHostnameVerifier(SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER);
            ClientConnectionManager ccm = client.getConnectionManager();
            SchemeRegistry sr = ccm.getSchemeRegistry();
            sr.register(new Scheme("https", ssf, 443));
            return new DefaultHttpClient(ccm, client.getParams());
        } catch (Exception ex) {
            return null;
        }
    }

    public class MySSLSocketFactory extends SSLSocketFactory {
        SSLContext sslContext = SSLContext.getInstance("TLS");

        public MySSLSocketFactory(KeyStore truststore) throws NoSuchAlgorithmException, KeyManagementException,
                KeyStoreException, UnrecoverableKeyException {
            super(truststore);

            TrustManager tm = new X509TrustManager() {
                public void checkClientTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                public void checkServerTrusted(X509Certificate[] chain, String authType)
                        throws CertificateException {
                }

                public X509Certificate[] getAcceptedIssuers() {
                    return null;
                }
            };

            sslContext.init(null, new TrustManager[] { tm }, null);
        }

        public MySSLSocketFactory(SSLContext context) throws KeyManagementException, NoSuchAlgorithmException,
                KeyStoreException, UnrecoverableKeyException {
            super(null);
            sslContext = context;
        }

        @Override
        public Socket createSocket(Socket socket, String host, int port, boolean autoClose)
                throws IOException, UnknownHostException {
            return sslContext.getSocketFactory().createSocket(socket, host, port, autoClose);
        }

        @Override
        public Socket createSocket() throws IOException {
            return sslContext.getSocketFactory().createSocket();
        }
    }

}