blob: 50665adf6861055c03212f26876bf0320647f429 [file] [log] [blame]
/*
* 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
*/
package com.android.providers.contacts.enterprise;
import android.annotation.Nullable;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.MediaStore;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Directory;
import android.provider.ContactsContract.PhoneLookup;
import android.text.TextUtils;
import android.util.Log;
import com.android.internal.util.ArrayUtils;
import com.android.providers.contacts.ContactsProvider2;
/**
* Wrap cursor returned from work-side ContactsProvider in order to rewrite values in some colums
*/
public class EnterpriseContactsCursorWrapper extends CursorWrapper {
private static final String TAG = "EnterpriseCursorWrapper";
private static final boolean VERBOSE_LOGGING = Log.isLoggable(TAG, Log.VERBOSE);
private static final UriMatcher sUriMatcher = ContactsProvider2.sUriMatcher;
// As some of the columns like PHOTO_URI requires contact id, but original projection may not
// have it, so caller may use a work projection instead of original project to make the
// query. Hence, we need also to restore the cursor to the origianl projection.
private final int[] contactIdIndices;
// Derived Fields
private final Long mDirectoryId;
private final boolean mIsDirectoryRemote;
private final String[] originalColumnNames;
public EnterpriseContactsCursorWrapper(Cursor cursor, String[] originalColumnNames,
int[] contactIdIndices, @Nullable Long directoryId) {
super(cursor);
this.contactIdIndices = contactIdIndices;
this.originalColumnNames = originalColumnNames;
this.mDirectoryId = directoryId;
this.mIsDirectoryRemote = directoryId != null
&& Directory.isRemoteDirectoryId(directoryId);
}
@Override
public int getColumnCount() {
return originalColumnNames.length;
}
@Override
public String[] getColumnNames() {
return originalColumnNames;
}
@Override
public String getString(int columnIndex) {
final String result = super.getString(columnIndex);
final String columnName = super.getColumnName(columnIndex);
final long contactId = super.getLong(contactIdIndices[0]);
switch (columnName) {
case Contacts.PHOTO_THUMBNAIL_URI:
if(mIsDirectoryRemote) {
return getRemoteDirectoryFileUri(result);
} else {
return getCorpThumbnailUri(contactId, getWrappedCursor());
}
case Contacts.PHOTO_URI:
if(mIsDirectoryRemote) {
return getRemoteDirectoryFileUri(result);
} else {
return getCorpDisplayPhotoUri(contactId, getWrappedCursor());
}
case Data.PHOTO_FILE_ID:
case Data.PHOTO_ID:
return null;
case Data.CUSTOM_RINGTONE:
String ringtoneUri = super.getString(columnIndex);
// TODO: Remove this conditional block once accessing sounds in corp
// profile becomes possible.
if (ringtoneUri != null
&& !Uri.parse(ringtoneUri).isPathPrefixMatch(
MediaStore.Audio.Media.INTERNAL_CONTENT_URI)) {
ringtoneUri = null;
}
return ringtoneUri;
case Contacts.LOOKUP_KEY:
final String lookupKey = super.getString(columnIndex);
if (TextUtils.isEmpty(lookupKey)) {
return null;
} else {
return Contacts.ENTERPRISE_CONTACT_LOOKUP_PREFIX + lookupKey;
}
default:
return result;
}
}
@Override
public int getInt(int column) {
return (int) getLong(column);
}
@Override
public long getLong(int column) {
long result = super.getLong(column);
if (ArrayUtils.contains(contactIdIndices, column)) {
return result + Contacts.ENTERPRISE_CONTACT_ID_BASE;
} else {
final String columnName = getColumnName(column);
switch (columnName) {
case Data.PHOTO_FILE_ID:
case Data.PHOTO_ID:
return 0;
default:
return result;
}
}
}
private String getRemoteDirectoryFileUri(final String photoUriString) {
if (photoUriString == null) {
return null;
}
// Assume that the authority of photoUri is directoryInfo.authority first
// TODO: Validate the authority of photoUri is directoryInfo.authority
Uri.Builder builder = Directory.ENTERPRISE_FILE_URI.buildUpon();
builder.appendPath(photoUriString);
builder.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, Long.toString(mDirectoryId));
final String outputUri = builder.build().toString();
if (VERBOSE_LOGGING) {
Log.v(TAG, "getCorpDirectoryFileUri: output URI=" + outputUri);
}
return outputUri;
}
/**
* Generate a photo URI for {@link PhoneLookup#PHOTO_THUMBNAIL_URI}.
*
* Example: "content://com.android.contacts/contacts_corp/ID/photo"
*
* {@link ContentProvider#openAssetFile} knows how to fetch from this URI.
*/
private static String getCorpThumbnailUri(long contactId, Cursor originalCursor) {
final int thumbnailUriIndex = originalCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
final String thumbnailUri = originalCursor.getString(thumbnailUriIndex);
if (thumbnailUri == null) {
// No thumbnail. Just return null.
return null;
}
final int uriCode = sUriMatcher.match(Uri.parse(thumbnailUri));
if (uriCode == ContactsProvider2.CONTACTS_ID_PHOTO) {
return ContentUris.appendId(Contacts.CORP_CONTENT_URI.buildUpon(), contactId)
.appendPath(Contacts.Photo.CONTENT_DIRECTORY).build().toString();
} else {
Log.e(TAG, "EnterpriseContactsCursorWrapper contains invalid PHOTO_THUMBNAIL_URI");
return null;
}
}
/**
* Generate a photo URI for {@link PhoneLookup#PHOTO_URI}.
*
* Example 1: "content://com.android.contacts/contacts_corp/ID/display_photo"
* Example 2: "content://com.android.contacts/contacts_corp/ID/photo"
*
* {@link ContentProvider#openAssetFile} knows how to fetch from this URI.
*/
private static String getCorpDisplayPhotoUri(long contactId, Cursor originalCursor) {
final int photoUriIndex = originalCursor.getColumnIndex(Contacts.PHOTO_URI);
final String photoUri = originalCursor.getString(photoUriIndex);
if (photoUri == null) {
return null;
}
final int uriCode = sUriMatcher.match(Uri.parse(photoUri));
if (uriCode == ContactsProvider2.CONTACTS_ID_PHOTO) {
return ContentUris.appendId(Contacts.CORP_CONTENT_URI.buildUpon(), contactId)
.appendPath(Contacts.Photo.CONTENT_DIRECTORY).build().toString();
} else if (uriCode == ContactsProvider2.CONTACTS_ID_DISPLAY_PHOTO
|| uriCode == ContactsProvider2.DISPLAY_PHOTO_ID) {
return ContentUris.appendId(Contacts.CORP_CONTENT_URI.buildUpon(), contactId)
.appendPath(Contacts.Photo.DISPLAY_PHOTO).build().toString();
} else {
Log.e(TAG, "EnterpriseContactsCursorWrapper contains invalid PHOTO_URI");
return null;
}
}
}