Add people and reverse lookup provider "Auskunft" (AT).

Change-Id: Ib57a4aca35bcd80f320c5a2a44faac4c9c9d3026
diff --git a/res/values/cm_arrays.xml b/res/values/cm_arrays.xml
index 6ab20c7..087ec89 100644
--- a/res/values/cm_arrays.xml
+++ b/res/values/cm_arrays.xml
@@ -44,17 +44,20 @@
     </string-array>
 
     <string-array name="people_lookup_providers" translatable="false">
+        <item>Auskunft</item>
         <item>WhitePages</item>
     </string-array>
 
     <string-array name="people_lookup_provider_names" translatable="false">
+        <item>Auskunft (AT)</item>
         <item>WhitePages (US)</item>
     </string-array>
 
     <string-array name="reverse_lookup_providers" translatable="false">
-        <item>OpenCnam</item>
+        <item>Auskunft</item>
         <item>DasTelefonbuch</item>
         <item>Gebeld</item>
+        <item>OpenCnam</item>
         <item>WhitePages</item>
         <item>WhitePages_CA</item>
         <item>YellowPages</item>
@@ -63,9 +66,10 @@
     </string-array>
 
     <string-array name="reverse_lookup_provider_names" translatable="false">
-        <item>OpenCnam (US)</item>
+        <item>Auskunft (AT)</item>
         <item>Das Telefonbuch (DE)</item>
         <item>Gebeld (NL)</item>
+        <item>OpenCnam (US)</item>
         <item>WhitePages (US)</item>
         <item>WhitePages (CA)</item>
         <item>YellowPages (US)</item>
diff --git a/src/com/android/dialer/lookup/LookupSettings.java b/src/com/android/dialer/lookup/LookupSettings.java
index 68d9bfa..f12e4a7 100644
--- a/src/com/android/dialer/lookup/LookupSettings.java
+++ b/src/com/android/dialer/lookup/LookupSettings.java
@@ -34,6 +34,7 @@
 
     /** People lookup providers */
     public static final String PLP_WHITEPAGES = "WhitePages";
+    public static final String PLP_AUSKUNFT = "Auskunft";
     public static final String PLP_DEFAULT = PLP_WHITEPAGES;
 
     /** Reverse lookup providers */
@@ -46,6 +47,7 @@
     public static final String RLP_CYNGN_CHINESE = "CyngnChinese";
     public static final String RLP_DASTELEFONBUCH = "DasTelefonbuch";
     public static final String RLP_GEBELD = "Gebeld";
+    public static final String RLP_AUSKUNFT = "Auskunft";
     public static final String RLP_DEFAULT = RLP_OPENCNAM;
 
     private LookupSettings() {
diff --git a/src/com/android/dialer/lookup/PeopleLookup.java b/src/com/android/dialer/lookup/PeopleLookup.java
index 08c3d7d..4a247c0 100644
--- a/src/com/android/dialer/lookup/PeopleLookup.java
+++ b/src/com/android/dialer/lookup/PeopleLookup.java
@@ -17,6 +17,7 @@
 package com.android.dialer.lookup;
 
 import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.lookup.auskunft.AuskunftPeopleLookup;
 import com.android.dialer.lookup.whitepages.WhitePagesPeopleLookup;
 
 import android.content.Context;
@@ -35,6 +36,8 @@
 
             if (provider.equals(LookupSettings.PLP_WHITEPAGES)) {
                 INSTANCE = new WhitePagesPeopleLookup(context);
+            } else if (provider.equals(LookupSettings.PLP_AUSKUNFT)) {
+                INSTANCE = new AuskunftPeopleLookup(context);
             }
         }
 
@@ -45,6 +48,9 @@
         if (provider.equals(LookupSettings.PLP_WHITEPAGES)
                 && INSTANCE instanceof WhitePagesPeopleLookup) {
             return true;
+        } else if (provider.equals(LookupSettings.PLP_AUSKUNFT)
+                && INSTANCE instanceof AuskunftPeopleLookup) {
+            return true;
         } else {
             return false;
         }
