Add basic ContactsDataKind to ContactsTest (1/2)

* Use the same icons for the test app as the
  regular app so that custom QuickContacts
  actions have it.

* Make a copy of the fallback xml and add
  a ContactsDataKind to it

* Update the TestSyncAdapter to add
  the custom mimetype data row for null
  contacts that it sweeps up.

Test: Add null contacts, add test accounts, grant
  contacts permission to test account, toggle
  contacts sync for test account

Bug: 31549157

Change-Id: I59f0798c286e7043e0b40ca02f4336533534fae7
diff --git a/tests/Android.mk b/tests/Android.mk
index 48a00f4..b895035 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -8,7 +8,9 @@
 LOCAL_STATIC_JAVA_LIBRARIES := android-support-test
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
-LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, res)
+
+res_dirs := res ../res-icons
+LOCAL_RESOURCE_DIR := $(addprefix $(LOCAL_PATH)/, $(res_dirs))
 
 LOCAL_PACKAGE_NAME := ContactsTests
 
diff --git a/tests/AndroidManifest.xml b/tests/AndroidManifest.xml
index 87f9a8a..65be28a 100644
--- a/tests/AndroidManifest.xml
+++ b/tests/AndroidManifest.xml
@@ -36,6 +36,8 @@
     <uses-permission android:name="android.permission.READ_SOCIAL_STREAM" />
 
     <application
+        android:icon="@mipmap/ic_contacts_launcher_square"
+        android:roundIcon="@mipmap/ic_contacts_launcher"
         android:label="@string/applicationLabel">
 
         <uses-library android:name="android.test.runner" />
@@ -49,7 +51,13 @@
             </intent-filter>
         </activity>
 
-        <activity android:name=".allintents.ResultActivity"/>
+        <activity android:name=".allintents.ResultActivity">
+            <intent-filter>
+                <action android:name="android.intent.action.VIEW" />
+                <category android:name="android.intent.category.DEFAULT" />
+                <data android:mimeType="vnd.android.cursor.item/vnd.contactstest.profile" />
+            </intent-filter>
+        </activity>
 
         <activity android:name=".quickcontact.QuickContactTestsActivity"/>
 
@@ -78,7 +86,7 @@
                 android:resource="@xml/test_basic_syncadapter" />
             <meta-data
                 android:name="android.provider.CONTACTS_STRUCTURE"
-                android:resource="@xml/contacts_fallback" />
+                android:resource="@xml/contacts_contactsdatakind" />
         </service>
 
         <service android:name=".QueryService" />
diff --git a/tests/res/xml/contacts_contactsdatakind.xml b/tests/res/xml/contacts_contactsdatakind.xml
new file mode 100644
index 0000000..c289e6e
--- /dev/null
+++ b/tests/res/xml/contacts_contactsdatakind.xml
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2016 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.
+-->
+
+<!--
+    contacts.xml to build a "fallback account type" equivalent with
+    an additional test ContactsDataKind
+-->
+
+<ContactsAccountType
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    accountType="com.android.contacts.tests.testauth.basic"
+    accountTypeLabel="@string/applicationLabel"
+    accountTypeIcon="@mipmap/ic_contacts_launcher_square"
+    >
+    <EditSchema
+        >
+        <DataKind kind="name"
+            maxOccurs="1"
+            supportsDisplayName="true"
+            supportsPrefix="true"
+            supportsMiddleName="true"
+            supportsSuffix="true"
+            supportsPhoneticFamilyName="true"
+            supportsPhoneticMiddleName="true"
+            supportsPhoneticGivenName="true"
+            >
+        </DataKind>
+        <DataKind kind="photo" maxOccurs="1" />
+        <DataKind kind="phone" >
+            <Type type="mobile" />
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="fax_work" />
+            <Type type="fax_home" />
+            <Type type="pager" />
+            <Type type="other" />
+            <Type type="custom"/>
+            <Type type="callback" />
+            <Type type="car" />
+            <Type type="company_main" />
+            <Type type="isdn" />
+            <Type type="main" />
+            <Type type="other_fax" />
+            <Type type="radio" />
+            <Type type="telex" />
+            <Type type="tty_tdd" />
+            <Type type="work_mobile"/>
+            <Type type="work_pager" />
+            <Type type="assistant" />
+            <Type type="mms" />
+        </DataKind>
+        <DataKind kind="email" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="mobile" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="nickname" maxOccurs="1" />
+        <DataKind kind="im" >
+            <Type type="aim" />
+            <Type type="msn" />
+            <Type type="yahoo" />
+            <Type type="skype" />
+            <Type type="qq" />
+            <Type type="google_talk" />
+            <Type type="icq" />
+            <Type type="jabber" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="postal" needsStructured="false" >
+            <Type type="home" />
+            <Type type="work" />
+            <Type type="other" />
+            <Type type="custom" />
+        </DataKind>
+        <DataKind kind="organization" maxOccurs="1" />
+        <DataKind kind="website" />
+        <DataKind kind="sip_address" maxOccurs="1" />
+        <DataKind kind="note" maxOccurs="1" />
+        <DataKind kind="group_membership" maxOccurs="1" />
+    </EditSchema>
+
+    <ContactsDataKind
+        android:mimeType="vnd.android.cursor.item/vnd.contactstest.profile"
+        android:icon="@mipmap/ic_contacts_launcher_square"
+        android:summaryColumn="data2"
+        android:detailColumn="data3" />
+
+</ContactsAccountType>
diff --git a/tests/res/xml/test_basic_authenticator.xml b/tests/res/xml/test_basic_authenticator.xml
index efcdadf..83bd135 100644
--- a/tests/res/xml/test_basic_authenticator.xml
+++ b/tests/res/xml/test_basic_authenticator.xml
@@ -19,5 +19,7 @@
 
 <account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
     android:accountType="com.android.contacts.tests.testauth.basic"
