Finalized Google Sign In!!!

This commit is contained in:
Nick Leeman 2021-02-24 00:35:54 +01:00
parent 0746a2a88e
commit fcbce77168
21 changed files with 1424 additions and 121 deletions

7
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,7 @@
{
"files.associations": {
"*.gui_script": "lua",
"*.script": "lua",
"typeinfo": "cpp"
}
}

View File

@ -1,2 +0,0 @@
# C++ symbol in your extension
name: "MyExtension"

View File

@ -1,112 +0,0 @@
// myextension.cpp
// Extension lib defines
#define LIB_NAME "MyExtension"
#define MODULE_NAME "myextension"
// include the Defold SDK
#include <dmsdk/sdk.h>
static int Reverse(lua_State* L)
{
// The number of expected items to be on the Lua stack
// once this struct goes out of scope
DM_LUA_STACK_CHECK(L, 1);
// Check and get parameter string from stack
char* str = (char*)luaL_checkstring(L, 1);
// Reverse the string
int len = strlen(str);
for(int i = 0; i < len / 2; i++) {
const char a = str[i];
const char b = str[len - i - 1];
str[i] = b;
str[len - i - 1] = a;
}
// Put the reverse string on the stack
lua_pushstring(L, str);
// Return 1 item
return 1;
}
// Functions exposed to Lua
static const luaL_reg Module_methods[] =
{
{"reverse", Reverse},
{0, 0}
};
static void LuaInit(lua_State* L)
{
int top = lua_gettop(L);
// Register lua names
luaL_register(L, MODULE_NAME, Module_methods);
lua_pop(L, 1);
assert(top == lua_gettop(L));
}
dmExtension::Result AppInitializeMyExtension(dmExtension::AppParams* params)
{
dmLogInfo("AppInitializeMyExtension\n");
return dmExtension::RESULT_OK;
}
dmExtension::Result InitializeMyExtension(dmExtension::Params* params)
{
// Init Lua
LuaInit(params->m_L);
dmLogInfo("Registered %s Extension\n", MODULE_NAME);
return dmExtension::RESULT_OK;
}
dmExtension::Result AppFinalizeMyExtension(dmExtension::AppParams* params)
{
dmLogInfo("AppFinalizeMyExtension\n");
return dmExtension::RESULT_OK;
}
dmExtension::Result FinalizeMyExtension(dmExtension::Params* params)
{
dmLogInfo("FinalizeMyExtension\n");
return dmExtension::RESULT_OK;
}
dmExtension::Result OnUpdateMyExtension(dmExtension::Params* params)
{
dmLogInfo("OnUpdateMyExtension\n");
return dmExtension::RESULT_OK;
}
void OnEventMyExtension(dmExtension::Params* params, const dmExtension::Event* event)
{
switch(event->m_Event)
{
case dmExtension::EVENT_ID_ACTIVATEAPP:
dmLogInfo("OnEventMyExtension - EVENT_ID_ACTIVATEAPP\n");
break;
case dmExtension::EVENT_ID_DEACTIVATEAPP:
dmLogInfo("OnEventMyExtension - EVENT_ID_DEACTIVATEAPP\n");
break;
case dmExtension::EVENT_ID_ICONIFYAPP:
dmLogInfo("OnEventMyExtension - EVENT_ID_ICONIFYAPP\n");
break;
case dmExtension::EVENT_ID_DEICONIFYAPP:
dmLogInfo("OnEventMyExtension - EVENT_ID_DEICONIFYAPP\n");
break;
default:
dmLogWarning("OnEventMyExtension - Unknown event id\n");
break;
}
}
// Defold SDK uses a macro for setting up extension entry points:
//
// DM_DECLARE_EXTENSION(symbol, name, app_init, app_final, init, update, on_event, final)
// MyExtension is the C++ symbol that holds all relevant extension data.
// It must match the name field in the `ext.manifest`
DM_DECLARE_EXTENSION(MyExtension, LIB_NAME, AppInitializeMyExtension, AppFinalizeMyExtension, InitializeMyExtension, OnUpdateMyExtension, OnEventMyExtension, FinalizeMyExtension)

17
assets/main.font Normal file
View File

@ -0,0 +1,17 @@
font: "/builtins/fonts/vera_mo_bd.ttf"
material: "/builtins/fonts/font.material"
size: 60
antialias: 1
alpha: 1.0
outline_alpha: 0.0
outline_width: 0.0
shadow_alpha: 0.0
shadow_blur: 0
shadow_x: 0.0
shadow_y: 0.0
extra_characters: ""
output_format: TYPE_BITMAP
all_chars: false
cache_width: 0
cache_height: 0
render_mode: MODE_SINGLE_LAYER

BIN
dmengine Executable file

Binary file not shown.

View File

@ -3,6 +3,21 @@ scale_along_z: 0
embedded_instances {
id: "go"
data: "components {\n"
" id: \"gui\"\n"
" component: \"/example/example.gui\"\n"
" position {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" }\n"
" rotation {\n"
" x: 0.0\n"
" y: 0.0\n"
" z: 0.0\n"
" w: 1.0\n"
" }\n"
"}\n"
"components {\n"
" id: \"example\"\n"
" component: \"/example/example.script\"\n"
" position {\n"

249
example/example.gui Normal file
View File