diff --git a/src/com/android/dialer/lookup/ReverseLookup.java b/src/com/android/dialer/lookup/ReverseLookup.java
index 32da8ae..e2eb7a5 100644
--- a/src/com/android/dialer/lookup/ReverseLookup.java
+++ b/src/com/android/dialer/lookup/ReverseLookup.java
@@ -17,6 +17,7 @@
 package com.android.dialer.lookup;
 
 import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.lookup.auskunft.AuskunftReverseLookup;
 import com.android.dialer.lookup.cyngn.CyngnChineseReverseLookup;
 import com.android.dialer.lookup.dastelefonbuch.TelefonbuchReverseLookup;
 import com.android.dialer.lookup.gebeld.GebeldReverseLookup;
@@ -59,6 +60,8 @@
                 INSTANCE = new TelefonbuchReverseLookup(context);
             } else if (provider.equals(LookupSettings.RLP_GEBELD)) {
                 INSTANCE = new GebeldReverseLookup(context);
+            } else if (provider.equals(LookupSettings.RLP_AUSKUNFT)) {
+                INSTANCE = new AuskunftReverseLookup(context);
             }
         }
 
@@ -89,6 +92,9 @@
         } else if (provider.equals(LookupSettings.RLP_GEBELD)
                 && INSTANCE instanceof GebeldReverseLookup) {
             return true;
+        } else if (provider.equals(LookupSettings.RLP_AUSKUNFT)
+                && INSTANCE instanceof AuskunftReverseLookup) {
+            return true;
         } else {
             return false;
         }
