Merge "Add Person class." into pi-preview1-androidx-dev
diff --git a/compat/api/current.txt b/compat/api/current.txt
index f315dad..666b594 100644
--- a/compat/api/current.txt
+++ b/compat/api/current.txt
@@ -571,6 +571,29 @@
     field public static final int IMPORTANCE_UNSPECIFIED = -1000; // 0xfffffc18
   }
 
+  public class Person {
+    method public static androidx.core.app.Person fromBundle(android.os.Bundle);
+    method public android.graphics.Bitmap getIcon();
+    method public java.lang.String getKey();
+    method public java.lang.CharSequence getName();
+    method public java.lang.String getUri();
+    method public boolean isBot();
+    method public boolean isImportant();
+    method public androidx.core.app.Person.Builder toBuilder();
+    method public android.os.Bundle toBundle();
+  }
+
+  public static class Person.Builder {
+    ctor public Person.Builder();
+    method public androidx.core.app.Person build();
+    method public androidx.core.app.Person.Builder setBot(boolean);
+    method public androidx.core.app.Person.Builder setIcon(android.graphics.Bitmap);
+    method public androidx.core.app.Person.Builder setImportant(boolean);
+    method public androidx.core.app.Person.Builder setKey(java.lang.String);
+    method public androidx.core.app.Person.Builder setName(java.lang.CharSequence);
+    method public androidx.core.app.Person.Builder setUri(java.lang.String);
+  }
+
   public final class RemoteInput {
     method public static void addDataResultToIntent(androidx.core.app.RemoteInput, android.content.Intent, java.util.Map<java.lang.String, android.net.Uri>);
     method public static void addResultsToIntent(androidx.core.app.RemoteInput[], android.content.Intent, android.os.Bundle);
diff --git a/compat/src/androidTest/java/androidx/core/app/PersonTest.java b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
new file mode 100644
index 0000000..20b2090
--- /dev/null
+++ b/compat/src/androidTest/java/androidx/core/app/PersonTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2018 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 androidx.core.app;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+import android.support.test.filters.SdkSuppress;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidJUnit4.class)
+public class PersonTest {
+    private static final CharSequence TEST_NAME = "Example Name";
+    private static final Bitmap TEST_ICON = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+    private static final String TEST_URI = "mailto:example@example.com";
+    private static final String TEST_KEY = "example-key";
+    private static final boolean TEST_IS_BOT = true;
+    private static final boolean TEST_IS_IMPORTANT = true;
+
+    @Test
+    @SdkSuppress(minSdkVersion = 12)
+    public void bundle() {
+        Person person = new Person.Builder()
+                .setImportant(TEST_IS_IMPORTANT)
+                .setBot(TEST_IS_BOT)
+                .setKey(TEST_KEY)
+                .setUri(TEST_URI)
+                .setIcon(TEST_ICON)
+                .setName(TEST_NAME)
+                .build();
+
+        Bundle personBundle = person.toBundle();
+        Person result = Person.fromBundle(personBundle);
+
+        assertEquals(TEST_NAME, result.getName());
+        assertEquals(TEST_URI, result.getUri());
+        assertEquals(TEST_KEY, result.getKey());
+        assertEquals(TEST_IS_BOT, result.isBot());
+        assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+        // Requires SDK >= 12
+        assertTrue(TEST_ICON.sameAs(result.getIcon()));
+    }
+
+    @Test
+    @SdkSuppress(minSdkVersion = 12)
+    public void toBuilder() {
+        Person person = new Person.Builder()
+                .setImportant(TEST_IS_IMPORTANT)
+                .setBot(TEST_IS_BOT)
+                .setKey(TEST_KEY)
+                .setUri(TEST_URI)
+                .setIcon(TEST_ICON)
+                .setName(TEST_NAME)
+                .build();
+        Person result = person.toBuilder().build();
+
+        assertEquals(TEST_NAME, result.getName());
+        assertEquals(TEST_URI, result.getUri());
+        assertEquals(TEST_KEY, result.getKey());
+        assertEquals(TEST_IS_BOT, result.isBot());
+        assertEquals(TEST_IS_IMPORTANT, result.isImportant());
+
+        // Requires SDK >= 12
+        assertTrue(TEST_ICON.sameAs(result.getIcon()));
+    }
+
+    @Test
+    public void getName() {
+        Person person = new Person.Builder().setName(TEST_NAME).build();
+        assertEquals(TEST_NAME, person.getName());
+    }
+
+    @Test
+    public void getIcon() {
+        Person person = new Person.Builder().setIcon(TEST_ICON).build();
+        assertEquals(TEST_ICON, person.getIcon());
+    }
+
+    @Test
+    public void getUri() {
+        Person person = new Person.Builder().setUri(TEST_URI).build();
+        assertEquals(TEST_URI, person.getUri());
+    }
+
+    @Test
+    public void getKey() {
+        Person person = new Person.Builder().setKey(TEST_KEY).build();
+        assertEquals(TEST_KEY, person.getKey());
+    }
+
+    @Test
+    public void isBot() {
+        Person person = new Person.Builder().setBot(TEST_IS_BOT).build();
+        assertEquals(TEST_IS_BOT, person.isBot());
+    }
+
+    @Test
+    public void isImportant() {
+        Person person = new Person.Builder().setImportant(TEST_IS_IMPORTANT).build();
+        assertEquals(TEST_IS_IMPORTANT, person.isImportant());
+    }
+}
diff --git a/compat/src/main/java/androidx/core/app/Person.java b/compat/src/main/java/androidx/core/app/Person.java
new file mode 100644
index 0000000..3bda510
--- /dev/null
+++ b/compat/src/main/java/androidx/core/app/Person.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright 2018 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 androidx.core.app;
+
+import android.graphics.Bitmap;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+/**
+ * Provides an immutable reference to an entity that appears repeatedly on different surfaces of the
+ * platform. For example, this could represent the sender of a message.
+ */
+public class Person {
+    private static final String NAME_KEY = "name";
+    private static final String ICON_KEY = "icon";
+    private static final String URI_KEY = "uri";
+    private static final String KEY_KEY = "key";
+    private static final String IS_BOT_KEY = "isBot";
+    private static final String IS_IMPORTANT_KEY = "isImportant";
+
+    /**
+     * Extracts and returns the {@link Person} written to the {@code bundle}. A bundle can be
+     * created from a {@link Person} using {@link #toBundle()}.
+     */
+    public static Person fromBundle(Bundle bundle) {
+        return new Builder()
+                .setName(bundle.getCharSequence(NAME_KEY))
+                .setIcon((Bitmap) bundle.getParcelable(ICON_KEY))
+                .setUri(bundle.getString(URI_KEY))
+                .setKey(bundle.getString(KEY_KEY))
+                .setBot(bundle.getBoolean(IS_BOT_KEY))
+                .setImportant(bundle.getBoolean(IS_IMPORTANT_KEY))
+                .build();
+    }
+
+    @Nullable private CharSequence mName;
+    @Nullable private Bitmap mIcon;
+    @Nullable private String mUri;
+    @Nullable private String mKey;
+    private boolean mIsBot;
+    private boolean mIsImportant;
+
+    private Person(Builder builder) {
+        mName = builder.mName;
+        mIcon = builder.mIcon;
+        mUri = builder.mUri;
+        mKey = builder.mKey;
+        mIsBot = builder.mIsBot;
+        mIsImportant = builder.mIsImportant;
+    }
+
+    /**
+     * Writes and returns a new {@link Bundle} that represents this {@link Person}. This bundle can
+     * be converted back by using {@link #fromBundle(Bundle)}.
+     */
+    public Bundle toBundle() {
+        Bundle result = new Bundle();
+        result.putCharSequence(NAME_KEY, mName);
+        result.putParcelable(ICON_KEY, mIcon);
+        result.putString(URI_KEY, mUri);
+        result.putString(KEY_KEY, mKey);
+        result.putBoolean(IS_BOT_KEY, mIsBot);
+        result.putBoolean(IS_IMPORTANT_KEY, mIsImportant);
+        return result;
+    }
+
+    /** Creates and returns a new {@link Builder} initialized with this Person's data. */
+    public Builder toBuilder() {
+        return new Builder(this);
+    }
+
+    /**
+     * Returns the name for this {@link Person} or {@code null} if no name was provided. This could
+     * be a full name, nickname, username, etc.
+     */
+    @Nullable
+    public CharSequence getName() {
+        return mName;
+    }
+
+    /** Returns the icon for this {@link Person} or {@code null} if no icon was provided. */
+    @Nullable
+    public Bitmap getIcon() {
+        return mIcon;
+    }
+
+    /**
+     * Returns the raw URI for this {@link Person} or {@code null} if no URI was provided. A URI can
+     * be any of the following:
+     * <ul>
+     *     <li>The {@code String} representation of a
+     *     {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+     *     <li>A {@code mailto:} schema*</li>
+     *     <li>A {@code tel:} schema*</li>
+     * </ul>
+     *
+     * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+     * database in their appropriate column, otherwise the reference should be discarded.
+     */
+    @Nullable
+    public String getUri() {
+        return mUri;
+    }
+
+    /**
+     * Returns the key for this {@link Person} or {@code null} if no key was provided. This is
+     * provided as a unique identifier between other {@link Person}s.
+     */
+    @Nullable
+    public String getKey() {
+        return mKey;
+    }
+
+    /**
+     * Returns whether or not this {@link Person} is a machine rather than a human. Used primarily
+     * to identify automated tooling.
+     */
+    public boolean isBot() {
+        return mIsBot;
+    }
+
+    /**
+     * Returns whether or not this {@link Person} is important to the user of this device with
+     * regards to how frequently they interact.
+     */
+    public boolean isImportant() {
+        return mIsImportant;
+    }
+
+    /** Builder for the immutable {@link Person} class. */
+    public static class Builder {
+        @Nullable private CharSequence mName;
+        @Nullable private Bitmap mIcon;
+        @Nullable private String mUri;
+        @Nullable private String mKey;
+        private boolean mIsBot;
+        private boolean mIsImportant;
+
+        /** Creates a new, empty {@link Builder}. */
+        public Builder() { }
+
+        private Builder(Person person) {
+            mName = person.mName;
+            mIcon = person.mIcon;
+            mUri = person.mUri;
+            mKey = person.mKey;
+            mIsBot = person.mIsBot;
+            mIsImportant = person.mIsImportant;
+        }
+
+        /**
+         * Give this {@link Person} a name to use for display. This can be, for example, a full
+         * name, nickname, username, etc.
+         */
+        @NonNull
+        public Builder setName(@Nullable CharSequence name) {
+            mName = name;
+            return this;
+        }
+
+        /**
+         * Set an icon for this {@link Person}.
+         *
+         * <p>The system will prefer this icon over any images that are resolved from
+         * {@link #setUri(String)}.
+         */
+        @NonNull
+        public Builder setIcon(@Nullable Bitmap icon) {
+            mIcon = icon;
+            return this;
+        }
+
+        /**
+         * Set a URI for this {@link Person} which can be any of the following:
+         * <ul>
+         *     <li>The {@code String} representation of a
+         *     {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}</li>
+         *     <li>A {@code mailto:} schema*</li>
+         *     <li>A {@code tel:} schema*</li>
+         * </ul>
+         *
+         * <p>*Note for these schemas, the path portion of the URI must exist in the contacts
+         * database in their appropriate column, otherwise the reference will be discarded.
+         */
+        @NonNull
+        public Builder setUri(@Nullable String uri) {
+            mUri = uri;
+            return this;
+        }
+
+        /**
+         * Set a unique identifier for this {@link Person}. This is especially useful if the
+         * {@link #setName(CharSequence)} value isn't unique. This value is preferred for
+         * identification, but if it's not provided, the person's name will be used in its place.
+         */
+        @NonNull
+        public Builder setKey(@Nullable String key) {
+            mKey = key;
+            return this;
+        }
+
+        /**
+         * Sets whether or not this {@link Person} represents a machine rather than a human. This is
+         * used primarily for testing and automated tooling.
+         */
+        @NonNull
+        public Builder setBot(boolean bot) {
+            mIsBot = bot;
+            return this;
+        }
+
+        /**
+         * Sets whether this is an important person. Use this method to denote users who frequently
+         * interact with the user of this device when {@link #setUri(String)} isn't provided with
+         * {@link android.provider.ContactsContract.Contacts#CONTENT_LOOKUP_URI}, and instead with
+         * the {@code mailto:} or {@code tel:} schemas.
+         */
+        @NonNull
+        public Builder setImportant(boolean important) {
+            mIsImportant = important;
+            return this;
+        }
+
+        /** Creates and returns the {@link Person} this builder represents. */
+        @NonNull
+        public Person build() {
+            return new Person(this);
+        }
+    }
+}