Get address for Dialer

Add PostalAddress class and load the data from cursor whose mimetype is
StructuredPostal.CONTENT_ITEM_TYPE.

Bug: 142085691
Test: Manually
Change-Id: I433d7273a36918619230d4b1897736dd382a3b1d
diff --git a/car-telephony-common/res/values/strings.xml b/car-telephony-common/res/values/strings.xml
index e4f7b52..46d55b6 100644
--- a/car-telephony-common/res/values/strings.xml
+++ b/car-telephony-common/res/values/strings.xml
@@ -42,4 +42,9 @@
     <!-- Status label for phone state. &#8230; is an ellipsis. [CHAR LIMIT=25] -->
     <string name="call_state_call_ending">Disconnecting&#8230;</string>
 
+    <!-- String format used to format a address Uri. -->
+    <string name="address_uri_format">geo:0,0?q=%s</string>
+    <!-- String format used to format a navigation Uri. -->
+    <string name="navigation_uri_format">https://maps.google.com/maps?daddr=%s&amp;nav=1</string>
+
 </resources>
\ No newline at end of file
diff --git a/car-telephony-common/src/com/android/car/telephony/common/Contact.java b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
index 9c017b4..2020123 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/Contact.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/Contact.java
@@ -159,11 +159,6 @@
     private String mLookupKey;
 
     /**
-     * All phone numbers of this contact mapping to the unique primary key for the raw data entry.
-     */
-    private List<PhoneNumber> mPhoneNumbers = new ArrayList<>();
-
-    /**
      * A URI that can be used to retrieve a thumbnail of the contact's photo.
      */
     @Nullable
@@ -198,6 +193,17 @@
     private boolean mIsVoiceMail;
 
     /**
+     * All phone numbers of this contact mapping to the unique primary key for the raw data entry.
+     */
+    private final List<PhoneNumber> mPhoneNumbers = new ArrayList<>();
+
+    /**
+     * All postal addresses of this contact mapping to the unique primary key for the raw data
+     * entry
+     */
+    private final List<PostalAddress> mPostalAddresses = new ArrayList<>();
+
+    /**
      * Parses a contact entry for a Cursor loaded from the Contact Database. A new contact will be
      * created and returned.
      */
@@ -244,6 +250,9 @@
             case ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE:
                 contact.addPhoneNumber(context, cursor);
                 break;
+            case ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE:
+                contact.addPostalAddress(cursor);
+                break;
             default:
                 Log.d(TAG,
                         String.format("This mimetype %s will not be loaded right now.", mimeType));
@@ -330,6 +339,15 @@
         }
     }
 
+    /**
+     * Loads the data whose mimetype is
+     * {@link ContactsContract.CommonDataKinds.StructuredPostal#CONTENT_ITEM_TYPE}.
+     */
+    private void addPostalAddress(Cursor cursor) {
+        PostalAddress postalAddress = PostalAddress.fromCursor(cursor);
+        mPostalAddresses.add(postalAddress);
+    }
+
     @Override
     public boolean equals(Object obj) {
         return obj instanceof Contact && mLookupKey.equals(((Contact) obj).mLookupKey)
@@ -455,6 +473,13 @@
     }
 
     /**
+     * Return all postal addresses associated with this contact.
+     */
+    public List<PostalAddress> getPostalAddresses() {
+        return mPostalAddresses;
+    }
+
+    /**
      * Returns if this Contact represents a voice mail number.
      */
     public boolean isVoicemail() {
@@ -534,6 +559,11 @@
         for (PhoneNumber phoneNumber : mPhoneNumbers) {
             dest.writeParcelable(phoneNumber, flags);
         }
+
+        dest.writeInt(mPostalAddresses.size());
+        for (PostalAddress postalAddress : mPostalAddresses) {
+            dest.writeParcelable(postalAddress, flags);
+        }
     }
 
     public static final Creator<Contact> CREATOR = new Creator<Contact>() {
@@ -571,7 +601,6 @@
         contact.mIsVoiceMail = source.readBoolean();
         contact.mPrimaryPhoneNumber = source.readParcelable(PhoneNumber.class.getClassLoader());
         int phoneNumberListLength = source.readInt();
-        contact.mPhoneNumbers = new ArrayList<>();
         for (int i = 0; i < phoneNumberListLength; i++) {
             PhoneNumber phoneNumber = source.readParcelable(PhoneNumber.class.getClassLoader());
             contact.mPhoneNumbers.add(phoneNumber);
@@ -580,6 +609,12 @@
             }
         }
 
+        int postalAddressListLength = source.readInt();
+        for (int i = 0; i < postalAddressListLength; i++) {
+            PostalAddress address = source.readParcelable(PostalAddress.class.getClassLoader());
+            contact.mPostalAddresses.add(address);
+        }
+
         return contact;
     }
 
diff --git a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
index 02bf44b..501fa02 100644
--- a/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
+++ b/car-telephony-common/src/com/android/car/telephony/common/InMemoryPhoneBook.java
@@ -105,10 +105,12 @@
                 ContactsContract.Data.CONTENT_URI,
                 null,
                 ContactsContract.Data.MIMETYPE + " = ? OR "
