首页 > 解决方案 > 如何使用 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

标签: androidfirebase-authenticationandroid-jetpackandroid-navigationandroid-viewmodel

解决方案


您可以参考@AlexMamo 的本教程以获取更多详细信息:

https://medium.com/firebase-tips-tricks/how-to-create-a-clean-firebase-authentication-using-mvvm-37f9b8eb7336

我已经完全修改了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:popUpToapp: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;
            }
        }
    });

}

最后一步是删除工具栏的后退按钮。为此,我们需要在 中定义我们的顶级目的地MainActivityR.id.mainFragmentR.id.loginFragment

appBarConfiguration =
                new AppBarConfiguration.Builder(R.id.mainFragment, R.id.loginFragment)
                        .build();

我希望它有帮助!


推荐阅读