[Huawei Developers] devwithzachary:
Part 4 - Login and Register​

Today we are going to look at getting the Login and Register process fully complete. This will include some refactoring to the code we have worked on before.

Authentication Manager​

With a CloudDBManager now in place that is able to handle the User object we created its time we make changes to the AuthenticationManager so that this CloudDBManager is correctly used to retrieve user data at login/register.

Firstly we have a number of variables that we might be passing into the AuthenticationManager. Up until this point we where only passing in a phone number or an email address and this was handled by the contactString variable. However now that we will be accepting registration information, more data needs to be accept.

When logging in the use may be using their mobile phone number or their email address. When registering they might provide either the phone number or email address or both, and in addition a username and display name.

With these elements in mind lets create a simple data object to store this and pass it into the AuthenticationManager as needed. This will look like below, with standard Getters/Setters and constructor.

public class LoginRegisterData {
String phoneNumber,email,username,displayName;

public LoginRegisterData(String phoneNumber, String email, String username, String displayName) {
this.phoneNumber = phoneNumber;
this.email = email;
this.username = username;
this.displayName = displayName;
}

public String getPhoneNumber() {
return phoneNumber;
}

public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}

public String getEmail() {
return email;
}

public void setEmail(String email) {
this.email = email;
}

public String getUsername() {
return username;
}

public void setUsername(String username) {
this.username = username;
}

public String getDisplayName() {
return displayName;
}

public void setDisplayName(String displayName) {
this.displayName = displayName;
}

}

We will pass this data into the AuthenticationManager constructor like so:

public class AuthenticationManager implements CloudDBManager.UserCallBack{

Toaster toaster = new Toaster();
Context context;
int authType;
LoginRegisterData loginRegisterData;
boolean isLogin;
private final CloudDBManager dbManager;
private String loginUserUID = "0";

public AuthenticationManager(Context context, int authType, LoginRegisterData loginRegisterData, boolean isLogin){
this.context = context;
this.authType = authType;
this.loginRegisterData = loginRegisterData;
this.isLogin = isLogin;

dbManager = new CloudDBManager(context, this);
dbManager.createObjectType();
dbManager.openCloudDBZoneV2();
}

...
}

You will also notice that we have removed the contactString from the construct. As this variable has been removed we should also make sure to remove its usage and replace with the correct data from the LoginRegisterData object.

In places where we where expecting this string to contain the email address we should now use loginRegisterData.getEmail() and in places where we where expecting the phone number we should use loginRegisterData.getPhoneNumber().

Next lets take a look at the getUser() method. Up until now we have simply gotten the AGConnectUser for the currently authenticated user, however we haven't actually then done anything with that. Now we should use that authenticated user to get the stored User object from the database.

private void getUser(){
AGConnectUser user = AGConnectAuth.getInstance().getCurrentUser();
loginUserUID = user.getUid();
CloudDBZoneQuery<User> snapshotQuery = CloudDBZoneQuery.where(User.class).equalTo("uid", loginUserUID);
dbManager.queryUsers(snapshotQuery);
}
...

@Override
public void onQuery(List<User> userList) {
if (userList.size() == 1) {
User user = userList.get(0);
if (user.getUid().equals(loginUserUID)){
saveLoginDetail(user);
proceedToFeed();
}
}
}

Here we are getting the UID of the authenticated user and then querying the database for the user with that UID.

In the onQuery callback we can check that only one user was returned, and then triple check that the returned user does match the UID. From here we call two new methods saveLoginDetail() and proceedToFeed().

saveLoginDetail( ) is used to save a local copy of the logged in users ID and set a flag to say that we are now logged in. This way the next time the user opens the application we can check this flag and the user will not have to login every time they open the app.

private void saveLoginDetail(User user) {
SharedPreferences preferences = context.getSharedPreferences("loginDetail", 0);
SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean("isLoginedIn", true);
editor.putInt("userId", user.getId());
}

The proceedToFeed() method will simply start the FeedActivity now that we are logged in.

private void proceedToFeed(){
context.startActivity(new Intent(context, FeedActivity.class));
}

From the Registration side of the process the only thing to change is the addition of being able to set the username and display name as below.

private void saveRegisteredUser(SignInResult signInResult){
User user = new User();
user.setId(dbManager.getMaxUserID() + 1);
user.setUid(signInResult.getUser().getUid());
user.setUsername(loginRegisterData.getUsername());
user.setDisplayname(loginRegisterData.getDisplayName());
dbManager.upsertUser(user);
}

In the onUpsert call back we use the same two methods saveLoginDetail() and proceedtoFeed() as the login process.

@Override
public void onUpsert(User user){
saveLoginDetail(user);
proceedToFeed();
}

And that's it! Your AuthenticationManager should now look like this: https://gist.github.com/devwithzachary/97e23d7c813fd917a13ab88de34f9751

Of course as we have now changed the constructor there are some changes that need to be made in both the login and register activities.

Login Activity​

Within the MainActivity which is the login activity for us, lets start by creating a simple method to generate the LoginRegisterData object

private LoginRegisterData getLoginRegisterData() {
String emailString = email.getText().toString().trim();
String phoneString = phone.getText().toString().trim();

return new LoginRegisterData(phoneString, emailString, "", "");
}

As you can see we take the email and phone number input and build the object. At this point if we used this method in the phoneLogin and emailLogin onClick listeners we can see there is code duplication. So instead lets extract a method to trigger the login process.

private void login(int authType) {
authManager = new AuthenticationManager(MainActivity.this,
authType,
getLoginRegisterData(),
true);
authManager.sendVerifyCode();
}

As you can see we generate the AuthenticateManager passing in the LoginRegisterData and the authType. The OnClick Listeners for each button are now just one line calling this method and passing in the AuthType as needed. The MainActivity should now look like this: https://gist.github.com/devwithzachary/b9cb25f1b86b0503a5f4883345201b9d

Register Activity​

For the register activity we do the same process, however we will also add two new EditText fields so that we can accept the user input for username and displayname. Otherwise the process is the same. Create the LoginRegisterData and pass that into the AuthenticationManager. This this in mind the Register activity will look something like: https://gist.github.com/devwithzachary/10a3837b0e9abbc32e1b3dcf083f4276

And that's it! we are now in a good state with the login and register flow which will result in a user being authenticated, logged in and use saving the ID of that user along with setting a flag to confirm the user is logged in.