Wednesday, 24 October 2018

App added in Accounts with Contact sync option but not showing in Contact Detail screen

I want to integrate my app with android default Contacts application just like Whatsapp,Paytm and TrueCaller is doing as shown in below screenshot.I would like to show an option "Recharge using MyApp" inside every Contacts Detail , clicking on which should open my app.I used below code through which I am able to see my app in Accounts Section with an option to sync Contacts but still my app is not showing in Contact detail page.The code adds a new contact rather than updating an existing contact.

I have added code for the implementation on github at this link.

Also i was following these tutorials- LINK1 LINK2

enter image description here

AccountAuthenticatorService

public class AccountAuthenticatorService extends Service {
     private static final String TAG = "AccountAuthenticatorService";
     private static AccountAuthenticatorImpl sAccountAuthenticator = null;

     public AccountAuthenticatorService() {
      super();
     }

     public IBinder onBind(Intent intent) {
      IBinder ret = null;
      if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT))
       ret = getAuthenticator().getIBinder();
      return ret;
     }

     private AccountAuthenticatorImpl getAuthenticator() {
      if (sAccountAuthenticator == null)
       sAccountAuthenticator = new AccountAuthenticatorImpl(this);
      return sAccountAuthenticator;
     }

     private static class AccountAuthenticatorImpl extends AbstractAccountAuthenticator {
      private Context mContext;

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

      /*
       *  The user has requested to add a new account to the system.  We return an intent that will launch our login screen if the user has not logged in yet,
       *  otherwise our activity will just pass the user's credentials on to the account manager.
       */
      @Override
      public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options)
        throws NetworkErrorException {
       Bundle reply = new Bundle();

       Intent i = new Intent(mContext, MainActivity.class);
       reply.putParcelable(AccountManager.KEY_INTENT, i);

       return reply;
      }

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

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

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

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

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

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

LoginActivity

Account account = new Account("9958154991", "com.example.bhuvnesh.myapplication");
        AccountManager am = AccountManager.get(this);
        boolean accountCreated = am.addAccountExplicitly(account, null, null);

authenticator.xml

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
    android:accountType="com.example.bhuvnesh.myapplication"
    android:icon="@mipmap/ic_launcher"
    android:smallIcon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:accountPreferences="@xml/account_preference"/>

account_preference.xml

<?xml version="1.0" encoding="utf-8"?>

<PreferenceScreen
    xmlns:android="http://schemas.android.com/apk/res/android">
    <PreferenceCategory
        android:title="General Settings" />

    <PreferenceScreen
        android:key="account_settings"
        android:title="Account Settings"
        android:summary="Sync frequency, notifications, etc.">
        <intent
            android:action="fm.last.android.activity.Preferences.ACCOUNT_SETUP"
            android:targetPackage="fm.last.android"
            android:targetClass="fm.last.android.activity.Preferences" />
    </PreferenceScreen>
</PreferenceScreen>

ContactsSyncAdapterService.class

public class ContactsSyncAdapterService extends Service {
     private static final String TAG = "ContactsSyncAdapter";
     private static SyncAdapterImpl sSyncAdapter = null;
     private static ContentResolver mContentResolver = null;

     public ContactsSyncAdapterService() {
      super();
     }

     private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {
      private Context mContext;

      public SyncAdapterImpl(Context context) {
       super(context, true);
       mContext = context;
      }

      @Override
      public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
       try {
        ContactsSyncAdapterService.performSync(mContext, account, extras, authority, provider, syncResult);
       } catch (OperationCanceledException e) {
       }
      }
     }

     @Override
     public IBinder onBind(Intent intent) {
      IBinder ret = null;
      ret = getSyncAdapter().getSyncAdapterBinder();
      return ret;
     }

     private SyncAdapterImpl getSyncAdapter() {
      if (sSyncAdapter == null)
       sSyncAdapter = new SyncAdapterImpl(this);
      return sSyncAdapter;
     }

     private static void performSync(Context context, Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult)
       throws OperationCanceledException {
      mContentResolver = context.getContentResolver();
      Log.i(TAG, "performSync: " + account.toString());
      //This is where the magic will happen!
     }
    }

sync_contacts.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.bhuvnesh.myapplication"/>

contacts.xml

<?xml version="1.0" encoding="utf-8"?>
<ContactsSource xmlns:android="http://schemas.android.com/apk/res/android">
    <ContactsDataKind
        android:icon="@mipmap/ic_launcher"
        android:mimeType="vnd.android.cursor.item/vnd.com.example.bhuvnesh.myapplication.profile"
        android:summaryColumn="data2"
        android:detailColumn="data3"
        android:detailSocialSummary="true" />
</ContactsSource>

performSync() method

    private void insertIconinContacts() {
        Cursor cursor = null;
        String idContact , account;
        try {
            cursor = getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, null, null, null, null);
            int contactIdIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone._ID);
            int nameIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DISPLAY_NAME);
            int phoneNumberIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.NUMBER);
            int photoIdIdx = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.PHOTO_ID);
            int acc = cursor.getColumnIndex(ContactsContract.CommonDataKinds.Phone.DATA);
            cursor.moveToFirst();
            do {
                idContact = cursor.getString(contactIdIdx);
                account = cursor.getString(acc);
//                updateIMContactField(getContentResolver(), idContact, chooseClass);
                addContact(getContentResolver(),nameIdx,phoneNumberIdx);
                //...
            } while (cursor.moveToNext());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (cursor != null) {
                cursor.close();
            }
        }
    }
private static void addContact(ContentResolver contentResolver,int name, int phoneNumber) {
    Log.i("BHUVNESH", "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.bhuvnesh.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.bhuvnesh.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.bhuvnesh.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("BHUVNESH", "Something went wrong during creation! " + e);
        e.printStackTrace();
    }
}

AndroidManifest

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

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

    <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">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service android:name=".service.AccountAuthenticatorService"
            android:exported="true" android:process=":auth">
            <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=".service.ContactsSyncAdapterService"
            android:exported="true" android:process=":contacts">
            <intent-filter>
                <action android:name="android.content.SyncAdapter" />
            </intent-filter>
            <meta-data android:name="android.content.SyncAdapter"
                android:resource="@xml/sync_contacts" />
            <meta-data android:name="android.provider.CONTACTS_STRUCTURE"
                android:resource="@xml/contacts" />

        </service>


    </application>

</manifest>

UPDATE

As per @marmor answer ,I also tried adding below code inside addContact() but app not showing in contact detail screen

builder = ContentProviderOperation.newUpdate(ContactsContract.AggregationExceptions.CONTENT_URI);
        builder.withValue(ContactsContract.AggregationExceptions.TYPE, ContactsContract.AggregationExceptions.TYPE_KEEP_TOGETHER);
        builder.withValue(ContactsContract.AggregationExceptions.RAW_CONTACT_ID1, id);
        builder.withValueBackReference(ContactsContract.AggregationExceptions.RAW_CONTACT_ID2, 0);

        operationList.add(builder.build());



from App added in Accounts with Contact sync option but not showing in Contact Detail screen

No comments:

Post a Comment