如何以编程方式在android中添加自定义帐户?

Posted

技术标签:

【中文标题】如何以编程方式在android中添加自定义帐户?【英文标题】:How to add programmatically a custom account in android? 【发布时间】:2014-08-05 07:25:05 【问题描述】:

我正在尝试为我的应用程序创建一个帐户,在那里我可以将我的联系人与我的帐户联系起来,例如 facebook、viber、whatsapp 等。我希望我的帐户也可以在设置的帐户部分中显示。有任何想法吗?我用谷歌搜索了很多,但找不到正确的答案从哪里开始。请帮忙。 我尝试创建的帐户如下。这导致我出错。

Account account = new Account("Title", "com.package.nom");
               String password = "password";

               AccountManager accountManager =
                       (AccountManager) MainPanel.this.getSystemService(
                               ACCOUNT_SERVICE);
               accountManager.addAccountExplicitly(account, password, null);

【问题讨论】:

这里有编程问题吗?听起来这是一个如何使用android的问题。 我想以编程方式完成整个事情。我尝试过的就是上面提到的。谢谢 android中还有一个账号管理库here。 【参考方案1】:

我为此写了一个library,它可以让你从管理android帐户所需的琐事中解脱出来,例如定义绑定服务、身份验证器xml等。只需5个简单的步骤即可:

第 1 步

将此添加到应用程序的 build.gradle 的依赖项中:

compile 'com.digigene.android:account-authenticator:1.3.0'

第 2 步

将您的身份验证帐户类型定义为strings.xml 中的字符串:

<string name="auth_account_type">DigiGene</string>

将“DigiGene”替换为您自己的帐户类型。这是 this screenshot 中的 Android 帐户中显示的内容。

第 3 步

设计用于注册用户的注册布局(例如this image):

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.digigene.authenticatortest.MainActivity">

    <EditText
        android:id="@+id/account_name"
        android:layout_
        android:layout_
        android:gravity="center_horizontal"
        android:hint="User Name"
        />

    <EditText
        android:id="@+id/password"
        android:layout_
        android:layout_
        android:layout_below="@+id/account_name"
        android:gravity="center_horizontal"
        android:hint="Password"
        android:inputType="textPassword"
        />

    <Button
        android:id="@+id/register"
        android:layout_
        android:layout_
        android:layout_below="@+id/password"
        android:text="register"
        android:onClick="startAuthentication"/>

</RelativeLayout>

并使用以下代码创建一个新类,例如 MyRegistrationActivity.java

import com.digigene.accountauthenticator.activity.RegistrationActivity;

public class MyRegistrationActivity extends RegistrationActivity 
    private EditText accountNameEditText, passwordEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.registration_layout);
        accountNameEditText = (EditText) findViewById(R.id.account_name);
        passwordEditText = (EditText) findViewById(R.id.password);
    

    public void startAuthentication(View view) 
        register(accountNameEditText.getText().toString(), passwordEditText.getText().toString(),
                null, null);
    

第 4 步

如here:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.digigene.authenticatortest.MainActivity">

    <EditText
        android:id="@+id/account_name"
        android:layout_
        android:layout_
        android:gravity="center_horizontal"
        android:hint="User Name"
        />

    <Button
        android:id="@+id/register"
        android:layout_
        android:layout_
        android:layout_below="@+id/account_name"
        android:text="Sign in"
        android:onClick="signIn"/>

    <Button
        android:id="@+id/add"
        android:layout_
        android:layout_
        android:layout_below="@+id/register"
        android:text="Add user"
        android:onClick="addUser"/>

</RelativeLayout>

此布局使用以下类:

import com.digigene.accountauthenticator.AuthenticatorManager;

public class MainActivity extends Activity 
    EditText accountNameEditText;

    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        accountNameEditText = (EditText) findViewById(R.id.account_name);
    

    public void signIn(View view) 
        AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this,
                getString(R.string.auth_account_type), this, MyRegistrationActivity.class,
                MyInterfaceImplementation.class);
        String authTokenType = "REGULAR_USER";
        AuthenticatorManager.authenticatorManager = authenticatorManager;
        authenticatorManager.getAccessToken(accountNameEditText.getText().toString(),
                authTokenType, null);
    

    public void addUser(View view) 
        AuthenticatorManager authenticatorManager = new AuthenticatorManager(MainActivity.this,
                getString(R.string.auth_account_type), this, MyRegistrationActivity.class,
                MyInterfaceImplementation.class);
        String authTokenType = "REGULAR_USER";
        AuthenticatorManager.authenticatorManager = authenticatorManager;
        authenticatorManager.addAccount(authTokenType, null, null);
    