@ -0,0 +1,249 @@
script: "/example/example.gui_script"
fonts {
name: "main"
font: "/assets/main.font"
}
background_color {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
nodes {
position {
x: 540.0
y: 170.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 900.0
y: 160.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "login_btn"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "Manual Login\n"
""
font: "main"
id: "text"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "login_btn"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
}
nodes {
position {
x: 540.0
y: 427.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 900.0
y: 160.0
z: 0.0
w: 1.0
}
color {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
type: TYPE_BOX
blend_mode: BLEND_MODE_ALPHA
texture: ""
id: "silent_login_btn"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
adjust_mode: ADJUST_MODE_FIT
layer: ""
inherit_alpha: true
slice9 {
x: 0.0
y: 0.0
z: 0.0
w: 0.0
}
clipping_mode: CLIPPING_MODE_NONE
clipping_visible: true
clipping_inverted: false
alpha: 1.0
template_node_child: false
size_mode: SIZE_MODE_MANUAL
}
nodes {
position {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
rotation {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
scale {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
size {
x: 200.0
y: 100.0
z: 0.0
w: 1.0
}
color {
x: 0.0
y: 0.0
z: 0.0
w: 1.0
}
type: TYPE_TEXT
blend_mode: BLEND_MODE_ALPHA
text: "Silent Login Btn"
font: "main"
id: "text1"
xanchor: XANCHOR_NONE
yanchor: YANCHOR_NONE
pivot: PIVOT_CENTER
outline {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
shadow {
x: 1.0
y: 1.0
z: 1.0
w: 1.0
}
adjust_mode: ADJUST_MODE_FIT
line_break: false
parent: "silent_login_btn"
layer: ""
inherit_alpha: true
alpha: 1.0
outline_alpha: 1.0
shadow_alpha: 1.0
template_node_child: false
text_leading: 1.0
text_tracking: 0.0
}
material: "/builtins/materials/gui.material"
adjust_reference: ADJUST_REFERENCE_PARENT
max_nodes: 512

View File

@ -0,0 +1,52 @@
function init(self)
msg.post(".", "acquire_input_focus")
if siwg then
siwg.set_callback(gpgs_callback)
end
end
function on_message(self, message_id, message, sender)
end
function on_input(self, action_id, action)
if action_id == hash("touch") and action.pressed then
if gui.pick_node(gui.get_node("login_btn"), action.x, action.y) then
if siwg then
print("Login Pressed!")
siwg.login()
end
end
if gui.pick_node(gui.get_node("silent_login_btn"), action.x, action.y) then
if siwg then
print("Login Pressed!")
siwg.silent_login()
end
end
end
end
function gpgs_callback(self, message_id, message)
if message_id == siwg.MSG_SIGN_IN or message_id == siwg.MSG_SILENT_SIGN_IN then
if message.status == siwg.STATUS_SUCCESS then
print("SIWG ID: " .. siwg.get_id())
print("SIWG DisplayName: " .. siwg.get_display_name())
if sys.get_config("siwg.client_id") then
print("SIWG id_token: ", siwg.get_id_token())
print("SIWG auth_code: ", siwg.get_server_auth_code())
end
elseif message.status == siwg.STATUS_FAILED then
print("Status: FAILED")
print("Error: " .. message.error)
end
elseif message_id == siwg.MSG_SIGN_OUT then
print("SIWG Logged out!")
end
print("SIWG is_logged_in: " .. tostring(siwg.is_logged_in()))
end

View File

@ -1,7 +1,5 @@
function init(self)
local s = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
local reverse_s = myextension.reverse(s)
print(reverse_s) --> ZYXWVUTSRQPONMLKJIHGFEDCBAzyxwvutsrqponmlkjihgfedcba
msg.post(".", "acquire_input_focus")
end
function final(self)

View File

@ -5,12 +5,30 @@ main_collection = /example/example.collectionc
shared_state = 1
[display]
width = 960
height = 640
width = 1080
height = 1920
[project]
title = SIGW-Extension
title = SIWG-Extension
[library]
include_dirs = SIGW
include_dirs = siwg
[native_extension]
app_manifest = /generated.appmanifest
[android]
input_method = HiddenInputField
immersive_mode = 1
package = com.aterve.partydeck
debuggable = 1
[siwg]
app_id = 715670885775
client_id = 715670885775-j3p3nr6ith9su03p2im8d2p752hptg56.apps.googleusercontent.com
request_server_auth_code = 1
request_id_token = 1
[ios]
bundle_identifier = com.aterve.partydeck

100
generated.appmanifest Normal file
View File

@ -0,0 +1,100 @@
# App manifest generated Fri Nov 20 2020 09:28:33 GMT+0100 (Central European Standard Time)
# Settings: OpenGL
platforms:
x86_64-osx:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
frameworks: []
linkFlags: []
x86_64-linux:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
js-web:
context:
excludeLibs: []
excludeJsLibs: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
wasm-web:
context:
excludeLibs: []
excludeJsLibs: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
x86-win32:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
x86_64-win32:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
armv7-android:
context:
excludeLibs: []
excludeJars: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
jetifier: true
arm64-android:
context:
excludeLibs: []
excludeJars: []
excludeSymbols: []
symbols: []
libs: []
linkFlags: []
jetifier: true
armv7-ios:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
frameworks: []
linkFlags: []
arm64-ios:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
frameworks: []
linkFlags: []
x86_64-ios:
context:
excludeLibs: []
excludeSymbols: []
symbols: []
libs: []
frameworks: []
linkFlags: []

1
siwg/ext.manifest Normal file
View File

@ -0,0 +1 @@
name: SIWG

View 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>

View 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'
}

View 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

View 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;
}
}

View 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
View 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
View 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
View 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
View 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