Android 使用 AccountManager 添加帐户
Android Adding Account with AccountManager
大家好,我正在尝试使用 androids accountManager 添加一个帐户,我一直在下面看到这个堆栈跟踪。
伙计们,我不能 post 我的所有代码,因为我打破了 Stack Overflow 允许我输入的限制。所以只有 post 您请求的代码片段,因为有更多的代码用于此。对不起,我只是乱搞乱码,直到我可以让它工作然后我清理它。
FATAL EXCEPTION: main
Process: com.example.rapid.rapid, PID: 6168
java.lang.SecurityException: uid 10335 cannot explicitly add accounts of type: com.example.rapid.rapid
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at android.accounts.IAccountManager$Stub$Proxy.addAccountExplicitly(IAccountManager.java:890)
at android.accounts.AccountManager.addAccountExplicitly(AccountManager.java:716)
at com.example.rapid.rapid.LoginActivity.onResponse(LoginActivity.java:174)
at com.example.rapid.rapid.LoginActivity.onResponse(LoginActivity.java:140)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:60)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7237)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
LoginActivity.java
public class LoginActivity extends Activity {
private static final String TAG = "LoginActivity";
public final static String ARG_ACCOUNT_TYPE = "com.example.rapid.rapid";
public final static String ARG_AUTH_TYPE = "AUTH_TYPE";
public final static String ARG_ACCOUNT_NAME = "com.example.rapid.rapid";
public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT";
public static final String KEY_ERROR_MESSAGE = "ERR_MSG";
public final static String PARAM_USER_PASS = "USER_PASS";
private static final int REQUEST_SIGNUP = 0;
private AccountManager mAccountManager;
public static final String ACCOUNT_TYPE = "com.example.rapid.rapid";
private static final String CONTENT_AUTHORITY = "com.example.rapid.rapid";
private static final String PREF_SETUP_COMPLETE = "setup_complete";
private static final long SYNC_FREQUENCY = 60 * 60; // 1 hour (in seconds)
private String mAuthTokenType;
private boolean mInvalidate;
private AlertDialog mAlertDialog;
@InjectView(R.id.loginEmailWrapper)
TextInputLayout _loginEmailWrapper;
@InjectView(R.id.loginPasswordWrapper)
TextInputLayout _loginPasswordWrapper;
@InjectView(R.id.loginEmailInput)
EditText _loginEmailInput;
@InjectView(R.id.loginPasswordInput)
EditText _loginPasswordInput;
@InjectView(R.id.loginPasswordVisibility)
ImageView _loginPasswordVisibility;
@InjectView(R.id.btn_login)
Button _loginButton;
@InjectView(R.id.link_signup)
TextView _signupLink;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*Uncomment this to make this screen of the app fullscreen.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);*/
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
mAccountManager = AccountManager.get(this);
boolean setupComplete = PreferenceManager
.getDefaultSharedPreferences(this.getApplicationContext()).getBoolean(PREF_SETUP_COMPLETE, false);
String accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);
if (mAuthTokenType == null)
mAuthTokenType = AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS;
if (accountName != null) {
_loginEmailInput.setText(accountName);
}
_loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Begin Login process...");
showAccountPicker(mAuthTokenType, false);
if (!validate()) {
onLoginFailed();
return;
}
final String email = _loginEmailInput.getText().toString();
final String password = _loginPasswordInput.getText().toString();
final String accountType = getIntent().getStringExtra(ARG_ACCOUNT_TYPE);
_loginButton.setEnabled(false);
final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this,
R.style.Theme_IAPTheme);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
// Response received from the server
Response.Listener<String> responseListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
String authtoken = null;
boolean newAccount = false;
try {
Log.i("tagconvertstr", "[" + response + "]");
JSONObject jsonResponse = new JSONObject(response);
boolean success = jsonResponse.getBoolean("success");
if (success) {
String trainer_name = jsonResponse.getString("trainer_name");
authtoken = jsonResponse.getString("token");
//String name = jsonResponse.getString("name");
//Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
//intent.putExtra("name", name);
//intent.putExtra("username", username);
//LoginActivity.this.startActivity(intent);
//Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
//LoginActivity.this.startActivity(intent);
//startActivityForResult(intent, 1);
String accountName = AccountManager.KEY_ACCOUNT_NAME;
String accountPassword = password;
//final Account account = new Account(email, "com.example.rapid.rapid");
if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, true)) {
Log.d("rapid", TAG + "> finishLogin > addAccountExplicitly");
authtoken = AccountManager.KEY_AUTHTOKEN;
String authtokenType = mAuthTokenType;
Account account = rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE);
AccountManager accountManager =
(AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
if (accountManager.addAccountExplicitly(account, null, null)) {
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
ContentResolver.addPeriodicSync(
account, CONTENT_AUTHORITY, new Bundle(),SYNC_FREQUENCY);
newAccount = true;
}
if (newAccount) {
TriggerRefresh();
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit()
.putBoolean(PREF_SETUP_COMPLETE, true).commit();
}
Log.d("rapid", TAG + "> ALL SETUP!");
// Creating the account on the device and setting the auth token we got
// (Not setting the auth token will cause another call to the server to authenticate the user)
//mAccountManager.addAccountExplicitly(account, null, null);
//mAccountManager.setAuthToken(account, authtokenType, authtoken);
} else {
Log.d("rapid", TAG + "> finishLogin > setPassword");
//mAccountManager.setPassword(account, accountPassword);
Log.d("rapid", TAG + "> done setting account password");
}
//setAccountAuthenticatorResult(intent.getExtras());
//setResult(RESULT_OK, intent);
Toast.makeText(getBaseContext(), "Login Successful", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
intent.putExtra("trainer_name", trainer_name);
startActivity(intent);
} else {
progressDialog.dismiss();
onLoginFailed();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
LoginRequest loginRequest = new LoginRequest(email, password, responseListener);
RequestQueue queue = Volley.newRequestQueue(LoginActivity.this);
queue.add(loginRequest);
}
});
_loginPasswordInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
//_trainerNameWrapper.setBackgroundColor(Color.parseColor("#0000ff"));
}
});
_loginPasswordVisibility.setOnTouchListener(mPasswordVisibleTouchListener);
_signupLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Start the Signup activity
Intent intent = new Intent(getApplicationContext(), RegisterActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
}
public static void TriggerRefresh() {
Bundle b = new Bundle();
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(
rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE), // Sync account
CONTENT_AUTHORITY, // Content authority
b); // Extras
}
private View.OnTouchListener mPasswordVisibleTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final boolean isOutsideView = event.getX() < 0 ||
event.getX() > v.getWidth() ||
event.getY() < 0 ||
event.getY() > v.getHeight();
// change input type will reset cursor position, so we want to save it
final int cursor = _loginPasswordInput.getSelectionStart();
if (isOutsideView || MotionEvent.ACTION_UP == event.getAction())
_loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PASSWORD);
else
_loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
_loginPasswordInput.setSelection(cursor);
return true;
}
};
/**
* Show all the accounts registered on the account manager. Request an auth token upon user select.
*
* @param authTokenType
*/
private void showAccountPicker(final String authTokenType, final boolean invalidate) {
mInvalidate = invalidate;
final Account availableAccounts[] = mAccountManager.getAccountsByType(AccountGeneral.ACCOUNT_TYPE);
if (availableAccounts.length == 0) {
Toast.makeText(this, "No accounts", Toast.LENGTH_SHORT).show();
} else {
String name[] = new String[availableAccounts.length];
for (int i = 0; i < availableAccounts.length; i++) {
name[i] = availableAccounts[i].name;
}
// Account picker
mAlertDialog = new AlertDialog.Builder(this).setTitle("Pick Account").setAdapter(new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, name), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (invalidate)
invalidateAuthToken(availableAccounts[which], authTokenType);
else
getExistingAccountAuthToken(availableAccounts[which], authTokenType);
}
}).create();
mAlertDialog.show();
}
}
/**
* Get the auth token for an existing account on the AccountManager
*
* @param account
* @param authTokenType
*/
private void getExistingAccountAuthToken(Account account, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
showMessage((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL");
Log.d("udinic", "GetToken Bundle is " + bnd);
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}).start();
}
/**
* Invalidates the auth token for the account
*
* @param account
* @param authTokenType
*/
private void invalidateAuthToken(final Account account, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
mAccountManager.invalidateAuthToken(account.type, authtoken);
showMessage(account.name + " invalidated");
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}).start();
}
/**
* Get an auth token for the account.
* If not exist - add it and then return its auth token.
* If one exist - return its auth token.
* If more than one exists - show a picker and return the select account's auth token.
*
* @param accountType
* @param authTokenType
*/
private void getTokenForAccountCreateIfNeeded(String accountType, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, this, null, null,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
Bundle bnd = null;
try {
bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
showMessage(((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL"));
Log.d("udinic", "GetTokenForAccount Bundle is " + bnd);
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}
, null);
}
private void showMessage(final String msg) {
if (TextUtils.isEmpty(msg))
return;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
}
rapidAuthenticator.java
public class rapidAuthenticator extends AbstractAccountAuthenticator {
private String TAG = "rapidAuthenticator";
private final Context mContext;
public rapidAuthenticator(Context context) {
super(context);
// I hate you! Google - set mContext as protected!
this.mContext = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
Log.d("rapid", TAG + "> addAccount");
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
Log.d("udinic", TAG + "> getAuthToken");
// If the caller requested an authToken type we don't support, then
// return an error
if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
return result;
}
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
Log.d("udinic", TAG + "> peekAuthToken returned - " + authToken);
// Lets give another try to authenticate the user
if (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
try {
Log.d("udinic", TAG + "> re-authenticating with the existing password");
authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// If we get an authToken - we return it
if (!TextUtils.isEmpty(authToken)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity.
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_ACCOUNT_NAME, account.name);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
if (AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
else if (AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY_LABEL;
else
return authTokenType + " (Label)";
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(KEY_BOOLEAN_RESULT, false);
return result;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
}
清单
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rapid.rapid">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<application>
<service android:name="com.example.rapid.rapid.rapidAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
</application>
</manifest>
Authenticator.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.example.rapid.rapid"
android:icon="@drawable/logo"
android:smallIcon="@drawable/logo"
android:label="rapid"
android:accountPreferences="@xml/prefs"/>
</selector>
如例外所述,调用者 uid 不同于 验证者的 uid。要显式添加帐户,caller 和 authenticator's uid 应该相同。
这应该与您的 应用程序 ID 相同,即 程序包名称。
android:accountType="com.example.rapid.rapid"
Android Developer Documentation
This method requires the caller to have a signature match with the
authenticator that owns the specified account.
我遇到了类似的问题,但在我重启设备后解决了。尝试一下可能会有所帮助。
大家好,我正在尝试使用 androids accountManager 添加一个帐户,我一直在下面看到这个堆栈跟踪。
伙计们,我不能 post 我的所有代码,因为我打破了 Stack Overflow 允许我输入的限制。所以只有 post 您请求的代码片段,因为有更多的代码用于此。对不起,我只是乱搞乱码,直到我可以让它工作然后我清理它。
FATAL EXCEPTION: main
Process: com.example.rapid.rapid, PID: 6168
java.lang.SecurityException: uid 10335 cannot explicitly add accounts of type: com.example.rapid.rapid
at android.os.Parcel.readException(Parcel.java:1620)
at android.os.Parcel.readException(Parcel.java:1573)
at android.accounts.IAccountManager$Stub$Proxy.addAccountExplicitly(IAccountManager.java:890)
at android.accounts.AccountManager.addAccountExplicitly(AccountManager.java:716)
at com.example.rapid.rapid.LoginActivity.onResponse(LoginActivity.java:174)
at com.example.rapid.rapid.LoginActivity.onResponse(LoginActivity.java:140)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:60)
at com.android.volley.toolbox.StringRequest.deliverResponse(StringRequest.java:30)
at com.android.volley.ExecutorDelivery$ResponseDeliveryRunnable.run(ExecutorDelivery.java:99)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:158)
at android.app.ActivityThread.main(ActivityThread.java:7237)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1230)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1120)
LoginActivity.java
public class LoginActivity extends Activity {
private static final String TAG = "LoginActivity";
public final static String ARG_ACCOUNT_TYPE = "com.example.rapid.rapid";
public final static String ARG_AUTH_TYPE = "AUTH_TYPE";
public final static String ARG_ACCOUNT_NAME = "com.example.rapid.rapid";
public final static String ARG_IS_ADDING_NEW_ACCOUNT = "IS_ADDING_ACCOUNT";
public static final String KEY_ERROR_MESSAGE = "ERR_MSG";
public final static String PARAM_USER_PASS = "USER_PASS";
private static final int REQUEST_SIGNUP = 0;
private AccountManager mAccountManager;
public static final String ACCOUNT_TYPE = "com.example.rapid.rapid";
private static final String CONTENT_AUTHORITY = "com.example.rapid.rapid";
private static final String PREF_SETUP_COMPLETE = "setup_complete";
private static final long SYNC_FREQUENCY = 60 * 60; // 1 hour (in seconds)
private String mAuthTokenType;
private boolean mInvalidate;
private AlertDialog mAlertDialog;
@InjectView(R.id.loginEmailWrapper)
TextInputLayout _loginEmailWrapper;
@InjectView(R.id.loginPasswordWrapper)
TextInputLayout _loginPasswordWrapper;
@InjectView(R.id.loginEmailInput)
EditText _loginEmailInput;
@InjectView(R.id.loginPasswordInput)
EditText _loginPasswordInput;
@InjectView(R.id.loginPasswordVisibility)
ImageView _loginPasswordVisibility;
@InjectView(R.id.btn_login)
Button _loginButton;
@InjectView(R.id.link_signup)
TextView _signupLink;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
/*Uncomment this to make this screen of the app fullscreen.
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);*/
setContentView(R.layout.activity_login);
ButterKnife.inject(this);
mAccountManager = AccountManager.get(this);
boolean setupComplete = PreferenceManager
.getDefaultSharedPreferences(this.getApplicationContext()).getBoolean(PREF_SETUP_COMPLETE, false);
String accountName = getIntent().getStringExtra(ARG_ACCOUNT_NAME);
mAuthTokenType = getIntent().getStringExtra(ARG_AUTH_TYPE);
if (mAuthTokenType == null)
mAuthTokenType = AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS;
if (accountName != null) {
_loginEmailInput.setText(accountName);
}
_loginButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Begin Login process...");
showAccountPicker(mAuthTokenType, false);
if (!validate()) {
onLoginFailed();
return;
}
final String email = _loginEmailInput.getText().toString();
final String password = _loginPasswordInput.getText().toString();
final String accountType = getIntent().getStringExtra(ARG_ACCOUNT_TYPE);
_loginButton.setEnabled(false);
final ProgressDialog progressDialog = new ProgressDialog(LoginActivity.this,
R.style.Theme_IAPTheme);
progressDialog.setIndeterminate(true);
progressDialog.setMessage("Authenticating...");
progressDialog.show();
// Response received from the server
Response.Listener<String> responseListener = new Response.Listener<String>() {
@Override
public void onResponse(String response) {
String authtoken = null;
boolean newAccount = false;
try {
Log.i("tagconvertstr", "[" + response + "]");
JSONObject jsonResponse = new JSONObject(response);
boolean success = jsonResponse.getBoolean("success");
if (success) {
String trainer_name = jsonResponse.getString("trainer_name");
authtoken = jsonResponse.getString("token");
//String name = jsonResponse.getString("name");
//Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
//intent.putExtra("name", name);
//intent.putExtra("username", username);
//LoginActivity.this.startActivity(intent);
//Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
//LoginActivity.this.startActivity(intent);
//startActivityForResult(intent, 1);
String accountName = AccountManager.KEY_ACCOUNT_NAME;
String accountPassword = password;
//final Account account = new Account(email, "com.example.rapid.rapid");
if (getIntent().getBooleanExtra(ARG_IS_ADDING_NEW_ACCOUNT, true)) {
Log.d("rapid", TAG + "> finishLogin > addAccountExplicitly");
authtoken = AccountManager.KEY_AUTHTOKEN;
String authtokenType = mAuthTokenType;
Account account = rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE);
AccountManager accountManager =
(AccountManager) getSystemService(Context.ACCOUNT_SERVICE);
if (accountManager.addAccountExplicitly(account, null, null)) {
// Inform the system that this account supports sync
ContentResolver.setIsSyncable(account, CONTENT_AUTHORITY, 1);
// Inform the system that this account is eligible for auto sync when the network is up
ContentResolver.setSyncAutomatically(account, CONTENT_AUTHORITY, true);
// Recommend a schedule for automatic synchronization. The system may modify this based
// on other scheduled syncs and network utilization.
ContentResolver.addPeriodicSync(
account, CONTENT_AUTHORITY, new Bundle(),SYNC_FREQUENCY);
newAccount = true;
}
if (newAccount) {
TriggerRefresh();
PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).edit()
.putBoolean(PREF_SETUP_COMPLETE, true).commit();
}
Log.d("rapid", TAG + "> ALL SETUP!");
// Creating the account on the device and setting the auth token we got
// (Not setting the auth token will cause another call to the server to authenticate the user)
//mAccountManager.addAccountExplicitly(account, null, null);
//mAccountManager.setAuthToken(account, authtokenType, authtoken);
} else {
Log.d("rapid", TAG + "> finishLogin > setPassword");
//mAccountManager.setPassword(account, accountPassword);
Log.d("rapid", TAG + "> done setting account password");
}
//setAccountAuthenticatorResult(intent.getExtras());
//setResult(RESULT_OK, intent);
Toast.makeText(getBaseContext(), "Login Successful", Toast.LENGTH_LONG).show();
Intent intent = new Intent(LoginActivity.this, UserHomeActivity.class);
intent.putExtra("trainer_name", trainer_name);
startActivity(intent);
} else {
progressDialog.dismiss();
onLoginFailed();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
};
LoginRequest loginRequest = new LoginRequest(email, password, responseListener);
RequestQueue queue = Volley.newRequestQueue(LoginActivity.this);
queue.add(loginRequest);
}
});
_loginPasswordInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
//_registerPasswordVisibility.setVisibility(s.length() > 0 ? View.VISIBLE : View.GONE);
//_trainerNameWrapper.setBackgroundColor(Color.parseColor("#0000ff"));
}
});
_loginPasswordVisibility.setOnTouchListener(mPasswordVisibleTouchListener);
_signupLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Start the Signup activity
Intent intent = new Intent(getApplicationContext(), RegisterActivity.class);
startActivityForResult(intent, REQUEST_SIGNUP);
}
});
}
public static void TriggerRefresh() {
Bundle b = new Bundle();
// Disable sync backoff and ignore sync preferences. In other words...perform sync NOW!
b.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
b.putBoolean(ContentResolver.SYNC_EXTRAS_EXPEDITED, true);
ContentResolver.requestSync(
rapidAuthenticatorService.GetAccount(ACCOUNT_TYPE), // Sync account
CONTENT_AUTHORITY, // Content authority
b); // Extras
}
private View.OnTouchListener mPasswordVisibleTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final boolean isOutsideView = event.getX() < 0 ||
event.getX() > v.getWidth() ||
event.getY() < 0 ||
event.getY() > v.getHeight();
// change input type will reset cursor position, so we want to save it
final int cursor = _loginPasswordInput.getSelectionStart();
if (isOutsideView || MotionEvent.ACTION_UP == event.getAction())
_loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_PASSWORD);
else
_loginPasswordInput.setInputType(InputType.TYPE_CLASS_TEXT |
InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
_loginPasswordInput.setSelection(cursor);
return true;
}
};
/**
* Show all the accounts registered on the account manager. Request an auth token upon user select.
*
* @param authTokenType
*/
private void showAccountPicker(final String authTokenType, final boolean invalidate) {
mInvalidate = invalidate;
final Account availableAccounts[] = mAccountManager.getAccountsByType(AccountGeneral.ACCOUNT_TYPE);
if (availableAccounts.length == 0) {
Toast.makeText(this, "No accounts", Toast.LENGTH_SHORT).show();
} else {
String name[] = new String[availableAccounts.length];
for (int i = 0; i < availableAccounts.length; i++) {
name[i] = availableAccounts[i].name;
}
// Account picker
mAlertDialog = new AlertDialog.Builder(this).setTitle("Pick Account").setAdapter(new ArrayAdapter<String>(getBaseContext(), android.R.layout.simple_list_item_1, name), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (invalidate)
invalidateAuthToken(availableAccounts[which], authTokenType);
else
getExistingAccountAuthToken(availableAccounts[which], authTokenType);
}
}).create();
mAlertDialog.show();
}
}
/**
* Get the auth token for an existing account on the AccountManager
*
* @param account
* @param authTokenType
*/
private void getExistingAccountAuthToken(Account account, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
showMessage((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL");
Log.d("udinic", "GetToken Bundle is " + bnd);
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}).start();
}
/**
* Invalidates the auth token for the account
*
* @param account
* @param authTokenType
*/
private void invalidateAuthToken(final Account account, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthToken(account, authTokenType, null, this, null, null);
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
mAccountManager.invalidateAuthToken(account.type, authtoken);
showMessage(account.name + " invalidated");
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}).start();
}
/**
* Get an auth token for the account.
* If not exist - add it and then return its auth token.
* If one exist - return its auth token.
* If more than one exists - show a picker and return the select account's auth token.
*
* @param accountType
* @param authTokenType
*/
private void getTokenForAccountCreateIfNeeded(String accountType, String authTokenType) {
final AccountManagerFuture<Bundle> future = mAccountManager.getAuthTokenByFeatures(accountType, authTokenType, null, this, null, null,
new AccountManagerCallback<Bundle>() {
@Override
public void run(AccountManagerFuture<Bundle> future) {
Bundle bnd = null;
try {
bnd = future.getResult();
final String authtoken = bnd.getString(AccountManager.KEY_AUTHTOKEN);
showMessage(((authtoken != null) ? "SUCCESS!\ntoken: " + authtoken : "FAIL"));
Log.d("udinic", "GetTokenForAccount Bundle is " + bnd);
} catch (Exception e) {
e.printStackTrace();
showMessage(e.getMessage());
}
}
}
, null);
}
private void showMessage(final String msg) {
if (TextUtils.isEmpty(msg))
return;
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getBaseContext(), msg, Toast.LENGTH_SHORT).show();
}
});
}
}
rapidAuthenticator.java
public class rapidAuthenticator extends AbstractAccountAuthenticator {
private String TAG = "rapidAuthenticator";
private final Context mContext;
public rapidAuthenticator(Context context) {
super(context);
// I hate you! Google - set mContext as protected!
this.mContext = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response, String accountType, String authTokenType, String[] requiredFeatures, Bundle options) throws NetworkErrorException {
Log.d("rapid", TAG + "> addAccount");
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, accountType);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_IS_ADDING_NEW_ACCOUNT, true);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
Log.d("udinic", TAG + "> getAuthToken");
// If the caller requested an authToken type we don't support, then
// return an error
if (!authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY) && !authTokenType.equals(AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ERROR_MESSAGE, "invalid authTokenType");
return result;
}
// Extract the username and password from the Account Manager, and ask
// the server for an appropriate AuthToken.
final AccountManager am = AccountManager.get(mContext);
String authToken = am.peekAuthToken(account, authTokenType);
Log.d("udinic", TAG + "> peekAuthToken returned - " + authToken);
// Lets give another try to authenticate the user
if (TextUtils.isEmpty(authToken)) {
final String password = am.getPassword(account);
if (password != null) {
try {
Log.d("udinic", TAG + "> re-authenticating with the existing password");
authToken = sServerAuthenticate.userSignIn(account.name, password, authTokenType);
} catch (Exception e) {
e.printStackTrace();
}
}
}
// If we get an authToken - we return it
if (!TextUtils.isEmpty(authToken)) {
final Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, account.name);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, account.type);
result.putString(AccountManager.KEY_AUTHTOKEN, authToken);
return result;
}
// If we get here, then we couldn't access the user's password - so we
// need to re-prompt them for their credentials. We do that by creating
// an intent to display our AuthenticatorActivity.
final Intent intent = new Intent(mContext, LoginActivity.class);
intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
intent.putExtra(LoginActivity.ARG_ACCOUNT_TYPE, account.type);
intent.putExtra(LoginActivity.ARG_AUTH_TYPE, authTokenType);
intent.putExtra(LoginActivity.ARG_ACCOUNT_NAME, account.name);
final Bundle bundle = new Bundle();
bundle.putParcelable(AccountManager.KEY_INTENT, intent);
return bundle;
}
@Override
public String getAuthTokenLabel(String authTokenType) {
if (AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_FULL_ACCESS_LABEL;
else if (AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY.equals(authTokenType))
return AccountGeneral.AUTHTOKEN_TYPE_READ_ONLY_LABEL;
else
return authTokenType + " (Label)";
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse response, Account account, String[] features) throws NetworkErrorException {
final Bundle result = new Bundle();
result.putBoolean(KEY_BOOLEAN_RESULT, false);
return result;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse response, String accountType) {
return null;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse response, Account account, Bundle options) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse response, Account account, String authTokenType, Bundle options) throws NetworkErrorException {
return null;
}
}
清单
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.rapid.rapid">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />
<uses-permission android:name="android.permission.WRITE_SYNC_SETTINGS" />
<application>
<service android:name="com.example.rapid.rapid.rapidAuthenticatorService">
<intent-filter>
<action android:name="android.accounts.AccountAuthenticator" />
</intent-filter>
<meta-data android:name="android.accounts.AccountAuthenticator"
android:resource="@xml/authenticator" />
</service>
</application>
</manifest>
Authenticator.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<account-authenticator xmlns:android="http://schemas.android.com/apk/res/android"
android:accountType="com.example.rapid.rapid"
android:icon="@drawable/logo"
android:smallIcon="@drawable/logo"
android:label="rapid"
android:accountPreferences="@xml/prefs"/>
</selector>
如例外所述,调用者 uid 不同于 验证者的 uid。要显式添加帐户,caller 和 authenticator's uid 应该相同。
这应该与您的 应用程序 ID 相同,即 程序包名称。
android:accountType="com.example.rapid.rapid"
Android Developer Documentation
This method requires the caller to have a signature match with the authenticator that owns the specified account.
我遇到了类似的问题,但在我重启设备后解决了。尝试一下可能会有所帮助。