创建新联系人,而不是更新现有联系人

时间:2018-10-20 11:36:05

标签: android service contacts android-syncadapter accountmanager

我正在将我的应用程序与android默认的联系人应用程序集成。我想在每个联系人详细信息中显示一个选项“ xyz using MyApp”。应用程序不与现有联系人合并,而是创建一个新联系人并在其中合并。

performSync()方法

data () {
    return {
        i: 0,
        socialEvents: [
            {name:'Musical show',location:'Colombo'},
            {name:"DJ Party",location:'New york'},
            {name:"Dinner dance",location:"Paris"}
        ]
    }
},
created () {
    setInterval (() =>{
        this.i++
    }, 15000)
},
computed: {
    displayingEvent () {
        return this.socialEvents[this.i % this.socialEvents.length]
    }
}

2 个答案:

答案 0 :(得分:3)

在您的addContact代码中,您缺少告诉Contacts DB将新的原始联系人加入现有联系人的部分,因此该联系人现在将包含您的原始联系人和您的应用-在通讯录应用中打开该联系人时会显示特定的行。

请查看有关如何将RawContact加入现有联系人why won't contacts aggregate?

的答案。

您可能需要将一些RawContact ID传递给addContact方法,以便能够将两者结合在一起。

更新

让我们尝试将您的新原始联系人与 ALL 聚集在一起,而不是将聚合操作与您的RawContact插入操作一起应用,我们尝试将其分为两个applyBatch调用>现有的原始联系人,而不仅仅是其中之一。 尝试以下代码,确保将现有的联系人ID (不是原始联系人ID)和新的原始联系人ID 传递给它。

private void joinIntoExistingContact(long existingContactId, long newRawContactId) {

    // get all existing raw-contact-ids that belong to the contact-id
    List<Long> existingRawIds = new ArrayList<>();
    Cursor cur = getContentResolver().query(RawContacts.CONTENT_URI, new String[] { RawContacts._ID }, RawContacts.CONTACT_ID + "=" + existingContactId, null, null);
    while (cur.moveToNext()) {
        existingRawIds.add(cur.getLong(0));
    }
    cur.close();
    Log.i("Join", "Found " + existingRawIds.size() + " raw-contact-ids");

    List<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();

    // go over all existing raw ids, and join with our new one
    for (Long existingRawId : existingRawIds) {
        Builder builder = ContentProviderOperation.newUpdate(AggregationExceptions.CONTENT_URI);
        builder.withValue(AggregationExceptions.TYPE, AggregationExceptions.TYPE_KEEP_TOGETHER);
        builder.withValue(AggregationExceptions.RAW_CONTACT_ID1, newRawContactId);
        builder.withValue(AggregationExceptions.RAW_CONTACT_ID2, existingRawId);
        ops.add(builder.build());
    }

    contentResolver.applyBatch(ContactsContract.AUTHORITY, ops);
}

PS
不要打开两个duplicate questions,一个就足够了。

另一个更新

您似乎对ID感到有些困惑。

Data个ID,RawContact个ID和Contact个ID。

CommonDataKinds.Phone._ID将返回一个Data ID,标识存储电话号码的数据表中的特定行。

您也可以从Phone表中获得其他ID,请使用: CommonDataKinds.Phone.RAW_CONTACT_ID CommonDataKinds.Phone.CONTACT_ID

您可以在此处了解更多信息: https://stackoverflow.com/a/50084029/819355

答案 1 :(得分:0)

在这里试试这对我来说是有效的代码

  

MainActivity

public class MainActivity extends AppCompatActivity {

    private ArrayList<String> mNames = new ArrayList<>();
    private ArrayList<String> mIDs = new ArrayList<>();
    private ArrayList<String> mNumbers = new ArrayList<>();

