New contact creating rather than updating existing contact

梦想的初衷 提交于 2019-12-18 07:05:45

问题


I am integrating my app with android default Contacts application.I would like to show an option "xyz using MyApp" inside every Contacts Detail.I am able to see my app in Accounts Section with an option to sync Contacts but still my app not merging with existing contacts but instead creating a new contact and merging in it.

performSync() method

private static void addContact(ContentResolver contentResolver,int name, int phoneNumber) {
    Log.i("XYZ", "Adding contact: " + name);
    ArrayList<ContentProviderOperation> operationList = new ArrayList<ContentProviderOperation>();

    //Create our RawContact
    ContentProviderOperation.Builder builder = ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI);
    builder.withValue(ContactsContract.RawContacts.ACCOUNT_NAME, name);
    builder.withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, "com.example.xyz.myapplication");
    builder.withValue(ContactsContract.RawContacts.SYNC1, phoneNumber);
    operationList.add(builder.build());

    //Create a Data record of common type 'StructuredName' for our RawContact
    builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
    builder.withValueBackReference(ContactsContract.CommonDataKinds.StructuredName.RAW_CONTACT_ID, 0);
    builder.withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE);
    builder.withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, name);
    operationList.add(builder.build());

    //Create a Data record of custom type "vnd.android.cursor.item/vnd.com.example.xyz.myapplication.profile" to display a link to the Last.fm profile
    builder = ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI);
    builder.withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0);
    builder.withValue(ContactsContract.Data.MIMETYPE, "vnd.android.cursor.item/vnd.com.example.xyz.myapplication.profile");
    builder.withValue(ContactsContract.Data.DATA1, phoneNumber);
    builder.withValue(ContactsContract.Data.DATA2, "Last.fm Profile");
    builder.withValue(ContactsContract.Data.DATA3, "View profile");
    operationList.add(builder.build());

    try {
        contentResolver.applyBatch(ContactsContract.AUTHORITY, operationList);
    } catch (Exception e) {
        Log.e("XYZ", "Something went wrong during creation! " + e);
        e.printStackTrace();
    }
}

回答1:


in your addContact code you're missing the part that tells Contacts DB to join your new raw-contact into the existing contact, so that contact will now contain your raw-contact and your app-specific line will be shown when opening that contact in the contacts app.

Check this answer on how to join a RawContact into an existing Contact: why won't contacts aggregate?

You'll probably need to pass in some RawContact ID to your addContact method so it'll be able to join the two together.

UPDATE

Instead of applying the aggregation operation together with your RawContact insert operation, let's try to separate into two applyBatch calls, also, let's aggregate your new raw-contact with ALL existing raw-contacts, not just one of them. Try the following code, make sure you pass to it the existing contact-id (not raw-contact id) and your new raw-contact-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);
}

P.S.
don't open two duplicate questions, one is enough.

ANOTHER UPDATE

You seem to have some confusion about IDs.

There are Data IDs, RawContact IDs, and Contact IDs.

CommonDataKinds.Phone._ID will return a Data ID, identifying the specific row in the Data table in which that phone number is stored.

You can get a from the Phone table the other IDs as well, use: CommonDataKinds.Phone.RAW_CONTACT_ID CommonDataKinds.Phone.CONTACT_ID

You can read more here: https://stackoverflow.com/a/50084029/819355




回答2:


Try this here is the working code for me

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();
    }
}

Authenticator

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;
    }
}

Constants

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" />

manifest

<?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>

OUTPUT

UPDATE

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);
            }
        }
    }
}


来源:https://stackoverflow.com/questions/52905223/new-contact-creating-rather-than-updating-existing-contact

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!