+    android:icon="@mipmap/ic_contacts_launcher_square"
+    android:smallIcon="@mipmap/ic_contacts_launcher_square"
     android:label="@string/applicationLabel"
 />
diff --git a/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java b/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java
index feef8ec..076baea 100644
--- a/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java
+++ b/tests/src/com/android/contacts/tests/testauth/TestSyncAdapter.java
@@ -18,20 +18,29 @@
 import android.accounts.Account;
 import android.content.AbstractThreadedSyncAdapter;
 import android.content.ContentProviderClient;
+import android.content.ContentProviderOperation;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.SyncResult;
+import android.database.Cursor;
+import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Data;
 import android.provider.ContactsContract.RawContacts;
 import android.util.Log;
 
+import java.util.ArrayList;
+
 /**
  * Simple (minimal) sync adapter.
  *
  */
 public class TestSyncAdapter extends AbstractThreadedSyncAdapter {
 
+    private static final String TEXT_CONTENT_ITEM_TYPE =
+            "vnd.android.cursor.item/vnd.contactstest.profile";
+
     private final Context mContext;
 
     public TestSyncAdapter(Context context, boolean autoInitialize) {
@@ -47,19 +56,51 @@
             ContentProviderClient provider, SyncResult syncResult) {
         Log.v(TestauthConstants.LOG_TAG, "TestSyncAdapter.onPerformSync() account=" + account);
 
-        // First, claim all local-only contacts, if any.
-        ContentResolver cr = mContext.getContentResolver();
-        ContentValues values = new ContentValues();
-        values.put(RawContacts.ACCOUNT_NAME, account.name);
-        values.put(RawContacts.ACCOUNT_TYPE, account.type);
-        final int count = cr.update(RawContacts.CONTENT_URI, values,
+        final ArrayList<ContentProviderOperation> ops = new ArrayList<>();
+
+        final ContentResolver contentResolver = mContext.getContentResolver();
+        final Cursor cursor = contentResolver.query(RawContacts.CONTENT_URI,
+                new String[] { RawContacts._ID },
                 RawContacts.ACCOUNT_NAME + " IS NULL AND " + RawContacts.ACCOUNT_TYPE + " IS NULL",
-                null);
-        if (count > 0) {
-            Log.v(TestauthConstants.LOG_TAG, "Claimed " + count + " local raw contacts");
+                null, null);
+        try {
+            while (cursor.moveToNext()) {
+                final String rawContactId = Long.toString(cursor.getLong(0));
+
+                // Claim all local-only contacts for the test account
+                ops.add(ContentProviderOperation.newUpdate(RawContacts.CONTENT_URI)
+                        .withValue(RawContacts.ACCOUNT_NAME, account.name)
+                        .withValue(RawContacts.ACCOUNT_TYPE, account.type)
+                        .withSelection(RawContacts._ID+"=?", new String[] { rawContactId })
+                        .build());
+
+                // Create custom QuickContact action data rows
+                final Uri dataUri = Data.CONTENT_URI.buildUpon()
+                        .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
+                        .build();
+                ops.add(ContentProviderOperation.newInsert(dataUri)
+                        .withValue(Data.RAW_CONTACT_ID, rawContactId)
+                        .withValue(Data.MIMETYPE, TEXT_CONTENT_ITEM_TYPE)
+                        .withValue(Data.DATA3, "Contacts test action")
+                        .withValue(Data.DATA5, "view")
+                        .build());
+            }
+        } finally {
+            cursor.close();
         }
+        if (ops.isEmpty()) return;
 
         // TODO: Clear isDirty flag
         // TODO: Remove isDeleted raw contacts
+
+        Log.v(TestauthConstants.LOG_TAG, "Claiming " + ops.size() + " local raw contacts");
+        for (ContentProviderOperation op : ops) {
+            Log.v(TestauthConstants.LOG_TAG, op.toString());
+        }
+        try {
+            contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
+        } catch (Exception e ) {
+            Log.e(TestauthConstants.LOG_TAG, "Failed to claim local raw contacts", e);
+        }
     }
 }