    @SuppressLint("StaticFieldLeak")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED &&
                ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
                        != PackageManager.PERMISSION_GRANTED) {

            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_EXTERNAL_STORAGE}, 123);

        } else {


//      Retrieve names from phone's contact list and save in mNames
            getContactDataBefore();

//      Apply changes to phone's contact list
            new AsyncTask<String, String, String>() {

                @Override
                protected String doInBackground(String... params) {
                    String name, number, id;
                    for (int i = 0; i < mIDs.size(); i++) {
//                    name = mNames.get(i);
                        id = mIDs.get(i);
                        number = mNumbers.get(i);
                        ContactsManager.addContact(MainActivity.this, new MyContact(id, number));
                    }
                    return null;
                }

                @Override
                protected void onPostExecute(String s) {
                    getContactDataAfter();
                }
            }.execute();
        }

    }

    private void getContactDataBefore() {
        int i = 0;

        // query all contact id's from device
        Cursor c1 = getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
                new String[]{ContactsContract.Contacts._ID}, null, null, null);

        if ((c1 != null) && c1.moveToFirst()) {


            do {
                mIDs.add(c1.getString(c1.getColumnIndexOrThrow(ContactsContract.Contacts._ID)));

                i++;
            } while (c1.moveToNext() && i < c1.getCount());

            c1.close();

        }

        getPhoneNumber();
    }

    private void getPhoneNumber(){


        for (String data:mIDs){

            Cursor cursor = getContentResolver().query(
                    ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                    null,
                    ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?",
                    new String[]{data}, null);

            while (cursor.moveToNext())
            {
                mNumbers.add(cursor.getString(cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER)));
            }

            cursor.close();
        }

    }
    /**
     * Method to fetch contacts after updation (for logging purposes)
     */
    private void getContactDataAfter() {
        Cursor c = getContentResolver()
                .query(ContactsContract.Contacts.CONTENT_URI, null, null, null, null);

        List<String> RIds = new ArrayList<>();
        mIDs = new ArrayList<>();
        mNumbers = new ArrayList<>();
        int i = 0;

        if (c != null && c.moveToFirst()) {
            do {
                mIDs.add(c.getString(c
                        .getColumnIndexOrThrow(ContactsContract.Contacts._ID)));
                mNames.add(c.getString(c
                        .getColumnIndexOrThrow(ContactsContract.Contacts.DISPLAY_NAME)));

                Cursor c2 = getContentResolver()
                        .query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI,
                                new String[]{ContactsContract.CommonDataKinds.Phone.NUMBER},
                                ContactsContract.CommonDataKinds.Phone.CONTACT_ID + "=?",
                                new String[]{mIDs.get(i)}, null);

                if (c2 != null && c2.moveToFirst()) {
                    do {
                        mNumbers.add(c2.getString(c2
                                .getColumnIndexOrThrow(ContactsContract.CommonDataKinds.Phone.NUMBER)));
                    } while (c2.moveToNext());
                    c2.close();
                }

                Cursor rawcontacts = getContentResolver()
                        .query(ContactsContract.RawContacts.CONTENT_URI,
                                new String[]{ContactsContract.RawContacts._ID},
                                ContactsContract.RawContacts.CONTACT_ID + "=?",
                                new String[]{mIDs.get(i)}, null);

                if (rawcontacts != null && rawcontacts.moveToFirst()) {
                    do {
                        RIds.add(rawcontacts.getString(rawcontacts
                                .getColumnIndexOrThrow(ContactsContract.RawContacts._ID)));
                    } while (rawcontacts.moveToNext());
                    rawcontacts.close();
                }

                i++;
            } while (c.moveToNext());
            c.close();
        }
    }
}
  

AuthenticatorActivity

public class AuthenticatorActivity extends AccountAuthenticatorActivity {

    private AccountManager mAccountManager;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_authenticator);


        Intent res = new Intent();
        res.putExtra(AccountManager.KEY_ACCOUNT_NAME, Constants.ACCOUNT_NAME);
        res.putExtra(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
        res.putExtra(AccountManager.KEY_AUTHTOKEN, Constants.ACCOUNT_TOKEN);
        Account account = new Account(Constants.ACCOUNT_NAME, Constants.ACCOUNT_TYPE);
        mAccountManager = AccountManager.get(this);
        mAccountManager.addAccountExplicitly(account, null, null);
//      mAccountManager.setAuthToken(account, Constants.AUTHTOKEN_TYPE_FULL_ACCESS, Constants.ACCOUNT_TOKEN);
        ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);
        setAccountAuthenticatorResult(res.getExtras());
        setResult(RESULT_OK, res);
        finish();
    }
}
  

SyncAdapter

public class SyncAdapter extends AbstractThreadedSyncAdapter {

    private Context mContext;

