save tmp
This commit is contained in:
parent
2fc94d2a56
commit
2fd2850fbd
File diff suppressed because one or more lines are too long
@ -12,13 +12,14 @@ function promiseCb(funId, promiseFun, dataParser) {
|
|||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* oauth login before init internal wallet
|
* oauth login before init internal wallet
|
||||||
* @param {*} channel 0: google, 1: apple, 2: tiktok, 3: facebook, 4: twitter 5: tg
|
* @param {*} channel 0: google, 1: apple, 2: tiktok, 3: facebook, 4: twitter 5: tg, 6: email, 7: discord 10: client
|
||||||
*/
|
*/
|
||||||
function walletLogin(funId, channel) {
|
function walletLogin(funId, channel, env) {
|
||||||
channel = parseInt(channel);
|
channel = parseInt(channel);
|
||||||
|
env = env || 'dev';
|
||||||
console.log('walletLogin: ' + channel);
|
console.log('walletLogin: ' + channel);
|
||||||
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
|
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
|
||||||
promiseCb(funId, wallet.preLogin(channel));
|
promiseCb(funId, wallet.preLogin(channel, env));
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* init internal wallet with password
|
* init internal wallet with password
|
||||||
@ -587,3 +588,15 @@ function restorePassLocal(funId, key) {
|
|||||||
function getLocalPassState(funId, key) {
|
function getLocalPassState(funId, key) {
|
||||||
promiseCb(funId, jc.wallet.nativeSvr.passStorageState(key));
|
promiseCb(funId, jc.wallet.nativeSvr.passStorageState(key));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function saveLocalVal(funId, key, val) {
|
||||||
|
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
|
||||||
|
wallet.saveLocalItem(key, val);
|
||||||
|
return JSON.stringify({ errcode: 0, data: 1 });
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadLocalVal(funId, key) {
|
||||||
|
const wallet = !window.jc || !jc.wallet ? new jcwallet.default({ type: 0 }) : jc.wallet;
|
||||||
|
let val = wallet.loadLocalItem(key);
|
||||||
|
return JSON.stringify({ errcode: 0, data: val });
|
||||||
|
}
|
||||||
|
@ -46,7 +46,7 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:screenOrientation="sensorLandscape"
|
android:screenOrientation="sensorLandscape"
|
||||||
android:theme="@style/CEBGThemeSelector"
|
android:theme="@style/CebgMainActivity"
|
||||||
android:launchMode="singleTask"
|
android:launchMode="singleTask"
|
||||||
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
|
android:configChanges="mcc|mnc|locale|touchscreen|keyboard|keyboardHidden|navigation|orientation|screenLayout|uiMode|screenSize|smallestScreenSize|fontScale|layoutDirection|density"
|
||||||
android:resizeableActivity="false"
|
android:resizeableActivity="false"
|
||||||
@ -103,6 +103,9 @@
|
|||||||
<activity
|
<activity
|
||||||
android:name=".activity.WebPageActivity"
|
android:name=".activity.WebPageActivity"
|
||||||
android:theme="@style/WebViewTheme" />
|
android:theme="@style/WebViewTheme" />
|
||||||
|
<activity
|
||||||
|
android:name=".activity.BiometricActivity"
|
||||||
|
android:theme="@style/AppCompatTranslucent" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".activity.CustomCaptureActivity"
|
android:name=".activity.CustomCaptureActivity"
|
||||||
android:theme="@style/CaptureTheme" />
|
android:theme="@style/CaptureTheme" />
|
||||||
@ -227,7 +230,7 @@
|
|||||||
<uses-permission
|
<uses-permission
|
||||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
android:maxSdkVersion="29" />
|
android:maxSdkVersion="29" />
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<queries>
|
<queries>
|
||||||
<package android:name="com.zhiliaoapp.musically" />
|
<package android:name="com.zhiliaoapp.musically" />
|
||||||
<package android:name="com.ss.android.ugc.trill" />
|
<package android:name="com.ss.android.ugc.trill" />
|
||||||
|
@ -1,5 +1,8 @@
|
|||||||
package com.cege.games.release;
|
package com.cege.games.release;
|
||||||
|
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
||||||
|
|
||||||
import android.Manifest;
|
import android.Manifest;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -7,10 +10,10 @@ import android.content.pm.ApplicationInfo;
|
|||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.provider.MediaStore;
|
import android.provider.MediaStore;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
import android.util.Base64;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
@ -19,6 +22,8 @@ import androidx.annotation.MainThread;
|
|||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
import androidx.biometric.BiometricManager;
|
||||||
|
import androidx.biometric.BiometricPrompt;
|
||||||
import androidx.browser.customtabs.CustomTabsIntent;
|
import androidx.browser.customtabs.CustomTabsIntent;
|
||||||
import androidx.core.app.ActivityOptionsCompat;
|
import androidx.core.app.ActivityOptionsCompat;
|
||||||
|
|
||||||
@ -28,7 +33,9 @@ import com.bytedance.sdk.open.tiktok.authorize.model.Authorization;
|
|||||||
import com.bytedance.sdk.open.tiktok.base.MediaContent;
|
import com.bytedance.sdk.open.tiktok.base.MediaContent;
|
||||||
import com.bytedance.sdk.open.tiktok.base.VideoObject;
|
import com.bytedance.sdk.open.tiktok.base.VideoObject;
|
||||||
import com.bytedance.sdk.open.tiktok.share.Share;
|
import com.bytedance.sdk.open.tiktok.share.Share;
|
||||||
|
import com.cege.games.release.activity.BiometricActivity;
|
||||||
import com.cege.games.release.activity.CustomCaptureActivity;
|
import com.cege.games.release.activity.CustomCaptureActivity;
|
||||||
|
import com.cege.games.release.activity.UnityPlayerActivity;
|
||||||
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;
|
||||||
@ -56,12 +63,14 @@ 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.FileUtils;
|
import com.jc.jcfw.security.BiometricHelper;
|
||||||
|
import com.jc.jcfw.security.CryptographyManager;
|
||||||
|
import com.jc.jcfw.security.CryptographyManagerImpl;
|
||||||
|
import com.jc.jcfw.security.EncryptedData;
|
||||||
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;
|
||||||
import com.king.zxing.util.LogUtils;
|
import com.king.zxing.util.LogUtils;
|
||||||
import com.unity3d.player.UnityPlayerActivity;
|
|
||||||
|
|
||||||
import net.openid.appauth.AppAuthConfiguration;
|
import net.openid.appauth.AppAuthConfiguration;
|
||||||
import net.openid.appauth.AuthState;
|
import net.openid.appauth.AuthState;
|
||||||
@ -93,6 +102,8 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||||
import pub.devrel.easypermissions.EasyPermissions;
|
import pub.devrel.easypermissions.EasyPermissions;
|
||||||
|
|
||||||
@ -136,7 +147,6 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
private CountDownLatch mAuthIntentLatch = new CountDownLatch(1);
|
private CountDownLatch mAuthIntentLatch = new CountDownLatch(1);
|
||||||
|
|
||||||
private TikTokOpenApi tiktokOpenApi;
|
private TikTokOpenApi tiktokOpenApi;
|
||||||
|
|
||||||
public String getFunId() {
|
public String getFunId() {
|
||||||
return funId;
|
return funId;
|
||||||
}
|
}
|
||||||
@ -150,6 +160,8 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
|
|
||||||
private FirebaseAnalytics mFirebaseAnalytics;
|
private FirebaseAnalytics mFirebaseAnalytics;
|
||||||
private AppEventsLogger fbLogger;
|
private AppEventsLogger fbLogger;
|
||||||
|
private BiometricPrompt.PromptInfo promptInfo;
|
||||||
|
private CryptographyManager cryptographyManager;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
@ -192,6 +204,8 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
|
|
||||||
PayClient payClient = PayClient.getInstance();
|
PayClient payClient = PayClient.getInstance();
|
||||||
payClient.init(this);
|
payClient.init(this);
|
||||||
|
promptInfo = BiometricHelper.createPromptInfo(this);
|
||||||
|
cryptographyManager = new CryptographyManagerImpl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -270,17 +284,7 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
// begin for unity
|
// begin for unity
|
||||||
@Override
|
|
||||||
protected void onNewIntent(Intent intent) {
|
|
||||||
// To support deep linking, we need to make sure that the client can get access
|
|
||||||
// to
|
|
||||||
// the last sent intent. The clients access this through a JNI api that allows
|
|
||||||
// them
|
|
||||||
// to get the intent set on launch. To update that after launch we have to
|
|
||||||
// manually
|
|
||||||
// replace the intent with the one caught here.
|
|
||||||
setIntent(intent);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void onLoadNativeLibraries() {
|
protected void onLoadNativeLibraries() {
|
||||||
try {
|
try {
|
||||||
@ -851,11 +855,15 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
public void showPage(String fid, final String url) {
|
public void showPage(String fid, final String url) {
|
||||||
runOnUiThread(() -> {
|
runOnUiThread(() -> {
|
||||||
Log.i(TAG, "show page: " + url);
|
Log.i(TAG, "show page: " + url);
|
||||||
Intent intent = new Intent(this, WebPageActivity.class);
|
// Intent intent = new Intent(this, WebPageActivity.class);
|
||||||
intent.putExtra("url", url);
|
// intent.putExtra("url", url);
|
||||||
startActivity(intent);
|
// Intent intent = new Intent(this, BiometricActivity.class);
|
||||||
|
// intent.putExtra("action", "encrypt");
|
||||||
|
// intent.putExtra("key", "1111");
|
||||||
|
// startActivity(intent);
|
||||||
// picker video file and share to tiktok
|
// picker video file and share to tiktok
|
||||||
// openFileSelector();
|
// openFileSelector();
|
||||||
|
authenticateToEncrypt("1111");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -902,4 +910,32 @@ public class MainActivity extends UnityPlayerActivity
|
|||||||
startActivityForResult(intent, FILE_SELECTOR_CODE);
|
startActivityForResult(intent, FILE_SELECTOR_CODE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void authenticateToEncrypt(String text) {
|
||||||
|
if (BiometricManager.from(this).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager
|
||||||
|
.BIOMETRIC_SUCCESS) {
|
||||||
|
Cipher cipher = cryptographyManager.getInitializedCipherForEncryption("cebg_wallet_key");
|
||||||
|
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(MainActivity.this, _cipher -> {
|
||||||
|
EncryptedData encryptedData = cryptographyManager.encryptData(text, _cipher);
|
||||||
|
String encryptedString = Base64.encodeToString(encryptedData.getCiphertext(), Base64.DEFAULT);
|
||||||
|
String ivString = Base64.encodeToString(encryptedData.getInitializationVector(), Base64.DEFAULT);
|
||||||
|
Log.i(TAG, "encrypted msg: " + encryptedString);
|
||||||
|
Log.i(TAG, "encrypted iv: " + ivString);
|
||||||
|
});
|
||||||
|
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void authenticateToDecrypt(String text, String iv) {
|
||||||
|
if (BiometricManager.from(this).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager
|
||||||
|
.BIOMETRIC_SUCCESS) {
|
||||||
|
byte[] ivData = Base64.decode(iv, Base64.DEFAULT);
|
||||||
|
byte[] textData = Base64.decode(text, Base64.DEFAULT);
|
||||||
|
Cipher cipher = cryptographyManager.getInitializedCipherForDecryption("cebg_wallet_key", ivData);
|
||||||
|
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(MainActivity.this, _cipher -> {
|
||||||
|
String decryptedMsg = cryptographyManager.decryptData(textData, _cipher);
|
||||||
|
Log.i(TAG, "decrypted msg: " + decryptedMsg);
|
||||||
|
});
|
||||||
|
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,76 @@
|
|||||||
|
package com.cege.games.release.activity;
|
||||||
|
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
import androidx.biometric.BiometricManager;
|
||||||
|
import androidx.biometric.BiometricPrompt;
|
||||||
|
|
||||||
|
import com.cege.games.release.R;
|
||||||
|
import com.jc.jcfw.security.BiometricHelper;
|
||||||
|
import com.jc.jcfw.security.CryptographyManager;
|
||||||
|
import com.jc.jcfw.security.CryptographyManagerImpl;
|
||||||
|
import com.jc.jcfw.security.EncryptedData;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
public class BiometricActivity extends AppCompatActivity {
|
||||||
|
private static final String TAG = BiometricActivity.class.getSimpleName();
|
||||||
|
private BiometricPrompt.PromptInfo promptInfo;
|
||||||
|
private CryptographyManager cryptographyManager;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
setContentView(R.layout.activity_biometric);
|
||||||
|
Log.i(TAG, "onCreate: " + getIntent().getDataString());
|
||||||
|
promptInfo = BiometricHelper.createPromptInfo(this);
|
||||||
|
cryptographyManager = new CryptographyManagerImpl();
|
||||||
|
Intent intent = getIntent();
|
||||||
|
String action = intent.getStringExtra("action");
|
||||||
|
String key = intent.getStringExtra("key");
|
||||||
|
// check if action is exists
|
||||||
|
// if ("encrypt".equals(action)) {
|
||||||
|
// authenticateToEncrypt(key);
|
||||||
|
// } else if ("decrypt".equals(action)) {
|
||||||
|
// String iv = intent.getStringExtra("iv");
|
||||||
|
// authenticateToDecrypt(key, iv);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticateToEncrypt(String text) {
|
||||||
|
if (BiometricManager.from(this)
|
||||||
|
.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
|
Cipher cipher = cryptographyManager.getInitializedCipherForEncryption("cebg_wallet_key");
|
||||||
|
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(this, _cipher -> {
|
||||||
|
EncryptedData encryptedData = cryptographyManager.encryptData(text, _cipher);
|
||||||
|
String encryptedString = Base64.encodeToString(encryptedData.getCiphertext(), Base64.DEFAULT);
|
||||||
|
String ivString = Base64.encodeToString(encryptedData.getInitializationVector(), Base64.DEFAULT);
|
||||||
|
Log.i(TAG, "encrypted msg: " + encryptedString);
|
||||||
|
Log.i(TAG, "encrypted iv: " + ivString);
|
||||||
|
});
|
||||||
|
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void authenticateToDecrypt(String text, String iv) {
|
||||||
|
if (BiometricManager.from(this)
|
||||||
|
.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
|
||||||
|
byte[] ivData = Base64.decode(iv, Base64.DEFAULT);
|
||||||
|
byte[] textData = Base64.decode(text, Base64.DEFAULT);
|
||||||
|
Cipher cipher = cryptographyManager.getInitializedCipherForDecryption("cebg_wallet_key", ivData);
|
||||||
|
BiometricPrompt biometricPrompt = BiometricHelper.createBiometricPrompt(this, _cipher -> {
|
||||||
|
String decryptedMsg = cryptographyManager.decryptData(textData, _cipher);
|
||||||
|
Log.i(TAG, "decrypted msg: " + decryptedMsg);
|
||||||
|
});
|
||||||
|
biometricPrompt.authenticate(promptInfo, new BiometricPrompt.CryptoObject(cipher));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
168
app/src/com/cege/games/release/activity/UnityPlayerActivity.java
Normal file
168
app/src/com/cege/games/release/activity/UnityPlayerActivity.java
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package com.cege.games.release.activity;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.view.KeyEvent;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.view.Window;
|
||||||
|
|
||||||
|
import androidx.appcompat.app.AppCompatActivity;
|
||||||
|
|
||||||
|
import com.unity3d.player.IUnityPlayerLifecycleEvents;
|
||||||
|
import com.unity3d.player.MultiWindowSupport;
|
||||||
|
import com.unity3d.player.UnityPlayer;
|
||||||
|
|
||||||
|
public class UnityPlayerActivity extends AppCompatActivity implements IUnityPlayerLifecycleEvents
|
||||||
|
{
|
||||||
|
protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code
|
||||||
|
|
||||||
|
// Override this in your custom UnityPlayerActivity to tweak the command line arguments passed to the Unity Android Player
|
||||||
|
// The command line arguments are passed as a string, separated by spaces
|
||||||
|
// UnityPlayerActivity calls this from 'onCreate'
|
||||||
|
// Supported: -force-gles20, -force-gles30, -force-gles31, -force-gles31aep, -force-gles32, -force-gles, -force-vulkan
|
||||||
|
// See https://docs.unity3d.com/Manual/CommandLineArguments.html
|
||||||
|
// @param cmdLine the current command line arguments, may be null
|
||||||
|
// @return the modified command line string or null
|
||||||
|
protected String updateUnityCommandLineArguments(String cmdLine)
|
||||||
|
{
|
||||||
|
return cmdLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup activity layout
|
||||||
|
@Override protected void onCreate(Bundle savedInstanceState)
|
||||||
|
{
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
|
||||||
|
String cmdLine = updateUnityCommandLineArguments(getIntent().getStringExtra("unity"));
|
||||||
|
getIntent().putExtra("unity", cmdLine);
|
||||||
|
|
||||||
|
mUnityPlayer = new UnityPlayer(this, this);
|
||||||
|
setContentView(mUnityPlayer);
|
||||||
|
mUnityPlayer.requestFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// When Unity player unloaded move task to background
|
||||||
|
@Override public void onUnityPlayerUnloaded() {
|
||||||
|
moveTaskToBack(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Callback before Unity player process is killed
|
||||||
|
@Override public void onUnityPlayerQuitted() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected void onNewIntent(Intent intent) {
|
||||||
|
// To support deep linking, we need to make sure that the client can get access to
|
||||||
|
// the last sent intent. The clients access this through a JNI api that allows them
|
||||||
|
// to get the intent set on launch. To update that after launch we have to manually
|
||||||
|
// replace the intent with the one caught here.
|
||||||
|
super.onNewIntent(intent);
|
||||||
|
setIntent(intent);
|
||||||
|
mUnityPlayer.newIntent(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Quit Unity
|
||||||
|
@Override protected void onDestroy ()
|
||||||
|
{
|
||||||
|
mUnityPlayer.destroy();
|
||||||
|
super.onDestroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the activity is in multi window mode or resizing the activity is allowed we will use
|
||||||
|
// onStart/onStop (the visibility callbacks) to determine when to pause/resume.
|
||||||
|
// Otherwise it will be done in onPause/onResume as Unity has done historically to preserve
|
||||||
|
// existing behavior.
|
||||||
|
@Override protected void onStop()
|
||||||
|
{
|
||||||
|
super.onStop();
|
||||||
|
|
||||||
|
if (!MultiWindowSupport.getAllowResizableWindow(this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mUnityPlayer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override protected void onStart()
|
||||||
|
{
|
||||||
|
super.onStart();
|
||||||
|
|
||||||
|
if (!MultiWindowSupport.getAllowResizableWindow(this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mUnityPlayer.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pause Unity
|
||||||
|
@Override protected void onPause()
|
||||||
|
{
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
MultiWindowSupport.saveMultiWindowMode(this);
|
||||||
|
|
||||||
|
if (MultiWindowSupport.getAllowResizableWindow(this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mUnityPlayer.pause();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Resume Unity
|
||||||
|
@Override protected void onResume()
|
||||||
|
{
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
if (MultiWindowSupport.getAllowResizableWindow(this) && !MultiWindowSupport.isMultiWindowModeChangedToTrue(this))
|
||||||
|
return;
|
||||||
|
|
||||||
|
mUnityPlayer.resume();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Low Memory Unity
|
||||||
|
@Override public void onLowMemory()
|
||||||
|
{
|
||||||
|
super.onLowMemory();
|
||||||
|
mUnityPlayer.lowMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trim Memory Unity
|
||||||
|
@Override public void onTrimMemory(int level)
|
||||||
|
{
|
||||||
|
super.onTrimMemory(level);
|
||||||
|
if (level == TRIM_MEMORY_RUNNING_CRITICAL)
|
||||||
|
{
|
||||||
|
mUnityPlayer.lowMemory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This ensures the layout will be correct.
|
||||||
|
@Override public void onConfigurationChanged(Configuration newConfig)
|
||||||
|
{
|
||||||
|
super.onConfigurationChanged(newConfig);
|
||||||
|
mUnityPlayer.configurationChanged(newConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Notify Unity of the focus change.
|
||||||
|
@Override public void onWindowFocusChanged(boolean hasFocus)
|
||||||
|
{
|
||||||
|
super.onWindowFocusChanged(hasFocus);
|
||||||
|
mUnityPlayer.windowFocusChanged(hasFocus);
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some reason the multiple keyevent type is not supported by the ndk.
|
||||||
|
// Force event injection by overriding dispatchKeyEvent().
|
||||||
|
|
||||||
|
@SuppressLint("RestrictedApi")
|
||||||
|
@Override public boolean dispatchKeyEvent(KeyEvent event)
|
||||||
|
{
|
||||||
|
if (event.getAction() == KeyEvent.ACTION_MULTIPLE)
|
||||||
|
return mUnityPlayer.injectEvent(event);
|
||||||
|
return super.dispatchKeyEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass any events not handled by (unfocused) views straight to UnityPlayer
|
||||||
|
@Override public boolean onKeyUp(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
|
||||||
|
@Override public boolean onKeyDown(int keyCode, KeyEvent event) { return mUnityPlayer.injectEvent(event); }
|
||||||
|
@Override public boolean onTouchEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
|
||||||
|
/*API12*/ public boolean onGenericMotionEvent(MotionEvent event) { return mUnityPlayer.injectEvent(event); }
|
||||||
|
}
|
62
app/src/com/jc/jcfw/security/BiometricHelper.java
Normal file
62
app/src/com/jc/jcfw/security/BiometricHelper.java
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
package com.jc.jcfw.security;
|
||||||
|
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
|
||||||
|
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.biometric.BiometricPrompt;
|
||||||
|
import androidx.core.content.ContextCompat;
|
||||||
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
|
import com.cege.games.release.R;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
public class BiometricHelper {
|
||||||
|
private static final String TAG = BiometricHelper.class.getSimpleName();
|
||||||
|
public static BiometricPrompt.PromptInfo createPromptInfo(Context context) {
|
||||||
|
BiometricPrompt.PromptInfo promptInfo = new BiometricPrompt.PromptInfo.Builder()
|
||||||
|
.setTitle(context.getString(R.string.prompt_info_title)) // e.g. "Sign in"
|
||||||
|
.setSubtitle(context.getString(R.string.prompt_info_subtitle)) // e.g. "Biometric for My App"
|
||||||
|
.setDescription(context.getString(R.string.prompt_info_description)) // e.g. "Confirm biometric to continue"
|
||||||
|
.setConfirmationRequired(false)
|
||||||
|
.setAllowedAuthenticators(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)
|
||||||
|
// .setDeviceCredentialAllowed(true) // Allow PIN/pattern/password authentication.
|
||||||
|
// Also note that setDeviceCredentialAllowed and setNegativeButtonText are
|
||||||
|
// incompatible so that if you uncomment one you must comment out the other
|
||||||
|
.build();
|
||||||
|
return promptInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static BiometricPrompt createBiometricPrompt(FragmentActivity activity, Consumer<Cipher> func) {
|
||||||
|
Executor executor = ContextCompat.getMainExecutor(activity.getApplicationContext());
|
||||||
|
return new BiometricPrompt(activity, executor, new BiometricPrompt.AuthenticationCallback() {
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
|
||||||
|
super.onAuthenticationError(errorCode, errString);
|
||||||
|
Log.i(TAG, "Authentication error: " + errString);
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationFailed() {
|
||||||
|
super.onAuthenticationFailed();
|
||||||
|
Log.i(TAG, "Authentication failed!");
|
||||||
|
}
|
||||||
|
@Override
|
||||||
|
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
|
||||||
|
super.onAuthenticationSucceeded(result);
|
||||||
|
Log.i(TAG, "Authentication succeeded!");
|
||||||
|
try {
|
||||||
|
func.accept(result.getCryptoObject().getCipher());
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e("BiometricHelper", e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
27
app/src/com/jc/jcfw/security/CryptographyManager.java
Normal file
27
app/src/com/jc/jcfw/security/CryptographyManager.java
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package com.jc.jcfw.security;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
|
||||||
|
public interface CryptographyManager {
|
||||||
|
/**
|
||||||
|
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
|
||||||
|
* with the key. The secret key uses [ENCRYPT_MODE][Cipher.ENCRYPT_MODE] is used.
|
||||||
|
*/
|
||||||
|
Cipher getInitializedCipherForEncryption(String keyName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method first gets or generates an instance of SecretKey and then initializes the Cipher
|
||||||
|
* with the key. The secret key uses [DECRYPT_MODE][Cipher.DECRYPT_MODE] is used.
|
||||||
|
*/
|
||||||
|
Cipher getInitializedCipherForDecryption(String keyName, byte[] initializationVector);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Cipher created with [getInitializedCipherForEncryption] is used here
|
||||||
|
*/
|
||||||
|
EncryptedData encryptData(String plaintext, Cipher cipher);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Cipher created with [getInitializedCipherForDecryption] is used here
|
||||||
|
*/
|
||||||
|
String decryptData(byte[] ciphertext, Cipher cipher);
|
||||||
|
}
|
97
app/src/com/jc/jcfw/security/CryptographyManagerImpl.java
Normal file
97
app/src/com/jc/jcfw/security/CryptographyManagerImpl.java
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
package com.jc.jcfw.security;
|
||||||
|
|
||||||
|
import static android.security.keystore.KeyProperties.BLOCK_MODE_CBC;
|
||||||
|
import static android.security.keystore.KeyProperties.ENCRYPTION_PADDING_PKCS7;
|
||||||
|
|
||||||
|
import android.security.keystore.KeyGenParameterSpec;
|
||||||
|
import android.security.keystore.KeyProperties;
|
||||||
|
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.security.KeyStore;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.KeyGenerator;
|
||||||
|
import javax.crypto.SecretKey;
|
||||||
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
|
||||||
|
public class CryptographyManagerImpl implements CryptographyManager {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cipher getInitializedCipherForEncryption(String keyName) {
|
||||||
|
Cipher cipher;
|
||||||
|
try {
|
||||||
|
cipher = getCipher();
|
||||||
|
SecretKey secretKey = getOrCreateSecretKey(keyName);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to initialize Cipher for encryption", e);
|
||||||
|
}
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Cipher getInitializedCipherForDecryption(String keyName, byte[] initializationVector) {
|
||||||
|
Cipher cipher;
|
||||||
|
try {
|
||||||
|
cipher = getCipher();
|
||||||
|
SecretKey secretKey = getOrCreateSecretKey(keyName);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKey, new IvParameterSpec(initializationVector));
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to initialize Cipher for decryption", e);
|
||||||
|
}
|
||||||
|
return cipher;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public EncryptedData encryptData(String plaintext, Cipher cipher) {
|
||||||
|
try {
|
||||||
|
byte[] ciphertext = cipher.doFinal(plaintext.getBytes(Charset.defaultCharset()));
|
||||||
|
return new EncryptedData(ciphertext, cipher.getIV());
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to encrypt data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String decryptData(byte[] ciphertext, Cipher cipher) {
|
||||||
|
try {
|
||||||
|
byte[] plaintext = cipher.doFinal(ciphertext);
|
||||||
|
return new String(plaintext, StandardCharsets.UTF_8);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RuntimeException("Failed to encrypt data", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Cipher getCipher() throws Exception {
|
||||||
|
return Cipher.getInstance(KeyProperties.KEY_ALGORITHM_AES + "/"
|
||||||
|
+ BLOCK_MODE_CBC + "/"
|
||||||
|
+ ENCRYPTION_PADDING_PKCS7);
|
||||||
|
}
|
||||||
|
|
||||||
|
private SecretKey getOrCreateSecretKey(String keyName) throws Exception {
|
||||||
|
String ANDROID_KEYSTORE = "AndroidKeyStore";
|
||||||
|
KeyStore keyStore = KeyStore.getInstance(ANDROID_KEYSTORE);
|
||||||
|
keyStore.load(null); // Keystore must be loaded before it can be accessed
|
||||||
|
SecretKey existingKey = (SecretKey) keyStore.getKey(keyName, null);
|
||||||
|
if (existingKey != null) {
|
||||||
|
return existingKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
// if you reach here, then a new SecretKey must be generated for that keyName
|
||||||
|
KeyGenParameterSpec.Builder paramsBuilder = new KeyGenParameterSpec.Builder(keyName,
|
||||||
|
KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT);
|
||||||
|
paramsBuilder.setBlockModes(BLOCK_MODE_CBC);
|
||||||
|
paramsBuilder.setEncryptionPaddings(ENCRYPTION_PADDING_PKCS7);
|
||||||
|
int KEY_SIZE = 256;
|
||||||
|
paramsBuilder.setKeySize(KEY_SIZE);
|
||||||
|
paramsBuilder.setUserAuthenticationRequired(false);
|
||||||
|
paramsBuilder.setUserAuthenticationValidityDurationSeconds(30);
|
||||||
|
|
||||||
|
KeyGenParameterSpec keyGenParams = paramsBuilder.build();
|
||||||
|
KeyGenerator keyGenerator = KeyGenerator.getInstance(KeyProperties.KEY_ALGORITHM_AES,
|
||||||
|
ANDROID_KEYSTORE);
|
||||||
|
keyGenerator.init(keyGenParams);
|
||||||
|
return keyGenerator.generateKey();
|
||||||
|
}
|
||||||
|
}
|
19
app/src/com/jc/jcfw/security/EncryptedData.java
Normal file
19
app/src/com/jc/jcfw/security/EncryptedData.java
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package com.jc.jcfw.security;
|
||||||
|
|
||||||
|
public class EncryptedData {
|
||||||
|
private final byte[] ciphertext;
|
||||||
|
private final byte[] initializationVector;
|
||||||
|
|
||||||
|
public EncryptedData(byte[] ciphertext, byte[] initializationVector) {
|
||||||
|
this.ciphertext = ciphertext;
|
||||||
|
this.initializationVector = initializationVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getCiphertext() {
|
||||||
|
return ciphertext;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getInitializationVector() {
|
||||||
|
return initializationVector;
|
||||||
|
}
|
||||||
|
}
|
8
app/src/main/res/layout/activity_biometric.xml
Normal file
8
app/src/main/res/layout/activity_biometric.xml
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -9,4 +9,8 @@
|
|||||||
<string name="facebook_app_id" translatable="false">1204701000119770</string>
|
<string name="facebook_app_id" translatable="false">1204701000119770</string>
|
||||||
<string name="fb_login_protocol_scheme" translatable="false">fb1204701000119770</string>
|
<string name="fb_login_protocol_scheme" translatable="false">fb1204701000119770</string>
|
||||||
<string name="facebook_client_token" translatable="false">3e29f2606ae15a99bb3824d2ef1a9d0b</string>
|
<string name="facebook_client_token" translatable="false">3e29f2606ae15a99bb3824d2ef1a9d0b</string>
|
||||||
|
|
||||||
|
<string name="prompt_info_title" translatable="false">Biometric login for Wallet</string>
|
||||||
|
<string name="prompt_info_subtitle" translatable="false">Login using your biometric credential</string>
|
||||||
|
<string name="prompt_info_description" translatable="false">Confirm biometric to continue</string>
|
||||||
</resources>
|
</resources>
|
@ -6,6 +6,21 @@
|
|||||||
</style>
|
</style>
|
||||||
<style name="BaseUnityTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
|
<style name="BaseUnityTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
|
||||||
</style>
|
</style>
|
||||||
|
<style name="AppCompatTranslucent" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowBackground">@android:color/transparent</item>
|
||||||
|
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||||
|
<item name="android:windowIsTranslucent">true</item>
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
|
<item name="android:windowAnimationStyle">@android:style/Animation.Translucent</item>
|
||||||
|
</style>
|
||||||
|
<style name="CebgMainActivity" parent="Theme.AppCompat.Light.NoActionBar">
|
||||||
|
<item name="android:windowBackground">@android:color/black</item>
|
||||||
|
<item name="android:windowNoTitle">true</item>
|
||||||
|
<item name="android:windowActionBar">false</item>
|
||||||
|
<item name="android:windowFullscreen">true</item>
|
||||||
|
<item name="android:windowContentOverlay">@null</item>
|
||||||
|
</style>
|
||||||
<style name="WebViewTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
|
<style name="WebViewTheme" parent="android:Theme.Holo.Light.NoActionBar.Fullscreen">
|
||||||
<item name="windowActionBar">false</item>
|
<item name="windowActionBar">false</item>
|
||||||
<item name="colorPrimary">@android:color/black</item>
|
<item name="colorPrimary">@android:color/black</item>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user