android - 如何使用 Android Jetpack 执行条件导航?
问题描述
我试图让用户在登录后访问我的主页,但我正在尝试使用 Android 条件导航来做到这一点。问题是我正在使用 android Firebase 在我的用户进入此屏幕之前对其进行身份验证,并且需要为其创建一个视图模型,但我不知道如何重构在线指南以实现此结果任何人都可以指出我需要什么做?
谢谢
这是 ViewModel 的指南
public class LoginViewModel extends ViewModel {
public enum AuthenticationState {
UNAUTHENTICATED, // Initial state, the user needs to authenticate
AUTHENTICATED, // The user has authenticated successfully
INVALID_AUTHENTICATION // Authentication failed
}
final MutableLiveData<AuthenticationState> authenticationState =
new MutableLiveData<>();
String username;
public LoginViewModel() {
// In this example, the user is always unauthenticated when MainActivity is launched
authenticationState.setValue(AuthenticationState.UNAUTHENTICATED);
username = "";
}
public void authenticate(String username, String password) {
if (passwordIsValidForUsername(username, password)) {
this.username = username;
authenticationState.setValue(AuthenticationState.AUTHENTICATED);
} else {
authenticationState.setValue(AuthenticationState.INVALID_AUTHENTICATION);
}
}
public void refuseAuthentication() {
authenticationState.setValue(AuthenticationState.UNAUTHENTICATED);
}
private boolean passwordIsValidForUsername(String username, String password) {
...
}
}
可以在以下位置找到主题参考:https ://developer.android.com/guide/navigation/navigation-conditional
解决方案
您可以参考@AlexMamo 的本教程以获取更多详细信息:
我已经完全修改了LoginViewModel
.
public class LoginViewModel extends ViewModel {
public static final String TAG = "debinf LogVIEWMODEL";
private LoginRepository loginRepository;
// https://developer.android.com/guide/navigation/navigation-conditional
// https://github.com/googlecodelabs/android-kotlin-login/blob/master/app/src/main/java/com/example/android/firebaseui_login_sample/LoginViewModel.kt
public LoginViewModel() {
loginRepository = new LoginRepository();
}
public LiveData<FirebaseUser> getFirebaseUserFromAuthState() {
return loginRepository.getFirebaseUserFromAuthState();
}
public LiveData<AuthResult> getFirebaseAuthResultFromPhoneSignIn(PhoneAuthCredential phoneAuthCredential) {
return loginRepository.getFirebaseAuthResultFromPhoneSignIn(phoneAuthCredential);
}
public LiveData<AuthResult> getFirebaseAuthResultFromEmailSignIn(String email, String password) {
return loginRepository.getFirebaseAuthResultFromEmailSignIn(email, password);
}
}
下面是LoginRepository
。
public class LoginRepository {
public LiveData<FirebaseUser> getFirebaseUserFromAuthState() {
return new FirebaseAuthLiveData();
}
public LiveData<AuthResult> getFirebaseAuthResultFromPhoneSignIn(PhoneAuthCredential phoneAuthCredential) {
return new FirebaseSignInLiveData(phoneAuthCredential);
}
public LiveData<AuthResult> getFirebaseAuthResultFromEmailSignIn(String email, String password) {
return new FirebaseSignInLiveData(email, password);
}
}
该类FirebaseAuthLiveData
用于检查用户是否已登录。
public class FirebaseAuthLiveData extends LiveData<FirebaseUser> {
// https://github.com/googlecodelabs/android-kotlin-login/blob/master/app/src/main/java/com/example/android/firebaseui_login_sample/FirebaseUserLiveData.kt
FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
FirebaseAuth.AuthStateListener authStateListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {
setValue(firebaseAuth.getCurrentUser());
}
};
@Override
protected void onActive() {
super.onActive();
firebaseAuth.addAuthStateListener(authStateListener);
}
@Override
protected void onInactive() {
super.onInactive();
firebaseAuth.removeAuthStateListener(authStateListener);
}
}
FirebaseSignInLiveData
用于使用电话号码或电子邮件和密码登录。
public class FirebaseSignInLiveData extends LiveData<AuthResult> {
public static final String TAG = "debinf SignInLiveData";
private FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
private PhoneAuthCredential phoneAuthCredential;
private String email, password;
private boolean isPhoneAuthentication;
private AuthResult authResult;
public FirebaseSignInLiveData(PhoneAuthCredential phoneAuthCredential) {
isPhoneAuthentication = true;
this.phoneAuthCredential = phoneAuthCredential;
}
public FirebaseSignInLiveData(String email, String password) {
isPhoneAuthentication = false;
this.email = email;
this.password = password;
}
OnCompleteListener<AuthResult> onCompleteListener = new OnCompleteListener<AuthResult>() {
@Override
public void onComplete(@NonNull Task<AuthResult> task) {
if (task.isSuccessful()) {
// Sign in success, update UI with the signed-in user's information
authResult = task.getResult();
setValue(authResult);
} else {
setValue(authResult);
if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
// The verification code entered was invalid
}
}
}
};
@Override
protected void onActive() {
super.onActive();
if (isPhoneAuthentication) {
firebaseAuth.signInWithCredential(phoneAuthCredential).addOnCompleteListener(onCompleteListener);
} else {
firebaseAuth.signInWithEmailAndPassword(email, password).addOnCompleteListener(onCompleteListener);
}
}
}
我已将条件导航放在 中MainActivity
,因为我不希望用户在登录之前导航到任何地方。但是我认为如果将条件放在其他片段(例如 Profile)中就可以了。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// REDIRECT TO LOGIN IF NOT LOGGED IN - CONDITIONAL NAVIGATION
LoginViewModel loginViewModel = ViewModelProviders.of(this).get(LoginViewModel.class);
loginViewModel.getFirebaseUserFromAuthState().observe(this, new Observer<FirebaseUser>() {
@Override
public void onChanged(FirebaseUser firebaseUser) {
if (firebaseUser != null) {
Log.i(TAG, "onAuthStateChanged: SAVE USER DATA AND GO TO MAIN FRAGMENT");
} else {
Log.i(TAG, "onAuthStateChanged: YOU ARE NOT LOGGED IN - REDIRECTING TO LOGIN");
navController.navigate(R.id.action_global_start_loginFragment);
}
}
});
}
通过在 中添加app:popUpTo
和app:popUpToInclusive="true"
到我们的全局操作,mainnav_graph.xml
如果用户未完成登录,应用程序将关闭。
<navigation
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/mainnav_graph"
app:startDestination="@id/mainFragment">
<fragment
android:id="@+id/mainFragment"
android:name="com.myapp.SectorMain.MainFragment"
tools:layout="@layout/fragment_main" />
<!-- Global action-->
<action
android:id="@+id/action_global_start_loginFragment"
app:destination="@id/login_navgraph"
app:launchSingleTop="true"
app:popUpTo="@id/mainnav_graph"
app:popUpToInclusive="true"/>
<navigation
android:id="@+id/login_navgraph"
app:startDestination="@id/loginFragment">
<fragment
android:id="@+id/loginFragment"
android:name="com.myapp.LoginFragment"
tools:layout="@layout/fragment_login" >
<action
android:id="@+id/action_loginFragment_to_termsFragment"
app:destination="@id/termsFragment" />
</fragment>
<fragment
android:id="@+id/termsFragment"
android:name="com.myapp.TermsFragment"
android:label="fragment_terms"
tools:layout="@layout/fragment_terms" >
<action
android:id="@+id/action_termsFragment_to_userregisterFragment"
app:destination="@id/userregisterFragment" />
</fragment>
<fragment
android:id="@+id/userregisterFragment"
android:name="com.myapp.UserregisterFragment"
android:label="fragment_userregister"
tools:layout="@layout/fragment_userregister" />
</navigation>
</navigation>
获取后phoneAuthCredential
,用户可以登录如下LoginFragment
private void sighInWithPhoneAuthCredential(PhoneAuthCredential phoneAuthCredential) {
Log.i(TAG, "sighInWithPhoneAuthCredential: ");
loginViewModel.getFirebaseAuthResultFromPhoneSignIn(phoneAuthCredential).observe(getViewLifecycleOwner(), new Observer<AuthResult>() {
@Override
public void onChanged(AuthResult authResult) {
if (authResult != null) {
FirebaseUser firebaseUser = authResult.getUser();
if (firebaseUser != null) {
Log.i(TAG, "Add listener to User's node in real time database or firestore");
/*navController.popBackStack(R.id.mainnav_graph, false);
navController.navigate(R.id.mainnav_graph);*/
// pop up LoginFragment and go to MainFragment
NavOptions.Builder navBuilder = new NavOptions.Builder();
NavOptions navOptions = navBuilder
.setPopUpTo(R.id.mainnav_graph, true)
.build();
NavHostFragment.findNavController(LoginFragment.this).navigate(R.id.mainFragment, null, navOptions);
} else {
Log.i(TAG, "User not found");
mVerificationId = null;
}
} else {
Log.i(TAG, "The verification code entered was invalid");
mVerificationId = null;
}
}
});
}
最后一步是删除工具栏的后退按钮。为此,我们需要在 中定义我们的顶级目的地,MainActivity
即R.id.mainFragment
和R.id.loginFragment
。
appBarConfiguration =
new AppBarConfiguration.Builder(R.id.mainFragment, R.id.loginFragment)
.build();
我希望它有帮助!
推荐阅读
- excel - UserForm 根据用户定义的范围自动填充字段
- php - 使用 rest api 设置的 WordPress 数据库
- class - 如何使用 mongo 模式中不需要的数据创建模型
- excel - VBA 保存的工作簿不会打开到最后一个活动工作表
- angularjs - angular-ui-swiper:未定义 swiper
- python - 如何解析非零填充的微秒?
- tensorflow - 用正确的损失函数对抗类别不平衡:IoU、Dice 还是 2-class Dice?
- visual-studio-code - 从 VS Code 路径变量中提取段
- django - 如何确保每次调用视图时代码都运行 - Django
- spring-boot - 使用 Springboot Application 处理 HttpSession 属性