New contact creating rather than updating existing contact

后端 未结 2 1276
野的像风
野的像风 2020-12-19 23:28

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

相关标签:
2条回答
  • 2020-12-20 00:12

    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

    0 讨论(0)
  • 2020-12-20 00:15

    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);
                }
            }
        }
    }
    
    0 讨论(0)
提交回复
热议问题