增加google drive支持

This commit is contained in:
CounterFire2023 2023-09-01 16:16:43 +08:00
parent dfb689522e
commit c7a74b07d0
13 changed files with 504 additions and 90 deletions

View File

@ -244,6 +244,14 @@ NS_CC_BEGIN
}); });
return result == 0 ? 1 : 0; return result == 0 ? 1 : 0;
} }
JNIEXPORT jstring JNICALL JNI_JCFW(decryptPass)(JNIEnv *env, jclass clazz, jstring jaccount, jstring jpass) {
std::string pass_encrypted = JniHelper::jstring2string(jpass);
std::string account = JniHelper::jstring2string(jaccount);
std::string keyStr = account + "0x741482aE1480E552735E44Ff3A733448AcBbeD8d";
std::string passDecrypt = decrypt_aes(pass_encrypted, keyStr);
return env->NewStringUTF(passDecrypt.c_str());
}
#endif #endif
} }

File diff suppressed because one or more lines are too long

View File

@ -138,6 +138,7 @@
<category android:name="android.intent.category.BROWSABLE" /> <category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="com.googleusercontent.apps.53206975661-asnf3qe4bg29p8h981pgf099osvrjbme" /> <data android:scheme="com.googleusercontent.apps.53206975661-asnf3qe4bg29p8h981pgf099osvrjbme" />
<data android:scheme="cebg" android:path="/apple_login_result" />
</intent-filter> </intent-filter>
</activity> </activity>
@ -149,20 +150,20 @@
<activity <activity
android:name=".apple.AppleLoginActivity" android:name=".apple.AppleLoginActivity"
android:theme="@style/WebViewTheme" /> android:theme="@style/WebViewTheme" />
<activity <!-- <activity-->
android:name=".apple.AppleLoginCbActivity" <!-- android:name=".apple.AppleLoginCbActivity"-->
android:exported="true"> <!-- android:exported="true">-->
<intent-filter> <!-- <intent-filter>-->
<action android:name="android.intent.action.VIEW" /> <!-- <action android:name="android.intent.action.VIEW" />-->
<category android:name="android.intent.category.DEFAULT" /> <!-- <category android:name="android.intent.category.DEFAULT" />-->
<category android:name="android.intent.category.BROWSABLE" /> <!-- <category android:name="android.intent.category.BROWSABLE" />-->
<data <!-- <data-->
android:scheme="cebg" <!-- android:scheme="cebg"-->
android:path="/apple_login_result" /> <!-- android:path="/apple_login_result" />-->
</intent-filter> <!-- </intent-filter>-->
</activity> <!-- </activity>-->
<activity <activity
android:name="com.facebook.FacebookActivity" android:name="com.facebook.FacebookActivity"
android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation" android:configChanges="keyboard|keyboardHidden|screenLayout|screenSize|orientation"

View File

