12/08/2018, 15:00

Xây dựng ứng dụng Android Sign-In bằng Google

Nhiều ứng dụng cần thiết lập danh tính của người dùng để thiết lập ứng dụng sao cho phù hợp với từng người dùng cụ thể hoặc để định danh người dùng hay thực hiện nhiều kịch bản khác tùy theo mục đích. Có nhiều cách để thiết lập danh tính người dùng như triển khai chức năng đăng nhập riêng của ứng ...

Nhiều ứng dụng cần thiết lập danh tính của người dùng để thiết lập ứng dụng sao cho phù hợp với từng người dùng cụ thể hoặc để định danh người dùng hay thực hiện nhiều kịch bản khác tùy theo mục đích. Có nhiều cách để thiết lập danh tính người dùng như triển khai chức năng đăng nhập riêng của ứng dụng, hoặc có thể cho phép người dùng đăng nhập sử dụng các chứng chỉ từ các nền tảng phổ biến như Facebook, Twitter hay Google. Và Google đã xây dựng Google Identity Platform để hỗ trợ việc này, nó bao gồm Google Sign-In for Android, iOS và the Web, Google Identity Toolkit cho phép bạn tích hợp nhiều identity providers, OAuth 2.0 cho các kịch bản sign-in phổ biến, và Chrome Identity API cho phép người dùng đăng nhập trên Chrome Apps. Sau đây mình sẽ giới thiệu cách đưa Google Sign-In vào ứng dụng Android của bạn.

Đầu tiên điều bạn cần đó là khởi tạo ứng dụng của bản thân trên: https://console.developers.google.com/project

Tạo tên ứng dụng và project ID mà bạn muốn. Sau khi thành công thì nó sẽ dư này

Enable the Google+ API

Khi đang ở trang API Library chọn mục Social APIs (biểu tượng là cái G+ màu đỏ đó) -> tiếp tục chọn Google+ API -> Enable Api

Configure Credentials

Tiếp theo chúng ra cần cấu hình chứng chỉ xác thực, để làm việc này thì ta chọn Create credentials.

Cấu hình chứng chỉ xác thực để đảm bảo rằng ứng dụng của bạn không phải là một ứng dụng giả mạo sử dụng hạ tầng của Google. Đối với mục đích phát triển thì bạn sẽ cần một debug certificate. Còn khi triển khai ứng dụng lên Play Store thì lúc đó bạn phải cần ‘real’ certificate, bạn có thế xem thêm tại đây: https://developer.android.com/tools/publishing/app-signing.html Khi đã có chứng chỉ thì bạn cần xác định bằng một SHA-1 fingerprint. Để có SHA-1 thì:

  • On a Mac or Linux, this is in the ~/.android directory.
  • On Windows it is at C:Users<your user name>.android.C:Users<your user name>.android.

rồi sử dụng comand:

keytool -list -v -keystore ~/.android/debug.keystore -alias androiddebugkey -storepass android -keypass android

và rồi ta sẽ được

sau khi đã có SHA-1, bạn cần thiết lập đầy đủ thông tin trong Create an OAuth 2.0 client ID và Set up the OAuth 2.0 consent screen

Khi thông tin đã đầy đủ ấn Done thôi

Lưu ý: package name của ứng dụng tạo phải đúng với package name khi bạn khai báo để tạo chứng chỉ xác thực bên trên.

Configure Build.gradle

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    
    compile 'com.android.support:appcompat-v7:25.2.0'
    compile 'com.google.android.gms:play-services:10.2.0'
}

Và bạn cần update Google Play Services, cứ hàng mới nhất mà xài.

Configure AndroidManifest.xml

Đồng thời bạn cũng cần thiết lập một số giá trị trong manifest để có thể sử dụng Google Sign-in

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.USE_CREDENTIALS" />

Build a Basic UI

Và làm thao để chúng ta có nút Google Sign-In. Tất cả đơn giản là đây

<com.google.android.gms.common.SignInButton
            android:id="@+id/sign_in_button"
            android:layout_awidth="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="false" />

Coding thôi

Implement các callback cần thiết cho MainActivity

public class MainActivity extends FragmentActivity implements ConnectionCallbacks, OnConnectionFailedListener, View.OnClickListener {

Build the Api Client

GoogleApiClient sẽ được sử dụng để control việc sign-in, vì vậy ta cần tạo một GoogleApiClient theo nhu cầu. Google Api Client là điểm truy cập chính đến Google Play services. Để tạo một Google Api Client, bạn cần chỉ định số thuộc tính:

