On my fragment where a user is checked to see whether their email is in the database and either go to the sign up or login flow, I use a ViewModel, this viewmodel uses email as MutableLiveData, this works fine, however when a user goes back, it will remember that email is already saved and then talk to the API instantly, pushing them back forward again.
What method is used for waiting for a user to invoke? With everything else so far, with menus, it was right that it would fetch the data without user intervention and change the data on display to the user if changed. This however is different.
My (admittedly poor) viewmodel is:
public class ExistingUserViewModel extends ViewModel {
private final MutableLiveData<String> email = new MutableLiveData<>();
private final LiveData<ApiResponse<ExistingUserResponse>> existingUser;
@Inject
public ExistingUserViewModel(@NonNull ExistingUserRepository existingUserRepository){
existingUser = Transformations.switchMap(email, input -> {
if (input == null){
return AbsentLiveData.create();
}
return existingUserRepository.isExistingUser(input);
});
}
public LiveData<ApiResponse<ExistingUserResponse>> getIsExistingUser() { return existingUser; }
@VisibleForTesting
public void setEmail(String email1){
email.setValue(email1);
}
}
My Fragment:
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
((MainActivity) getActivity()).getSupportActionBar().hide();
existingUserViewModel = ViewModelProviders.of(this, viewModelFactory).get(ExistingUserViewModel.class);
existingUserListener();
}
private void refreshAndObserve() {
existingUserViewModel.getIsExistingUser().observe(this, result -> {
if (result.isSuccessful()) {
try {
String email = binding.get().emailEditText.getText().toString();
if (result.body.getExists()) { // user exists
navigationController.navigateToLogin(email,null);
} else {
navigationController.navigateToRegisterName(email);
}
} catch (NullPointerException e){
Toast.makeText(getActivity(),e.toString(),Toast.LENGTH_SHORT).show();
}
} else {
Toast.makeText(getActivity(),result.errorMessage,Toast.LENGTH_SHORT).show();
}
});
}
private void existingUserListener(){
binding.get().continueButton.setOnClickListener(v -> submitDetails(v));
binding.get().emailEditText.setOnKeyListener((v, keyCode, event) -> {
if ((event.getAction() == KeyEvent.ACTION_DOWN)
&& (keyCode == KeyEvent.KEYCODE_ENTER)) {
submitDetails(v);
return true;
}
return false;
});
}
private void submitDetails(View v) {
refreshAndObserve();
String email = binding.get().emailEditText.getText().toString();
dismissKeyboard(v.getWindowToken());
//todo: if valid do below
existingUserViewModel.setEmail(email);
}
I have played around when to do the observe but not sure how is best to deal with this data retention issue
The only method i can think of for now is to just apply a boolean to submitDetails to isUserClicked and put that as false in onCreateView and put it true again in submitDetails then change the observer to be && isUserClicked. Not happy with this as a solution, but it will at least work.
also noticed I had to remove an observer as it was stacking onto my popstack, so solved that if anyone else was interested
try {
String email = binding.get().emailEditText.getText().toString();
existingUserViewModel.getIsExistingUser().removeObservers(this);
if (result.body.getExists()) { // user exists
navigationController.navigateToLogin(email, null);
} else {
navigationController.navigateToRegisterName(email);
}
} catch (NullPointerException e) {
Toast.makeText(getActivity(), e.toString(), Toast.LENGTH_SHORT).show();
}