+                        + ContactsContract.Data.MIMETYPE + " = ? OR "
                         + ContactsContract.Data.MIMETYPE + " = ?",
                 new String[]{
                         ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE,
-                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE},
+                        ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE,
+                        ContactsContract.CommonDataKinds.StructuredPostal.CONTENT_ITEM_TYPE},
                 ContactsContract.Contacts.DISPLAY_NAME + " ASC ");
         mContactListAsyncQueryLiveData = new AsyncQueryLiveData<List<Contact>>(mContext,
                 QueryParam.of(contactListQueryParam), Executors.newSingleThreadExecutor()) {
diff --git a/car-telephony-common/src/com/android/car/telephony/common/PostalAddress.java b/car-telephony-common/src/com/android/car/telephony/common/PostalAddress.java
new file mode 100644
index 0000000..7d0baec
--- /dev/null
+++ b/car-telephony-common/src/com/android/car/telephony/common/PostalAddress.java
@@ -0,0 +1,150 @@
+/*
+ * Copyright (C) 2019 The Android Open Source 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.car.telephony.common;
+
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.provider.ContactsContract;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Encapsulates data about an address entry. Typically loaded from the local Address store.
+ */
+public class PostalAddress implements Parcelable {
+    private static final String TAG = "CD.PostalAddress";
+
+    /**
+     * The formatted address.
+     */
+    private String mFormattedAddress;
+
+    /**
+     * The address type. See more at {@link ContactsContract.CommonDataKinds.StructuredPostal#TYPE}
+     */
+    private int mType;
+
+    /**
+     * The user defined label. See more at
+     * {@link ContactsContract.CommonDataKinds.StructuredPostal#LABEL}
+     */
+    @Nullable
+    private String mLabel;
+
+    /**
+     * Parses a PostalAddress entry for a Cursor loaded from the Address Database.
+     */
+    public static PostalAddress fromCursor(Cursor cursor) {
+        int formattedAddressColumn = cursor.getColumnIndex(
+                ContactsContract.CommonDataKinds.StructuredPostal.FORMATTED_ADDRESS);
+        int addressTypeColumn = cursor.getColumnIndex(
+                ContactsContract.CommonDataKinds.StructuredPostal.TYPE);
+        int labelColumn = cursor.getColumnIndex(
+                ContactsContract.CommonDataKinds.StructuredPostal.LABEL);
+
+        PostalAddress postalAddress = new PostalAddress();
+        postalAddress.mFormattedAddress = cursor.getString(formattedAddressColumn);
+        postalAddress.mType = cursor.getInt(addressTypeColumn);
+        postalAddress.mLabel = cursor.getString(labelColumn);
+
+        return postalAddress;
+    }
+
+    /**
+     * Returns {@link #mFormattedAddress}
+     */
+    public String getFormattedAddress() {
+        return mFormattedAddress;
+    }
+
+    /**
+     * Returns {@link #mType}
+     */
+    public int getType() {
+        return mType;
+    }
+
+    /**
+     * Returns {@link #mLabel}
+     */
+    @Nullable
+    public String getLabel() {
+        return mLabel;
+    }
+
+    /**
+     * Returns a human readable string label. For example, Home, Work, etc.
+     */
+    public CharSequence getReadableLabel(Resources res) {
+        return ContactsContract.CommonDataKinds.StructuredPostal.getTypeLabel(res, mType, mLabel);
+    }
+
+    /**
+     * Returns the address Uri for {@link #mFormattedAddress}.
+     */
+    public Uri getAddressUri(Resources res) {
+        String address = String.format(res.getString(R.string.address_uri_format),
+                Uri.encode(mFormattedAddress));
+        Log.d(TAG, "The address is: " + address);
+        return Uri.parse(address);
+    }
+
+    /**
+     * Returns the navigation Uri for {@link #mFormattedAddress}.
+     */
+    public Uri getNavigationUri(Resources res) {
+        String address = String.format(res.getString(R.string.navigation_uri_format),
+                Uri.encode(mFormattedAddress));
+        Log.d(TAG, "The address is: " + address);
+        return Uri.parse(address);
+    }
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mType);
+        dest.writeString(mLabel);
+        dest.writeString(mFormattedAddress);
+    }
+
+    /**
+     * Create {@link PostalAddress} object from saved parcelable.
+     */
+    public static Creator<PostalAddress> CREATOR = new Creator<PostalAddress>() {
+        @Override
+        public PostalAddress createFromParcel(Parcel source) {
+            PostalAddress postalAddress = new PostalAddress();
+            postalAddress.mType = source.readInt();
+            postalAddress.mLabel = source.readString();
+            postalAddress.mFormattedAddress = source.readString();
+            return postalAddress;
+        }
+
+        @Override
+        public PostalAddress[] newArray(int size) {
+            return new PostalAddress[size];
+        }
+    };
+}