import android.text.TextUtils;
import android.util.Log;
import android.provider.CallLog.Calls;
+import android.provider.ContactsContract.Contacts;
import android.provider.CallLog;
import java.io.IOException;
private BluetoothPbapVcardManager mVcardManager;
+ private int mOrderBy = ORDER_BY_INDEXED;
+
+ public static int ORDER_BY_INDEXED = 0;
+
+ public static int ORDER_BY_ALPHABETICAL = 1;
+
public static class ContentType {
public static final int PHONEBOOK = 1;
if (D) Log.d(TAG, "Guess what carkit actually want from current path (" +
mCurrentPath + ")");
- if (mCurrentPath.compareTo(PB_PATH) == 0) {
+ if (mCurrentPath.equals(PB_PATH)) {
appParamValue.needTag = ContentType.PHONEBOOK;
- } else if (mCurrentPath.compareTo(ICH_PATH) == 0) {
+ } else if (mCurrentPath.equals(ICH_PATH)) {
appParamValue.needTag = ContentType.INCOMING_CALL_HISTORY;
- } else if (mCurrentPath.compareTo(OCH_PATH) == 0) {
+ } else if (mCurrentPath.equals(OCH_PATH)) {
appParamValue.needTag = ContentType.OUTGOING_CALL_HISTORY;
- } else if (mCurrentPath.compareTo(MCH_PATH) == 0) {
+ } else if (mCurrentPath.equals(MCH_PATH)) {
appParamValue.needTag = ContentType.MISSED_CALL_HISTORY;
mNeedNewMissedCallsNum = true;
- } else if (mCurrentPath.compareTo(CCH_PATH) == 0) {
+ } else if (mCurrentPath.equals(CCH_PATH)) {
appParamValue.needTag = ContentType.COMBINED_CALL_HISTORY;
} else {
Log.w(TAG, "mCurrentpath is not valid path!!!");
public String searchValue;
+ // Indicate which vCard parameter the search operation shall be carried
+ // out on. Can be "Name | Number | Sound", default value is "Name".
public String searchAttr;
+ // Indicate which sorting order shall be used for the
+ // <x-bt/vcard-listing> listing object.
+ // Can be "Alphabetical | Indexed | Phonetical", default value is
+ // "Indexed".
+ public String order;
+
public int needTag;
public boolean vcard21;
listStartOffset = 0;
searchValue = "";
searchAttr = "";
+ order = "";
needTag = 0x00;
vcard21 = true;
}
public void dump() {
Log.i(TAG, "maxListCount=" + maxListCount + " listStartOffset=" + listStartOffset
+ " searchValue=" + searchValue + " searchAttr=" + searchAttr + " needTag="
- + needTag + " vcard21=" + vcard21);
+ + needTag + " vcard21=" + vcard21 + " order=" + order);
}
}
i += ApplicationParameter.TRIPLET_LENGTH.FILTER_LENGTH;
break;
case ApplicationParameter.TRIPLET_TAGID.ORDER_TAGID:
- // TODO capture this field to use
i += 2; // length and tag field in triplet
+ appParamValue.order = Byte.toString(appParam[i]);
i += ApplicationParameter.TRIPLET_LENGTH.ORDER_LENGTH;
break;
case ApplicationParameter.TRIPLET_TAGID.SEARCH_VALUE_TAGID:
break;
case ApplicationParameter.TRIPLET_TAGID.FORMAT_TAGID:
i += 2;// length field in triplet
- if (Byte.toString(appParam[i]).compareTo("0") != 0) {
+ if (appParam[i] != 0) {
appParamValue.vcard21 = false;
}
i += ApplicationParameter.TRIPLET_LENGTH.FORMAT_LENGTH;
// Phonebook listing request
if (type == ContentType.PHONEBOOK) {
// begin of search by name
- if (searchAttr.compareTo("0") == 0) {
- ArrayList<String> nameList = mVcardManager.getPhonebookNameList();
+ if (searchAttr.equals("0")) {
+ ArrayList<String> nameList = mVcardManager.getPhonebookNameList(mOrderBy );
int requestSize = nameList.size() >= maxListCount ? maxListCount : nameList.size();
int startPoint = listStartOffset;
int endPoint = startPoint + requestSize;
}
}// end of search by name
// begin of search by number
- else if (searchAttr.compareTo("1") == 0) {
+ else if (searchAttr.equals("1")) {
ArrayList<String> numberList = mVcardManager.getPhonebookNumberList();
int requestSize = numberList.size() >= maxListCount ? maxListCount : numberList
.size();
// If searchAttr is not set by PCE, set default value per spec.
appParamValue.searchAttr = "0";
if (D) Log.d(TAG, "searchAttr is not set by PCE, assume search by name by default");
- } else if (searchAttr.compareTo("0") != 0 && searchAttr.compareTo("1") != 0) {
+ } else if (!searchAttr.equals("0") && !searchAttr.equals("1")) {
Log.w(TAG, "search attr not supported");
- if (searchAttr.compareTo("2") == 0) {
+ if (searchAttr.equals("2")) {
// search by sound is not supported currently
Log.w(TAG, "do not support search by sound");
return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
return ResponseCodes.OBEX_HTTP_OK;
}
+ String orderPara = appParamValue.order.trim();
+ if (TextUtils.isEmpty(orderPara)) {
+ // If order parameter is not set by PCE, set default value per spec.
+ appParamValue.order = "0";
+ if (D) Log.d(TAG, "Order parameter is not set by PCE. " +
+ "Assume order by 'Indexed' by default");
+ } else if (!orderPara.equals("0") && !orderPara.equals("1")) {
+ if (V) Log.v(TAG, "Order parameter is not supported: " + appParamValue.order);
+ if (orderPara.equals("2")) {
+ // Order by sound is not supported currently
+ Log.w(TAG, "Do not support order by sound");
+ return ResponseCodes.OBEX_HTTP_NOT_IMPLEMENTED;
+ }
+ return ResponseCodes.OBEX_HTTP_PRECON_FAILED;
+ } else {
+ Log.i(TAG, "Order parameter is valid: " + orderPara);
+ }
+
+ if (orderPara.equals("0")) {
+ mOrderBy = ORDER_BY_INDEXED;
+ } else if (orderPara.equals("1")) {
+ mOrderBy = ORDER_BY_ALPHABETICAL;
+ }
+
int sendResult = sendVcardListingXml(appParamValue.needTag, op, appParamValue.maxListCount,
appParamValue.listStartOffset, appParamValue.searchValue,
appParamValue.searchAttr);
String ownerVcard = mVcardManager.getOwnerPhoneNumberVcard(vcard21);
return pushBytes(op, ownerVcard);
} else {
- return mVcardManager.composeAndSendPhonebookVcards(op, intIndex, intIndex, vcard21,
- null);
+ return mVcardManager.composeAndSendPhonebookOneVcard(op, intIndex, vcard21, null,
+ mOrderBy );
}
} else {
if (intIndex <= 0 || intIndex > size) {
private static final int CONTACTS_DISPLAY_NAME_COLUMN_INDEX = 4;
- static final String SORT_ORDER_NAME = Contacts.DISPLAY_NAME + " ASC";
-
static final String SORT_ORDER_PHONE_NUMBER = CommonDataKinds.Phone.NUMBER + " ASC";
+ static final String[] CONTACTS_PROJECTION = new String[] {
+ Contacts._ID, // 0
+ Contacts.DISPLAY_NAME, // 1
+ };
+
+ static final int CONTACTS_ID_COLUMN_INDEX = 0;
+
+ static final int CONTACTS_NAME_COLUMN_INDEX = 1;
+
+ // call histories use dynamic handles, and handles should order by date; the
+ // most recently one should be the first handle. In table "calls", _id and
+ // date are consistent in ordering, to implement simply, we sort by _id
+ // here.
+ static final String CALLLOG_SORT_ORDER = Calls._ID + " DESC";
+
public BluetoothPbapVcardManager(final Context context) {
mContext = context;
mResolver = mContext.getContentResolver();
}
public final int getContactsSize() {
- Uri myUri = RawContacts.CONTENT_URI;
+ final Uri myUri = Contacts.CONTENT_URI;
int size = 0;
Cursor contactCursor = null;
try {
}
public final int getCallHistorySize(final int type) {
- Uri myUri = CallLog.Calls.CONTENT_URI;
+ final Uri myUri = CallLog.Calls.CONTENT_URI;
String selection = BluetoothPbapObexServer.createSelectionPara(type);
int size = 0;
Cursor callCursor = null;
}
public final ArrayList<String> loadCallHistoryList(final int type) {
- Uri myUri = CallLog.Calls.CONTENT_URI;
+ final Uri myUri = CallLog.Calls.CONTENT_URI;
String selection = BluetoothPbapObexServer.createSelectionPara(type);
String[] projection = new String[] {
Calls.NUMBER, Calls.CACHED_NAME
ArrayList<String> list = new ArrayList<String>();
try {
callCursor = mResolver.query(myUri, projection, selection, null,
- CallLog.Calls.DEFAULT_SORT_ORDER);
+ CALLLOG_SORT_ORDER);
if (callCursor != null) {
for (callCursor.moveToFirst(); !callCursor.isAfterLast();
callCursor.moveToNext()) {
return list;
}
- public final ArrayList<String> getPhonebookNameList() {
+ public final ArrayList<String> getPhonebookNameList(final int orderByWhat) {
ArrayList<String> nameList = new ArrayList<String>();
nameList.add(BluetoothPbapService.getLocalPhoneName());
- Uri myUri = Phone.CONTENT_URI;
- Cursor phoneCursor = null;
+ final Uri myUri = Contacts.CONTENT_URI;
+ Cursor contactCursor = null;
try {
- phoneCursor = mResolver.query(myUri, PHONES_PROJECTION, null, null, SORT_ORDER_NAME);
- if (phoneCursor != null) {
- for (phoneCursor.moveToFirst(); !phoneCursor.isAfterLast(); phoneCursor
+ if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
+ contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, null, null,
+ Contacts._ID);
+ } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
+ contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, null, null,
+ Contacts.DISPLAY_NAME);
+ }
+ if (contactCursor != null) {
+ for (contactCursor.moveToFirst(); !contactCursor.isAfterLast(); contactCursor
.moveToNext()) {
- String name = phoneCursor.getString(CONTACTS_DISPLAY_NAME_COLUMN_INDEX);
+ String name = contactCursor.getString(CONTACTS_NAME_COLUMN_INDEX);
if (TextUtils.isEmpty(name)) {
name = mContext.getString(android.R.string.unknownName);
}
}
}
} finally {
- if (phoneCursor != null) {
- phoneCursor.close();
+ if (contactCursor != null) {
+ contactCursor.close();
}
}
return nameList;
ArrayList<String> numberList = new ArrayList<String>();
numberList.add(BluetoothPbapService.getLocalPhoneNum());
- Uri myUri = Phone.CONTENT_URI;
+ final Uri myUri = Phone.CONTENT_URI;
Cursor phoneCursor = null;
try {
phoneCursor = mResolver.query(myUri, PHONES_PROJECTION, null, null,
}
String typeSelection = BluetoothPbapObexServer.createSelectionPara(type);
- Uri myUri = CallLog.Calls.CONTENT_URI;
+ final Uri myUri = CallLog.Calls.CONTENT_URI;
final String[] CALLLOG_PROJECTION = new String[] {
CallLog.Calls._ID, // 0
};
try {
// Need test to see if order by _ID is ok here, or by date?
callsCursor = mResolver.query(myUri, CALLLOG_PROJECTION, typeSelection, null,
- CallLog.Calls._ID);
+ CALLLOG_SORT_ORDER);
if (callsCursor != null) {
callsCursor.moveToPosition(startPoint - 1);
startPointId = callsCursor.getLong(ID_COLUMN_INDEX);
if (startPoint == endPoint) {
recordSelection = Calls._ID + "=" + startPointId;
} else {
- recordSelection = Calls._ID + ">=" + startPointId + " AND " + Calls._ID + "<="
- + endPointId;
+ // The query to call table is by "_id DESC" order, so change
+ // correspondingly.
+ recordSelection = Calls._ID + ">=" + endPointId + " AND " + Calls._ID + "<="
+ + startPointId;
}
String selection;
Log.e(TAG, "internal error: startPoint or endPoint is not correct.");
return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
}
- Uri myUri = RawContacts.CONTENT_URI;
- final String[] RAW_CONTACTS_PROJECTION = new String[] {
- RawContacts._ID, // 0
- };
- final int ID_COLUMN_INDEX = 0;
+ final Uri myUri = Contacts.CONTENT_URI;
Cursor contactCursor = null;
long startPointId = 0;
long endPointId = 0;
try {
- contactCursor = mResolver.query(myUri, RAW_CONTACTS_PROJECTION, null, null,
- RawContacts._ID);
+ contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, null, null,
+ Contacts._ID);
if (contactCursor != null) {
contactCursor.moveToPosition(startPoint - 1);
- startPointId = contactCursor.getLong(ID_COLUMN_INDEX);
+ startPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
if (V) Log.v(TAG, "Query startPointId = " + startPointId);
if (startPoint == endPoint) {
endPointId = startPointId;
} else {
contactCursor.moveToPosition(endPoint - 1);
- endPointId = contactCursor.getLong(ID_COLUMN_INDEX);
+ endPointId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
}
if (V) Log.v(TAG, "Query endPointId = " + endPointId);
}
}
}
- String selection;
+ final String selection;
if (startPoint == endPoint) {
- selection = RawContacts._ID + "=" + startPointId;
+ selection = Contacts._ID + "=" + startPointId;
} else {
- selection = RawContacts._ID + ">=" + startPointId + " AND " + RawContacts._ID + "<="
+ selection = Contacts._ID + ">=" + startPointId + " AND " + Contacts._ID + "<="
+ endPointId;
}
return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
}
+ public final int composeAndSendPhonebookOneVcard(final Operation op, final int offset,
+ final boolean vcardType21, String ownerVCard, int orderByWhat) {
+ if (offset < 1) {
+ Log.e(TAG, "Internal error: offset is not correct.");
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ final Uri myUri = Contacts.CONTENT_URI;
+ Cursor contactCursor = null;
+ String selection = null;
+ long contactId = 0;
+ if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_INDEXED) {
+ try {
+ contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, null, null,
+ Contacts._ID);
+ if (contactCursor != null) {
+ contactCursor.moveToPosition(offset - 1);
+ contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+ if (V) Log.v(TAG, "Query startPointId = " + contactId);
+ }
+ } finally {
+ if (contactCursor != null) {
+ contactCursor.close();
+ }
+ }
+ } else if (orderByWhat == BluetoothPbapObexServer.ORDER_BY_ALPHABETICAL) {
+ try {
+ contactCursor = mResolver.query(myUri, CONTACTS_PROJECTION, null, null,
+ Contacts.DISPLAY_NAME);
+ if (contactCursor != null) {
+ contactCursor.moveToPosition(offset - 1);
+ contactId = contactCursor.getLong(CONTACTS_ID_COLUMN_INDEX);
+ if (V) Log.v(TAG, "Query startPointId = " + contactId);
+ }
+ } finally {
+ if (contactCursor != null) {
+ contactCursor.close();
+ }
+ }
+ } else {
+ Log.e(TAG, "Parameter orderByWhat is not supported!");
+ return ResponseCodes.OBEX_HTTP_INTERNAL_ERROR;
+ }
+ selection = Contacts._ID + "=" + contactId;
+
+ if (V) Log.v(TAG, "Query selection is: " + selection);
+
+ return composeAndSendVCards(op, selection, vcardType21, ownerVCard, true);
+ }
+
public final int composeAndSendVCards(final Operation op, final String selection,
final boolean vcardType21, String ownerVCard, boolean isContacts) {
long timestamp = 0;