Adding a class for saving a name to "Me" contact.
If no "Me" contact exists, new one is created, otherwise the name is updated.
Bug: 73557407
Test: Manual on mojave. Wait for the profile to get created/changed.
Change-Id: I51909ed292147b51b62fe5ad593b852024abfe5c
diff --git a/car-settings-lib/src/com/android/car/settingslib/util/ProfileNameSaver.java b/car-settings-lib/src/com/android/car/settingslib/util/ProfileNameSaver.java
new file mode 100644
index 0000000..8f7067c
--- /dev/null
+++ b/car-settings-lib/src/com/android/car/settingslib/util/ProfileNameSaver.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 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 com.android.car.settingslib.util;
+
+import android.content.ContentResolver;
+import android.content.ContentUris;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.text.TextUtils;
+import android.util.Log;
+
+/**
+ * Saves name as GIVEN_NAME on "Me" Profile. If "Me" contact does not exist, it's created.
+ */
+public class ProfileNameSaver {
+ private static final String TAG = "ProfileNameSaver";
+ private static final Uri PROFILE_URI = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI;
+ private static final int INVALID_ID = -1;
+
+ /**
+ * The projection into the profiles database that retrieves just the id of a profile.
+ */
+ private static final String[] CONTACTS_PROJECTION =
+ new String[]{ContactsContract.RawContacts._ID};
+
+ /**
+ * A projection into the profile data for one particular contact that includes the
+ * contact's MIME type.
+ */
+ private static final String[] CONTACTS_PROFILE_PROJECTION = new String[]{
+ ContactsContract.Data._ID,
+ ContactsContract.Data.MIMETYPE};
+
+ /**
+ * A selection into the profiles database that queries for a MIME type.
+ */
+ private static final String CONTACT_MIME_QUERY = ContactsContract.Data.MIMETYPE + " = ?";
+
+ /**
+ * Updates the "Me" contact profile card with the given name as the first name of the
+ * contact. If the "Me" contact does not exist, then one will be created.
+ *
+ * @param name The first name for the "Me" contact. If this value is {@code null} or
+ * empty, then the contact will not be updated.
+ */
+ public static void updateMeContactWith(String name, ContentResolver contentResolver) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "updateMeContactWith name=" + name);
+ }
+
+ if (TextUtils.isEmpty(name)) {
+ return;
+ }
+
+ // Check if there is already an existing "me" contact.
+ Cursor cursor = contentResolver.query(
+ PROFILE_URI,
+ CONTACTS_PROJECTION,
+ /* selection= */ null,
+ /* selectionArgs= */ null,
+ /* sortOrder= */ null);
+
+ if (cursor == null) {
+ // Error in querying the content resolver, skip the update flow
+ Log.e(TAG, "Received null from query to the \"me\" contact at " + PROFILE_URI);
+ return;
+ }
+
+ long meRawContactId = -1; // no ID can be < 0
+ boolean newContact = true;
+ try {
+ if (cursor.moveToFirst()) {
+ meRawContactId = cursor.getLong(0);
+ newContact = false; // An entry indicates that the "me" contact exists.
+ }
+ } finally {
+ cursor.close();
+ }
+
+ ContentValues values = new ContentValues();
+
+ if (newContact) {
+ meRawContactId = ContentUris.parseId(contentResolver.insert(PROFILE_URI, values));
+ }
+
+ values.put(ContactsContract.Data.RAW_CONTACT_ID, meRawContactId);
+ values.put(ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME, name);
+ values.put(ContactsContract.Data.MIMETYPE,
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
+
+ long structuredNameId = getProfileItem(meRawContactId, contentResolver);
+ if (newContact || structuredNameId == INVALID_ID) {
+ contentResolver.insert(ContactsContract.Data.CONTENT_URI, values);
+ } else {
+ contentResolver.update(ContentUris.withAppendedId(
+ ContactsContract.Data.CONTENT_URI, structuredNameId), values,
+ /* where= */ null, /* selectionArgs= */ null);
+ }
+ }
+
+ /**
+ * Helper method to search for the first profile item of the type {@link
+ * ContactsContract.CommonDataKinds.StructuredName#CONTENT_ITEM_TYPE}.
+ *
+ * @return The item's ID or {@link #INVALID_ID} if not found.
+ */
+ private static long getProfileItem(long profileContactId, ContentResolver contentResolver) {
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.v(TAG, "getProfileItem profileContactId=" + profileContactId);
+ }
+ Uri dataUri = ContactsContract.Profile.CONTENT_RAW_CONTACTS_URI.buildUpon()
+ .appendPath(String.valueOf(profileContactId))
+ .appendPath(ContactsContract.RawContacts.Data.CONTENT_DIRECTORY)
+ .build();
+
+ Cursor cursor = contentResolver.query(
+ dataUri,
+ CONTACTS_PROFILE_PROJECTION,
+ CONTACT_MIME_QUERY,
+ new String[]{
+ ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE},
+ /* sortOrder= */ null);
+
+ // Error in querying the content resolver.
+ if (cursor == null) {
+ Log.e(TAG, "Received null from query to the first profile item at " + dataUri);
+ return INVALID_ID;
+ }
+
+ try {
+ return cursor.moveToFirst() ? cursor.getLong(0) : INVALID_ID;
+ } finally {
+ cursor.close();
+ }
+ }
+}