diff options
Diffstat (limited to 'java/com/android/dialer/lookup/google/GoogleForwardLookup.java')
-rw-r--r-- | java/com/android/dialer/lookup/google/GoogleForwardLookup.java | 259 |
1 files changed, 259 insertions, 0 deletions
diff --git a/java/com/android/dialer/lookup/google/GoogleForwardLookup.java b/java/com/android/dialer/lookup/google/GoogleForwardLookup.java new file mode 100644 index 000000000..fb19ec387 --- /dev/null +++ b/java/com/android/dialer/lookup/google/GoogleForwardLookup.java @@ -0,0 +1,259 @@ +/* + * Copyright (C) 2014 Xiao-Long Chen <chillermillerlong@hotmail.com> + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.dialer.lookup.google; + +import com.android.dialer.phonenumbercache.ContactInfo; +import com.android.dialer.lookup.ContactBuilder; +import com.android.dialer.lookup.ForwardLookup; +import com.android.dialer.lookup.LookupUtils; + +import android.content.Context; +import android.content.pm.PackageManager; +import android.location.Location; +import android.net.Uri; +import android.os.Build; +import android.provider.ContactsContract.CommonDataKinds.Phone; +import android.provider.ContactsContract.CommonDataKinds.StructuredPostal; +import android.provider.ContactsContract.CommonDataKinds.Website; +import android.text.Html; +import android.util.Log; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class GoogleForwardLookup extends ForwardLookup { + private static final String TAG = + GoogleForwardLookup.class.getSimpleName(); + + private static final boolean DEBUG = false; + + private static final String QUERY_FILTER = "q"; + private static final String QUERY_LANGUAGE = "hl"; + private static final String QUERY_LOCATION = "sll"; + private static final String QUERY_RADIUS = "radius"; + private static final String QUERY_RANDOM = "gs_gbg"; + + private static final String RESULT_ADDRESS = "a"; + private static final String RESULT_NUMBER = "b"; + private static final String RESULT_DISTANCE = "c"; + private static final String RESULT_PHOTO_URI = "d"; + private static final String RESULT_WEBSITE = "f"; + private static final String RESULT_CITY = "g"; + + /** Base for the query URL */ + private static final String LOOKUP_URL = + "https://www.google.com/complete/search?gs_ri=dialer"; + + /** Minimum query length + * (default for dialer_nearby_places_min_query_len) */ + private static final int MIN_QUERY_LEN = 2; + + /** Maximum query length + * (default for dialer_nearby_places_max_query_len) */ + private static final int MAX_QUERY_LEN = 50; + + /** Radius (in miles) + * (default for dialer_nearby_places_directory_radius_meters) */ + private static final int RADIUS = 1000; + + /** User agent string */ + private String mUserAgent = ""; + + public GoogleForwardLookup(Context context) { + StringBuilder sb = new StringBuilder("GoogleDialer "); + try { + sb.append(context.getPackageManager().getPackageInfo( + context.getPackageName(), 0).versionName); + sb.append(" "); + sb.append(Build.FINGERPRINT); + mUserAgent = sb.toString(); + } catch (PackageManager.NameNotFoundException e) { + } + } + + @Override + public ContactInfo[] lookup(Context context, + String filter, Location lastLocation) { + int length = filter.length(); + + if (length >= MIN_QUERY_LEN) { + if (length > MAX_QUERY_LEN) { + filter = filter.substring(0, MAX_QUERY_LEN); + } + + try { + Uri.Builder builder = Uri.parse(LOOKUP_URL).buildUpon(); + + // Query string + builder = builder.appendQueryParameter(QUERY_FILTER, filter); + + // Language + builder = builder.appendQueryParameter(QUERY_LANGUAGE, + context.getResources().getConfiguration() + .locale.getLanguage()); + + // Location (latitude and longitude) + builder = builder.appendQueryParameter(QUERY_LOCATION, + String.format("%f,%f", + lastLocation.getLatitude(), + lastLocation.getLongitude())); + + // Radius distance + builder = builder.appendQueryParameter(QUERY_RADIUS, + Integer.toString(RADIUS)); + + // Random string (not really required) + builder = builder.appendQueryParameter(QUERY_RANDOM, + getRandomNoiseString()); + + Map<String, String> headers = new HashMap<String, String>(); + headers.put("User-Agent", mUserAgent); + JSONArray results = new JSONArray(LookupUtils.httpGet(builder.build().toString(), + headers)); + + if (DEBUG) Log.v(TAG, "Results: " + results); + + return getEntries(results); + } catch (IOException e) { + Log.e(TAG, "Failed to execute query", e); + } catch (JSONException e) { + Log.e(TAG, "JSON error", e); + } + } + + return null; + } + + /** + * Parse JSON results and return them as an array of ContactInfo + * + * @param results The JSON results returned from the server + * @return Array of ContactInfo containing the result information + */ + private ContactInfo[] getEntries(JSONArray results) + throws JSONException { + ArrayList<ContactInfo> details = + new ArrayList<ContactInfo>(); + + JSONArray entries = results.getJSONArray(1); + + for (int i = 0; i < entries.length(); i++) { + try { + JSONArray entry = entries.getJSONArray(i); + + String displayName = decodeHtml(entry.getString(0)); + + JSONObject params = entry.getJSONObject(3); + + String phoneNumber = decodeHtml( + params.getString(RESULT_NUMBER)); + + String address = decodeHtml(params.getString(RESULT_ADDRESS)); + String city = decodeHtml(params.getString(RESULT_CITY)); + + String profileUrl = params.optString(RESULT_WEBSITE, null); + String photoUri = params.optString(RESULT_PHOTO_URI, null); + + ContactBuilder builder = new ContactBuilder( + ContactBuilder.FORWARD_LOOKUP, null, phoneNumber); + builder.setName(ContactBuilder.Name.createDisplayName(displayName)); + builder.addPhoneNumber(ContactBuilder.PhoneNumber.createMainNumber(phoneNumber)); + builder.addWebsite(ContactBuilder.WebsiteUrl.createProfile(profileUrl)); + + ContactBuilder.Address a = new ContactBuilder.Address(); + a.formattedAddress = address; + a.city = city; + a.type = StructuredPostal.TYPE_WORK; + builder.addAddress(a); + + if (photoUri != null) { + builder.setPhotoUri(photoUri); + } else { + builder.setPhotoUri(ContactBuilder.PHOTO_URI_BUSINESS); + } + + details.add(builder.build()); + } catch (JSONException e) { + Log.e(TAG, "Skipping the suggestions at index " + i, e); + } + } + + if (details.size() > 0) { + return details.toArray(new ContactInfo[details.size()]); + } else { + return null; + } + } + + /** + * Generate a random string of alphanumeric characters of length [4, 36) + * + * @return Random alphanumeric string + */ + private String getRandomNoiseString() { + StringBuilder garbage = new StringBuilder(); + + int length = getRandomInteger(32) + 4; + + for (int i = 0; i < length; i++) { + int asciiCode; + + if (Math.random() >= 0.3) { + if (Math.random() <= 0.5) { + // Lowercase letters + asciiCode = getRandomInteger(26) + 97; + } else { + // Uppercase letters + asciiCode = getRandomInteger(26) + 65; + } + } else { + // Numbers + asciiCode = getRandomInteger(10) + 48; + } + + garbage.append(Character.toString((char) asciiCode)); + } + + return garbage.toString(); + } + + /** + * Generate number in the range [0, max). + * + * @param max Upper limit (non-inclusive) + * @return Random number inside [0, max) + */ + private int getRandomInteger(int max) { + return (int) Math.floor(Math.random() * max); + } + + /** + * Convert HTML to unformatted plain text. + * + * @param s HTML content + * @return Unformatted plain text + */ + private String decodeHtml(String s) { + return Html.fromHtml(s).toString(); + } +} |