我编写了一个示例应用程序,它从GAL返回联系人数据(公司目录,在" Accounts&#34中定义为Exchange ActiveSync;)。同样的应用程序在一堆不同的设备上运行得非常好,但在我测试的HTC上却没有(One X +与Android 4.1.1和One VX与4.0.4)。
基本上,对ContactsContract.Directory.CONTENT_URI的查询仅返回ID为Directory.DEFAULT和Directory.LOCAL_INVISIBLE的目录。在其他设备上,例如,使用类型com.android.exchange添加id = 5。从本地"人物"应用程序也适用于HTC。
这是两个类的完整代码清单
的 MainActivity
package com.example.galcontactssearch;
import com.example.galcontactssearch.CursorQueryWrapper.CursorResultIterator;
import android.content.Context;
import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.Directory;
import android.support.v4.app.Fragment;
import android.support.v7.app.ActionBarActivity;
import android.text.TextUtils;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.HashSet;
public class MainActivity extends ActionBarActivity {
public static final String TAG = "SEARCH_IN_GAL";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment())
.commit();
}
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
/**
* A placeholder fragment containing a simple view.
*/
public static class PlaceholderFragment extends Fragment {
private ArrayList<Long> dirIds;
private String[] projection;
public PlaceholderFragment() {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
final Context context = getActivity();
String app_ver = null;
try
{
app_ver =context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
}
catch (NameNotFoundException e)
{
Log.e(TAG, "Cannot find version name", e);
}
Button searchBtn = (Button) rootView.findViewById(R.id.button1);
if(app_ver!=null){
searchBtn.setText(searchBtn.getText() + " v." + app_ver);
}
final EditText text = (EditText) rootView.findViewById(R.id.editText1);
final TextView resultsView = (TextView) rootView.findViewById(R.id.resultsView);
searchBtn.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
final String searchStr = text.getText().toString();
new Thread(new Runnable() {
@Override
public void run() {
final String result;
if (TextUtils.isEmpty(searchStr)) {
result = "No input";
} else {
result = search(searchStr);
}
resultsView.post(new Runnable() {
@Override
public void run() {
resultsView.setText(result);
}
});
}
}).start();
}
});
CursorQueryWrapper wrapper = new CursorQueryWrapper(TAG);
dirIds = new ArrayList<Long>();
wrapper.query(getActivity(), ContactsContract.Directory.CONTENT_URI, new String[] {
Directory._ID, Directory.ACCOUNT_NAME, Directory.ACCOUNT_TYPE
}, null, null, null,
new CursorResultIterator() {
@Override
public void iterate(Cursor cursor) throws Exception {
long id = cursor.getLong(0);
if (Directory.DEFAULT != id && Directory.LOCAL_INVISIBLE != id) {
dirIds.add(id);
Log.d(TAG, "Account: id="+id + " name="+cursor.getString(1) +" type="+cursor.getString(2));
}
}
});
if (dirIds.isEmpty()) {
String result = "Cannot find additional accounts";
Log.e(TAG, result);
searchBtn.setEnabled(false);
resultsView.setText(result);
}else{
HashSet<String> projSet = new HashSet<String>();
projSet.add(StructuredName.DISPLAY_NAME);
projSet.add(StructuredName.GIVEN_NAME);
projSet.add(StructuredName.FAMILY_NAME);
projSet.add(Email.ADDRESS);
projSet.add(Phone.NUMBER);
projSet.add(Contacts.Data.MIMETYPE);
projSet.add(Email.TYPE);
projSet.add(Phone.TYPE);
projection = projSet.toArray(new String[projSet.size()]);
}
return rootView;
}
private String search(final String searchString) {
final StringBuilder bld = new StringBuilder();
CursorQueryWrapper wrapper = new CursorQueryWrapper(TAG);
final Context context = getActivity();
for (Long id : dirIds) {
final String idStr = String.valueOf(id);
Uri uri = ContactsContract.Contacts.CONTENT_FILTER_URI.buildUpon().appendEncodedPath(searchString)
.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, idStr).build();
final ArrayList<String> lookupKeys = new ArrayList<String>();
wrapper.query(context, uri, new String[] {
Contacts.LOOKUP_KEY
}, null, null, null, new CursorResultIterator() {
@Override
public void iterate(Cursor cursor) throws Exception {
lookupKeys.add(cursor.getString(0));
}
});
if (lookupKeys.isEmpty()) {
continue;
}
for (String lookupKey : lookupKeys) {
uri = ContactsContract.Contacts.CONTENT_LOOKUP_URI.buildUpon().appendEncodedPath(lookupKey)
.appendPath(Contacts.Entity.CONTENT_DIRECTORY)
.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, idStr).build();
wrapper.query(context, uri, projection, null, null, null, new CursorResultIterator() {
@Override
public void iterate(Cursor cursor) throws Exception {
String mime = cursor.getString(cursor.getColumnIndex(Contacts.Data.MIMETYPE));
if (StructuredName.CONTENT_ITEM_TYPE.equals(mime)) {
String result = "Given name:"
+ cursor.getString(cursor.getColumnIndex(StructuredName.GIVEN_NAME))
+ " Family name:"
+ cursor.getString(cursor.getColumnIndex(StructuredName.FAMILY_NAME))
+ " Display name:"
+ cursor.getString(cursor.getColumnIndex(StructuredName.DISPLAY_NAME));
Log.d(TAG, result);
bld.append(result).append('\n');
} else if (Email.CONTENT_ITEM_TYPE.equals(mime)) {
String result = "Email address:"
+ cursor.getString(cursor.getColumnIndex(Email.ADDRESS))
+ " with type:"
+ Email.getTypeLabel(context.getResources(), cursor.getInt(cursor
.getColumnIndex(Email.TYPE)), null);
Log.d(TAG, result);
bld.append(result).append('\n');
}
else if (Phone.CONTENT_ITEM_TYPE.equals(mime)) {
String result = "Phone num:"
+ cursor.getString(cursor.getColumnIndex(Phone.NUMBER))
+ " with type:"
+ Phone.getTypeLabel(context.getResources(), cursor.getInt(cursor
.getColumnIndex(Phone.TYPE)), null);
Log.d(TAG, result);
bld.append(result).append('\n');
}
}
});
String result = "*************************************";
Log.d(TAG, result);
bld.append(result).append('\n');
}
}
if (bld.length() == 0) {
String result = "Cannot find matching contacts for the string '" + searchString + "'";
Log.d(TAG, result);
return result;
}
return bld.toString();
}
}
}
CursorQueryWrapper
package com.example.galcontactssearch;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
public class CursorQueryWrapper {
private final String tag;
private QueryInterface queryInterface;
public static class CursorIteratonInterrupt extends Exception {
private static final long serialVersionUID = -1124533346986767621L;
}
public static abstract class CursorResultIterator {
public void iterate(Cursor cursor) throws Exception {
}
public boolean shouldIterate() {
return true;
}
public void prepareForIterations(Cursor cursor) {
}
}
public interface QueryInterface {
Cursor query(Context context, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder);
}
public static class ContextQuery implements QueryInterface {
@Override
public Cursor query(Context context, Uri uri, String[] projection, String selection, String[] selectionArgs,
String sortOrder) {
return context.getContentResolver()
.query(uri, projection, selection, selectionArgs, sortOrder);
}
}
public CursorQueryWrapper(final String tag) {
this(tag, null);
}
public CursorQueryWrapper(final String tag, QueryInterface queryInterface) {
this.tag = tag;
this.queryInterface = queryInterface == null ? new ContextQuery() : queryInterface;
}
public boolean query(Context context, Uri uri, String[] projection,
String selection, String[] selectionArgs, String sortOrder, CursorResultIterator iterator) {
Cursor cursor = null;
Log.d(tag, "Starting query");
try {
StringBuilder bld = new StringBuilder(
"Running query");
bld.append("\nUri: ").append(uri.toString());
bld.append("\nProjection: ").append(projection == null ? "null" : TextUtils.join(", ", projection));
bld.append("\nSelection: ").append(selection);
bld.append("\nSelectionArgs: ").append(
selectionArgs == null ? "null" : TextUtils.join(", ", selectionArgs));
bld.append("\nSortOrder: ").append(sortOrder);
Log.d(tag, bld.toString());
cursor = queryInterface.query(context, uri, projection, selection, selectionArgs, sortOrder);
if (cursor == null) {
Log.e(tag, "Cannot process the query. The cursor is null as a result of some error.");
return false;
}
Log.d(tag, "Cursor's count is " + cursor.getCount());
iterator.prepareForIterations(cursor);
if (iterator.shouldIterate()) {
while (cursor.moveToNext()) {
iterator.iterate(cursor);
}
}
} catch (Exception e) {
if (!(e instanceof CursorIteratonInterrupt)) {
Log.e(tag,
"Cannot process the query.", e);
return false;
}
} finally {
if (cursor != null) {
cursor.close();
}
}
Log.d(tag, "The query is ended successfully");
return true;
}
}
此外,还有this app, "True contacts",这基本上只是最新的ICS联系人应用程序编译的库存。例如,它在三星Galaxy S3上完美搜索GAL,但不在HTC上搜索。请问有人对这个问题有所了解吗?