    public SyncAdapter(Context context, boolean autoInitialize) {
        super(context, autoInitialize);
        mContext = context;
    }

    @Override
    public void onPerformSync(Account account, Bundle extras, String authority,
                              ContentProviderClient provider, SyncResult syncResult) {
    }
}
  

SyncService

public class SyncService extends Service {

    private static final Object sSyncAdapterLock = new Object();
    private static SyncAdapter mSyncAdapter = null;

    @Override
    public void onCreate() {
        synchronized (sSyncAdapterLock){
            if(mSyncAdapter == null){
                mSyncAdapter = new SyncAdapter(getApplicationContext(),true);
            }
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return mSyncAdapter.getSyncAdapterBinder();
    }
}
  

AuthenticationService

public class AuthenticationService extends Service {

    private static final String TAG = "AuthenticationService";
    private Authenticator mAuthenticator;

    @Override
    public void onCreate() {
        if (android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE)) {
            android.util.Log.v(TAG, "SyncAdapter Authentication Service started.");
        }
        mAuthenticator = new Authenticator(this);
    }

    @Override
    public void onDestroy() {
        if (android.util.Log.isLoggable(TAG, android.util.Log.VERBOSE)) {
            Log.v(TAG, "SyncAdapter Authentication Service stopped.");
        }
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        if (Log.isLoggable(TAG, Log.VERBOSE)) {
            Log.v(TAG, "getBinder()...  returning the AccountAuthenticator binder for intent "
                    + intent);
        }
        return mAuthenticator.getIBinder();
    }
}
  

身份验证者

public class Authenticator extends AbstractAccountAuthenticator {

    private final Context mContext;

    public Authenticator(Context context) {
        super(context);
        mContext = context;
    }

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
        return null;
    }

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
        final Intent intent = new Intent(mContext, AuthenticatorActivity.class);
        intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);

        final Bundle bundle = new Bundle();
        bundle.putParcelable(AccountManager.KEY_INTENT, intent);
        return bundle;
    }

    @Override
    public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        final Bundle result = new Bundle();
        result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        result.putString(AccountManager.KEY_ACCOUNT_TYPE, Constants.ACCOUNT_TYPE);
        return result;
    }

    @Override
    public String getAuthTokenLabel(String authTokenType) {
        return null;
    }

    @Override
    public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
        return null;
    }

    @Override
    public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
        return null;
    }
}
  

常量

public class Constants {

    public static final String ACCOUNT_TYPE = "com.example.ajay.contacts_4";
    public static final String ACCOUNT_NAME = "Nilesh_Rathod";
    public static final String ACCOUNT_TOKEN = "733N";
}
  

ContactsManager

public class ContactsManager {

    private static String MIMETYPE = "vnd.android.cursor.item/com.example.ajay.contacts_4";


    public static void addContact(Context context, MyContact contact) {
        ContentResolver resolver = context.getContentResolver();
        boolean mHasAccount = isAlreadyRegistered(resolver, contact.Id);

        if (mHasAccount) {
            Log.I("Account is Exist");
        } else {

            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();

            // insert account name and account type
            ops.add(ContentProviderOperation
                    .newInsert(addCallerIsSyncAdapterParameter(ContactsContract.RawContacts.CONTENT_URI, true))
                    .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, Constants.ACCOUNT_NAME)
                    .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, Constants.ACCOUNT_TYPE)
                    .withValue(ContactsContract.RawContacts.AGGREGATION_MODE,
                            ContactsContract.RawContacts.AGGREGATION_MODE_DEFAULT)
                    .build());

