如何让我的syncadapter帐户显示在Google通讯录应用中?

时间:2017-08-24 14:57:46

标签: android android-contacts

我正在尝试让我的Android应用程序的自定义联系人显示在来自Play商店的Google(最新)联系人应用程序中,但无法让我的用户的帐户出现在副驾驶架上Contacts app快速帐户切换器列表的抽屉。我需要做些什么才能让我的帐户出现在此列表中?

我已实施以下内容:

我的Android Manifest有以下条目:

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

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


    <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="com.test.contactsynctest.ContactSyncActivity"
            android:label="@string/app_name"
            android:theme="@style/AppTheme.NoActionBar">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

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

        <service
            android:name="com.test.contactsynctest.TestSyncService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.content.SyncAdapter"/>
            </intent-filter>
            <meta-data
                android:name="android.content.SyncAdapter"
                android:resource="@xml/syncadapter_contacts"/>
        </service>
        <service
            android:name="com.test.contactsynctest.AuthenticatorService"
            android:exported="true">
            <intent-filter>
                <action android:name="android.accounts.AccountAuthenticator"/>
            </intent-filter>
            <meta-data
                android:name="android.accounts.AccountAuthenticator"
                android:resource="@xml/authenticator"/>
        </service>
    </application>

</manifest>

AccountAuthenticator

package com.test.contactsynctest;

import android.accounts.AbstractAccountAuthenticator;
import android.accounts.Account;
import android.accounts.AccountAuthenticatorResponse;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.content.ContentResolver;
import android.content.Context;
import android.os.Bundle;
import android.provider.ContactsContract;

public class AccountAuthenticator extends AbstractAccountAuthenticator {

    private static boolean removalAllowed = false;
    private Context context;

    public static final String ACCOUNT_TYPE = "com.test.contactsynctest";
    public static final String USERID = "bob@test.com";

    public AccountAuthenticator(Context context) {
        super(context);
        this.context = context;
    }

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse response,
                             String accountType, String authTokenType,
                             String[] requiredFeatures, Bundle options)
            throws NetworkErrorException {

        Account account = new Account(USERID, ACCOUNT_TYPE);
        AccountManager.get(context).addAccountExplicitly(account, null, null);

        ContentResolver.setIsSyncable(account, ContactsContract.AUTHORITY, 1);
        ContentResolver.setSyncAutomatically(account, ContactsContract.AUTHORITY, true);

        Bundle b = new Bundle();
        b.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
        b.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
        return b;
    }

   // The following are required but all return null or a default value

    @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 confirmCredentials(AccountAuthenticatorResponse arg0,
                                     Account arg1, Bundle arg2) {
        return null;
    }

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

    @Override
    public Bundle getAccountRemovalAllowed(AccountAuthenticatorResponse response, Account account) {
        Bundle b = new Bundle();
        b.putBoolean(AccountManager.KEY_BOOLEAN_RESULT, removalAllowed);
        return b;
    }
}

AuthenticatorService

package com.test.contactsynctest;

import android.accounts.AccountManager;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;

public class AuthenticatorService extends Service {

    private static AccountAuthenticator auth = null;

    public AuthenticatorService() {
        super();
    }

    @Override
    public IBinder onBind(Intent intent) {

        if (intent.getAction().equals(AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
            if (auth == null) {
                auth = new AccountAuthenticator(this);
            }
            return auth.getIBinder();
        }
        return null;
    }
}

ContactSyncActivity

package com.test.contactsynctest;

import android.Manifest;
import android.accounts.AccountManager;
import android.content.ContentProviderOperation;
import android.content.OperationApplicationException;
import android.content.pm.PackageManager;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.ContactsContract;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.Calendar;

public class ContactSyncActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_contacts_export);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                writeContact("Sally" + Calendar.getInstance().getTime().toString(), "123-456-789");
                Toast.makeText(ContactSyncActivity.this, "Contact created", Toast.LENGTH_LONG).show();
            }
        });

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS)
                != PackageManager.PERMISSION_GRANTED) {

                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.WRITE_CONTACTS}, 2);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.menu_contacts_export, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {

        int id = item.getItemId();

        //noinspection SimplifiableIfStatement
        if (id == R.id.action_settings) {
            AccountManager.get(this).addAccount(AccountAuthenticator.ACCOUNT_TYPE, null, null, null, null, null, null);
            Toast.makeText(ContactSyncActivity.this, "Account registered", Toast.LENGTH_LONG).show();
            return true;
        }

        return super.onOptionsItemSelected(item);
    }

    private void writeContact(String displayName, String number) {
        ArrayList contentProviderOperations = new ArrayList();
        //insert raw contact using RawContacts.CONTENT_URI
        contentProviderOperations.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI)
                .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE).withValue(ContactsContract.RawContacts.ACCOUNT_NAME, AccountAuthenticator.USERID).build());
        //insert contact display name using Data.CONTENT_URI
        contentProviderOperations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Contacts.Data.RAW_CONTACT_ID, 0).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE)
                .withValue(ContactsContract.CommonDataKinds.StructuredName.DISPLAY_NAME, displayName).build());
        //insert mobile number using Data.CONTENT_URI
        contentProviderOperations.add(ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI)
                .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, 0).withValue(ContactsContract.Data.MIMETYPE, ContactsContract.CommonDataKinds.Phone.CONTENT_ITEM_TYPE)
                .withValue(ContactsContract.CommonDataKinds.Phone.NUMBER, number).withValue(ContactsContract.CommonDataKinds.Phone.TYPE, ContactsContract.CommonDataKinds.Phone.TYPE_MOBILE).build());
        try {
            getApplicationContext().getContentResolver().
                    applyBatch(ContactsContract.AUTHORITY, contentProviderOperations);
        } catch (RemoteException e) {
            e.printStackTrace();
        } catch (OperationApplicationException e) {
            e.printStackTrace();
        }
    }
}

TestSyncService

package com.test.contactsynctest;

import android.accounts.Account;
import android.app.Service;
import android.content.AbstractThreadedSyncAdapter;
import android.content.ContentProviderClient;
import android.content.Context;
import android.content.Intent;
import android.content.SyncResult;
import android.os.Bundle;
import android.os.IBinder;

public class TestSyncService extends Service {

    private static SyncAdapterImpl syncAdapter = null;

    @Override
    public IBinder onBind(Intent intent) {
        if (syncAdapter == null) {
            syncAdapter = new SyncAdapterImpl(this);
        }
        return syncAdapter.getSyncAdapterBinder();
    }

    private static class SyncAdapterImpl extends AbstractThreadedSyncAdapter {

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

        @Override
        public void onPerformSync(Account account, Bundle extra, String authority, ContentProviderClient provider, SyncResult result) {
            // nothing needed here for this test
        }

    }
}

在我的xml目录中,我有以下文件

帐户认证器          

syncadapter_contacts.xml

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

account_preferences.xml

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

</PreferenceScreen>

我测试过其他一些应用,看到Android 7和Android 8之间的行为不一致 - 例如,Android 7.x上的雅虎应用程序没有出现在左侧抽屉快速帐户切换器列表中,而确切相同版本的雅虎应用程序和在Android 8.0上运行的联系人应用程序确实在列表中显示了雅虎帐户。所有Gmail帐户似乎始终显示在“通讯录”应用帐户切换器中,但其他帐户会有所不同。

0 个答案:

没有答案