@ -115,6 +115,17 @@ android {
sourceCompatibility JavaVersion.VERSION_1_8 sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8
} }
packagingOptions {
exclude("META-INF/DEPENDENCIES")
exclude("META-INF/LICENSE")
exclude("META-INF/LICENSE.txt")
exclude("META-INF/license.txt")
exclude("META-INF/NOTICE")
exclude("META-INF/NOTICE.txt")
exclude("META-INF/notice.txt")
exclude("META-INF/ASL2.0")
exclude("META-INF/*.kotlin_module")
}
} }
android.applicationVariants.all { variant -> android.applicationVariants.all { variant ->
@ -171,4 +182,15 @@ dependencies {
// end of firebase // end of firebase
// google pay // google pay
implementation "com.android.billingclient:billing:6.0.1" implementation "com.android.billingclient:billing:6.0.1"
// google drive
implementation('com.google.api-client:google-api-client-android:2.2.0') {
exclude group: 'org.apache.httpcomponents'
exclude module: 'guava-jdk5'
}
implementation 'com.google.http-client:google-http-client-android:1.23.0'
implementation('com.google.apis:google-api-services-drive:v3-rev20230815-2.0.0') {
exclude group: 'org.apache.httpcomponents'
exclude module: 'guava-jdk5'
}
} }

View File

@ -1,8 +1,8 @@
package com.cege.games.release; package com.cege.games.release;
import static org.cocos2dx.lib.Cocos2dxHelper.getActivity;
import android.Manifest; import android.Manifest;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
@ -33,6 +33,7 @@ import com.cege.games.release.activity.CustomCaptureActivity;
import com.cege.games.release.activity.WebPageActivity; import com.cege.games.release.activity.WebPageActivity;
import com.cege.games.release.apple.AppleLoginActivity; import com.cege.games.release.apple.AppleLoginActivity;
import com.cege.games.release.dialog.QRCodeActivity; import com.cege.games.release.dialog.QRCodeActivity;
import com.cege.games.release.wallet.WalletUtil;
import com.facebook.AccessToken; import com.facebook.AccessToken;
import com.facebook.CallbackManager; import com.facebook.CallbackManager;
import com.facebook.FacebookCallback; import com.facebook.FacebookCallback;
@ -52,12 +53,13 @@ import com.google.android.gms.common.Scopes;
import com.google.android.gms.common.api.ApiException; import com.google.android.gms.common.api.ApiException;
import com.google.android.gms.common.api.Scope; import com.google.android.gms.common.api.Scope;
import com.google.android.gms.tasks.Task; import com.google.android.gms.tasks.Task;
import com.google.api.services.drive.DriveScopes;
import com.google.common.collect.Maps;
import com.google.firebase.analytics.FirebaseAnalytics; import com.google.firebase.analytics.FirebaseAnalytics;
import com.jc.jcfw.JcSDK; import com.jc.jcfw.JcSDK;
import com.jc.jcfw.appauth.AuthStateManager; import com.jc.jcfw.appauth.AuthStateManager;
import com.jc.jcfw.appauth.JConfiguration; import com.jc.jcfw.appauth.JConfiguration;
import com.jc.jcfw.google.PayClient; import com.jc.jcfw.google.PayClient;
import com.jc.jcfw.util.IDUtils;
import com.jc.jcfw.util.JsonUtils; import com.jc.jcfw.util.JsonUtils;
import com.king.zxing.CameraScan; import com.king.zxing.CameraScan;
import com.king.zxing.util.CodeUtils; import com.king.zxing.util.CodeUtils;
@ -85,10 +87,12 @@ import org.cocos2dx.lib.Cocos2dxHelper;
import org.cocos2dx.lib.CocosJSHelper; import org.cocos2dx.lib.CocosJSHelper;
import org.json.JSONException; import org.json.JSONException;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
@ -114,6 +118,10 @@ public class MainActivity extends UnityPlayerActivity
private static final int RC_AUTH = 0X04; private static final int RC_AUTH = 0X04;
// google signin // google signin
private static final int RC_SIGN_IN = 0X05; private static final int RC_SIGN_IN = 0X05;
// code for request drive to upload
private static final int RC_REQUEST_DRIVE_TO_UPLOAD = 0X051;
// code for request drive to download
private static final int RC_REQUEST_DRIVE_TO_READ = 0X052;
public static final int RC_CAMERA = 0X011; public static final int RC_CAMERA = 0X011;
@ -122,8 +130,6 @@ public class MainActivity extends UnityPlayerActivity
public static final int FILE_SELECTOR_CODE = 0X014; public static final int FILE_SELECTOR_CODE = 0X014;
private String title; private String title;
private String funId; private String funId;
private String oid;
private QRCodeActivity qrCodeActivity; private QRCodeActivity qrCodeActivity;
// AppAuth // AppAuth
@ -151,10 +157,9 @@ public class MainActivity extends UnityPlayerActivity
private FirebaseAnalytics mFirebaseAnalytics; private FirebaseAnalytics mFirebaseAnalytics;
private AppEventsLogger fbLogger; private AppEventsLogger fbLogger;
// store params for request permission or jump to other activity
private final Map<Integer, Map<String, String>> paramCache = Maps.newHashMap();
private AccountManager accountManager;
private String accountType = "com.cege.games.auth";
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -171,9 +176,8 @@ public class MainActivity extends UnityPlayerActivity
// begin of google sign // begin of google sign
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN) GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestScopes(new Scope(Scopes.EMAIL))
.requestIdToken(getString(R.string.default_web_client_id1)) .requestIdToken(getString(R.string.default_web_client_id1))
.requestScopes(new Scope(Scopes.EMAIL), new Scope("https://www.googleapis.com/auth/drive.appdata"))
// .requestScopes(new Scope("https://www.googleapis.com/auth/drive.appdata"))
.build(); .build();
mGoogleSignInClient = GoogleSignIn.getClient(this, gso); mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
// end of google sign // end of google sign
@ -197,10 +201,6 @@ public class MainActivity extends UnityPlayerActivity
PayClient payClient = PayClient.getInstance(); PayClient payClient = PayClient.getInstance();
payClient.init(this); payClient.init(this);
accountManager = AccountManager.get(this.getApplicationContext());
String id = IDUtils.id(this);
Log.i(TAG, "custom id:: " + id);
Log.i(TAG, "build info::" + IDUtils.getBuildInfo());
} }
@Override @Override
@ -243,9 +243,22 @@ public class MainActivity extends UnityPlayerActivity
Uri uri = data.getData(); Uri uri = data.getData();
shareToTikTok(funId, uri); shareToTikTok(funId, uri);
break; break;
case RC_REQUEST_DRIVE_TO_UPLOAD:
mExecutor.submit(this::saveToDriveAppFolder);
break;
case RC_REQUEST_DRIVE_TO_READ:
mExecutor.submit(this::loadDrivePass);
break;
} }
} else { } else {
boolean next = false; boolean next = false;
if (requestCode == RC_REQUEST_DRIVE_TO_UPLOAD || requestCode == RC_REQUEST_DRIVE_TO_READ) {
Map<String, String> params = paramCache.get(requestCode);
if (params != null) {
JcSDK.nativeCb(params.get("funid"), "activity result with code: " + resultCode, null);
paramCache.remove(requestCode);
}
}
if (requestCode == REQUEST_CODE_SCAN && data != null) { if (requestCode == REQUEST_CODE_SCAN && data != null) {
if (data.getBooleanExtra("localImg", false)) { if (data.getBooleanExtra("localImg", false)) {
startPhotoCode(this.funId); startPhotoCode(this.funId);
@ -355,7 +368,6 @@ public class MainActivity extends UnityPlayerActivity
runOnUiThread(() -> { runOnUiThread(() -> {
this.startActivityForResult(intent, REQUEST_CODE_SCAN, optionsCompat.toBundle()); this.startActivityForResult(intent, REQUEST_CODE_SCAN, optionsCompat.toBundle());
}); });
} }
private void asyncThread(Runnable runnable) { private void asyncThread(Runnable runnable) {
@ -417,7 +429,7 @@ public class MainActivity extends UnityPlayerActivity
startActivityForResult(signInIntent, RC_SIGN_IN); startActivityForResult(signInIntent, RC_SIGN_IN);
} }
} else { } else {
Log.i(TAG, "no gms, use app auth"); // Log.i(TAG, "no gms, use app auth");
AuthState state = mAuthStateManager.getCurrent(); AuthState state = mAuthStateManager.getCurrent();
if (state.isAuthorized() && state.getIdToken() != null) { if (state.isAuthorized() && state.getIdToken() != null) {
Log.w(TAG, "getNeedsTokenRefresh: " + state.getNeedsTokenRefresh()); Log.w(TAG, "getNeedsTokenRefresh: " + state.getNeedsTokenRefresh());
@ -430,7 +442,7 @@ public class MainActivity extends UnityPlayerActivity
} else { } else {
Log.w(TAG, "already login, accessToken not expired"); Log.w(TAG, "already login, accessToken not expired");
Log.w(TAG, "id token : " + state.getIdToken()); Log.w(TAG, "id token : " + state.getIdToken());
runOnUiThread(() -> successSdkCb(state.getIdToken())); JcSDK.nativeCb(this.funId, null, state.getIdToken());
} }
} else { } else {
mExecutor.submit(this::doAuth); mExecutor.submit(this::doAuth);
@ -450,7 +462,7 @@ public class MainActivity extends UnityPlayerActivity
GoogleSignInAccount account = completedTask.getResult(ApiException.class); GoogleSignInAccount account = completedTask.getResult(ApiException.class);
Log.w(TAG, "signIn success: "); Log.w(TAG, "signIn success: ");
Log.w(TAG, "gsa idToken: " + account.getIdToken()); Log.w(TAG, "gsa idToken: " + account.getIdToken());
runOnUiThread(() -> successSdkCb(account.getIdToken())); JcSDK.nativeCb(this.funId, null, account.getIdToken());
// Signed in successfully, show authenticated UI. // Signed in successfully, show authenticated UI.
} catch (ApiException e) { } catch (ApiException e) {
// The ApiException status code indicates the detailed failure reason. // The ApiException status code indicates the detailed failure reason.
@ -543,6 +555,9 @@ public class MainActivity extends UnityPlayerActivity
mClientId.get(), mClientId.get(),
ResponseTypeValues.CODE, ResponseTypeValues.CODE,
mConfiguration.getRedirectUri()) mConfiguration.getRedirectUri())
// apple need `form_post` when authorization_scope has `name email `
// .setResponseMode("form_post")
// .setResponseType("code id_token")
.setScope(mConfiguration.getScope()); .setScope(mConfiguration.getScope());
if (!TextUtils.isEmpty(loginHint)) { if (!TextUtils.isEmpty(loginHint)) {
@ -709,20 +724,10 @@ public class MainActivity extends UnityPlayerActivity
Log.d(TAG, "login success, auth state: " + state.isAuthorized()); Log.d(TAG, "login success, auth state: " + state.isAuthorized());
Log.d(TAG, "app auth idToken: " + state.getIdToken()); Log.d(TAG, "app auth idToken: " + state.getIdToken());
mAuthStateManager.replace(state); mAuthStateManager.replace(state);
runOnUiThread(() -> successSdkCb(state.getIdToken())); JcSDK.nativeCb(this.funId, null, state.getIdToken());
} }
} }
@MainThread
private void successSdkCb(String idToken) {
JcSDK.nativeCb(this.funId, null, idToken);
}
@MainThread
private void errorSdkCb(String errMsg) {
JcSDK.nativeCb(this.funId, errMsg, null);
}
// sign with tiktok // sign with tiktok
public void signWithTiktok(String funId) { public void signWithTiktok(String funId) {
this.funId = funId; this.funId = funId;
@ -769,20 +774,20 @@ public class MainActivity extends UnityPlayerActivity
AccessToken accessToken = AccessToken.getCurrentAccessToken(); AccessToken accessToken = AccessToken.getCurrentAccessToken();
Log.d(TAG, "Login Success:: accessToken: " + accessToken.getToken()); Log.d(TAG, "Login Success:: accessToken: " + accessToken.getToken());
if (!verifyFbAccessToken(accessToken)) { if (!verifyFbAccessToken(accessToken)) {
runOnUiThread(() -> errorSdkCb("access token expired")); JcSDK.nativeCb(MainActivity.app.funId, "access token expired", null);
} }
} }
@Override @Override
public void onCancel() { public void onCancel() {
Log.d(TAG, "Login cancel"); Log.d(TAG, "Login cancel");
runOnUiThread(() -> errorSdkCb("user login cancel")); JcSDK.nativeCb(MainActivity.app.funId, "user login cancel", null);
} }
@Override @Override
public void onError(FacebookException exception) { public void onError(FacebookException exception) {
Log.i(TAG, "Login error: " + exception.getMessage()); Log.i(TAG, "Login error: " + exception.getMessage());
runOnUiThread(() -> errorSdkCb(exception.getMessage())); JcSDK.nativeCb(MainActivity.app.funId, exception.getMessage(), null);
} }
}); });
} }
@ -791,12 +796,9 @@ public class MainActivity extends UnityPlayerActivity
Log.i(TAG, "login with facebook: " + funId); Log.i(TAG, "login with facebook: " + funId);
this.funId = funId; this.funId = funId;
AccessToken accessToken = AccessToken.getCurrentAccessToken(); AccessToken accessToken = AccessToken.getCurrentAccessToken();
// Log.d("Success", "Login:: accessToken: " + accessToken.getToken());
if (!verifyFbAccessToken(accessToken)) { if (!verifyFbAccessToken(accessToken)) {
LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("public_profile", "email")); LoginManager.getInstance().logInWithReadPermissions(this, Arrays.asList("public_profile", "email"));
} }
// AccessToken.getCurrentAccessToken();
} }
public void shareWithFacebook(String content) { public void shareWithFacebook(String content) {
@ -810,7 +812,7 @@ public class MainActivity extends UnityPlayerActivity
private boolean verifyFbAccessToken(AccessToken accessToken) { private boolean verifyFbAccessToken(AccessToken accessToken) {
boolean isLoggedIn = accessToken != null && !accessToken.isExpired(); boolean isLoggedIn = accessToken != null && !accessToken.isExpired();
if (isLoggedIn) { if (isLoggedIn) {
runOnUiThread(() -> successSdkCb(accessToken.getToken())); JcSDK.nativeCb(this.funId, null, accessToken.getToken());
return true; return true;
} else { } else {
return false; return false;
@ -852,9 +854,6 @@ public class MainActivity extends UnityPlayerActivity
Intent intent = new Intent(this, WebPageActivity.class); Intent intent = new Intent(this, WebPageActivity.class);
intent.putExtra("url", url); intent.putExtra("url", url);
startActivity(intent); startActivity(intent);
// picker video file and share to tiktok
// openFileSelector();
// authenticateToEncrypt("1111");
}); });
} }
@ -905,19 +904,48 @@ public class MainActivity extends UnityPlayerActivity
Log.i(TAG, "passStorageState with: " + account); Log.i(TAG, "passStorageState with: " + account);
} }
public void storagePass(String funid, String account, String password) { private void saveToDriveAppFolder() {
Log.i(TAG, "storagePass with: " + account + " | " + password); Map<String, String> params = paramCache.get(RC_REQUEST_DRIVE_TO_UPLOAD);
Bundle userData = new Bundle(); if (params == null) {
userData.putString("pass", password); return;
// save to account manage
runOnUiThread(() -> {
final Account act = new Account(account, accountType);
if (accountManager.addAccountExplicitly(act, password, userData)) {
Log.i(TAG, "storage pass success");
} else {
Log.i(TAG, "storage pass error");
} }
}); WalletUtil.saveToDriveAppFolder(params.get("funid"), params.get("account"), JcSDK::nativeCb);
paramCache.remove(RC_REQUEST_DRIVE_TO_UPLOAD);
}
private void loginAndRequestDrivePermission(String funid, String account, int requestCode) {
Log.i(TAG, "no drive permission");
Map<String, String> params = Maps.newHashMap();
params.put("funid", funid);
params.put("account", account);
paramCache.put(requestCode, params);
GoogleSignInOptions gso = new GoogleSignInOptions.Builder(GoogleSignInOptions.DEFAULT_SIGN_IN)
.requestIdToken(getString(R.string.default_web_client_id1))
.requestScopes(new Scope(Scopes.EMAIL), new Scope(DriveScopes.DRIVE_APPDATA))
.build();
mGoogleSignInClient = GoogleSignIn.getClient(this, gso);
Intent signInIntent = mGoogleSignInClient.getSignInIntent();
startActivityForResult(signInIntent, requestCode);
}
public void storagePass(String funid, String account, String password) {
Log.i(TAG, String.format("storagePass with: %s | %s | %s", funid, account, password));
String filePath;
try {
filePath = WalletUtil.savePassToLocal(this, account, password);
} catch (JSONException | IOException e) {
Log.i(TAG, String.format("error storage pass to local, %s", e.getMessage()));
JcSDK.nativeCb(funid, "error storage pass to local", null);
return;
}
if (!GoogleSignIn.hasPermissions(
GoogleSignIn.getLastSignedInAccount(getActivity()),
new Scope(DriveScopes.DRIVE_APPDATA))) {
loginAndRequestDrivePermission(funid, account, RC_REQUEST_DRIVE_TO_UPLOAD);
} else {
Log.i(TAG, "had drive permission");
WalletUtil.saveToDriveAppFolder(funid, account, JcSDK::nativeCb);
}
// runOnUiThread(() -> { // runOnUiThread(() -> {
// Intent intent = new Intent(this, BiometricActivity.class); // Intent intent = new Intent(this, BiometricActivity.class);
// intent.putExtra("action", "encrypt"); // intent.putExtra("action", "encrypt");
@ -927,13 +955,30 @@ public class MainActivity extends UnityPlayerActivity
// startActivity(intent); // startActivity(intent);
// }); // });
} }
private void loadDrivePass() {
Map<String, String> params = paramCache.get(RC_REQUEST_DRIVE_TO_READ);
if (params == null) {
return;
}
WalletUtil.downloadCfgToLocal(params.get("funid"), params.get("account"));
WalletUtil.getPassLocal(this, params.get("funid"), params.get("account"));
paramCache.remove(RC_REQUEST_DRIVE_TO_READ);
}
public void authGetStoragePass(String funid, String account) { public void authGetStoragePass(String funid, String account) {
Log.i(TAG, "authGetStoragePass with: " + account); Log.i(TAG, "authGetStoragePass with: " + account);
// Account[] accounts = accountManager.getAccountsByType(accountType); if (!WalletUtil.localCfgExists(this, account)) {
// for (Account act : accounts) { if (!GoogleSignIn.hasPermissions(
// Log.i(TAG, "authGetStoragePass account: " + act.name + " | " + act.type ); GoogleSignIn.getLastSignedInAccount(getActivity()),
// } new Scope(DriveScopes.DRIVE_APPDATA))) {
loginAndRequestDrivePermission(funid, account, RC_REQUEST_DRIVE_TO_READ);
} else {
WalletUtil.downloadCfgToLocal(funid, account);
WalletUtil.getPassLocal(this, funid, account);
}
} else {
WalletUtil.getPassLocal(this, funid, account);
}
// runOnUiThread(() -> { // runOnUiThread(() -> {
// Intent intent = new Intent(this, BiometricActivity.class); // Intent intent = new Intent(this, BiometricActivity.class);
@ -946,6 +991,5 @@ public class MainActivity extends UnityPlayerActivity
public void getClientId(String funid) { public void getClientId(String funid) {
Log.i(TAG, "getClientId "); Log.i(TAG, "getClientId ");
} }
} }

