Finalized Google Sign In!!!
This commit is contained in:
1
siwg/ext.manifest
Normal file
1
siwg/ext.manifest
Normal file
@@ -0,0 +1 @@
|
||||
name: SIWG
|
7
siwg/manifests/android/AndroidManifest.xml
Normal file
7
siwg/manifests/android/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="{{android.package}}">
|
||||
<uses-sdk android:minSdkVersion="{{android.minimum_sdk_version}}" android:targetSdkVersion="{{android.target_sdk_version}}" />
|
||||
<application>
|
||||
<meta-data android:name="com.google.android.gms.games.APP_ID" android:value="\{{siwg.app_id}}" />
|
||||
</application>
|
||||
</manifest>
|
6
siwg/manifests/android/build.gradle
Normal file
6
siwg/manifests/android/build.gradle
Normal file
@@ -0,0 +1,6 @@
|
||||
dependencies {
|
||||
// https://developers.google.com/android/guides/setup#split
|
||||
implementation 'com.google.android.gms:play-services-base:17.5.0'
|
||||
implementation 'com.google.android.gms:play-services-auth:18.1.0'
|
||||
implementation 'com.google.android.gms:play-services-games:20.0.1'
|
||||
}
|
21
siwg/src/com_aterve_siwg_SiwgJNI.h
Normal file
21
siwg/src/com_aterve_siwg_SiwgJNI.h
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#include <jni.h>
|
||||
/* Header for class com_aterve_siwg_SiwgJNI */
|
||||
|
||||
#ifndef COM_ATERVE_SIWG_SIWGJNI_H
|
||||
#define COM_ATERVE_SIWG_SIWGJNI_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
/*
|
||||
* Class: com_aterve_siwg_SiwgJNI
|
||||
* Method: siwgAddToQueue_first_arg
|
||||
* Signature: (ILjava/lang/String;I)V
|
||||
*/
|
||||
JNIEXPORT void JNICALL Java_com_aterve_siwg_SiwgJNI_siwgAddToQueue
|
||||
(JNIEnv *, jclass, jint, jstring);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
271
siwg/src/java/com/aterve/siwg/SiwgJNI.java
Normal file
271
siwg/src/java/com/aterve/siwg/SiwgJNI.java
Normal file
@@ -0,0 +1,271 @@
|
||||
package com.aterve.siwg;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import androidx.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
import android.view.Gravity;
|
||||
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignIn;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInClient;
|
||||
import com.google.android.gms.auth.api.signin.GoogleSignInOptions;
|
||||
import com.google.android.gms.common.api.ApiException;
|
||||
import com.google.android.gms.common.ConnectionResult;
|
||||
import com.google.android.gms.common.GoogleApiAvailability;
|
||||
|
||||
import com.google.android.gms.tasks.OnCompleteListener;
|
||||
import com.google.android.gms.tasks.OnFailureListener;
|
||||
import com.google.android.gms.tasks.OnSuccessListener;
|
||||
import com.google.android.gms.tasks.Task;
|
||||
import com.google.android.gms.tasks.Continuation;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import org.json.JSONException;
|
||||
|
||||
public class SiwgJNI {
|
||||
|
||||
private static final String TAG = "SiwgJNI";
|
||||
|
||||
// Request code used to invoke sign in user interactions.
|
||||
private static final int RC_UNUSED = 5001;
|
||||
private static final int RC_SIGN_IN = 9001;
|
||||
|
||||
//
|
||||
// Global Constants (Same as in sigw.h)
|
||||
//
|
||||
|
||||
private static final int MSG_SIGN_IN = 1;
|
||||
private static final int MSG_SILENT_SIGN_IN = 2;
|
||||
private static final int MSG_SIGN_OUT = 3;
|
||||
private static final int MSG_GET_EVENTS = 11;
|
||||
|
||||
private static final int STATUS_SUCCESS = 1;
|
||||
private static final int STATUS_FAILED = 2;
|
||||
private static final int STATUS_CONFLICT = 4;
|
||||
|
||||
//
|
||||
// Global Properties
|
||||
//
|
||||
|
||||
private Activity activity;
|
||||
private String client_id;
|
||||
private boolean is_request_id_token;
|
||||
private boolean is_request_auth_code;
|
||||
private boolean is_supported;
|
||||
|
||||
//
|
||||
// Client Handles
|
||||
//
|
||||
|
||||
private GoogleSignInAccount mSignedInAccount = null;
|
||||
private GoogleSignInOptions mSignInOptions;
|
||||
private GoogleSignInClient mGoogleSignInClient;
|
||||
|
||||
// JNI Add to Queue
|
||||
public static native void siwgAddToQueue(int msg, String json);
|
||||
|
||||
//
|
||||
// Constructor
|
||||
//
|
||||
|
||||
public SiwgJNI(Activity activity, boolean is_disk_active, boolean is_request_auth_code, boolean is_request_id_token, String client_id) {
|
||||
this.activity = activity;
|
||||
this.client_id = client_id;
|
||||
this.is_request_auth_code = is_request_auth_code;
|
||||
this.is_request_id_token = is_request_id_token;
|
||||
|
||||
// Check if device supports google sign in
|
||||
this.is_supported = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(activity) == ConnectionResult.SUCCESS;
|
||||
|
||||
// Create GoogleSignIn Client
|
||||
mGoogleSignInClient = GoogleSignIn.getClient(activity, getSignInOptions());
|
||||
}
|
||||
|
||||
//
|
||||
// Event Listeners
|
||||
//
|
||||
|
||||
private OnFailureListener newOnFailureListener(final int messageId, final String message) {
|
||||
return new OnFailureListener() {
|
||||
@Override
|
||||
public void onFailure(@NonNull Exception e) {
|
||||
sendCallbackMessage(messageId, "status", STATUS_FAILED, "error", message);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private OnSuccessListener<Intent> newOnSuccessListenerForIntent(final int requestCode) {
|
||||
return new OnSuccessListener<Intent>() {
|
||||
@Override
|
||||
public void onSuccess(Intent intent) {
|
||||
activity.startActivityForResult(intent, requestCode);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//
|
||||
// Callback Message Methods
|
||||
//
|
||||
|
||||
private void sendCallbackMessage(int msg, String key_1, int value_1) {
|
||||
String message = null;
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(key_1, value_1);
|
||||
message = obj.toString();
|
||||
} catch (JSONException e) {
|
||||
message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
|
||||
}
|
||||
siwgAddToQueue(msg, message);
|
||||
}
|
||||
|
||||
private void sendCallbackMessage(int msg, String key_1, int value_1, String key_2, String value_2) {
|
||||
String message = null;
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(key_1, value_1);
|
||||
obj.put(key_2, value_2);
|
||||
message = obj.toString();
|
||||
} catch (JSONException e) {
|
||||
message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
|
||||
}
|
||||
siwgAddToQueue(msg, message);
|
||||
}
|
||||
|
||||
private void sendCallbackMessage(int msg, String key_1, int value_1, String key_2, int value_2, String key_3, String value_3) {
|
||||
String message = null;
|
||||
try {
|
||||
JSONObject obj = new JSONObject();
|
||||
obj.put(key_1, value_1);
|
||||
obj.put(key_2, value_2);
|
||||
obj.put(key_3, value_3);
|
||||
message = obj.toString();
|
||||
} catch (JSONException e) {
|
||||
message = "{ \"error\": \"Error while converting callback message to JSON: " + e.getMessage() + "\" }";
|
||||
}
|
||||
siwgAddToQueue(msg, message);
|
||||
}
|
||||
|
||||
private void onConnected(GoogleSignInAccount googleSignInAccount, final int msg) {
|
||||
if (mSignedInAccount != googleSignInAccount) {
|
||||
mSignedInAccount = googleSignInAccount;
|
||||
sendCallbackMessage(msg, "status", STATUS_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
private GoogleSignInOptions getSignInOptions() {
|
||||
if (mSignInOptions == null) {
|
||||
|
||||
// Create Google Sign in (Default)
|
||||
GoogleSignInOptions.Builder builder = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN);
|
||||
|
||||
if (is_request_id_token && client_id != null) {
|
||||
builder.requestIdToken(client_id);
|
||||
}
|
||||
|
||||
if (is_request_auth_code && client_id != null) {
|
||||
builder.requestServerAuthCode(client_id);
|
||||
}
|
||||
|
||||
mSignInOptions = builder.build();
|
||||
}
|
||||
|
||||
return mSignInOptions;
|
||||
}
|
||||
|
||||
public void activityResult(int requestCode, int resultCode, Intent intent) {
|
||||
if (requestCode == RC_SIGN_IN) {
|
||||
if (intent != null) {
|
||||
Task<GoogleSignInAccount> task = GoogleSignIn.getSignedInAccountFromIntent(intent);
|
||||
|
||||
if (task.isSuccessful()) {
|
||||
onConnected(task.getResult(), MSG_SIGN_IN);
|
||||
} else {
|
||||
sendCallbackMessage(MSG_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Sign-in Failed. Task Not Successfull!");
|
||||
}
|
||||
|
||||
} else {
|
||||
sendCallbackMessage(MSG_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Sign-in Failed. Intent does not exist!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void silentLogin() {
|
||||
this.activity.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
GoogleSignInAccount account = GoogleSignIn.getLastSignedInAccount(activity);
|
||||
|
||||
if (GoogleSignIn.hasPermissions(account, getSignInOptions().getScopeArray())) {
|
||||
onConnected(account, MSG_SILENT_SIGN_IN);
|
||||
} else {
|
||||
|
||||
mGoogleSignInClient.silentSignIn().addOnCompleteListener(activity,
|
||||
new OnCompleteListener<GoogleSignInAccount>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<GoogleSignInAccount> task) {
|
||||
if (task.isSuccessful()) {
|
||||
onConnected(task.getResult(), MSG_SILENT_SIGN_IN);
|
||||
} else {
|
||||
sendCallbackMessage(MSG_SILENT_SIGN_IN, "status", STATUS_FAILED, "error", "SIWG Silent Sign-in Failed.");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//
|
||||
// JNI Passed Methods
|
||||
//
|
||||
|
||||
public void login() {
|
||||
Intent intent = mGoogleSignInClient.getSignInIntent();
|
||||
this.activity.startActivityForResult(intent, RC_SIGN_IN);
|
||||
}
|
||||
|
||||
public void logout() {
|
||||
mGoogleSignInClient.signOut().addOnCompleteListener(this.activity,
|
||||
new OnCompleteListener<Void>() {
|
||||
@Override
|
||||
public void onComplete(@NonNull Task<Void> task) {
|
||||
mSignedInAccount = null;
|
||||
sendCallbackMessage(MSG_SIGN_OUT, "status", STATUS_SUCCESS);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return isLoggedIn() ? mSignedInAccount.getDisplayName() : null;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return isLoggedIn() ? mSignedInAccount.getId() : null;
|
||||
}
|
||||
|
||||
public String getIdToken() {
|
||||
return isLoggedIn() ? mSignedInAccount.getIdToken() : null;
|
||||
}
|
||||
|
||||
public String getServerAuthCode() {
|
||||
return isLoggedIn() ? mSignedInAccount.getServerAuthCode() : null;
|
||||
}
|
||||
|
||||
public boolean isLoggedIn() {
|
||||
return mSignedInAccount != null;
|
||||
}
|
||||
|
||||
public boolean isSupported() {
|
||||
return is_supported;
|
||||
}
|
||||
|
||||
}
|
24
siwg/src/private_siwg_callback.h
Normal file
24
siwg/src/private_siwg_callback.h
Normal file
@@ -0,0 +1,24 @@
|
||||
#pragma once
|
||||
|
||||
#include <dmsdk/sdk.h>
|
||||
#include "siwg.h"
|
||||
|
||||
struct SIWG_callback
|
||||
{
|
||||
SIWG_callback() : m_L(0), m_Callback(LUA_NOREF), m_Self(LUA_NOREF) {}
|
||||
lua_State* m_L;
|
||||
int m_Callback;
|
||||
int m_Self;
|
||||
};
|
||||
|
||||
struct CallbackData
|
||||
{
|
||||
MESSAGE_ID msg;
|
||||
char* json;
|
||||
};
|
||||
|
||||
void siwg_set_callback(lua_State* L, int pos);
|
||||
void siwg_callback_initialize();
|
||||
void siwg_callback_finalize();
|
||||
void siwg_callback_update();
|
||||
void siwg_add_to_queue(MESSAGE_ID msg, const char*json);
|
403
siwg/src/siwg.cpp
Normal file
403
siwg/src/siwg.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
#define EXTENSION_NAME SIWG
|
||||
#define LIB_NAME "SIWG"
|
||||
#define MODULE_NAME "siwg"
|
||||
|
||||
// include the Defold SDK
|
||||
#include <dmsdk/sdk.h>
|
||||
|
||||
#if defined(DM_PLATFORM_ANDROID)
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#include "siwg.h"
|
||||
#include "siwg_jni.h"
|
||||
#include "private_siwg_callback.h"
|
||||
#include "com_aterve_siwg_SiwgJNI.h"
|
||||
|
||||
static bool luaL_checkbool(lua_State *L, int numArg)
|
||||
{
|
||||
bool b = false;
|
||||
if (lua_isboolean(L, numArg))
|
||||
{
|
||||
b = lua_toboolean(L, numArg);
|
||||
}
|
||||
else
|
||||
{
|
||||
luaL_typerror(L, numArg, lua_typename(L, LUA_TBOOLEAN));
|
||||
}
|
||||
return b;
|
||||
}
|
||||
|
||||
static bool luaL_checkboold(lua_State *L, int numArg, int def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return luaL_checkbool(L, numArg);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
static lua_Number luaL_checknumberd(lua_State *L, int numArg, lua_Number def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return luaL_checknumber(L, numArg);
|
||||
}
|
||||
return def;
|
||||
}
|
||||
|
||||
static char* luaL_checkstringd(lua_State *L, int numArg, const char* def)
|
||||
{
|
||||
int type = lua_type(L, numArg);
|
||||
if (type != LUA_TNONE && type != LUA_TNIL)
|
||||
{
|
||||
return (char*)luaL_checkstring(L, numArg);
|
||||
}
|
||||
return (char*)def;
|
||||
}
|
||||
|
||||
static lua_Number luaL_checktable_number(lua_State *L, int numArg, const char* field, lua_Number def)
|
||||
{
|
||||
lua_Number result = def;
|
||||
if(lua_istable(L, numArg))
|
||||
{
|
||||
lua_getfield(L, numArg, field);
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
result = luaL_checknumber(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static char* luaL_checktable_string(lua_State *L, int numArg, const char* field, char* def)
|
||||
{
|
||||
char* result = def;
|
||||
if(lua_istable(L, numArg))
|
||||
{
|
||||
lua_getfield(L, numArg, field);
|
||||
if(!lua_isnil(L, -1))
|
||||
{
|
||||
result = (char*)luaL_checkstring(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// JNI Method Executors
|
||||
//
|
||||
|
||||
// void method()
|
||||
static int CallVoidMethod(jobject instance, jmethodID method)
|
||||
{
|
||||
ThreadAttacher attacher;
|
||||
JNIEnv *env = attacher.env;
|
||||
env->CallVoidMethod(instance, method);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// string method()
|
||||
static int CallStringMethod(lua_State* L, jobject instance, jmethodID method)
|
||||
{
|
||||
DM_LUA_STACK_CHECK(L, 1);
|
||||
ThreadAttacher attacher;
|
||||
JNIEnv *env = attacher.env;
|
||||
jstring return_value = (jstring)env->CallObjectMethod(instance, method);
|
||||
if (return_value)
|
||||
{
|
||||
const char* cstr = env->GetStringUTFChars(return_value, 0);
|
||||
lua_pushstring(L, cstr);
|
||||
env->ReleaseStringUTFChars(return_value, cstr);
|
||||
env->DeleteLocalRef(return_value);
|
||||
}
|
||||
else
|
||||
{
|
||||
lua_pushnil(L);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// boolean method()
|
||||
static int CallBooleanMethod(lua_State* L, jobject instance, jmethodID method)
|
||||
{
|
||||
DM_LUA_STACK_CHECK(L, 1);
|
||||
ThreadAttacher attacher;
|
||||
JNIEnv *env = attacher.env;
|
||||
jboolean return_value = (jboolean)env->CallBooleanMethod(instance, method);
|
||||
lua_pushboolean(L, JNI_TRUE == return_value);
|
||||
return 1;
|
||||
}
|
||||
|
||||
//
|
||||
// SIWG Handler
|
||||
//
|
||||
|
||||
struct SIWG
|
||||
{
|
||||
jobject m_SiwgJNI;
|
||||
jmethodID m_silentLogin;
|
||||
jmethodID m_login;
|
||||
jmethodID m_logout;
|
||||
jmethodID m_activityResult;
|
||||
jmethodID m_getDisplayName;
|
||||
jmethodID m_getId;
|
||||
jmethodID m_getIdToken;
|
||||
jmethodID m_getServerAuthCode;
|
||||
jmethodID m_isLoggedIn;
|
||||
jmethodID m_isSupported;
|
||||
};
|
||||
|
||||
static SIWG g_siwg;
|
||||
|
||||
static int SiwgAuth_Login(lua_State* L)
|
||||
{
|
||||
CallVoidMethod(g_siwg.m_SiwgJNI, g_siwg.m_login);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SiwgAuth_Logout(lua_State* L)
|
||||
{
|
||||
CallVoidMethod(g_siwg.m_SiwgJNI, g_siwg.m_logout);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SiwgAuth_SilentLogin(lua_State* L)
|
||||
{
|
||||
CallVoidMethod(g_siwg.m_SiwgJNI, g_siwg.m_silentLogin);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int SiwgAuth_getDisplayName(lua_State* L)
|
||||
{
|
||||
return CallStringMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_getDisplayName);
|
||||
}
|
||||
|
||||
static int SiwgAuth_getId(lua_State* L)
|
||||
{
|
||||
return CallStringMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_getId);
|
||||
}
|
||||
|
||||
static int SiwgAuth_getIdToken(lua_State* L)
|
||||
{
|
||||
return CallStringMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_getIdToken);
|
||||
}
|
||||
|
||||
static int SiwgAuth_getServerAuthCode(lua_State* L)
|
||||
{
|
||||
return CallStringMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_getServerAuthCode);
|
||||
}
|
||||
|
||||
static int SiwgAuth_isLoggedIn(lua_State* L)
|
||||
{
|
||||
return CallBooleanMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_isLoggedIn);
|
||||
}
|
||||
|
||||
static int SiwgAuth_isSupported(lua_State* L)
|
||||
{
|
||||
return CallBooleanMethod(L, g_siwg.m_SiwgJNI, g_siwg.m_isSupported);
|
||||
}
|
||||
|
||||
static int SiwgAuth_set_callback(lua_State* L)
|
||||
{
|
||||
siwg_set_callback(L, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//
|
||||
// JNI Extension Methods
|
||||
//
|
||||
|
||||
static void OnActivityResult(void *env, void* activity, int32_t request_code, int32_t result_code, void* result)
|
||||
{
|
||||
ThreadAttacher attacher;
|
||||
JNIEnv *_env = attacher.env;
|
||||
|
||||
_env->CallVoidMethod(g_siwg.m_SiwgJNI, g_siwg.m_activityResult, request_code, result_code, result);
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_aterve_siwg_SiwgJNI_siwgAddToQueue(JNIEnv * env, jclass cls, jint jmsg, jstring jjson)
|
||||
{
|
||||
const char* json = env->GetStringUTFChars(jjson, 0);
|
||||
siwg_add_to_queue((MESSAGE_ID)jmsg, json);
|
||||
env->ReleaseStringUTFChars(jjson, json);
|
||||
}
|
||||
|
||||
// Functions exposed to Lua
|
||||
static const luaL_reg Siwg_methods[] =
|
||||
{
|
||||
{"is_supported", SiwgAuth_isSupported},
|
||||
{"login", SiwgAuth_Login},
|
||||
{"logout", SiwgAuth_Logout},
|
||||
{"silent_login", SiwgAuth_SilentLogin},
|
||||
{"get_display_name", SiwgAuth_getDisplayName},
|
||||
{"get_id", SiwgAuth_getId},
|
||||
{"get_id_token", SiwgAuth_getIdToken},
|
||||
{"get_server_auth_code", SiwgAuth_getServerAuthCode},
|
||||
{"is_logged_in", SiwgAuth_isLoggedIn},
|
||||
{"set_callback", SiwgAuth_set_callback},
|
||||
{0, 0}
|
||||
};
|
||||
|
||||
dmExtension::Result AppInitializeSIWG(dmExtension::AppParams* params)
|
||||
{
|
||||
dmLogInfo("[SIWG] Registered Extension!");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
static void LuaInit(lua_State* L)
|
||||
{
|
||||
DM_LUA_STACK_CHECK(L, 0);
|
||||
luaL_register(L, MODULE_NAME, Siwg_methods);
|
||||
|
||||
#define SETCONSTANT(name) \
|
||||
lua_pushnumber(L, (lua_Number) name); \
|
||||
lua_setfield(L, -2, #name); \
|
||||
|
||||
SETCONSTANT(MSG_SIGN_IN)
|
||||
SETCONSTANT(MSG_SILENT_SIGN_IN)
|
||||
SETCONSTANT(MSG_SIGN_OUT)
|
||||
|
||||
SETCONSTANT(STATUS_SUCCESS)
|
||||
SETCONSTANT(STATUS_FAILED)
|
||||
SETCONSTANT(STATUS_CONFLICT)
|
||||
#undef SETCONSTANT
|
||||
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
//
|
||||
// JNI Setup Methods
|
||||
//
|
||||
|
||||
static void InitJNIMethods(JNIEnv* env, jclass cls)
|
||||
{
|
||||
//general
|
||||
g_siwg.m_isSupported = env->GetMethodID(cls, "isSupported", "()Z");
|
||||
|
||||
//authorization
|
||||
g_siwg.m_silentLogin = env->GetMethodID(cls, "silentLogin", "()V");
|
||||
g_siwg.m_login = env->GetMethodID(cls, "login", "()V");
|
||||
g_siwg.m_logout = env->GetMethodID(cls, "logout", "()V");
|
||||
g_siwg.m_isLoggedIn = env->GetMethodID(cls, "isLoggedIn", "()Z");
|
||||
g_siwg.m_getDisplayName = env->GetMethodID(cls, "getDisplayName", "()Ljava/lang/String;");
|
||||
g_siwg.m_getId = env->GetMethodID(cls, "getId", "()Ljava/lang/String;");
|
||||
g_siwg.m_getIdToken = env->GetMethodID(cls, "getIdToken", "()Ljava/lang/String;");
|
||||
g_siwg.m_getServerAuthCode = env->GetMethodID(cls, "getServerAuthCode", "()Ljava/lang/String;");
|
||||
|
||||
//private methods
|
||||
g_siwg.m_activityResult = env->GetMethodID(cls, "activityResult", "(IILandroid/content/Intent;)V");
|
||||
}
|
||||
|
||||
|
||||
static void CheckInitializationParams(const char* client_id, bool request_server_auth_code, bool request_id_token)
|
||||
{
|
||||
bool is_empty_client_id = client_id == 0 || strlen(client_id) == 0;
|
||||
|
||||
if (is_empty_client_id && request_server_auth_code)
|
||||
{
|
||||
dmLogError("'siwg.client_id' must be defined to request server auth code!");
|
||||
}
|
||||
|
||||
if (is_empty_client_id && request_id_token)
|
||||
{
|
||||
dmLogError("'siwg.client_id' must be defined to request id token!");
|
||||
}
|
||||
}
|
||||
|
||||
static void InitializeJNI(const char* client_id, bool request_server_auth_code, bool request_id_token)
|
||||
{
|
||||
CheckInitializationParams(client_id, request_server_auth_code > 0, request_id_token > 0);
|
||||
dmLogInfo("[SIWG] InitJNI Checked Params");
|
||||
|
||||
ThreadAttacher attacher;
|
||||
JNIEnv *env = attacher.env;
|
||||
ClassLoader class_loader = ClassLoader(env);
|
||||
jclass cls = class_loader.load("com.aterve.siwg.SiwgJNI");
|
||||
dmLogInfo("[SIWG] InitJNI Loaded Class");
|
||||
|
||||
InitJNIMethods(env, cls);
|
||||
dmLogInfo("[SIWG] InitJNI Methods Initialized");
|
||||
|
||||
jmethodID jni_constructor = env->GetMethodID(cls, "<init>", "(Landroid/app/Activity;ZZZLjava/lang/String;)V");
|
||||
jstring java_client_id = env->NewStringUTF(client_id);
|
||||
|
||||
g_siwg.m_SiwgJNI = env->NewGlobalRef(env->NewObject(cls, jni_constructor, dmGraphics::GetNativeAndroidActivity(), true, request_server_auth_code, request_id_token, java_client_id));
|
||||
env->DeleteLocalRef(java_client_id);
|
||||
dmLogInfo("[SIWG] InitJNI JNI Refrences Initialized");
|
||||
}
|
||||
|
||||
//
|
||||
// Extention Event Methods
|
||||
//
|
||||
|
||||
dmExtension::Result InitializeSIWG(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("[SIWG] Initializing Extension...");
|
||||
|
||||
LuaInit(params->m_L);
|
||||
int request_server_auth_code = dmConfigFile::GetInt(params->m_ConfigFile, "siwg.request_server_auth_code", 0);
|
||||
int request_id_token = dmConfigFile::GetInt(params->m_ConfigFile, "siwg.request_id_token", 0);
|
||||
const char* client_id = dmConfigFile::GetString(params->m_ConfigFile, "siwg.client_id", 0);
|
||||
dmLogInfo("[SIWG] Loaded Settings");
|
||||
|
||||
InitializeJNI(client_id, request_server_auth_code > 0, request_id_token > 0);
|
||||
|
||||
dmExtension::RegisterAndroidOnActivityResultListener(OnActivityResult);
|
||||
dmLogInfo("[SIWG] Activity Result Listener Initialized");
|
||||
|
||||
siwg_callback_initialize();
|
||||
|
||||
dmLogInfo("[SIWG] Initialization Completed!");
|
||||
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result UpdateSIWG(dmExtension::Params* params)
|
||||
{
|
||||
siwg_callback_update();
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result FinalizeSIWG(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("[SIWG] Finalizing Extension...");
|
||||
|
||||
siwg_callback_finalize();
|
||||
|
||||
dmExtension::UnregisterAndroidOnActivityResultListener(OnActivityResult);
|
||||
dmLogInfo("[SIWG] Activity Result Listener Unregistered");
|
||||
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result AppFinalizeSIWG(dmExtension::AppParams* params)
|
||||
{
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, AppInitializeSIWG, AppFinalizeSIWG, InitializeSIWG, UpdateSIWG, 0, FinalizeSIWG)
|
||||
|
||||
#else
|
||||
|
||||
dmExtension::Result InitializeSIWG(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("[SIWG] Initializing Extension...");
|
||||
dmLogInfo("[SIWG] Initialization Failed! (Invalid OS Type)");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
dmExtension::Result FinalizeSIWG(dmExtension::Params* params)
|
||||
{
|
||||
dmLogInfo("[SIWG] Finalizing Extension...");
|
||||
return dmExtension::RESULT_OK;
|
||||
}
|
||||
|
||||
DM_DECLARE_EXTENSION(EXTENSION_NAME, LIB_NAME, 0, 0, InitializeSIWG, 0, 0, FinalizeSIWG)
|
||||
|
||||
#endif
|
17
siwg/src/siwg.h
Normal file
17
siwg/src/siwg.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
// Internal to the extension
|
||||
enum MESSAGE_ID
|
||||
{
|
||||
MSG_SIGN_IN = 1,
|
||||
MSG_SILENT_SIGN_IN = 2,
|
||||
MSG_SIGN_OUT = 3,
|
||||
};
|
||||
|
||||
// Internal to the extension
|
||||
enum STATUS
|
||||
{
|
||||
STATUS_SUCCESS = 1,
|
||||
STATUS_FAILED = 2,
|
||||
STATUS_CONFLICT = 4,
|
||||
};
|
157
siwg/src/siwg_callback.cpp
Normal file
157
siwg/src/siwg_callback.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
#if defined(DM_PLATFORM_IOS) || defined(DM_PLATFORM_ANDROID)
|
||||
#include "private_siwg_callback.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
static SIWG_callback m_callback;
|
||||
static dmArray<CallbackData> m_callbacksQueue;
|
||||
static dmMutex::HMutex m_mutex;
|
||||
|
||||
static void RegisterCallback(lua_State* L, int index)
|
||||
{
|
||||
SIWG_callback *cbk = &m_callback;
|
||||
if(cbk->m_Callback != LUA_NOREF)
|
||||
{
|
||||
dmScript::Unref(cbk->m_L, LUA_REGISTRYINDEX, cbk->m_Callback);
|
||||
dmScript::Unref(cbk->m_L, LUA_REGISTRYINDEX, cbk->m_Self);
|
||||
}
|
||||
|
||||
cbk->m_L = dmScript::GetMainThread(L);
|
||||
|
||||
luaL_checktype(L, index, LUA_TFUNCTION);
|
||||
lua_pushvalue(L, index);
|
||||
cbk->m_Callback = dmScript::Ref(L, LUA_REGISTRYINDEX);
|
||||
|
||||
dmScript::GetInstance(L);
|
||||
cbk->m_Self = dmScript::Ref(L, LUA_REGISTRYINDEX);
|
||||
}
|
||||
|
||||
static void UnregisterCallback()
|
||||
{
|
||||
SIWG_callback *cbk = &m_callback;
|
||||
if(cbk->m_Callback != LUA_NOREF)
|
||||
{
|
||||
dmScript::Unref(cbk->m_L, LUA_REGISTRYINDEX, cbk->m_Callback);
|
||||
dmScript::Unref(cbk->m_L, LUA_REGISTRYINDEX, cbk->m_Self);
|
||||
cbk->m_Callback = LUA_NOREF;
|
||||
}
|
||||
}
|
||||
|
||||
static void siwg_invoke_callback(MESSAGE_ID type, char*json)
|
||||
{
|
||||
SIWG_callback *cbk = &m_callback;
|
||||
if(cbk->m_Callback == LUA_NOREF)
|
||||
{
|
||||
dmLogInfo("GPGS callback do not exist.");
|
||||
return;
|
||||
}
|
||||
|
||||
lua_State* L = cbk->m_L;
|
||||
int top = lua_gettop(L);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, cbk->m_Callback);
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, cbk->m_Self);
|
||||
lua_pushvalue(L, -1);
|
||||
dmScript::SetInstance(L);
|
||||
|
||||
if (!dmScript::IsInstanceValid(L))
|
||||
{
|
||||
UnregisterCallback();
|
||||
dmLogError("Could not run GPGS callback because the instance has been deleted.");
|
||||
lua_pop(L, 2);
|
||||
}
|
||||
else {
|
||||
lua_pushnumber(L, type);
|
||||
int count_table_elements = 1;
|
||||
bool is_fail = false;
|
||||
dmJson::Document doc;
|
||||
dmJson::Result r = dmJson::Parse(json, &doc);
|
||||
if (r == dmJson::RESULT_OK && doc.m_NodeCount > 0) {
|
||||
char error_str_out[128];
|
||||
if (dmScript::JsonToLua(L, &doc, 0, error_str_out, sizeof(error_str_out)) < 0) {
|
||||
dmLogError("Failed converting object JSON to Lua; %s", error_str_out);
|
||||
is_fail = true;
|
||||
}
|
||||
} else {
|
||||
dmLogError("Failed to parse JSON object(%d): (%s)", r, json);
|
||||
is_fail = true;
|
||||
}
|
||||
dmJson::Free(&doc);
|
||||
if (is_fail) {
|
||||
lua_pop(L, 2);
|
||||
assert(top == lua_gettop(L));
|
||||
return;
|
||||
}
|
||||
|
||||
int number_of_arguments = 3;
|
||||
int ret = lua_pcall(L, number_of_arguments, 0, 0);
|
||||
if(ret != 0)
|
||||
{
|
||||
dmLogError("Error running callback: %s", lua_tostring(L, -1));
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
assert(top == lua_gettop(L));
|
||||
}
|
||||
|
||||
void siwg_callback_initialize()
|
||||
{
|
||||
m_mutex = dmMutex::New();
|
||||
dmLogInfo("[SIWG] Callback initialized");
|
||||
}
|
||||
|
||||
void siwg_callback_finalize()
|
||||
{
|
||||
dmMutex::Delete(m_mutex);
|
||||
UnregisterCallback();
|
||||
dmLogInfo("[SIWG] Callback Unregistered");
|
||||
}
|
||||
|
||||
void siwg_set_callback(lua_State* L, int pos)
|
||||
{
|
||||
int type = lua_type(L, pos);
|
||||
if (type == LUA_TNONE || type == LUA_TNIL)
|
||||
{
|
||||
UnregisterCallback();
|
||||
}
|
||||
else
|
||||
{
|
||||
RegisterCallback(L, pos);
|
||||
}
|
||||
}
|
||||
|
||||
void siwg_add_to_queue(MESSAGE_ID msg, const char*json)
|
||||
{
|
||||
DM_MUTEX_SCOPED_LOCK(m_mutex);
|
||||
|
||||
CallbackData data;
|
||||
data.msg = msg;
|
||||
data.json = json ? strdup(json) : NULL;
|
||||
|
||||
if(m_callbacksQueue.Full())
|
||||
{
|
||||
m_callbacksQueue.OffsetCapacity(1);
|
||||
}
|
||||
m_callbacksQueue.Push(data);
|
||||
}
|
||||
|
||||
void siwg_callback_update()
|
||||
{
|
||||
if (m_callbacksQueue.Empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DM_MUTEX_SCOPED_LOCK(m_mutex);
|
||||
|
||||
for(uint32_t i = 0; i != m_callbacksQueue.Size(); ++i)
|
||||
{
|
||||
CallbackData* data = &m_callbacksQueue[i];
|
||||
siwg_invoke_callback(data->msg, data->json);
|
||||
if(data->json)
|
||||
{
|
||||
free(data->json);
|
||||
data->json = 0;
|
||||
}
|
||||
}
|
||||
m_callbacksQueue.SetSize(0);
|
||||
}
|
||||
#endif
|
54
siwg/src/siwg_jni.h
Normal file
54
siwg/src/siwg_jni.h
Normal file
@@ -0,0 +1,54 @@
|
||||
#pragma once
|
||||
|
||||
#if defined(DM_PLATFORM_ANDROID)
|
||||
|
||||
#include <dmsdk/sdk.h>
|
||||
#include <jni.h>
|
||||
|
||||
struct ThreadAttacher {
|
||||
JNIEnv *env;
|
||||
bool has_attached;
|
||||
ThreadAttacher() : env(NULL), has_attached(false) {
|
||||
if (dmGraphics::GetNativeAndroidJavaVM()->GetEnv((void **)&env, JNI_VERSION_1_6) != JNI_OK) {
|
||||
dmGraphics::GetNativeAndroidJavaVM()->AttachCurrentThread(&env, NULL);
|
||||
has_attached = true;
|
||||
}
|
||||
}
|
||||
~ThreadAttacher() {
|
||||
if (has_attached) {
|
||||
if (env->ExceptionCheck()) {
|
||||
env->ExceptionDescribe();
|
||||
}
|
||||
env->ExceptionClear();
|
||||
dmGraphics::GetNativeAndroidJavaVM()->DetachCurrentThread();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
struct ClassLoader {
|
||||
private:
|
||||
JNIEnv *env;
|
||||
jobject class_loader_object;
|
||||
jmethodID find_class;
|
||||
public:
|
||||
ClassLoader(JNIEnv *env) : env(env) {
|
||||
jclass activity_class = env->FindClass("android/app/NativeActivity");
|
||||
jmethodID get_class_loader = env->GetMethodID(activity_class, "getClassLoader", "()Ljava/lang/ClassLoader;");
|
||||
class_loader_object = env->CallObjectMethod(dmGraphics::GetNativeAndroidActivity(), get_class_loader);
|
||||
jclass class_loader = env->FindClass("java/lang/ClassLoader");
|
||||
find_class = env->GetMethodID(class_loader, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
|
||||
env->DeleteLocalRef(activity_class);
|
||||
env->DeleteLocalRef(class_loader);
|
||||
}
|
||||
~ClassLoader() {
|
||||
env->DeleteLocalRef(class_loader_object);
|
||||
}
|
||||
jclass load(const char *class_name) {
|
||||
jstring str_class_name = env->NewStringUTF(class_name);
|
||||
jclass loaded_class = (jclass)env->CallObjectMethod(class_loader_object, find_class, str_class_name);
|
||||
env->DeleteLocalRef(str_class_name);
|
||||
return loaded_class;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user