diff --git a/src/com/android/dialer/lookup/auskunft/AuskunftApi.java b/src/com/android/dialer/lookup/auskunft/AuskunftApi.java
new file mode 100644
index 0000000..91c2ecd
--- /dev/null
+++ b/src/com/android/dialer/lookup/auskunft/AuskunftApi.java
@@ -0,0 +1,122 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * 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.auskunft;
+
+import android.content.Context;
+import android.net.Uri;
+import android.util.Log;
+
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.lookup.ContactBuilder;
+import com.android.dialer.lookup.LookupUtils;
+
+import java.io.IOException;
+import java.util.List;
+
+public final class AuskunftApi {
+    private static final String TAG = AuskunftApi.class.getSimpleName();
+
+    private static final String PEOPLE_LOOKUP_URL =
+            "https://auskunft.at/suche";
+
+    private static final String SEARCH_RESULTS_REGEX =
+            "(?i)class=[\"']?search-list(.*?)class=[\"']?pagination";
+    private static final String NAME_REGEX =
+            "(?i)<h1[\\s]+itemprop=[\"']?name[\"']?>(.*?)</h1";
+    private static final String NUMBER_REGEX =
+            "(?i)phone[\"'][\\s]+?href=[\"']{1}tel:(.*?)[\"']{1}";
+    private static final String ADDRESS_REGEX =
+            "(?i)<span[\\s]+itemprop=[\"']?streetAddress[\"']?>(.*?)</a";
+
+    private static final String BUSINESS_IDENTIFIER = "(Firma)";
+
+    private AuskunftApi() {
+    }
+
+    public static ContactInfo[] query(String filter, int lookupType, String normalizedNumber,
+            String formattedNumber) throws IOException {
+        // build URI
+        Uri uri = Uri.parse(PEOPLE_LOOKUP_URL)
+                .buildUpon()
+                .appendQueryParameter("query", filter)
+                .build();
+
+        // get search results from HTML to speedup subsequent matching and avoid errors
+        String output = LookupUtils.firstRegexResult(LookupUtils.httpGet(uri.toString(), null),
+                SEARCH_RESULTS_REGEX, true);
+
+        // get all names, abort lookup if nothing found
+        List<String> names = LookupUtils.allRegexResults(output, NAME_REGEX, true);
+        if (names == null || names.isEmpty()) {
+            Log.w(TAG, "nothing found");
+            return null;
+        }
+
+        // get all numbers and addresses
+        List<String> numbers = LookupUtils.allRegexResults(output, NUMBER_REGEX, true);
+        List<String> addresses = LookupUtils.allRegexResults(output, ADDRESS_REGEX, true);
+
+        // abort on invalid data (all the data arrays must have the same size because we query
+        // all the results at once and expect them to be in the right order)
+        if (numbers == null || addresses == null || names.size() != numbers.size() ||
+                numbers.size() != addresses.size()) {
+            Log.w(TAG, "names, numbers and address data do not match");
+            return null;
+        }
+
+        // build and return contact list
+        ContactInfo[] details = new ContactInfo[names.size()];
+        for (int i = 0; i < names.size(); i++) {
+            // figure out if we have a business contact
+            boolean isBusiness = names.get(i).contains(BUSINESS_IDENTIFIER);
+            // cleanup results
+            String name = cleanupResult(names.get(i));
+            String number = cleanupResult(numbers.get(i));
+            String address = cleanupResult(addresses.get(i));
+            // set normalized and formatted number if we're not doing a reverse lookup
+            if (lookupType != ContactBuilder.REVERSE_LOOKUP) {
+                normalizedNumber = formattedNumber = number;
+            }
+            // build contact and add to list
+            ContactBuilder builder = new ContactBuilder(lookupType, normalizedNumber,
+                    formattedNumber);
+            builder.setName(ContactBuilder.Name.createDisplayName(name));
+            builder.addPhoneNumber(
+                    ContactBuilder.PhoneNumber.createMainNumber(number));
+            builder.addWebsite(ContactBuilder.WebsiteUrl.createProfile(uri.toString()));
+            builder.addAddress(ContactBuilder.Address.createFormattedHome(address));
+            builder.setIsBusiness(isBusiness);
+            details[i] = builder.build();
+        }
+        return details;
+    }
+
+    private static String cleanupResult(String result) {
+        // get displayable text
+        result = LookupUtils.fromHtml(result);
+        // replace newlines with spaces
+        result = result.replaceAll("\\r|\\n", " ");
+        // replace multiple spaces with one
+        result = result.replaceAll("\\s+", " ");
+        // remove business identifier that is originally not part of the name
+        result = result.replace(BUSINESS_IDENTIFIER, "");
+        // final trimming
+        result = result.trim();
+
+        return result;
+    }
+}
diff --git a/src/com/android/dialer/lookup/auskunft/AuskunftPeopleLookup.java b/src/com/android/dialer/lookup/auskunft/AuskunftPeopleLookup.java
new file mode 100644
index 0000000..2898fc6
--- /dev/null
+++ b/src/com/android/dialer/lookup/auskunft/AuskunftPeopleLookup.java
@@ -0,0 +1,44 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * 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.auskunft;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.lookup.ContactBuilder;
+import com.android.dialer.lookup.PeopleLookup;
+
+import java.io.IOException;
+
+public class AuskunftPeopleLookup extends PeopleLookup {
+    private static final String TAG = AuskunftPeopleLookup.class.getSimpleName();
+
+    public AuskunftPeopleLookup(Context context) {
+    }
+
+    @Override
+    public ContactInfo[] lookup(Context context, String filter) {
+        ContactInfo[] infos = null;
+        try {
+            infos = AuskunftApi.query(filter, ContactBuilder.PEOPLE_LOOKUP, null, null);
+        } catch (IOException e) {
+            Log.e(TAG, "People lookup failed", e);
+        }
+        return infos;
+    }
+}
diff --git a/src/com/android/dialer/lookup/auskunft/AuskunftReverseLookup.java b/src/com/android/dialer/lookup/auskunft/AuskunftReverseLookup.java
new file mode 100644
index 0000000..894fd3b
--- /dev/null
+++ b/src/com/android/dialer/lookup/auskunft/AuskunftReverseLookup.java
@@ -0,0 +1,46 @@
+/**
+ * Copyright (c) 2015, The CyanogenMod Project
+ *
+ * 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.auskunft;
+
+import android.content.Context;
+
+import com.android.dialer.calllog.ContactInfo;
+import com.android.dialer.lookup.ContactBuilder;
+import com.android.dialer.lookup.ReverseLookup;
+
+import java.io.IOException;
+
+public class AuskunftReverseLookup extends ReverseLookup {
+    private static final String TAG = AuskunftReverseLookup.class.getSimpleName();
+
+    public AuskunftReverseLookup(Context context) {
+    }
+
+    @Override
+    public ContactInfo lookupNumber(Context context, String normalizedNumber,
+            String formattedNumber) throws IOException {
+        // only Austrian numbers are supported
+        if (normalizedNumber.startsWith("+") && !normalizedNumber.startsWith("+43")) {
+            return null;
+        }
+
+        // query the API and return null if nothing found or general error
+        ContactInfo[] infos = AuskunftApi.query(normalizedNumber, ContactBuilder.REVERSE_LOOKUP,
+                normalizedNumber, formattedNumber);
+        return (infos != null && infos.length != 0) ? infos[0] : null;
+    }
+}