            // insert contact number
            ops.add(ContentProviderOperation
                    .newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true))
                    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                    .withValue(ContactsContract.Data.MIMETYPE, CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
                    .withValue(CommonDataKinds.Phone.NUMBER, contact.number)
                    .build());


            // insert mime-type data
            ops.add(ContentProviderOperation
                    .newInsert(addCallerIsSyncAdapterParameter(ContactsContract.Data.CONTENT_URI, true))
                    .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0)
                    .withValue(ContactsContract.Data.MIMETYPE, MIMETYPE)
                    .withValue(ContactsContract.Data.DATA1, 12345)
                    .withValue(ContactsContract.Data.DATA2, "Nilesh")
                    .withValue(ContactsContract.Data.DATA3, "ContactsDemo")
                    .build());


        }
    }

    /**
     * Check if contact is already registered with app
     */
    public static boolean isAlreadyRegistered(ContentResolver resolver, String id) {

        boolean isRegistered = false;
        List<String> str = new ArrayList<>();

        //query raw contact id's from the contact id
        Cursor c = resolver.query(RawContacts.CONTENT_URI, new String[]{RawContacts._ID},
                RawContacts.CONTACT_ID + "=?",
                new String[]{id}, null);

        //fetch all raw contact id's and save them in a list of string
        if (c != null && c.moveToFirst()) {
            do {
                str.add(c.getString(c.getColumnIndexOrThrow(RawContacts._ID)));
            } while (c.moveToNext());
            c.close();
        }

        //query account types and check the account type for each raw contact id
        for (int i = 0; i < str.size(); i++) {
            Cursor c1 = resolver.query(RawContacts.CONTENT_URI, new String[]{RawContacts.ACCOUNT_TYPE},
                    RawContacts._ID + "=?",
                    new String[]{str.get(i)}, null);

            if (c1 != null) {
                c1.moveToFirst();
                String accType = c1.getString(c1.getColumnIndexOrThrow(RawContacts.ACCOUNT_TYPE));
                if (accType != null && accType.equals("com.example.ajay.contacts_4")) {
                    isRegistered = true;
                    break;
                }
                c1.close();
            }
        }

        return isRegistered;
    }

    /**
     * Check for sync call
     */
    private static Uri addCallerIsSyncAdapterParameter(Uri uri, boolean isSyncOperation) {
        if (isSyncOperation) {
            return uri.buildUpon()
                    .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true")
                    .build();
        }
        return uri;
    }


}
  

authenticator.xml

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.example.ajay.contacts_4"
    android:icon="@drawable/icon"
    android:smallIcon="@drawable/icon"
    android:label="@string/app_name" />
  

contacts.xml

<?xml version="1.0" encoding="utf-8"?>
<ContactsSource
    xmlns:android="http://schemas.android.com/apk/res/android">
    <ContactsDataKind
        android:mimeType="vnd.android.cursor.item/com.example.ajay.contacts_4"
        android:icon="@drawable/icon"
        android:summaryColumn="data2"
        android:detailColumn="data3" />
</ContactsSource>
  

syncadapter.xml

<?xml version="1.0" encoding="utf-8"?>
<sync-adapter xmlns:android="http://schemas.android.com/apk/res/android"
    android:contentAuthority="com.android.contacts"
    android:accountType="com.example.ajay.contacts_4"
    android:supportsUploading="false"
    android:userVisible="true" />
  

清单

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="neel.com.contactssyncingapp">

    <uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
    <uses-permission android:name="android.permission.WRITE_CONTACTS" />
    <uses-permission android:name="android.permission.READ_CONTACTS" />
    <uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
    <uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.READ_SYNC_SETTINGS" />
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">

        <service android:name=".utils.AuthenticationService" >
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator" />
            </intent-filter>

            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator" />
        </service>

        <service android:name=".sync.SyncService" >
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>

            <meta-data
                android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter" />
            <meta-data
                android:name="android.provider.CONTACTS_STRUCTURE"
                android:resource="@xml/contacts" />
        </service>

        <activity android:name=".activity.MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>
        </activity>


        <activity
            android:name=".activity.ContactActivity"
            android:label="ContactActivity"
            android:screenOrientation="portrait"
            android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />
                <category android:name="android.intent.category.DEFAULT" />
                <data android:mimeType="vnd.android.cursor.item/com.example.ajay.contacts_4" />
            </intent-filter>
        </activity>


        <activity android:name=".activity.AuthenticatorActivity" />
    </application>

</manifest>

输出

enter image description here

更新

public class ContactActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contact);


        Uri intentData = getIntent().getData();
        if (!Uri.EMPTY.equals(intentData))
        {
            Cursor cursor = getContentResolver().query(intentData, null, null, null, null);
            if (cursor.moveToNext())
            {
                String username = cursor.getString(cursor.getColumnIndex("data2"));
                String number = cursor.getString(cursor.getColumnIndex("data3"));

                Log.e("USER_NAME",username);
                Log.e("USER_NUMBER",number);
            }
        }
    }
}