View File

@ -0,0 +1,138 @@
package com.cege.games.release.wallet;
import android.content.Context;
import android.util.Log;
import com.cege.games.release.MainActivity;
import com.cege.games.release.R;
import com.google.android.gms.auth.api.signin.GoogleSignIn;
import com.google.android.gms.auth.api.signin.GoogleSignInAccount;
import com.google.android.gms.common.api.Scope;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.googleapis.json.GoogleJsonResponseException;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.DriveScopes;
import com.google.common.base.Strings;
import com.jc.jcfw.JcSDK;
import com.jc.jcfw.NativeResult;
import com.jc.jcfw.security.BiometricResult;
import com.jc.jcfw.util.DriveUtils;
import com.jc.jcfw.util.FileUtils;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.function.Consumer;
public class WalletUtil {
private static final String TAG = WalletUtil.class.getSimpleName();
public static String generateFileName(String account) {
return String.format("wallet_%s.json", account);
}
public static File getWalletCfgFile(Context context, String account) {
String filename = generateFileName(account);
File dic = new File(context.getFilesDir() + "/wallets");
if (!dic.exists()) {
dic.mkdir();
}
return new File(context.getFilesDir() + "/wallets", filename);
}
public static String savePassToLocal(Context context, String account, String pass) throws IOException, JSONException {
File filePath = getWalletCfgFile(context, account);
JSONObject content = new JSONObject();
content.put("pass", pass);
FileUtils.writeFile(filePath, content.toString());
return filePath.getAbsolutePath();
}
public static void getPassLocal(Context context, String funId, String account) {
try {
JSONObject json = loadLocalCfg(context, account);
String passEncrypted = json.getString("pass");
String passDecrypted = JcSDK.decryptPass(account, passEncrypted);
JcSDK.nativeCb(funId, null, passDecrypted);
} catch (JSONException e) {
JcSDK.nativeCb(funId, "error decode json", null);
} catch (IOException e) {
JcSDK.nativeCb(funId, "error read cfg file", null);
}
}
public static boolean localCfgExists(Context context, String account) {
File filePath = getWalletCfgFile(context, account);
return filePath.exists();
}
public static JSONObject loadLocalCfg(Context context, String account) throws JSONException, IOException {
File filePath = getWalletCfgFile(context, account);
if (!filePath.exists()) {
return null;
}
return FileUtils.readJsonFromFile(filePath);
}
public static void saveToDriveAppFolder(String funid, String account, Consumer<NativeResult> func) {
Context context = MainActivity.app;
GoogleSignInAccount ga = GoogleSignIn.getLastSignedInAccount(context);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context,
Collections.singletonList(DriveScopes.DRIVE_APPDATA));
credential.setSelectedAccount(ga.getAccount());
Drive service = DriveUtils.generateService(credential, context.getString(R.string.app_name));
try {
File file = getWalletCfgFile(MainActivity.app, account);
String fileName = file.getName();
String fileId = DriveUtils.queryOneAppFile(service, fileName);
if (Strings.isNullOrEmpty(fileId)) {
Log.i(TAG, String.format("%s not exists in drive, upload...", fileName));
fileId = DriveUtils.uploadAppFile(service, file, "application/json");
}
Log.i(TAG, "File ID: " + fileId);
func.accept(new NativeResult(funid, null, fileId));
} catch (GoogleJsonResponseException e) {
Log.i(TAG, "Unable to create file: " + e.getDetails());
func.accept(new NativeResult(funid, e.getMessage(), null));
} catch (IOException e) {
Log.i(TAG, e.getMessage());
func.accept(new NativeResult(funid, e.getMessage(), null));
}
}
public static void downloadCfgToLocal(String funid, String account) {
Context context = MainActivity.app;
GoogleSignInAccount ga = GoogleSignIn.getLastSignedInAccount(context);
GoogleAccountCredential credential = GoogleAccountCredential.usingOAuth2(context,
Collections.singletonList(DriveScopes.DRIVE_APPDATA));
credential.setSelectedAccount(ga.getAccount());
Drive service = DriveUtils.generateService(credential, context.getString(R.string.app_name));
String fileName = generateFileName(account);
String fileId = DriveUtils.queryOneAppFile(service, fileName);
if (Strings.isNullOrEmpty(fileId)) {
Log.i(TAG, "file not found in drive");
return;
}
boolean downloadSuccess = false;
while (!downloadSuccess) {
try {
String jsonStr = DriveUtils.downloadFile(service, fileId);
File fileLocal = getWalletCfgFile(context, account);
FileUtils.writeFile(fileLocal, jsonStr);
downloadSuccess = true;
} catch (IOException e) {
Log.i(TAG, "error download file");
}
}
}
}

