2025년 10월 22일 수요일

Unity Google Sign In ...

Prepare your apps for Google Play’s 16 KB page size compatibility requirement

 

Deprecated 되었으나 로그인 관련 수정을 하고 싶지는 않았다 ㅠㅠ

 

1. Unity Engine support for 16 KB memory page sizes (Android 15+)


    
    Unity 2021.3.39f1 버전을 사용중이어서 위 링크의 내용을 얼핏 보고 2021.3.45f1 버전도
    지원하는 것으로 착각하고 열심히 버전을 올리고 스토어에 업로드 했는데 아니었다.
    Enterprise나 Industry 고객만 다운로드 할 수 있는 2021.3.48f1+만 지원 대상이었다.
    
    하는 수 없이 2022.3.62f1 버전으로 업데이트를 진행했다.
 

2. 유니티 엔진 업데이트로는 모두 다 해결되지 않는다.


    프로젝트에서 사용중인 외부 라이브러리도 모두 16 KB 페이지 사이즈를 사용하도록
    빌드 되어 있어야만 한다.

    아무것도 모르고 빌드가 성공하고 잠깐의 테스트가 성공해서 스토어에 배포를 
    시작했다가 무한 크래시로 유저들의 빗발치는 악담을 들은 건 넘어가기로 하고...

    문제는 Google Sign-In Unity Plugin은 구글이 이제 거의 7년을 방치해 놓고 있다는 사실.

3. 해결 방법.


    공식적으로 구글이 유니티 플러그인으로 공식 지원하는 건
    Google Play Games plugin for Unity 

    이걸 사용하려고 했다면 처음부터 그랬겠지만 이것 저것 덕지 덕지 붙어 있는 건 별로라.

    에셋 스토어에 있는 에셋부터 Google Sign-In Unity Plugin 컴파일까지 고려한 해결 
    방법은 많았지만 Google Sign-In 플러그인을 만들기로 했다. 
    아주 쉽게 Unity Android native plugin 방식으로.

    우선 Google Sign-In Unity Plugin 패키지를 지워준다.

    그리고, Java로 native plugin을 만든 뒤 /Assets/Plugins/Android/ 경로에 옮겨준다.
    동작이야 Google Sign-In Unity Plugin과 비슷하므로 자세한 설명은 생략하고
    코드만 정리해 놓는다.

   
package com.ships.googlesigninplugin;
 
import org.json.JSONObject;
import org.json.JSONException;
 
import android.app.Activity;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
 
import com.google.android.gms.auth.api.signin.*;
import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.tasks.Task;
import com.google.android.gms.tasks.OnCompleteListener;
import com.unity3d.player.UnityPlayer;
 
public class GoogleSignInPlugin {
 
    private static final String TAG = "GoogleSignInPlugin";
    private static final int RC_SIGN_IN = 1002;
    private static GoogleSignInClient mGoogleSignInClient;
    private static String unityCallbackObject = "_GoogleManagers";
 
    public static void setWebClientId(String clientId) {
        Activity activity = UnityPlayer.currentActivity;
        Log.d(TAG, "setWebClientID - " + clientId);
        GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
                .requestEmail()
                .requestServerAuthCode(clientId)
                .build();
 
        mGoogleSignInClient = GoogleSignIn.getClient(activity, gso);
    }
 
    public static void signIn() {
        Activity activity = UnityPlayer.currentActivity;
 
        Intent signInIntent = mGoogleSignInClient.getSignInIntent();
        activity.startActivityForResult(signInIntent, RC_SIGN_IN);
    }
 
