2016年10月10日 星期一

雲端記事本<一>:以firebase實作使用者登入驗證(Login Authentication)

此範例完成後將會顯示一登入畫面

可快速選擇預設兩個使用者作登入
(當然你也可以自己在firebase console新增帳戶)

登入後主畫面下方會顯示目前的使用者

 



也可以利用主畫面右上方logout登出,重新回到登入畫面

整個流程如下圖所示:

這樣流程的好處是,當下次啟動程式時,若上次使用已有登入,即可直接進入MainActivity主畫面,不需作重新登入的動作!

有了這個身分認證的步驟,後面的應用就相當多了

可根據不同登入的使用者,結合firebase後端作不同的應用(如套用各自偏好設定,顯示個別紀錄等等)

另外firebase也提供了許多身分認證的方式:自建電子郵件帳密、Google帳戶、Facebook、Twitter、Github、匿名登入...

這個範例將會使用「自建電子郵件帳密」作身分認證,在使用上也會比較有彈性

直接看程式碼吧!解說的部份我直接放在程式碼註解部份~

> AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.pingtung.ccstudio.notepadcloud">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".LoginActivity"></activity>
    </application>

</manifest>

> activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_login"
    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="com.pingtung.ccstudio.notepadcloud.LoginActivity">

    <TextView
        android:text="Email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/textView"
        android:textSize="24sp" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textEmailAddress"
        android:text="email"
        android:ems="10"
        android:layout_below="@+id/textView"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/etMail" />

    <TextView
        android:text="Password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/etMail"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/textView2"
        android:textSize="24sp" />

    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:inputType="textPersonName"
        android:text="password"
        android:ems="10"
        android:layout_below="@+id/textView2"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/etPwd" />

    <Button
        android:text="Login"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="103dp"
        android:id="@+id/btnLogin" />

    <RadioGroup
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/btnLogin"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="18dp"
        android:id="@+id/radioGroup">

        <RadioButton
            android:text="User1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/rbUser1" />

        <RadioButton
            android:text="Test1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:id="@+id/rbTest1"/>
    </RadioGroup>
</RelativeLayout>

> LoginActivity.java
package com.pingtung.ccstudio.notepadcloud;

import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.tasks.OnCompleteListener;
import com.google.android.gms.tasks.Task;
import com.google.firebase.auth.AuthResult;
import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;


public class LoginActivity extends AppCompatActivity {
    //UI variables
    private EditText etMail;
    private EditText etPwd;
    private Button btnLogin;
    private RadioGroup radioGroup;
    private RadioButton rbUser1, rbTest1;

    //Firebase instance variables
    private FirebaseAuth auth;
    private FirebaseUser user;
    private FirebaseAuth.AuthStateListener authListener;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_login);

        etMail = (EditText)findViewById(R.id.etMail);
        etPwd = (EditText)findViewById(R.id.etPwd);
        btnLogin = (Button)findViewById(R.id.btnLogin);
        radioGroup = (RadioGroup)findViewById(R.id.radioGroup);
        rbUser1 = (RadioButton)findViewById(R.id.rbUser1);
        rbTest1 = (RadioButton)findViewById(R.id.rbTest1);

        //取得auth實體(只有一個實體app內皆能共用)
        auth = FirebaseAuth.getInstance();

        btnLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String email = etMail.getText().toString();
                String password = etPwd.getText().toString();

                //使用email, password作登入
                auth.signInWithEmailAndPassword(email,password)
                        .addOnCompleteListener(new OnCompleteListener<AuthResult>() {
                    @Override
                    public void onComplete(@NonNull Task<AuthResult> task) {
                        if(task.isSuccessful()) {
                            //登入成功
                            Log.d("android", "signInWithEmail:onComplete:" + task.isSuccessful());
                        }
                        else {
                            //登入失敗
                            Log.w("android", "signInWithEmail:failed", task.getException());
                            Toast.makeText(LoginActivity.this, "請檢查登入帳密是否正確輸入",
                                    Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        });

        radioGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(RadioGroup radioGroup, int checkedId) {
                switch(checkedId){
                    case R.id.rbUser1:
                        etMail.setText("user1@example.com");
                        etPwd.setText("abc123");
                        break;
                    case R.id.rbTest1:
                        etMail.setText("test1@example.com");
                        etPwd.setText("xyz123");
                        break;
                }
            }
        });
        rbUser1.setChecked(true);

        //宣告auth狀態變更的監聽器
        authListener = new FirebaseAuth.AuthStateListener() {
            @Override
            public void onAuthStateChanged(@NonNull FirebaseAuth firebaseAuth) {

                user = firebaseAuth.getCurrentUser();

                if (user!=null) {
                    Log.d("android", "已登入: UserUid="+user.getUid());
                    startActivity(new Intent(LoginActivity.this, MainActivity.class));
                    finish();
                }else{
                    Log.d("android", "已登出");
                }

            }
        };
    }

    @Override
    protected void onStart() {
        super.onStart();

        //加入auth監聽器
        auth.addAuthStateListener(authListener);
    }

    @Override
    protected void onStop() {
        super.onStop();

        //移除auth監聽器
        auth.removeAuthStateListener(authListener);
    }
}

> activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    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="com.pingtung.ccstudio.notepadcloud.MainActivity">

    <TextView
        android:text="LoginUser:"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:id="@+id/textView6"
        android:textSize="18sp"
        android:textColor="@android:color/black" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="UserName"
        android:id="@+id/tvUser"
        android:textSize="18sp"
        android:layout_marginLeft="17dp"
        android:layout_marginStart="17dp"
        android:layout_alignParentBottom="true"
        android:layout_toRightOf="@+id/textView6"
        android:layout_toEndOf="@+id/textView6"
        android:textColor="@android:color/holo_red_light" />

</RelativeLayout>

> MainActivity.java
package com.pingtung.ccstudio.notepadcloud;

import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

import com.google.firebase.auth.FirebaseAuth;
import com.google.firebase.auth.FirebaseUser;

public class MainActivity extends AppCompatActivity {
    //UI variables
    private TextView tvUser;

    //Firebase instance variables
    private FirebaseAuth auth;
    private FirebaseUser user;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //find UId
        tvUser = (TextView)findViewById(R.id.tvUser);

        //取得auth實體(只有一個實體app內皆能共用)
        auth = FirebaseAuth.getInstance();
        user = auth.getCurrentUser();

        //若無登入帳戶,則getCurrentUser()傳回null
        //開啟LoginActivity
        if(user==null){
            startActivity(new Intent(this,LoginActivity.class));
            finish();
        }else{
            tvUser.setText(user.getEmail());
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_main,menu);

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){
            case R.id.logout:
                //將auth登出
                auth.signOut();
                startActivity(new Intent(this,LoginActivity.class));
                finish();
                break;
        }

        return super.onOptionsItemSelected(item);
    }
}

> menu_main.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:apps="http://schemas.android.com/apk/res-auto">
    <item android:title="logout"
        android:id="@+id/logout"
        apps:showAsAction="never"></item>
</menu>


> 完整程式碼(Github)


沒有留言:

張貼留言