View File

@ -7,7 +7,9 @@ import android.net.Uri;
import android.util.Log; import android.util.Log;
import com.cege.games.release.MainActivity; import com.cege.games.release.MainActivity;
import com.google.common.base.Strings;
import com.jc.jcfw.google.PayClient; import com.jc.jcfw.google.PayClient;
import com.jc.jcfw.util.ThreadUtils;
import org.cocos2dx.lib.CocosJSHelper; import org.cocos2dx.lib.CocosJSHelper;
import org.json.JSONException; import org.json.JSONException;
@ -27,6 +29,8 @@ public class JcSDK {
private static native int runJS(final String funId, final String methodName, final String params); private static native int runJS(final String funId, final String methodName, final String params);
public static native String decryptPass(final String account, final String pass);
public static void initCommonCB(UnityCallback callBack) { public static void initCommonCB(UnityCallback callBack) {
Log.i(TAG, "call init common callback from unity"); Log.i(TAG, "call init common callback from unity");
commonCB = callBack; commonCB = callBack;
@ -174,18 +178,15 @@ public class JcSDK {
MainActivity.app.getClientId(funid); MainActivity.app.getClientId(funid);
} }
/**
* 回调至js
*/
public static void nativeCb(String funId, String error, String dataStr) { public static void nativeCb(String funId, String error, String dataStr) {
JSONObject result = new JSONObject(); JSONObject result = new JSONObject();
try { try {
if (error != null && !error.isEmpty()) { if (Strings.isNullOrEmpty(error)) {
result.put("errcode", 1);
result.put("errmessage", error);
} else {
result.put("errcode", 0); result.put("errcode", 0);
result.put("data", dataStr); result.put("data", dataStr);
} else {
result.put("errcode", 1);
result.put("errmessage", error);
} }
} catch (JSONException e) { } catch (JSONException e) {
Log.e(TAG, "JSONException: " + e.getMessage()); Log.e(TAG, "JSONException: " + e.getMessage());
@ -193,6 +194,17 @@ public class JcSDK {
if (funId == null || funId.isEmpty()) { if (funId == null || funId.isEmpty()) {
funId = MainActivity.app.getFunId(); funId = MainActivity.app.getFunId();
} }
Log.i(TAG, String.format("%s native cb, error: %s, data: %s", funId, error, dataStr ));
if (ThreadUtils.isMainThread()) {
JcSDK.runJS(funId, "jniCallback", result.toString()); JcSDK.runJS(funId, "jniCallback", result.toString());
} else {
String finalFunId = funId;
MainActivity.app.runOnUiThread(() -> JcSDK.runJS(finalFunId, "jniCallback", result.toString()));
}
}
public static void nativeCb(NativeResult result) {
nativeCb(result.getFunid(), result.getError(), result.getDataStr());
} }
} }

View File

@ -0,0 +1,37 @@
package com.jc.jcfw;
public class NativeResult {
private String funid;
private String error;
private String dataStr;
public NativeResult(String funid, String error, String dataStr) {
this.funid = funid;
this.error = error;
this.dataStr = dataStr;
}
public String getFunid() {
return funid;
}
public void setFunid(String funid) {
this.funid = funid;
}
public String getError() {
return error;
}
public void setError(String error) {
this.error = error;
}
public String getDataStr() {
return dataStr;
}
public void setDataStr(String dataStr) {
this.dataStr = dataStr;
}
}

View File

@ -0,0 +1,101 @@
package com.jc.jcfw.util;
import android.util.Log;
import com.google.api.client.extensions.android.http.AndroidHttp;
import com.google.api.client.extensions.android.json.AndroidJsonFactory;
import com.google.api.client.googleapis.extensions.android.gms.auth.GoogleAccountCredential;
import com.google.api.client.http.FileContent;
import com.google.api.services.drive.Drive;
import com.google.api.services.drive.model.File;
import com.google.api.services.drive.model.FileList;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Collections;
import java.util.function.Consumer;
public class DriveUtils {
private static final String TAG = DriveUtils.class.getSimpleName();
public static Drive generateService(GoogleAccountCredential credential, String appName) {
return new Drive.Builder(
AndroidHttp.newCompatibleTransport(),
AndroidJsonFactory.getDefaultInstance(),
credential)
.setApplicationName(appName)
.build();
}
/**
* query app file by filename
*
* @param service
* @param fileName
* @throws IOException
*/
public static String queryOneAppFile(Drive service, String fileName) {
boolean querySuccess = false;
String fileId = "";
while (!querySuccess) {
try {
FileList files = service.files().list()
.setSpaces("appDataFolder")
.setFields("nextPageToken, files(id, name)")
.setPageSize(100)
.execute();
querySuccess = true;
for (File file : files.getFiles()) {
Log.i(TAG, String.format("Found file: %s (%s)\n", file.getName(), file.getId()));
if (file.getName().equals(fileName)) {
fileId = file.getId();
break;
}
}
} catch (IOException e) {
Log.i(TAG, "error query file from drive");
}
}
return fileId;
}
/**
* download one file from drive
*
* @param service
* @param fileId
* @throws IOException
*/
public static String downloadFile(Drive service, String fileId) throws IOException {
OutputStream outputStream = new ByteArrayOutputStream();
service.files().get(fileId).executeMediaAndDownloadTo(outputStream);
// convert outputStream to JSON string
// String json = outputStream.toString();
return outputStream.toString();
}
/**
* upload one file to Drive
*
* @param service
* @param filePath file absolute path
* @param fileType application/json
* @throws IOException
*/
public static String uploadAppFile(Drive service, java.io.File filePath, String fileType) throws IOException {
String fileName = filePath.getName();
File fileMetadata = new File();
fileMetadata.setName(fileName);
fileMetadata.setParents(Collections.singletonList("appDataFolder"));
// "application/json"
FileContent mediaContent = new FileContent(fileType, filePath);
File file = service.files().create(fileMetadata, mediaContent)
.setFields("id")
.execute();
Log.i(TAG, String.format("%s upload success, File ID: %s", fileName, file.getId()));
return file.getId();
}
}

View File

@ -13,10 +13,15 @@ import android.os.Environment;
import android.provider.MediaStore; import android.provider.MediaStore;
import android.util.Log; import android.util.Log;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream; import java.io.OutputStream;
import java.io.RandomAccessFile;
public class FileUtils { public class FileUtils {
private static final String TAG = FileUtils.class.getSimpleName(); private static final String TAG = FileUtils.class.getSimpleName();
@ -26,7 +31,7 @@ public class FileUtils {
* @param fileName path * @param fileName path
* @return TRUE or FALSE * @return TRUE or FALSE
*/ */
static boolean fileIsExist(String fileName) { public static boolean fileIsExist(String fileName) {
File file = new File(fileName); File file = new File(fileName);
if (file.exists()) if (file.exists())
return true; return true;
@ -35,6 +40,20 @@ public class FileUtils {
} }
} }
public static void writeFile(File filePath, String content) throws IOException {
FileOutputStream out = new FileOutputStream(filePath);
out.write(content.getBytes());
out.close();
}
public static JSONObject readJsonFromFile(File filePath) throws IOException, JSONException {
RandomAccessFile f = new RandomAccessFile(filePath, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new JSONObject(new String(bytes));
}
/** /**
* get image base path of external storage * get image base path of external storage
*/ */

View File

@ -9,7 +9,8 @@ import android.util.Log;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.cege.games.release.MainActivity; import org.json.JSONException;
import org.json.JSONObject;
import java.io.File; import java.io.File;
import java.io.FileOutputStream; import java.io.FileOutputStream;
@ -20,7 +21,7 @@ import java.util.UUID;
public class IDUtils { public class IDUtils {
private static String sID = null; private static String sID = null;
private static final String WALLET_STATS = "wallets"; private static final String WALLET_STATS = "wallet.json";
public synchronized static String id(Context context) { public synchronized static String id(Context context) {
if (sID == null) { if (sID == null) {
@ -37,18 +38,23 @@ public class IDUtils {
return sID; return sID;
} }
private static String readInstallationFile(File installation) throws IOException { private static String readInstallationFile(File installation) throws IOException, JSONException {
RandomAccessFile f = new RandomAccessFile(installation, "r"); RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()]; byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes); f.readFully(bytes);
f.close(); f.close();
return new String(bytes); String jsonStr = new String(bytes);
Log.i("IDUtils", jsonStr);
JSONObject content = new JSONObject(new String(bytes));
return content.getString("id");
} }
private static void writeInstallationFile(File installation) throws IOException { private static void writeInstallationFile(File installation) throws IOException, JSONException {
FileOutputStream out = new FileOutputStream(installation); FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString(); String id = UUID.randomUUID().toString();
out.write(id.getBytes()); JSONObject content = new JSONObject();
content.put("id", id);
out.write(content.toString().getBytes());
out.close(); out.close();
} }

View File

@ -0,0 +1,14 @@
package com.jc.jcfw.util;
import android.os.Looper;
public class ThreadUtils {
/**
* check if current thread is main thread
*
* @return
*/
public static boolean isMainThread() {
return Looper.getMainLooper() == Looper.myLooper();
}
}

12
res/raw/apple_config.json Normal file
View File

@ -0,0 +1,12 @@
{
"client_id": "wallet.cebggame.com",
"redirect_uri": "https://wallet.cebggame.com/apple/oauth_redirect",
"end_session_redirect_uri": "com.apple.apps.wallet.cebggame.com:/oauth2redirect",
"authorization_scope": "name email",
"discovery_uri": "https://appleid.apple.com/.well-known/openid-configuration",
"authorization_endpoint_uri": "",
"token_endpoint_uri": "",
"registration_endpoint_uri": "",
"user_info_endpoint_uri": "",
"https_required": true
}