  • Location of Connection Callbacks: Google Api Client có tính chất không đồng bộ cao, các function cần được implemented khi nó được kết nối (onConnected()) và khi kết nối bị đình chỉ (onConnectionSuspended()).
  • Location of Connection Failed callback: onConnectedFailed() sẽ được gọi đến khi connection failure.
  • Google Play services supports khá nhiều API, vì vậy bạn cần chỉ định rõ dịch vụ mà mình cần kết nối tới.
  • Trong trường hợp đăng nhập bạn cũng cần phải chỉ định Login Scope (Phạm vi đăng nhập). Điều này sẽ xác định số lượng thông tin về người dùng mà ứng dụng sẽ truy cập. Chi tiết về phạm vi bạn có thể đọc ở đây: https://developers.google.com/+/api/oauth
private GoogleApiClient buildGoogleApiClient() {
        return new GoogleApiClient.Builder(this)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .addApi(Plus.API, Plus.PlusOptions.builder().build())
                .addScope(new Scope("email"))
                .build();
}

Implement onStart() and onStop()

Ta sẽ thực hiện kết nối đến Google Play services tại onStart() và ngừng kết nối tại onStop() của Activity.

@Override
protected void onStart() {
    super.onStart();
    mGoogleApiClient.connect();
}

@Override
protected void onStop() {
    super.onStop();
    mGoogleApiClient.disconnect();
}

Handle Suspended scenarios

Trong một số trường hợp kết nối đến Google Play services có thể bị drop, nó không bị ngắt kết nối nhưng do một số lý do mà bị mất kết nối. Vì vậy nếu điều này xảy ra, bạn nên cố gắng kết nối trở lại.

@Override
public void onConnectionSuspended(int cause) {
    mGoogleApiClient.connect();
}

Handle Connection to Google Play services

Thời điểm ứng dụng kết nối với dịch vụ chính là lúc người dùng đăng nhập. Khi onConnected() callback được gọi đến, bạn sẽ biết rằng người dùng đã được kết nối. Và ta có thể nhận được một số thông tin của người dùng, nhưng hãy nhớ thông tin lấy đươc thuộc phạm vi nhất định, nếu bạn cố gắng lấy thông tin nào ngoài phạm vi đó thì sẽ nhảy ra exception.

@Override
public void onConnected(Bundle connectionHint) {
    mSignInButton.setEnabled(false);
    mSignOutButton.setEnabled(true);
    mRevokeButton.setEnabled(true);

    mSignInProgress = SIGNED_IN;

    try {
        String emailAddress = Plus.AccountApi.getAccountName(mGoogleApiClient);
        mStatus.setText(String.format("Signed In to My App as %s", emailAddress));
    }
    catch(Exception ex){
        String exception = ex.getLocalizedMessage();
        String exceptionString = ex.toString();
    }
}

Handle Connection Failures

Đăng nhập với Google có một chút đặc biệt khi sảy ra Connection Failures. Thông thường, kết nối không thành công với Google Play services xảy ra bởi vì bạn mất kết nối, nhưng khi signin nó sẽ thường xuyên xảy ra hơn vì người dùng đang trong trạng thái chuyển tiếp, nơi họ chưa đăng nhập hoàn toàn. Có thể có 2 bước trung gian và khi người dùng chưa hoàn thành chúng, Google Play services sẽ trả về connection failure. Bước đầu tiên trong 2 bước xảy ra khi người dùng có nhiều tài khoản Google trên cùng thiết bị. Android sẽ show popup hỏi tài khoản nào nên sử dụng. Tại thời điểm này, kết nối của bạn không thành công. Bước thứ 2 xảy ra khi ứng dụng hiển thị popup yêu cầu người dùng cho phép ứng dụng truy cập thông tin dựa trên phạm vi được chỉ định.

    @Override
    public void onConnectionFailed(ConnectionResult result) {
        if (mSignInProgress != STATE_IN_PROGRESS) {
            mSignInIntent = result.getResolution();
            if (mSignInProgress == STATE_SIGNING_IN) {
                resolveSignInError();
            }
        }
        onSignedOut();
    }

    private void resolveSignInError() {
        if (mSignInIntent != null) {
            try {
                mSignInProgress = STATE_IN_PROGRESS;
                startIntentSenderForResult(mSignInIntent.getIntentSender(),
                        RC_SIGN_IN, null, 0, 0, 0);
            } catch (IntentSender.SendIntentException e) {
                mSignInProgress = STATE_SIGNING_IN;
                mGoogleApiClient.connect();
            }
        } else {
        
        }
    }

Phương thức onConnectionFailed() kiểm tra xem quá trình đăng nhập khi nó vẫn đang trong tiến trình và nếu có, một Intent sẽ được trả về. Intent này sẽ đại diện cho hộp thoại yêu cầu người dùng đặp nhập hoặc cấp phép cho ứng dụng. Trong trường hợp này, phương thức resolveSignInError() sẽ được gọi đến. Phản hồi của người dùng sẽ được handle trong onActivityResult().

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case RC_SIGN_IN:
                if (resultCode == RESULT_OK) {
                    mSignInProgress = STATE_SIGNING_IN;
                } else {
                    mSignInProgress = SIGNED_IN;
                }

                if (!mGoogleApiClient.isConnecting()) {
                    mGoogleApiClient.connect();
                }
                break;
        }
    }

Implement onSignedOut()

Khi người dùng đăng xuất hoặc nếu đăng nhập của họ vì một lý do nào đó, ứng dụng của bạn cần phải cập nhật UI, vô hiệu hóa đăng xuất và thu hồi các nút truy cập và enable button login để người dùng có thể sử dụng lại.

private void onSignedOut() {
        mSignInButton.setEnabled(true);
        mSignOutButton.setEnabled(false);
        mRevokeButton.setEnabled(false);

        mStatus.setText("Signed out");
}

Implement the onClick handlers

    @Override
    public void onClick(View v) {
        if (!mGoogleApiClient.isConnecting()) {
            switch (v.getId()) {
                case R.id.sign_in_button:
                    mStatus.setText("Signing In");
                    resolveSignInError();
                    break;
                case R.id.sign_out_button:
                    Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
                    mGoogleApiClient.disconnect();
                    mGoogleApiClient.connect();
                    break;
                case R.id.revoke_access_button:
                    Plus.AccountApi.clearDefaultAccount(mGoogleApiClient);
                    Plus.AccountApi.revokeAccessAndDisconnect(mGoogleApiClient);
                    mGoogleApiClient = buildGoogleApiClient();
                    mGoogleApiClient.connect();
                    break;
            }
        }
    }

Run App

0