    public static void silentSignIn() {
        Activity activity = UnityPlayer.currentActivity;
 
        if (mGoogleSignInClient == null) {
            sendResult(falsenullnull"GoogleSignInClient not initialized");
            return;
        }
 
        Task<GoogleSignInAccount> task = mGoogleSignInClient.silentSignIn();
        if (task.isSuccessful()) {
            handleSignInResult(task);
        } else {
            task.addOnCompleteListener(activity, new OnCompleteListener<GoogleSignInAccount>() {
                @Override
                public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
                    if (task.isSuccessful()) {
                        handleSignInResult(task);
                    } else {
                        // fallback to interactive
                        signIn();
                    }
                }
            });
        }
    }
 
    public static void handleActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d(TAG, "handleActivityResult");
        if (requestCode == RC_SIGN_IN) {
            Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(data);
            handleSignInResult(task);
        } else {
            Log.d(TAG, "requestCode: " + requestCode + "resultCode: " + resultCode);
        }
    }
 
    public static void signOut() {
        if (mGoogleSignInClient != null) {
            mGoogleSignInClient.signOut();
        }
    }
 
    private static void handleSignInResult(Task<GoogleSignInAccount> completedTask) {
        try {
            GoogleSignInAccount account = completedTask.getResult(ApiException.class);
 
            String email = account.getEmail();
            String idToken = account.getServerAuthCode();
 
            sendResult(true, email, idToken, null);
        } catch (ApiException e) {
            Log.w(TAG, "signInResult:failed code=" + e.getStatusCode());
            sendResult(falsenullnull, e.getMessage());
        }
    }
 
    private static void sendResult(boolean success, String email, String idToken, String error) {
        try {
            JSONObject json = new JSONObject();
            json.put("success", success);
            json.put("email", email != null ? email : JSONObject.NULL);
            json.put("idToken", idToken != null ? idToken : JSONObject.NULL);
 
            Log.d(TAG, json.toString());
            UnityPlayer.UnitySendMessage(unityCallbackObject, "GoogleLoginResult", json.toString());
        } catch (Exception e) {
            Log.d(TAG, "Error sending result to Unity", e);
        }
    }
 
    private static void handleAccount(GoogleSignInAccount account) throws JSONException {
        JSONObject jsonObject = new JSONObject();
        if (account != null) {
            String email = account.getEmail();
            String idToken = account.getIdToken();
            String id = account.getId();
 
            jsonObject.put("success"true);
            jsonObject.put("email", email);
            jsonObject.put("idToken", idToken);
            Log.d(TAG, "Result: " + jsonObject.toString());
            UnityPlayer.UnitySendMessage(unityCallbackObject, "GoogleLoginResult", jsonObject.toString());
        } else {
            jsonObject.put("success"false);
            jsonObject.put("email""Account is null");
            Log.d(TAG, "Result: " + jsonObject.toString());
            UnityPlayer.UnitySendMessage(unityCallbackObject, "GoogleLoginResult", jsonObject.toString());
        }
    }
}
 
cs
 
    그리고 유니티에서 인증 결과를 받을 수 있도록 게임 오브젝트와 스크립트를 준비한다.

   
public class GoogleLoginResult
{
    public bool success;
    public string email;
    public string idToken;
}
 
public class GoogleAccountManager : MonoBehavior
{
    void Start()
    {
        _pluginClass = new AndroidJavaClass("com.ships.googlesigninplugin.GoogleSignInPlugin");
    }
 
    void SignIn()
    {
        _pluginClass?.CallStatic("setWebClientId", clientId);
    }
 
    public void GoogleLoginResult(string json)
    {
        GoogleLoginResult r = JsonUtility.FromJson<GoogleLoginResult>(json);
        if (r.success == true)
        {
            // 인증 검증.
        }
    }
}
cs


    이 후에 한참 디버깅 했는데, Main Activity에서 구글 로그인 결과를 다시 
    플러그인으로 넘겨줘야 한다.

   
public class MessagingUnityPlayerActivity extends UnityPlayerActivity {
    // 나머지 코드는 생략.
 
    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        Log.d("MessagingUnityPlayerActivity""Override onActivityResult" + this);
        GoogleSignInPlugin.handleActivityResult(requestCode, resultCode, data);
        super.onActivityResult(requestCode, resultCode, data);
    }
}
cs

4. 결론.


    구글 정말 싫다~~~~~~

댓글 없음:

댓글 쓰기