Adding myApp to Android Account Manager is really critical and vital task. It will Show myApp icon in my phone-book like whatsapp, Facebook and gmail does.
There are various step included in this process.
I will not discuss each and every class.
Most important is code. So directly I am going for code.
Firstly have a look how to arrange all classes in different packages.
Lest start coding one class by one.
I used Google account to authenticate myApp here. You have to provide your server authentications
Code Starts from here.
<====================================================================>
// Class for Account authentication
public class AccountAuthenticatorService extends Service {
/**
* The tag used for the logs.
*/
private static final String LOG_TAG = AccountAuthenticatorService.class.getSimpleName();
/**
* The implementation of the class |AccountAuthenticatorImpl|.
* It is implemented as a singleton
*/
private static AccountAuthenticatorImpl sAccountAuthenticator = null;
/**
* The main constructor.
*/
public AccountAuthenticatorService() {
super();
}
/**
* The bind method of the service.
* @param intent The intent used to invoke the service
* @return The binder of the class which has implemented |AbstractAccountAuthenticator|
*/
@Override
public IBinder onBind(Intent intent) {
Log.v(AccountAuthenticatorService.LOG_TAG, "Binding the service");
IBinder ret = null;
if (intent.getAction().equals(android.accounts.AccountManager.ACTION_AUTHENTICATOR_INTENT)) {
ret = getAuthenticator().getIBinder();
}
return ret;
}
/**
* The method used to obtain the authenticator. It is implemented as a singleton
* @return The implementation of the class |AbstractAccountAuthenticator|
*/
private AccountAuthenticatorImpl getAuthenticator() {
if (AccountAuthenticatorService.sAccountAuthenticator == null) {
AccountAuthenticatorService.sAccountAuthenticator = new AccountAuthenticatorImpl(this);
}
return AccountAuthenticatorService.sAccountAuthenticator;
}
/**
* The class which implements the class |AbstractAccountAuthenticator|.
* It is the one which the Android system calls to perform any action related with the account
*/
private static class AccountAuthenticatorImpl extends AbstractAccountAuthenticator {
/**
* The Context used.
*/
private final Context mContext;
/**
* The main constructor of the class.
* @param context The context used
*/
public AccountAuthenticatorImpl(Context context) {
super(context);
mContext = context;
}
@Override
public Bundle addAccount(AccountAuthenticatorResponse response,
String accountType,
String authTokenType,
String[] requiredFeatures,
Bundle options) throws NetworkErrorException {
Log.d(AccountAuthenticatorService.LOG_TAG, "Adding new account");
Bundle reply = new Bundle();
Log.d(AccountAuthenticatorService.LOG_TAG, "The auth token type is " + authTokenType);
Intent i = new Intent(mContext, AddNewAccountActivity.class);
i.setAction("com.gowex.pista.addnewaccount");
i.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE, response);
i.putExtra("AuthTokenType", authTokenType);
reply.putParcelable(AccountManager.KEY_INTENT, i);
return reply;
}
@Override
public Bundle confirmCredentials(AccountAuthenticatorResponse arg0, Account arg1,
Bundle arg2) throws NetworkErrorException {
return null;
}
@Override
public Bundle editProperties(AccountAuthenticatorResponse arg0, String arg1) {
return null;
}
@Override
public Bundle getAuthToken(AccountAuthenticatorResponse arg0, Account arg1,
String arg2, Bundle arg3) throws NetworkErrorException {
return null;
}
@Override
public String getAuthTokenLabel(String arg0) {
return null;
}
@Override
public Bundle hasFeatures(AccountAuthenticatorResponse arg0, Account arg1,
String[] arg2) throws NetworkErrorException {
return null;
}
@Override
public Bundle updateCredentials(AccountAuthenticatorResponse arg0, Account arg1,
String arg2, Bundle arg3) throws NetworkErrorException {
return null;
}
}
}
<====================================================================>
/**
* This class is called when the user want to create an account in the configuration of Android.
*/
public class AddNewAccountActivity extends Activity {
/**
* The tag utilized for the log.
*/
private static final String LOG_TAG = AddNewAccountActivity.class.getSimpleName();
/**
* The context of the program.
*/
private Context context;
/**
* The user name input by the user.
*/
private EditText usernameET;
/**
* The password input by the user.
*/
private EditText passwordET;
/**
* The button to add a new account.
*/
private Button addNewAccountButton;
/**
* The response passed by the service.
* It is used to give the user name and the password to the account manager
*/
private AccountAuthenticatorResponse response;
/**
* The account manager used to request and add account.
*/
private AccountManager accountManager;
/**
* Called when the activity is first created.
* @param savedInstanceState The state saved previously
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//Remove title bar
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
//Remove notification bar
//this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
// Lock the screen orientation
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.add_new_account_layout);
context = this;
usernameET = (EditText) findViewById(R.id.username);
passwordET = (EditText) findViewById(R.id.password);
addNewAccountButton = (Button) findViewById(R.id.createNewAccountButton);
addNewAccountButton.setOnClickListener(onClickListener);
Bundle extras = getIntent().getExtras();
if (extras != null) {
/*
* Pass the new account back to the account manager
*/
response = extras.getParcelable(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE);
}
}
/**
* The listener for the button pressed.
*/
private final View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final String username = usernameET.getText().toString();
final String password = passwordET.getText().toString();
// Check the contents
// Check the user name
if (username == null || username.equalsIgnoreCase("")) {
Toast.makeText(context, getResources().getString(R.string.warning_username_empty), Toast.LENGTH_LONG).show();
return;
}
// Check the password
if (password == null || password.equalsIgnoreCase("")) {
Toast.makeText(context, getResources().getString(R.string.warning_password_empty), Toast.LENGTH_LONG).show();
return;
}
accountManager = AccountManager.get(context);
/*
* Check if the account already exists.
*/
if (AccountUtils.getUserAccount(context, username) != null) {
Toast.makeText(context, R.string.warning_account_already_exists, Toast.LENGTH_SHORT).show();
return;
}
/*
* Check the user name and the password against the server.
*/
Account newUserAccount = new Account(username, getResources().getString(R.string.account_type));
try {
String encryptedPassword = SecurityUtils.encryptToHex(password);
boolean accountCreated = accountManager.addAccountExplicitly(newUserAccount, encryptedPassword, null);
if (accountCreated) {
if (response != null) {
Bundle result = new Bundle();
result.putString(AccountManager.KEY_ACCOUNT_NAME, username);
result.putString(AccountManager.KEY_ACCOUNT_TYPE, getString(R.string.account_type));
response.onResult(result);
Toast.makeText(context, R.string.add_new_account_done, Toast.LENGTH_LONG).show();
finish();
} else {
Toast.makeText(context, R.string.error_creating_account, Toast.LENGTH_LONG).show();
}
} else {
Toast.makeText(context, R.string.error_creating_account, Toast.LENGTH_LONG).show();
}
} catch (Exception e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
Toast.makeText(context, R.string.error_creating_account, Toast.LENGTH_LONG).show();
}
}
};
}
<====================================================================>
/**
* This class is used to implement the error completion handler.
*/
public interface Callback {
/**
* Method called when the operation has been finished.
* @param error Indication of if any error happened or not
*/
void done(final boolean error);
}
<====================================================================>
/**
* This class is used to implement the error completion handler.
*/
public interface RequestDataCallback {
/**
* Method called when the operation has been finished.
* @param data An array of bytes returned by the server
* @param error Indication of if any error happened or not
*/
void done(final byte[] data, final boolean error);
}
<====================================================================>
/**
* This class is used to implement the error completion handler.
*/
public interface RequestJSONCallback {
/**
* Method called when the operation has been finished.
* @param jsonObject The JSON object returned by the server
* @param error Indication of if any error happened or not
*/
void done(final JSONObject jsonObject, final boolean error);
}
<====================================================================>
/**
* The customized Session exception
*
*/
public class SessionException extends Exception {
/**
* The default Serial version UID
*/
private static final long serialVersionUID = 1L;
public SessionException(String message) {
super(message);
}
}
<====================================================================>
/**
* The Session class models a user's session. It is the intermediate level between Controllers and Service.
*/
public final class Session {
private static final String LOG_TAG = Session.class.getSimpleName();
private Service service;
private Preferences preferences;
private static Session currentSession = null;
/**
* The constructor of the session.
* Because it is a singleton, there is not parameters for the constructors and it's private
*/
private Session() {}
/**
* SingletonHolder is loaded on the first execution of Singleton.getInstance() or the first access to
* SingletonHolder.INSTANCE, not before.
*/
private static class SingletonHolder {
private static final Session INSTANCE = new Session();
}
// It is synchronized to avoid problems with multithreading
// Once get, it must initialize the service and the preferences based on the context
private static synchronized Session getInstance() {
return SingletonHolder.INSTANCE;
}
// To avoid clone problem
@Override
public Object clone() throws CloneNotSupportedException {
throw new CloneNotSupportedException();
}
/**
* This method starts the sign up process.
* @param context Context in which execution must be
* @param username the name of the user
* @param password User's password
* @param callback Callback to call when process finishes
*/
public static void signUp(final Context context, final String username,
final String password, final Callback callback) {
Service service = new Service(username);
service.signUp(username, password, new Callback() {
@Override
public void done(final boolean error) {
// If there is not error, the method login will call callback
if (!error) {
Session.logIn(context, username, password, callback);
} else {
callback.done(error);
}
}
});
}
/**
* Allow the user to log in.
* It checks the user name and the password against the server.
* @param context The context utilized
* @param username The name of the user
* @param password The password
* @param callback The callback to call when the server returns an answer
*/
public static void logIn(final Context context, final String username,
final String password, final Callback callback) {
final Service service = new Service(username);
// The preferences is here to avoid to be used statically
final Preferences preferences = new Preferences(context);
service.logIn(username, password, new RequestJSONCallback() {
@Override
public void done(JSONObject jsonObject, boolean error) {
if (!error) {
/*
* Creates an instance of the current session.
*/
Session session = Session.getInstance();
session.setService(service);
session.setPreferences(preferences);
/*
* Save the current session
*/
Session.setCurrentSession(session);
Session.currentSession.saveAsCurrentSession(context, username, password);
}
callback.done(error);
}
});
}
/**
* Save the session in a persistent way.
* @param context The context utilized to get the data
* @param username The name of the verified user
* @param password The password of the verified user
*/
private void saveAsCurrentSession(Context context, String username, String password) {
// Save the user name
preferences.setUserName(username);
// Save the password
AccountUtils.setPasswordByUserName(context, username, password);
}
/**
* Check if there is any saved session
* @param context The context used
* @return true if there is any saved session
* false otherwise
*/
public static boolean isSavedSessionExists(Context context) {
Preferences preferences = new Preferences(context);
// Check if the user name exists in the Shared preferences
String username = preferences.getUsername();
if (username == null || username.equals(Preferences.DEFAULT_USERNAME)) {
return false;
}
// Check if the user account exists
if (AccountUtils.getUserAccount(context, username) == null) {
return false;
}
return true;
}
/**
* Get the current session.
* @param context The context utilized to retrieve the data
* @return The current session
* @throws SessionException if some data is missing
*/
public static synchronized Session getCurrentSession(Context context) throws SessionException {
if (Session.currentSession == null) {
Session.currentSession = Session.sessionFromCurrentSession(context);
}
return Session.currentSession;
}
/**
* Creates a new session from the data saved in the persistent data storage.
* It might return null. Use the method existSavedSession to check if
* the session saved exists
* Precondition: The user name exists in the Shared preferences and the user
* account exists.
* @param context The context utilized.
* @return A new session if all the data are set
* @throws SessionException if some data is missing
*/
private static Session sessionFromCurrentSession(Context context) throws SessionException {
Preferences preferences = new Preferences(context);
// Get the user name
String username = preferences.getUsername();
if (username == null || username.equals(Preferences.DEFAULT_USERNAME)) {
throw new SessionException("Error getting the session from the current one. " +
"The previous session doesn't exsit");
}
Service newService = new Service(username);
Session newSession = Session.getInstance();
newSession.setService(newService);
newSession.setPreferences(preferences);
return newSession;
}
// Getters & setters
private static synchronized void setCurrentSession(Session session) {
Session.currentSession = session;
}
/*
private Service getService() {
return service;
}*/
/**
* Set the service as the service utilized for the sessions
* This is private to prevent other to set the service
* The service won't be set until the user has logged in.
* @param service The service to set.
*/
private void setService(Service service) {
this.service = service;
}
public Preferences getPreferences() {
return preferences;
}
/**
* Set the preferences as the preferences utilized for the session.
* This is private to prevent other ot set the preferences from outside
* The preferences won't be set until the user has logged in.
* @param preferences The preferences to set.
*/
private void setPreferences(Preferences preferences) {
this.preferences = preferences;
}
}
<====================================================================>
/**
* The Request class used to establish REST communication with the server.
*/
public class HttpRequest {
/**
* The tag used in logs.
*/
private static final String LOG_TAG = HttpRequest.class.getSimpleName();
/**
* Enumerated data which represents the REST methods used in the Request.
*/
public enum RequestMethod {
/**
* Method get.
*/
RequestMethodGet,
/**
* Method post.
*/
RequestMethodPost,
/**
* Method put.
*/
RequestMethodPut,
/**
* Method delete.
*/
RequestMethodDelete
}
/**
* The uri used to establish the REST communication.
*/
private Uri uri;
/**
* The list of parameters to sent to the server.
*/
private Map<String, String> parameters;
/**
* The request method established.
*/
private final RequestMethod requestMethod;
/**
* The headers used.
*/
private final Map<String, String> headerFields;
/**
* The instance of server Fetcher which is used to communicates with the server.
*/
private ServerFetcher serverFetcher;
/**
* The instance of server Fetcher which is used to communicates with the server.
*/
private final ExecutorServiceSingleton executorServiceSingleton;
/**
* The threadPool which contains all the threads used to communicate with the server.
*/
private final ExecutorService threadPool;
/**
* The handler used to execute all the callbacks in the main thread.
*/
private final Handler handler;
/**
* Method used to set the default header fields.
* @return A map of default fields
*/
private Map<String, String> defaultHeaderFields() {
Map<String, String> headerFields = new HashMap<String, String>();
/*
* TODO Get locale language
*/
String localelanguage = "en";
headerFields.put("Accept-Language", localelanguage);
return headerFields;
}
/**
* The method used to parse URI and the parameters.
* @param uri The URI of the server to connect
* @param parameters The list of parameters to append
* @return A URI with appended parameters
*/
private Uri parseUriAndParameters(Uri uri, Map<String, String> parameters) {
String listParameters = "";
if (parameters != null) {
for (String key : parameters.keySet()) {
listParameters += key + "=" + parameters.get(key);
listParameters += "&";
}
}
/*
* Remove the last character if it is "&"
*/
if (listParameters.endsWith("&")) {
listParameters = listParameters.substring(0, listParameters.length() - 1);
}
Uri result = Uri.parse(uri.toString() + listParameters);
return result;
}
/**
* Method used to set the authentication data in the request.
* @param username The user name to set
* @param password The password to set
*/
public void setUsernamePassword(String username, String password) {
/*
* Encode the data
*/
byte[] ascii = EncodingUtils.getAsciiBytes(username + password);
/*
* The code should not end with the EOL character
*/
String authentication = SecurityUtils.base64Encode(ascii);
/*
* Add the authentication to the header
*/
headerFields.put("Authorization", authentication);
}
/**
* The main constructor of the class.
* @param uri The URI of the server.
* @param parameters The list of the parameters used.
* @param requestMethod The request method
*/
public HttpRequest(Uri uri, Map<String, String> parameters, RequestMethod requestMethod) {
executorServiceSingleton = ExecutorServiceSingleton.instance();
threadPool = executorServiceSingleton.getExecutorService();
this.requestMethod = requestMethod;
// If the request method is get, all the parameters is shown in the uri
if (requestMethod == RequestMethod.RequestMethodGet) {
this.uri = parseUriAndParameters(uri, parameters);
Log.d("Request uri", uri.toString());
} else {
this.uri = uri;
this.parameters = parameters;
}
headerFields = defaultHeaderFields();
handler = new Handler();
}
/**
* Method used to send request to the server, which returns an array of bytes.
* @param requestDataCallback The callback to call when the communication finishes
*/
public void performRequestWithHandler(final RequestDataCallback requestDataCallback) {
serverFetcher = new ServerFetcher(requestMethod, uri, parameters, headerFields, new RequestDataCallback() {
@Override
public void done(final byte[] data, final boolean error) {
handler.post(new Runnable() {
@Override
public void run() {
Log.d(LOG_TAG, "Request to " + uri.toString() + "done");
requestDataCallback.done(data, error);
}
});
}
});
threadPool.execute(serverFetcher);
}
/**
* Method used to send request to the server, which parse the content returned to a json object.
* @param jsonHandler The callback to call when the communication finishes.
*/
public void performRequestWithJSONHandler(final RequestJSONCallback jsonHandler) {
/*
* Add new header
*/
headerFields.put("Accept", "application/json");
serverFetcher = new ServerFetcher(requestMethod, uri,
parameters, headerFields,
new RequestDataCallback() {
@Override
public void done(final byte[] data, final boolean error) {
handler.post(new Runnable() {
@Override
public void run() {
if (!error && data != null && data.length > 0) {
try {
// For latin languages, the codification must be ISO 8859-1
//http://es.wikipedia.org/wiki/ISO_8859-1
String jsonString = new String(data, "US-ASCII");
Log.v(HttpRequest.LOG_TAG, jsonString);
/*
* Parse JSON data
*/
JSONObject jsonObject = new JSONObject(jsonString);
jsonHandler.done(jsonObject, error);
} catch (Exception exception) {
Log.e(HttpRequest.LOG_TAG, exception.getLocalizedMessage(), exception);
jsonHandler.done(null, true);
}
} else {
jsonHandler.done(null, error);
}
}
});
}
});
threadPool.execute(serverFetcher);
}
/**
* Informs that the request has been finished or not.
* @return True if the request has been finished.
* False otherwise
*/
public boolean hasFinished() {
return (serverFetcher == null || !serverFetcher.isRunning);
}
/**
* Method used to cancel the actual request.
*/
public void cancelRequest() {
if (serverFetcher != null && serverFetcher.isRunning) {
serverFetcher.stopFetching();
}
serverFetcher = null;
}
public Uri getUri() {
return uri;
}
/**
* The runnable class used to connect with the server.
*/
private class ServerFetcher implements Runnable {
/**
* The tag utilized for the log.
*/
private static final String LOG_TAG = "ServerFetcher";
/**
* The registration time out before it launches the exception.
*/
private static final int REGISTRATION_TIMEOUT = 3 * 1000;
/**
* The wait time out before it launches the exception.
*/
private static final int WAIT_TIMEOUT = 30 * 1000;
/**
* The http client utilized.
*/
private final HttpClient httpClient = new DefaultHttpClient();
/**
* The variable to record the running state (Yes/No).
*/
private boolean isRunning = false;
/**
* The list of parameters of the HTTP client.
*/
private final HttpParams params = httpClient.getParams();
/**
* The http response from the server.
*/
private HttpResponse response;
/**
* The URI to connect.
*/
private final Uri uri;
/**
* The list of parameters that includes in the http request.
*/
private final Map<String, String> parameters;
/**
* The headers used.
*/
private final Map<String, String> headerFields;
/**
* The callback to call when the operation finishes.
*/
private final RequestDataCallback requestDataCallback;
/**
* The final data obtained from the server.
*/
private byte[] dataObtained;
/**
* The variable to save the final state of the operation.
*/
private boolean error = false;
/**
* The main constructor.
* @param requestMethod The REST method to perform
* @param uri The Uri of the server to connect
* @param parameters The list of parameters to be added to the HTTP request
* @param headerFields The header of the HTTP request
* @param requestDataCallback The Callback to call when the operation finishes
*/
public ServerFetcher(RequestMethod requestMethod, Uri uri, Map<String,
String> parameters, Map<String, String> headerFields,
RequestDataCallback requestDataCallback) {
this.uri = uri;
this.parameters = parameters;
this.headerFields = headerFields;
this.requestDataCallback = requestDataCallback;
}
/**
* The method which tells the actual state of the operation.
* @return True if the server has not returned the response yet
* False otherwise
*/
public boolean isRunning() {
return isRunning;
}
/**
* This methods stops the communication with the server.
*/
public void stopFetching() {
/*
* TODO Cancel the http request
*/
}
@Override
public void run() {
Log.v(ServerFetcher.LOG_TAG, ServerFetcher.LOG_TAG + " running");
isRunning = true;
try {
/*
* Set the connection parameters
*/
HttpConnectionParams.setConnectionTimeout(params, ServerFetcher.REGISTRATION_TIMEOUT);
HttpConnectionParams.setSoTimeout(params, ServerFetcher.WAIT_TIMEOUT);
ConnManagerParams.setTimeout(params, ServerFetcher.WAIT_TIMEOUT);
/*
* Create ServerFetcher and prepare the data
*/
if (requestMethod == RequestMethod.RequestMethodGet) {
HttpGet httpGet = new HttpGet(uri.toString());
/*
* Add the headers
*/
for (String key : headerFields.keySet()) {
httpGet.addHeader(key, headerFields.get(key));
}
/*
* Response from the Http Request
*/
response = httpClient.execute(httpGet);
} else if (requestMethod == RequestMethod.RequestMethodPost) {
HttpPost httpPost = new HttpPost(uri.toString());
/*
* Add the headers
*/
for (String key : headerFields.keySet()) {
httpPost.addHeader(key, headerFields.get(key));
}
/*
* Add the values in the parameters
*/
if (parameters != null) {
List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>();
for (String key : parameters.keySet()) {
nameValuePairs.add(new BasicNameValuePair(key, parameters.get(key)));
}
httpPost.setEntity(new UrlEncodedFormEntity(nameValuePairs, "UTF-8"));
}
/*
* Response from the Http Request
*/
response = httpClient.execute(httpPost);
} else if (requestMethod == RequestMethod.RequestMethodPut) {
/*
* TODO Implement it
*/
Log.w(ServerFetcher.LOG_TAG, "Executing Request method not implemented: put");
} else if (requestMethod == RequestMethod.RequestMethodDelete) {
/*
* TODO Implement it
*/
Log.w(ServerFetcher.LOG_TAG, "Executing Request method not implemented: delete");
}
StatusLine statusLine = response.getStatusLine();
Log.v(ServerFetcher.LOG_TAG, "Status code" + statusLine.getStatusCode());
/*
* Check the Http Request for success
*/
ByteArrayOutputStream out = new ByteArrayOutputStream();
if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
response.getEntity().writeTo(out);
out.close();
dataObtained = out.toByteArray();
error = false;
} else if (statusLine.getStatusCode() != HttpStatus.SC_OK) {
out.close();
response.getEntity().getContent().close();
error = true;
} else {
/*
* Close the connection
*/
response.getEntity().getContent().close();
throw new IOException(statusLine.getReasonPhrase());
}
} catch (Exception e) {
Log.w(LOG_TAG, e.getLocalizedMessage(), e);
error = true;
} finally {
isRunning = false;
if (requestDataCallback != null) {
requestDataCallback.done(dataObtained, error);
}
}
}
}
/**
* The class which creates the threadPool as singleton.
*/
private static final class ExecutorServiceSingleton {
/**
* The number of the threads which are running in parallel.
*/
private static final int MAXIMUM_NUM_RUNNING_THREAD = 3;
/**
* A class to hold the singleton.
*/
private static class SingletonHolder {
/**
* The instance of the class.
*/
private static final ExecutorServiceSingleton INSTANCE = new ExecutorServiceSingleton();
}
/**
* Creates the executor server as soft reference.
*/
private SoftReference<ExecutorService> executorServiceReference = new SoftReference<ExecutorService>(
createExecutorService());
/**
* The empty constructor of the class.
*/
private ExecutorServiceSingleton() {
};
/**
* The public method to return the instance.
* @return A instance of the Singleton holder
*/
public static ExecutorServiceSingleton instance() {
return SingletonHolder.INSTANCE;
}
/**
* Method used to get the executor service.
* @return The executor service. Create it if it has not been created before
*/
public ExecutorService getExecutorService() {
ExecutorService executorService = executorServiceReference.get();
if (executorService == null) {
// (the reference was cleared)
executorService = createExecutorService();
executorServiceReference = new SoftReference<ExecutorService>(executorService);
}
return executorService;
}
/**
* The method which creates a threadPool with limit number of threads.
* Those threads will be re-utilized and they should exist while the application is running.
* @return A threadPool with always same number of threads
*/
private ExecutorService createExecutorService() {
return Executors.newFixedThreadPool(ExecutorServiceSingleton.MAXIMUM_NUM_RUNNING_THREAD);
}
}
}
<====================================================================>
/**
* This class is used to communicates with the remote server.
*/
public class Service {
/**
* The tag used for log.
*/
private static final String LOG_TAG = Service.class.getSimpleName();
/**
* The base of the server url
*/
private static final String BASE_URL = "http:google.com/"; // Provide here your server link
/**
* The user name used for the login, sign up and/or authentication.
* The password is saved on the Account manager and it is associated
* with the user name.
*/
private String username;
/**
* The main constructor.
* @param username The user name used for login, sign up and/or authentication
* This is necessary because the user name is used to get the hash for
* the authentication.
*/
public Service(String username) {
this.username = username;
}
/**
* Method used for sign up.
* @param username The user name used for sign up
* @param invitationCode The invitation code used for sign up
* @param password The password used for sign up
* @param callback The callback to call when the sign up process has been finished
*/
public void signUp(final String username,
final String password, final Callback callback) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("email", username);
parameters.put("password", password);
final Uri finalUri = Uri.parse(BASE_URL + "/signup"); // Provide here your server link
HttpRequest httpRequest = new HttpRequest(finalUri, parameters, RequestMethod.RequestMethodPost);
httpRequest.performRequestWithHandler(new RequestDataCallback() {
@Override
public void done(final byte[] data, final boolean error) {
callback.done(error);
}
});
}
/**
* Method used for login.
* @param username The user name used for login
* @param password The password used for login
* @param jsonCallback The callback to call when the login process has been finished
*/
public void logIn(final String username, final String password,
final RequestJSONCallback jsonCallback) {
Map<String, String> parameters = new HashMap<String, String>();
parameters.put("username_login", username);
parameters.put("password", password);
final Uri finalUri = Uri.parse(BASE_URL + "/login"); // Provide here your server link
HttpRequest httpRequest = new HttpRequest(finalUri, parameters, RequestMethod.RequestMethodPost);
httpRequest.performRequestWithJSONHandler(new RequestJSONCallback() {
@Override
public void done(JSONObject jsonObject, boolean error) {
jsonCallback.done(jsonObject, error);
}
});
}
// Getters & setters
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
<====================================================================>
public class AccountUtils {
/**
* The tag used for log
*/
private static final String LOG_TAG = AccountUtils.class.getSimpleName();
/**
* Check if an account with the specific user name exists or not
* @param context The context used
* @param username The user name of the account
* @return If the account exists, return the account
* Otherwise return null
*/
public static Account getUserAccount(Context context, String username) {
AccountManager accountManager = AccountManager.get(context);
Account[] accounts = accountManager.getAccountsByType(context.getResources().getString(R.string.account_type));
for (Account account : accounts) {
if (account.name.equalsIgnoreCase(username)) {
return account;
}
}
return null;
}
/**
* Obtains the password stored in the account manager.
* Password is encrypted, so it must be decrypted to get its real value
* @param context The context used
* @param username The user name that the password is associated
* @return The password stored in the account manager
* "" if the account is not set
*/
public static String getPasswordyByUserName(Context context, String username) {
AccountManager accountManager = AccountManager.get(context);
String encryptedPassword;
String decryptedPassword = "";
Account account = getUserAccount(context, username);
if (account != null) {
encryptedPassword = accountManager.getPassword(account);
try {
decryptedPassword = SecurityUtils.decrypt(encryptedPassword);
} catch (Exception e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
}
}
return decryptedPassword;
}
/**
* Set the encrypted password in the user account. If the account doesn't exist
* before, it creates a new one.
* @param password The password to be stored in the account manager
* @param username The user name associated with the password
* @return true if the password has been set
* false otherwise
*/
public static boolean setPasswordByUserName(Context context, String username, String password) {
String accountType = context.getResources().getString(R.string.account_type);
AccountManager accountManager = AccountManager.get(context);
// Encrypt the password
try {
String encryptedPassword = SecurityUtils.encryptToHex(password);
Account account = getUserAccount(context, username);
if (account != null) {
// Check if the old password is the same as the new one
String oldPassword = accountManager.getPassword(account);
if (!oldPassword.equalsIgnoreCase(encryptedPassword)) {
accountManager.setPassword(account, encryptedPassword);
}
}
// If the account doesn't exist before, create it.
else {
Account newUserAccount = new Account(username, accountType);
accountManager.addAccountExplicitly(newUserAccount, encryptedPassword, null);
}
return true;
} catch (Exception e) {
Log.e(LOG_TAG, e.getLocalizedMessage(), e);
return false;
}
}
}
<====================================================================>
/**
* Class utilized to save the user elemental data persistently.
*/
public class Preferences {
/**
* The tag utilized for the log.
*/
private static final String LOG_TAG = Preferences.class.getSimpleName();
/**
* The name of the file utilized to store the data.
*/
private static final String FILE_NAME = "Preferences";
/**
* The set of keys utilized.
*/
/**
* The key for the user name.
*/
private static final String USERNAME_KEY = "username";
/**
* The default user name
*/
public static final String DEFAULT_USERNAME = "";
/**
* The context passed by any Android's component.
*/
private final Context context;
/**
* The shared preferences to save/restore the data.
*/
private final SharedPreferences sharedPreferences;
/**
* The editor to save the data.
*/
private final SharedPreferences.Editor editor;
/**
* The main constructor.
* @param context The context passed by any Android's component.
*/
public Preferences(Context context) {
this.context = context;
sharedPreferences = context.getSharedPreferences(Preferences.FILE_NAME, Context.MODE_PRIVATE);
editor = sharedPreferences.edit();
}
/*
* User name
*/
/**
* Obtains the user name stored in the shared preferences.
* @return The user name if it has been stored
* "" if the user id has not been stored
*/
public String getUsername() {
String username = sharedPreferences.getString(Preferences.USERNAME_KEY, DEFAULT_USERNAME);
return username;
}
/**
* Stores the user name in the shared preferences.
* @param username The user name to be stored
*/
public void setUserName(String username) {
editor.putString(Preferences.USERNAME_KEY, username);
editor.commit();
}
/**
* Remove everything from the shared preferences.
*/
public void clear() {
editor.clear();
editor.commit();
}
}
<====================================================================>
/**
* The SecurityUtils class contains methods used for password encryption/decryption.
*/
public final class SecurityUtils {
/**
* The SecurityUtils class should not be instantiated, so its constructor.
* is private to prevent instantiation by other objects.
*/
private SecurityUtils() { }
/**
* Digits used in hex format.
*/
private static final String HEX = "0123456789ABCDEF";
/**
* Seed to encryption.
*/
private static final String SEED = "com.jiahaoliuliu.android.sampleaccountandserver";
/**
* This method appends a byte to a string buffer with hex format.
* @param sb String buffer to append in
* @param b Byte to append to the buffer
*/
private static void appendHex(StringBuffer sb, byte b) {
sb.append(SecurityUtils.HEX.charAt((b >> 4) & 0x0f)).append(SecurityUtils.HEX.charAt(b & 0x0f));
}
/**
* This method gets a raw key from the given seed.
* Raw key is needed for encrypt/decrypt text
* @param seed Seed used to get the raw key
* @return Byte array with the raw key
* @throws Exception
*/
private static byte[] getRawKey(byte[] seed) throws Exception {
KeyGenerator kgen = KeyGenerator.getInstance("AES");
SecureRandom sr = SecureRandom.getInstance("SHA1PRNG");
sr.setSeed(seed);
kgen.init(128, sr); // 192 and 256 bits may not be available
SecretKey skey = kgen.generateKey();
byte[] raw = skey.getEncoded();
return raw;
}
/**
* This method encrypts text with the given raw byte array.
* The algorithm used is AES
* @param raw
* @param clear Byte representation of text to encrypt
* @return Byte array with encrypted text
* @throws Exception
*/
private static byte[] encrypt(byte[] raw, byte[] clear) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
byte[] encrypted = cipher.doFinal(clear);
return encrypted;
}
/**
* This method decrypts encrypted text with the given raw byte array.
* @param raw Byte array
* @param encrypted Text encrypted for decryption
* @return Byte array with decrypted text
* @throws Exception
*/
private static byte[] decrypt(byte[] raw, byte[] encrypted) throws Exception {
SecretKeySpec skeySpec = new SecretKeySpec(raw, "AES");
Cipher cipher = Cipher.getInstance("AES");
cipher.init(Cipher.DECRYPT_MODE, skeySpec);
byte[] decrypted = cipher.doFinal(encrypted);
return decrypted;
}
/**
* This method converts a byte value into a String with hexadecimal format.
* @param value Value to convert
* @return String with hexadecimal format
*/
private static String toHexadecimal(byte[] value) {
String result = "";
for (byte aux : value) {
int b = aux & 0xff;
if (Integer.toHexString(b).length() == 1) {
result += "0";
}
result += Integer.toHexString(b);
}
return result;
}
/**
* This method encrypts text.
* @param cleartext Text to encrypt
* @return String in hex format with |cleartext| encrypted
* @throws Exception
*/
public static String encryptToHex(String cleartext) throws Exception {
byte[] rawKey = SecurityUtils.getRawKey(SecurityUtils.SEED.getBytes());
byte[] result = SecurityUtils.encrypt(rawKey, cleartext.getBytes());
return SecurityUtils.toHex(result);
}
/**
* This method encrypts text.
* @param cleartext Text to encrypt
* @return |cleartext| encrypted into byte array
* @throws Exception
*/
public static byte[] encryptToBytes(String cleartext) throws Exception {
byte[] rawKey = SecurityUtils.getRawKey(SecurityUtils.SEED.getBytes());
byte[] result = SecurityUtils.encrypt(rawKey, cleartext.getBytes());
return result;
}
/**
* This method decrypts text.
* @param encrypted Text to decrypt
* @return String |encrypted| decrypted into a string
* @throws Exception
*/
public static String decrypt(String encrypted) throws Exception {
byte[] rawKey = SecurityUtils.getRawKey(SecurityUtils.SEED.getBytes());
byte[] enc = SecurityUtils.toByte(encrypted);
byte[] result = SecurityUtils.decrypt(rawKey, enc);
return new String(result);
}
/**
* This method encodes a byte array into a string with base64 format.
* @param bytes Byte array to encode
* @return String with |bytes| encoded into base64
*/
public static String base64Encode(byte[] bytes) {
return Base64.encodeToString(bytes, Base64.NO_WRAP);
}
/**
* This method decodes a base64 encoded string.
* @param base64 String encoded in base64 format
* @return String with |base64| decoded
*/
public static String base64Decode(String base64) {
return SecurityUtils.toHexadecimal(Base64.decode(base64, Base64.NO_WRAP));
}
/**
* This method converts a string to its hex representation.
*/
public static String toHex(String txt) {
return SecurityUtils.toHex(txt.getBytes());
}
/**
* This method converts a string in hex format to a byte array.
*/
public static String fromHex(String hex) {
return new String(SecurityUtils.toByte(hex));
}
/**
* This method converts a string in hex format to a byte array.
* @param hexString String in hex format to convert
* @return Byte array gets from |hexString|
*/
public static byte[] toByte(String hexString) {
int len = hexString.length() / 2;
byte[] result = new byte[len];
for (int i = 0; i < len; i++) {
result[i] = Integer.valueOf(hexString.substring(2 * i, 2 * i + 2), 16).byteValue();
}
return result;
}
/**
* This method gives a hex conversion of a byte array.
* @param buf Byte array to get its hex representation
* @return String with |buf| in hex format
*/
public static String toHex(byte[] buf) {
if (buf == null) {
return "";
}
StringBuffer result = new StringBuffer(2 * buf.length);
for (int i = 0; i < buf.length; i++) {
SecurityUtils.appendHex(result, buf[i]);
}
return result.toString();
}
}
<====================================================================>
And You are done with All java files..
Now layout files
<====================================================================>
add_new_account_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
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=".AddNewAccount">
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/username"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_alignLeft="@+id/username"
android:layout_alignRight="@+id/username"
android:ems="10"
android:inputType="textPassword"
android:hint="@string/hint_password"
android:lines="1"
android:imeOptions="actionDone"
/>
<Button
android:id="@+id/createNewAccountButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/password"
android:layout_alignRight="@+id/password"
android:layout_below="@+id/password"
android:layout_marginTop="24dp"
android:text="@string/create_new_account"/>
<EditText
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="160dp"
android:ems="10"
android:hint="@string/hint_username"
android:imeOptions="actionNext"
android:lines="1" />
</RelativeLayout>
<====================================================================>
add_new_account_layout.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
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=".AddNewAccount">
<EditText
android:id="@+id/password"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/username"
android:layout_centerHorizontal="true"
android:layout_marginTop="20dp"
android:layout_alignLeft="@+id/username"
android:layout_alignRight="@+id/username"
android:ems="10"
android:inputType="textPassword"
android:hint="@string/hint_password"
android:lines="1"
android:imeOptions="actionDone"
/>
<Button
android:id="@+id/createNewAccountButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="@+id/password"
android:layout_alignRight="@+id/password"
android:layout_below="@+id/password"
android:layout_marginTop="24dp"
android:text="@string/create_new_account"/>
<EditText
android:id="@+id/username"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="160dp"
android:ems="10"
android:hint="@string/hint_username"
android:imeOptions="actionNext"
android:lines="1" />
</RelativeLayout>
<====================================================================>
Your manifest file should contains these permission
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
And full manifest file should look like this...
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ankit.account.manager"
android:versionCode="1"
android:versionName="1.0" >
<uses-sdk
android:minSdkVersion="8"
android:targetSdkVersion="21" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.AUTHENTICATE_ACCOUNTS"/>
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS"/>
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<service
android:name="ankit.account.manager.accountmanager.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>
<activity
android:name="ankit.account.manager.accountmanager.AddNewAccountActivity"
android:screenOrientation="portrait"
android:configChanges="keyboardHidden|keyboard|orientation"
>
</activity>
</application>
</manifest>
<====================================================================>
Enjoy...!!!
please upload project?
ReplyDelete@xml/authenticator ?
You can download source code here (https://github.com/AnkitIISc/Android-Acoount-Manager)
Delete