[DESCRIPTION]
预置联系人/Service Number
此方法比较适合预置联系人的数目不是特别多的情况
联系人比较多的情况,请参考:FAQ12935 预置联系人之Vcard预置联系人
[SOLUTION]
本方案实现预置联系人(包含姓名、号码信息)至手机中;并保证该联系人是只读的,无法被删除/编辑。
代码分为两部分:
Part One 将****预置的联系人插入到数据库中;
Part Two 保证预置联系人只读,无法被编辑删
(在三个地方屏蔽对预置联系人进行编辑处理:联系人详情界面、联系人多选界面、新建联系人选择合并联系人时)。
【注意】
如果您不需要限制预置联系人的删除/编辑操作,加入Part One部分代码即可,并去掉第一步importDefaultReadonlyContact() 中的语句:contactvalues.put(RawContacts.IS_SDN_CONTACT, -2);
Part One
1.****新建PresetContactsImportProcessor.java
Before Android N:
Path: alps\packages\apps\Contacts\src\com\mediatek\contacts\simservice
package com.mediatek.contacts.simservice;import com.mediatek.contacts.simservice.SIMProcessorManager.ProcessorCompleteListener;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Email; //for usim
import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.Groups;
import android.provider.ContactsContract.RawContacts;
import com.android.contacts.common.model.account.AccountType;
import android.os.RemoteException;
import java.util.ArrayList;
import com.mediatek.contacts.simservice.SIMProcessorManager.ProcessorCompleteListener;
import com.mediatek.contacts.simservice.SIMServiceUtils;
import com.mediatek.contacts.simservice.SIMServiceUtils.ServiceWorkData;
import com.mediatek.contacts.simcontact.SimCardUtils;
import com.mediatek.contacts.util.LogUtils;
import android.provider.ContactsContract.PhoneLookup;public class PresetContactsImportProcessor extends SIMProcessorBase {
private static final String TAG = "PresetContactsImportProcessor";
private static boolean sIsRunningNumberCheck = false;
private static final int INSERT_PRESET_NUMBER_COUNT = xxx; //预置联系人的个数
private static final String INSERT_PRESET_NAME[] = {"xxx1","xxx2",...}; //各预置联系人的姓名
private static final String INSERT_PRESET_NUMBER[] = {"xxx1","xxx2",...}; //各预置联系人的号码private int mSlotId;
private Context mContext;
public PresetContactsImportProcessor(Context context, int slotId, Intent intent, ProcessorCompleteListener listener) {
super(intent, listener);
mContext = context;
mSlotId = slotId;
}@Override
public int getType() {
return SIMServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS;
}@Override
public void doWork() {
if (isCancelled()) {
LogUtils.d(TAG, "[doWork]cancel import preset contacts work. Thread id=" + Thread.currentThread().getId());
return;
}
importDefaultReadonlyContact();
}private void importDefaultReadonlyContact(){ Log.i(TAG, "isRunningNumberCheck before: " + sIsRunningNumberCheck); if (sIsRunningNumberCheck) { return; } sIsRunningNumberCheck = true; for(int i = 0;i < INSERT_PRESET_NUMBER_COUNT; i++) { Log.i(TAG, "isRunningNumberCheck after: " + sIsRunningNumberCheck); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(INSERT_PRESET_NUMBER[i])); Log.i(TAG, "getContactInfoByPhoneNumbers(), uri = " + uri); Cursor contactCursor = mContext.getContentResolver().query(uri,
new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_ID}, null, null, null);
try {
if (contactCursor != null && contactCursor.getCount() > 0) {
return;
} else {
final ArrayList operationList = new ArrayList();
ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI);
ContentValues contactvalues = new ContentValues();
contactvalues.put(RawContacts.ACCOUNT_NAME, AccountType.ACCOUNT_NAME_LOCAL_PHONE);
contactvalues.put(RawContacts.ACCOUNT_TYPE, AccountType.ACCOUNT_TYPE_LOCAL_PHONE);
contactvalues.put(RawContacts.INDICATE_PHONE_SIM, ContactsContract.RawContacts.INDICATE_PHONE);
contactvalues.put(RawContacts.IS_SDN_CONTACT, -2);
builder.withValues(contactvalues);
builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED);
operationList.add(builder.build());
builder = ContentProviderOperation.newInsert(Data.CONTENT_URI);
builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0);
builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE);
builder.withValue(Phone.NUMBER, INSERT_PRESET_NUMBER[i]);
builder.withValue(Data.IS_PRIMARY, 1);
operationList.add(builder.build());builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); builder.withValue(StructuredName.DISPLAY_NAME, INSERT_PRESET_NAME[i]); operationList.add(builder.build()); try { mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList); } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } } } finally { // when this service start,but the contactsprovider has not been started yet. // the contactCursor perhaps null, but not always.(first load will weekup the provider) // so add null block to avoid nullpointerexception if (contactCursor != null) { contactCursor.close(); } } //END for Log.i(TAG, "isRunningNumberCheck insert: " + sIsRunningNumberCheck); sIsRunningNumberCheck = false; } }
}
Android N:
Path: alps\packages\apps\Contacts\SimProcessor\src\com\mediatek\SimProcessor
package com.mediatek.simprocessor;import android.content.Context;
import android.content.Intent;
import android.content.ContentProviderOperation;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.CommonDataKinds.Phone;
import android.provider.ContactsContract.CommonDataKinds.StructuredName;
import android.provider.ContactsContract.Data;
import android.provider.ContactsContract.RawContacts;
import android.os.RemoteException;
import java.util.ArrayList;
import com.mediatek.simprocessor.SimProcessorManager.ProcessorCompleteListener;
import com.mediatek.simprocessor.SimServiceUtils;
import com.mediatek.simprocessor.Log;
import android.provider.ContactsContract.PhoneLookup;public class PresetContactsImportProcessor extends SimProcessorBase {
private static final String TAG = "PresetContactsImportProcessor";
private static boolean sIsRunningNumberCheck = false;
private static final int INSERT_PRESET_NUMBER_COUNT = xxx; //预置联系人的个数
private static final String INSERT_PRESET_NAME[] = {"xxx1","xxx2", ...}; //各预置联系人的姓名
private static final String INSERT_PRESET_NUMBER[] = {"xxxx","xxxx", ...}; //各预置联系人的号码private int mSubId; private Context mContext; public PresetContactsImportProcessor(Context context, int subId, Intent intent, ProcessorCompleteListener listener) { super(intent, listener); mContext = context; mSubId = subId; } @Override public int getType() { return SimServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS; } @Override public void doWork() { if (isCancelled()) { Log.d(TAG, "[doWork]cancel import preset contacts work. Thread id=" + Thread.currentThread().getId()); return; } importDefaultReadonlyContact(); } private void importDefaultReadonlyContact(){ Log.i(TAG, "isRunningNumberCheck before: " + sIsRunningNumberCheck); if (sIsRunningNumberCheck) { return; } sIsRunningNumberCheck = true; for(int i = 0;i < INSERT_PRESET_NUMBER_COUNT; i++) { Log.i(TAG, "isRunningNumberCheck after: " + sIsRunningNumberCheck); Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(INSERT_PRESET_NUMBER[i])); Log.i(TAG, "getContactInfoByPhoneNumbers(), uri = " + uri); Cursor contactCursor = mContext.getContentResolver().query(uri, new String[] {PhoneLookup.DISPLAY_NAME, PhoneLookup.PHOTO_ID}, null, null, null); try { if (contactCursor != null && contactCursor.getCount() > 0) { return; } else { final ArrayList operationList = new ArrayList(); ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(RawContacts.CONTENT_URI); ContentValues contactvalues = new ContentValues(); contactvalues.put(RawContacts.ACCOUNT_NAME, "Phone"); contactvalues.put(RawContacts.ACCOUNT_TYPE, "Local Phone Account"); contactvalues.put(RawContacts.INDICATE_PHONE_SIM, ContactsContract.RawContacts.INDICATE_PHONE); contactvalues.put(RawContacts.IS_SDN_CONTACT, -2); builder.withValues(contactvalues); builder.withValue(RawContacts.AGGREGATION_MODE, RawContacts.AGGREGATION_MODE_DISABLED); operationList.add(builder.build()); builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(Phone.RAW_CONTACT_ID, 0); builder.withValue(Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE); builder.withValue(Phone.TYPE, Phone.TYPE_MOBILE); builder.withValue(Phone.NUMBER, INSERT_PRESET_NUMBER[i]); builder.withValue(Data.IS_PRIMARY, 1); operationList.add(builder.build()); builder = ContentProviderOperation.newInsert(Data.CONTENT_URI); builder.withValueBackReference(StructuredName.RAW_CONTACT_ID, 0); builder.withValue(Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE); builder.withValue(StructuredName.DISPLAY_NAME, INSERT_PRESET_NAME[i]); operationList.add(builder.build()); try { mContext.getContentResolver().applyBatch(ContactsContract.AUTHORITY, operationList); } catch (RemoteException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } catch (OperationApplicationException e) { Log.e(TAG, String.format("%s: %s", e.toString(), e.getMessage())); } } } finally { // when this service start,but the contactsprovider has not been started yet. // the contactCursor perhaps null, but not always.(first load will weekup the provider) // so add null block to avoid nullpointerexception if (contactCursor != null) { contactCursor.close(); } } Log.i(TAG, "isRunningNumberCheck insert: " + sIsRunningNumberCheck); sIsRunningNumberCheck = false; } }
}
2.
Before Android N:
修改SIMServiceUtils.java
Path: alps\packages\apps\ContactsCommon\src\com\mediatek\contacts\simservice
Android N:
修改SimServiceUtils.java
Path: alps\packages\apps\Contacts\SimProcessor\src\com\mediatek\SimProcessor
添加
public static final int SERVICE_WORK_IMPORT_PRESET_CONTACTS = 5;
3.
**Before Android N: **
修改SIMProcessorManager.java
Path: alps\packages\apps\Contacts\src\com\mediatek\contacts\simservice
在createProcessor函数里添加
else if (workType == SIMServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS) {
processor = new PresetContactsImportProcessor(context, subId, intent, listener);
}
**Android N: **
修改SimProcessorManager.java
Path: alps\packages\apps\Contacts\SimProcessor\src\com\mediatek\SimProcessor
在createProcessor函数里添加
else if (workType == SimServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS) {
processor = new PresetContactsImportProcessor(context, subId, intent, listener);
}
4.修改BootCmpReceiver.java
Before Android N:
Path: alps\packages\apps\Contacts\src\com\mediatek\contacts\simservice
添加如下方法:
/**
- when boot complete,preset the service number directly.
*/
private void presetServiceNumber(Context context) {
Log.d(TAG, "presetServiceNumber");
startSimService(context, -1, SIMServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS);
}
**Android N: **
Path: alps\packages\apps\Contacts\SimProcessor\src\com\mediatek\SimProcessor
添加如下方法:
/**
- when boot complete,preset the service number directly.
*/
private void presetServiceNumber(Context context) {
Log.d(TAG, "presetServiceNumber");
startSimService(context, -1, SimServiceUtils.SERVICE_WORK_IMPORT_PRESET_CONTACTS);
}
5. 修改BootCmpReceiver.java
**Before Android N: **
Path: alps\packages\apps\Contacts\src\com\mediatek\contacts\simservice
在onReceive()方法
public void onReceive(Context context, Intent intent) {
... ...
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// fix ALPS01003520,when boot complete,remove the contacts if the
// card of a slot has been removed
if (!isPhbReady()) {
processBootComplete(context);
}
}
... ...
}
修改为:
public void onReceive(Context context, Intent intent) {
... ...
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
// fix ALPS01003520,when boot complete,remove the contacts if the
// card of a slot has been removed
if (!isPhbReady()) {
processBootComplete(context);
}// [START] add for Preset service number presetServiceNumber(context); // [END] } ... ...
}
Android N:
Path: alps\packages\apps\Contacts\SimProcessor\src\com\mediatek\SimProcessor
在onReceive()方法
public void onReceive(Context context, Intent intent) {
... ...
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
if (!isPhbReady()) {
processBootComplete(context);
} else {
processDupSimContacts(context);
}
}
... ...
}
修改为:
public void onReceive(Context context, Intent intent) {
... ...
} else if (Intent.ACTION_BOOT_COMPLETED.equals(action)) {
if (!isPhbReady()) {
processBootComplete(context);
} else {
processDupSimContacts(context);
}// [START] add for Preset service number presetServiceNumber(context); // [END] } ... ...
}
Part Two
**1. **
Before Android N:
File:DefaultContactListAdapter.java
Path: alps\packages\apps\ContactsCommon\src\com\android\contacts\common\list
Android N:
File:ContactsCommonListUtils.java
Path: alps\packages\apps\ContactsCommon\src\com\mediatek\contacts\util
configureOnlyShowPhoneContactsSelection函数中如下语句:
selection.append(Contacts.INDICATE_PHONE_SIM + "= ?");selectionArgs.add("-1");
之后增加下面的代码
selection.append(" AND " + RawContacts.IS_SDN_CONTACT + " > -2");
**2. File:Contact.java **
Path: alps\packages\apps\ContactsCommon\src\com\android\contacts\common\model
增加如下函数:
//add for Preset Contacts
public boolean isReadOnlyContact() {
return mIsSdnContact == -2;
}
**3. **
**Before Android N: **
File:ContactLoaderFragment.java
Path:alps\packages\apps\contacts\src\com\android\contacts\detail
将isContactEditable函数修改为:
public boolean isContactEditable() {
// return mContactData != null && !mContactData.isDirectoryEntry() && !mContactData.isSdnContacts() && !mContactData.isInternationalDialNumber(); // before
return mContactData != null && !mContactData.isDirectoryEntry() && !mContactData.isSdnContacts() && !mContactData.isInternationalDialNumber() && !mContactData.isReadOnlyContact() ; // after
}
** Android N:**
File:QuickContactActivity.java
Path:alps\packages\apps\contacts\src\com\android\contacts\quickcontact
将isContactEditable函数修改为:
private boolean isContactEditable() {
// return mContactData != null && !mContactData.isDirectoryEntry() && !mContactData.isSdnContacts() ; // before
return mContactData != null && !mContactData.isDirectoryEntry() && !mContactData.isSdnContacts() && !mContactData.isReadOnlyContact() ; // after
}
4.
File:ContactEntryListAdapter.java
Path:alps\packages\apps\contactscommon\src\com\android\contacts\common\list
在文件最后增加以下代码:
public boolean showReadOnlyContact = true;public void setShowReadOnlyContact(boolean canDelete) {
showReadOnlyContact = canDelete;
}
**5. **
File:ContactEntryListFragment.java
Path:alps\packages\apps\contactscommon\src\com\android\contacts\common\list
添加代码:
protected boolean isInstanceOfContactsMultiDeletionFragment(){
return false;
}
在onCreateLoader函数中,倒数第二句
mAdapter.configureLoader(loader, directoryId);
之前增加语句:
mAdapter.setShowReadOnlyContact(isInstanceOfContactsMultiDeletionFragment() ? false : true);
mAdapter.configureLoader(loader, directoryId);
6.
Before Android M:
File: ContactsMultiDeletionFragment.java
Path:alps\packages\apps\Contacts\src\com\mediatek\contacts\list
添加代码:
protected boolean isInstanceOfContactsMultiDeletionFragment(){
return true;
}
[ Android M:
C File: packages/apps/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListAdapter.java
public boolean isSdnNumber(int position) {
boolean isSdnNumber = false;
int partition = getPartitionForPosition(position);
if (partition >= 0) {
// Save the old cursor position - the call to getItem() may modify the cursor
// position.
int offset = getCursor(partition).getPosition();
Cursor cursor = (Cursor) getItem(position);
if (cursor != null) {
// [START]
// Before
// isSdnNumber = cursor.getInt(ContactQuery.IS_SDN_CONTACT) == 1;
// After
final long isSdn = cursor.getInt(ContactQuery.IS_SDN_CONTACT);
if (isSdn == 1 || isSdn == -2) {
isSdnNumber = true;
}
Log.d(TAG, "[isSdnNumber] isSdn = " + isSdn + ",isSdnNumber = " + isSdnNumber); //add log
// [END]// Restore the old cursor position. cursor.moveToPosition(offset); } } return isSdnNumber;
}
Android N:
C File: packages/apps/ContactsCommon/src/com/android/contacts/common/list/ContactEntryListAdapter.java
public boolean isSdnNumber(int position) {
boolean isSdnNumber = false;
int partition = getPartitionForPosition(position);
if (partition >= 0) {
// Save the old cursor position - the call to getItem() may modify the cursor
// position.
int offset = getCursor(partition).getPosition();
Cursor cursor = (Cursor) getItem(position);
if (cursor != null) {
// [START]
// Before
// isSdnNumber = cursor.getInt(cursor.getColumnIndex(Contacts.IS_SDN_CONTACT)) == 1;
// After
final long isSdn = cursor.getInt(cursor.getColumnIndex(Contacts.IS_SDN_CONTACT));
if (isSdn == 1 || isSdn == -2) {
isSdnNumber = true;
}
Log.d(TAG, "[isSdnNumber] isSdn = " + isSdn + ",isSdnNumber = " + isSdnNumber); //add log
// [END]// Restore the old cursor position. cursor.moveToPosition(offset); } } return isSdnNumber;
}
** 7.**
** Before Android M:**
File:MultiContactsBasePickerAdapter.java
Path:alps\packages\apps\contacts\src\com\mediatek\contacts\list
** After Android M:**
File:MultiBasePickerAdapter.java
Path:alps\packages\apps\contacts\src\com\mediatek\contacts\list
在configureSelection函数最后的语句
loader.setSelection(selection.toString());
之前增加语句:
if (!showReadOnlyContact ) {
selection.append(" AND " + Contacts.IS_SDN_CONTACT + "=0");
}loader.setSelection(selection.toString());
8.File:AggregationSuggestionEngine.java
Path:alps\packages\apps\contacts\src\com\android\contacts\editor
在loadAggregationSuggestions函数 在语句:
sb.append(" AND " + Contacts.INDICATE_PHONE_SIM + "=-1");
之后添加:
sb.append(" AND " + Contacts.IS_SDN_CONTACT + "!=-2");
9.
File:JoinContactListAdapter.java
Path:packages\apps\contacts\src\com\android\contacts\list 函数:public void configureLoader(CursorLoader cursorLoader, long directoryId)
Before Android N:
将:
loader.setSelection(Contacts._ID + "!=?"+" AND " + Contacts.INDICATE_PHONE_SIM + "=-1");
修改为:
loader.setSelection(Contacts._ID + "!=?"+" AND " + Contacts.INDICATE_PHONE_SIM + "=-1" + " AND " + Contacts.IS_SDN_CONTACT + "!=-2");
Android N:
将:
if (ContactsPortableUtils.MTK_PHONE_BOOK_SUPPORT) {
loader.setSelection(Contacts._ID + "!=?" + " AND " + Contacts.INDICATE_PHONE_SIM
+ "=-1");
} else {
loader.setSelection(Contacts._ID + "!=?");
}
修改为:
if (ContactsPortableUtils.MTK_PHONE_BOOK_SUPPORT) {
loader.setSelection(Contacts._ID + "!=?" + " AND " + Contacts.INDICATE_PHONE_SIM
+ "=-1" + " AND " + Contacts.IS_SDN_CONTACT + "!=-2");
} else {
loader.setSelection(Contacts._ID + "!=?" + " AND " + Contacts.IS_SDN_CONTACT + "!=-2");
}
10.
Android N:
File: MultiSelectEntryContactListAdapter
Path: packages\apps\contacts\src\com\android\contacts\list
语句:
if (ContactsPortableUtils.MTK_PHONE_BOOK_SUPPORT
&& cursor.getInt(
cursor.getColumnIndex(ContactsContract.Contacts.IS_SDN_CONTACT)) == 1) {
view.setClickable(false);
view.hideCheckBox();
return;
}
修改为:
if ((ContactsPortableUtils.MTK_PHONE_BOOK_SUPPORT
&& cursor.getInt(cursor.getColumnIndex(
ContactsContract.Contacts.IS_SDN_CONTACT)) == 1)
|| cursor.getInt(cursor.getColumnIndex(
ContactsContract.Contacts.IS_SDN_CONTACT)) == -2) {
view.setClickable(false);
view.hideCheckBox();
return;
}
//End for Test