第 5 步

这是连接到服务器以进行注册和登录所需的方法以及之后实施的最后一步。下面,与真实案例相反,模拟服务器连接,只是为了演示库的功能。您可以将以下实现替换为您自己的真实实现。

import com.digigene.accountauthenticator.AbstractInterfaceImplementation;
import com.digigene.accountauthenticator.AuthenticatorManager;
import com.digigene.accountauthenticator.result.RegisterResult;
import com.digigene.accountauthenticator.result.SignInResult;
import com.digigene.accountauthenticator.result.SignUpResult;

public class MyInterfaceImplementation extends AbstractInterfaceImplementation 
    public static int accessTokenCounter = 0;
    public static int refreshTokenCounter = 0;
    public static int demoCounter = 0;
    public static int accessTokenNo = 0;
    public static int refreshTokenNo = 0;
    public final int ACCESS_TOKEN_EXPIRATION_COUNTER = 2;
    public final int REFRESH_TOKEN_EXPIRATION_COUNTER = 5;
    public final int DEMO_COUNTER = 15;

    @Override
    public String[] userAccessTypes() 
        return new String[]"REGULAR_USER", "SUPER_USER";
    

    @Override
    public void doAfterSignUpIsUnsuccessful(Context context, Account account, String
            authTokenType, SignUpResult signUpResult, Bundle options) 
        Toast.makeText(context, "Sign-up was not possible due to the following:\n" + signUpResult
                .errMessage, Toast.LENGTH_LONG).show();
        AuthenticatorManager.authenticatorManager.addAccount(authTokenType, null, options);
    

    @Override
    public void doAfterSignInIsSuccessful(Context context, Account account, String authTokenType,
                                          String authToken, SignInResult signInResult, Bundle
                                                  options) 
        demoCounter = demoCounter + 1;
        Toast.makeText(context, "User is successfully signed in: \naccessTokenNo=" +
                accessTokenNo + "\nrefreshTokenNo=" + refreshTokenNo +
                "\ndemoCounter=" + demoCounter, Toast.LENGTH_SHORT).show();
    

    @Override
    public SignInResult signInToServer(Context context, Account account, String authTokenType,
                                       String accessToken, Bundle options) 
        accessTokenCounter = accessTokenCounter + 1;
        SignInResult signInResult = new SignInResult();
        signInResult.isSuccessful = true;
        synchronized (this) 
            try 
                this.wait(2000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        if ((accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER || demoCounter > DEMO_COUNTER)) 
            signInResult.isSuccessful = false;
            signInResult.isAccessTokenExpired = true;
            if (demoCounter < DEMO_COUNTER) 
                signInResult.errMessage = "Access token is expired";
                return signInResult;
            
        
        return signInResult;
    

    @Override
    public SignUpResult signUpToServer(Context context, Account account, String authTokenType,
                                       String refreshToken, Bundle options) 
        SignUpResult signUpResult = new SignUpResult();
        synchronized (this) 
            try 
                this.wait(2000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        refreshTokenCounter = refreshTokenCounter + 1;
        signUpResult.isSuccessful = true;
        signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo;
        signUpResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo;
        if (demoCounter > DEMO_COUNTER) 
            signUpResult.isSuccessful = false;
            signUpResult.errMessage = "You have reached your limit of using the demo version. " +
                    "Please buy it for further usage";
            return signUpResult;
        
        if (refreshTokenCounter > REFRESH_TOKEN_EXPIRATION_COUNTER) 
            refreshTokenCounter = 0;
            signUpResult.isSuccessful = false;
            signUpResult.errMessage = "User credentials have expired, please login again";
            return signUpResult;
        
        if (accessTokenCounter > ACCESS_TOKEN_EXPIRATION_COUNTER) 
            accessTokenCounter = 0;
            accessTokenNo = accessTokenNo + 1;
            signUpResult.accessToken = "ACCESS_TOKEN_NO_" + accessTokenNo;
        
        return signUpResult;
    

    @Override
    public RegisterResult registerInServer(Context context, Account account, String password,
                                           String authTokenType, String[] requiredFeatures,
                                           Bundle options) 
        RegisterResult registerResult = new RegisterResult();
        registerResult.isSuccessful = false;
        synchronized (this) 
            try 
                this.wait(2000);
             catch (InterruptedException e) 
                e.printStackTrace();
            
        
        if (true)   // password is checked here and, if true, refresh token is generated for the
            // user
            refreshTokenNo = refreshTokenNo + 1;
            accessTokenNo = accessTokenNo + 1;
            registerResult.isSuccessful = true;
            registerResult.refreshToken = "REFRESH_TOKEN_NO_" + refreshTokenNo;
        
        return registerResult;
    

    @Override
    public boolean setDoesCallbackRunInBackgroundThread() 
        return false;
    

结果

下面显示了正在运行的库。 您可以在我的网站上的这三个帖子中找到完整的教程here 以及关于AccountManager 在android 中的工作原理:part 1、part 2、part 3。

【讨论】:

【参考方案2】:

您需要设置多个组件才能以编程方式创建帐户。你需要:

AccountAuthenticator 提供对 AccountAuthenticator 的访问的服务 一些权限

身份验证器

身份验证器是一个对象,它将在帐户类型和有权管理它的权限(即 linux 用户)之间进行映射。

声明身份验证器在 xml 中完成:

创建文件res/xml/authenticator.xml

内容如下:

<?xml version="1.0" encoding="utf-8"?>
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
                   android:accountType="com.company.demo.account.DEMOACCOUNT"
                   android:icon="@drawable/ic_launcher"
                   android:smallIcon="@drawable/ic_launcher"
                   android:label="@string/my_custom_account"/>

注意 accountType :当您创建帐户时,它必须在代码中重用。 “设置”应用将使用图标和标签来显示该类型的帐户。

实现 AccountAuthenticator

您必须扩展 AbstractAccountAuthenticator 才能做到这一点。这将被第三方应用用于访问帐户数据。

以下示例不允许对 3rd-party 应用程序进行任何访问,因此每个方法的实现都很简单。

public class CustomAuthenticator extends AbstractAccountAuthenticator 

    public CustomAuthenticator(Context context) 
        super(context);
    

    @Override
    public Bundle addAccount(AccountAuthenticatorResponse accountAuthenticatorResponse, String s, String s2, String[] strings, Bundle bundle) throws NetworkErrorException 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public Bundle editProperties(AccountAuthenticatorResponse accountAuthenticatorResponse, String s) 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public Bundle confirmCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, Bundle bundle) throws NetworkErrorException 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public Bundle getAuthToken(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public String getAuthTokenLabel(String s) 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public Bundle updateCredentials(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String s, Bundle bundle) throws NetworkErrorException 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

    @Override
    public Bundle hasFeatures(AccountAuthenticatorResponse accountAuthenticatorResponse, Account account, String[] strings) throws NetworkErrorException 
        return null;  //To change body of implemented methods use File | Settings | File Templates.
    

暴露账户类型的服务

创建一个服务来操作该类型的帐户:

public class AuthenticatorService extends Service 
    @Override
    public IBinder onBind(Intent intent) 
        CustomAuthenticator authenticator = new CustomAuthenticator(this);
        return authenticator.getIBinder();
    

在清单中声明服务

<service android:name="com.company.demo.account.AuthenticatorService" android:exported="false">
        <intent-filter>
            <action android:name="android.accounts.AccountAuthenticator"/>
        </intent-filter>
        <meta-data
            android:name="android.accounts.AccountAuthenticator"
            android:resource="@xml/authenticator"/>
    </service>

这里,过滤器和引用声明身份验证器的xml资源的元数据是关键。

权限

请务必在您的清单中声明以下权限

<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>

(本文中提供的示例代码并非全部都需要,但您可能会有更多关于帐户管理的代码,最后它们都会很有用)

在代码中创建一个帐户

现在一切准备就绪,您可以使用以下代码创建一个帐户。请注意addAccountExplicitly 返回的boolean,通知您成功或失败。

    AccountManager accountManager = AccountManager.get(this); //this is Activity
    Account account = new Account("MyAccount","com.company.demo.account.DEMOACCOUNT");
    boolean success = accountManager.addAccountExplicitly(account,"password",null);
    if(success)
        Log.d(TAG,"Account created");
    else
        Log.d(TAG,"Account creation failed. Look at previous logs to investigate");
    

最后提示

不要将您的应用安装在外部存储设备上

如果您的应用安装在外部存储上,Android 很有可能会在卸载 sdcard 时删除您的帐户数据(因为该帐户的身份验证器将无法再访问)。因此,为了避免这种损失(每次重启!!!)您必须安装应用程序,仅在内部存储上声明身份验证器:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
      android:installLocation="internalOnly"
      ...

遇到问题时

仔细阅读日志,AccountManger 正在输出许多日志以帮助您调试代码。

【讨论】:

感谢 ben75,它真的对我有用。将其标记为正确答案。希望你以后能继续帮助我。 但是我的账户名不在它应该在的地方。我无法显示我的帐户名称,而是仅显示我的图标。现在该怎么办? 尝试将 accountLabel 定义为资源:android:label="@string/my_custom_account" 再次感谢 ben75。它就像魔术一样起作用。你能告诉我如何从服务器同步这个帐户吗?需要那个。 @ben75 这对本很有帮助。我添加了帐户详细信息。请您帮我从另一个应用程序访问帐户详细信息。喜欢 Messenger “继续以 XXXXX” 继续前进!【参考方案3】:

这里的代码被剪断了我正在做(对不起德国评论) 不要忘记在清单文件中设置适当的权限。

/**
 * ueberprueft, ob es den account fuer diese app schon gibt und legt ihn
 * gegebenenfalls an.
 * 
 * @param none
 * @return void
 */
public void verifyAccount() 
    if (debug)
        Log.i(TAG, "verifyAccount() ");

    boolean bereitsAngelegt = false;
    String accountType;
    accountType = this.getPackageName();

    AccountManager accountManager = AccountManager
            .get(getApplicationContext());
    Account[] accounts = accountManager.getAccounts();
    for (int i = 0; i < accounts.length; i++) 
        if (debug)
            Log.v(TAG, accounts[i].toString());
        if ((accounts[i].type != null)
                && (accounts[i].type.contentEquals(accountType))) 
            bereitsAngelegt = true;
            if (debug)
                Log.v(TAG, "verifyAccount(): bereitsAngelegt "
                        + accounts[i].type);
        
    

    if (!bereitsAngelegt) 
        if (debug)
            Log.v(TAG, "verifyAccount(): !bereitsAngelegt ");

        // This is the magic that addes the account to the Android Account
        // Manager

        AccountManager accMgr = AccountManager.get(this);

        String password = "some_password";

        if (debug)
            Log.d(TAG, "verifyAccount(): ADD: accountName: "
                    + Konst.accountName + " accountType: " + accountType
                    + " password: " + password);

        final Account account = new Account(Konst.accountName, accountType);
        if (debug)
            Log.v(TAG, "verifyAccount(): nach final Account account ");
        try 
            accMgr.addAccountExplicitly(account, password, null);
         catch (Exception e1) 
            if (debug)
                Log.v(TAG, "verifyAccount(): Exception e1 " + e1.toString());
            this.finish();
        
        if (debug)
            Log.v(TAG,
                    "verifyAccount(): nach accMgr.addAccountExplicitly() ");
     else 
        if (debug)
            Log.v(TAG, "verifyAccount(): bereitsAngelegt ");
    
 // end of public void verifyAccount()

我希望这会有所帮助。

【讨论】:

我正在尝试这个@hans。我应该在 Menifest 中添加哪些权限?我目前正在使用“android.permission.AUTHENTICATE_ACCOUNTS”。如果这有帮助,让我回复你。非常感谢。 没错,你需要android.permission.AUTHENTICATE_ACCOUNTS。顺便说一句:布尔 bereitsAngelegt 可以翻译为已经存在

以上是关于如何以编程方式在android中添加自定义帐户?的主要内容,如果未能解决你的问题,请参考以下文章

如何从自定义帐户中检索联系方式?

如何以编程方式在 UITableViewCell 中添加自定义视图?

如何以编程方式使用自动布局添加自定义视图

如何以编程方式添加尺寸类自定义

如何在自定义单元格中点击以编程方式添加的 UiImageViews 推送新视图

SharePoint:如何以编程方式将项目添加到自定义列表实例