Compare commits
2 Commits
dc622282f2
...
dev_flexio
| Author | SHA1 | Date | |
|---|---|---|---|
| 4d3eae1c88 | |||
| f06074d9cc |
@@ -54,13 +54,13 @@
|
|||||||
追加 ${applicationId}(Flexion 包名)作为后缀后,与 GP 版 authority 不同,
|
追加 ${applicationId}(Flexion 包名)作为后缀后,与 GP 版 authority 不同,
|
||||||
两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。
|
两个渠道包可同时安装在同一设备上,不会触发 INSTALL_FAILED_CONFLICTING_PROVIDER。
|
||||||
参考:Flexion 官方文档《Unique package name - follow-on requirements》
|
参考:Flexion 官方文档《Unique package name - follow-on requirements》
|
||||||
|
-->
|
||||||
<provider
|
<provider
|
||||||
android:name="com.facebook.FacebookContentProvider"
|
android:name="com.facebook.FacebookContentProvider"
|
||||||
android:authorities="com.facebook.app.FacebookContentProvider162066986963808.${applicationId}"
|
android:authorities="com.facebook.app.FacebookContentProvider162066986963808.${applicationId}"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
tools:replace="android:authorities" />
|
tools:replace="android:authorities" />
|
||||||
-->
|
|
||||||
</application>
|
</application>
|
||||||
<queries>
|
<queries>
|
||||||
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" />
|
<provider android:authorities="com.facebook.katana.provider.PlatformProvider" />
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
package com.unity3d.player;
|
package com.unity3d.player;
|
||||||
|
|
||||||
public class ChannelEntry {
|
public class ChannelEntry {
|
||||||
|
|
||||||
public static final String CHANNEL = "Flexion";
|
public static final String CHANNEL = "Flexion";
|
||||||
|
|
||||||
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
|
public static SDKManager.IChannelSDKAdapter createSDKAdapter() {
|
||||||
return new FlexionSDKService();
|
return new FlexionSDKAdapter();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -14,7 +14,7 @@ public class ChannelEntry {
|
|||||||
public static String normalizeSkuList(String msg) {
|
public static String normalizeSkuList(String msg) {
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//public SDKExtBuilder;
|
//public SDKExtBuilder;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.unity3d.player;
|
package com.unity3d.player;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Looper;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.flexionmobile.ddpx.listener.ConnectionStateListener;
|
import com.flexionmobile.ddpx.listener.ConnectionStateListener;
|
||||||
@@ -30,17 +32,38 @@ import java.util.List;
|
|||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
import java.util.concurrent.Executors;
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
public class FlexionSDKService implements
|
public class FlexionSDKAdapter implements
|
||||||
SDKManager.IChannelSDKAdapter,
|
SDKManager.IChannelSDKAdapter,
|
||||||
SDKManager.IPaymentAdapter,
|
SDKManager.IPaymentAdapter,
|
||||||
SDKManager.IProductQueryAdapter,
|
SDKManager.IProductQueryAdapter,
|
||||||
SDKManager.IPendingPurchaseAdapter {
|
SDKManager.IPendingPurchaseAdapter,
|
||||||
private static final String TAG = "FlexionSDKService";
|
SDKManager.IBillingLifecycleAdapter {
|
||||||
|
private static final String TAG = "FlexionSDKAdapter";
|
||||||
private static BillingService billingService;
|
private static BillingService billingService;
|
||||||
private static boolean billingConnected = false;
|
private static boolean billingConnected = false;
|
||||||
|
private static boolean billingConnecting = false;
|
||||||
|
private static Activity billingActivity;
|
||||||
|
private static final Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||||
private static final List<String> pendingPurchases = new ArrayList<>();
|
private static final List<String> pendingPurchases = new ArrayList<>();
|
||||||
|
private static final List<PendingBillingOperation> pendingBillingOperations = new ArrayList<>();
|
||||||
private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread"));
|
private static final ExecutorService billingExecutor = Executors.newSingleThreadExecutor(r -> new Thread(r, "FlexionBillingThread"));
|
||||||
|
|
||||||
|
private interface BillingReadyOperation {
|
||||||
|
void run(BillingService service);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class PendingBillingOperation {
|
||||||
|
final String operation;
|
||||||
|
final BillingReadyOperation onReady;
|
||||||
|
final Runnable onFail;
|
||||||
|
|
||||||
|
PendingBillingOperation(String operation, BillingReadyOperation onReady, Runnable onFail) {
|
||||||
|
this.operation = operation;
|
||||||
|
this.onReady = onReady;
|
||||||
|
this.onFail = onFail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> {
|
private static final PurchasesUpdateListener purchasesListener = (billingResult, purchase) -> {
|
||||||
Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode()
|
Log.w(TAG, "[PAY-FLOW] PurchasesUpdateListener: code=" + billingResult.getResponseCode()
|
||||||
+ " hasPurchase=" + (purchase != null));
|
+ " hasPurchase=" + (purchase != null));
|
||||||
@@ -100,12 +123,7 @@ public class FlexionSDKService implements
|
|||||||
public void pay(Activity activity, String productId, String productPrice,
|
public void pay(Activity activity, String productId, String productPrice,
|
||||||
String productName, String productCount, String prodorderId,
|
String productName, String productCount, String prodorderId,
|
||||||
String appInfo) {
|
String appInfo) {
|
||||||
Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingReady=" + billingConnected);
|
Log.i(TAG, "[PAY-FLOW] pay called: productId=" + productId + " billingConnected=" + billingConnected);
|
||||||
if (billingService == null || !billingConnected || !billingService.isReady()) {
|
|
||||||
Log.e(TAG, "[PAY-FLOW] billing not ready");
|
|
||||||
sendPayCallback(1, "billing_not_ready", null, null, null, null);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
String developerPayload = "";
|
String developerPayload = "";
|
||||||
try {
|
try {
|
||||||
byte[] decoded = Base64.getDecoder().decode(appInfo);
|
byte[] decoded = Base64.getDecoder().decode(appInfo);
|
||||||
@@ -127,31 +145,32 @@ public class FlexionSDKService implements
|
|||||||
Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage());
|
Log.w(TAG, "Failed to extract payload fields from appInfo: " + e.getMessage());
|
||||||
}
|
}
|
||||||
String finalPayload = developerPayload;
|
String finalPayload = developerPayload;
|
||||||
billingExecutor.execute(() -> {
|
ensureBillingReady(activity, "pay", bs ->
|
||||||
try {
|
billingExecutor.execute(() -> {
|
||||||
BillingService bs = billingService;
|
try {
|
||||||
if (bs == null || !bs.isReady()) {
|
if (bs == null || !bs.isReady()) {
|
||||||
Log.e(TAG, "[PAY-FLOW] billingService became null or not ready");
|
Log.e(TAG, "[PAY-FLOW] billingService became null or not ready");
|
||||||
sendPayCallback(1, "billing_not_ready", null, null, null, null);
|
sendPayCallback(1, "billing_not_ready", null, null, null, null);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload);
|
Log.i(TAG, "[PAY-FLOW] launching billing flow for: " + productId + " payload=" + finalPayload);
|
||||||
BillingResult result = bs.launchBillingFlow(activity,
|
BillingResult result = bs.launchBillingFlow(activity,
|
||||||
new BillingFlowParams(productId, "inapp", finalPayload));
|
new BillingFlowParams(productId, "inapp", finalPayload));
|
||||||
Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode()
|
Log.i(TAG, "[PAY-FLOW] launchBillingFlow result: code=" + result.getResponseCode()
|
||||||
+ " debugMsg=" + result.getDebugMessage());
|
+ " debugMsg=" + result.getDebugMessage());
|
||||||
if (result.getResponseCode() != 2000) {
|
if (result.getResponseCode() != 2000) {
|
||||||
Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode()
|
Log.e(TAG, "[PAY-FLOW] launchBillingFlow error: code=" + result.getResponseCode()
|
||||||
+ " debugMsg=" + result.getDebugMessage());
|
+ " debugMsg=" + result.getDebugMessage());
|
||||||
sendPayCallback(1, "billing_flow_error_" + result.getResponseCode()
|
sendPayCallback(1, "billing_flow_error_" + result.getResponseCode()
|
||||||
+ "_" + result.getDebugMessage(), null, null, null, null);
|
+ "_" + result.getDebugMessage(), null, null, null, null);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName()
|
Log.e(TAG, "[PAY-FLOW] launchBillingFlow exception: " + e.getClass().getName()
|
||||||
+ " msg=" + e.getMessage());
|
+ " msg=" + e.getMessage());
|
||||||
sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null);
|
sendPayCallback(1, "launch_billing_failed: " + e.getMessage(), null, null, null, null);
|
||||||
}
|
}
|
||||||
});
|
}),
|
||||||
|
() -> sendPayCallback(1, "billing_not_ready", null, null, null, null));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -162,36 +181,119 @@ public class FlexionSDKService implements
|
|||||||
@Override
|
@Override
|
||||||
public void queryProducts(String productIds) {
|
public void queryProducts(String productIds) {
|
||||||
Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected);
|
Log.i(TAG, "queryProducts(productIds) called, billingConnected=" + billingConnected);
|
||||||
if (billingService == null || !billingConnected) {
|
|
||||||
sendExtensionError("billing_not_ready");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
List<String> ids = Arrays.asList(productIds.split(","));
|
List<String> ids = Arrays.asList(productIds.split(","));
|
||||||
Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config");
|
Log.i(TAG, "queryProductDetails with " + ids.size() + " items from config");
|
||||||
queryProductDetails(ids);
|
ensureBillingReady(null, "queryProducts",
|
||||||
|
bs -> queryProductDetails(bs, ids),
|
||||||
|
() -> sendExtensionError("billing_not_ready"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void initBillingService(Activity activity) {
|
private static void initBillingService(Activity activity) {
|
||||||
Log.i(TAG, "[PAY-FLOW] initBillingService");
|
Log.i(TAG, "[PAY-FLOW] initBillingService");
|
||||||
billingService = FLX.createBillingService(activity, purchasesListener);
|
ensureBillingReady(activity, "initBillingService",
|
||||||
billingService.startConnection(activity, new ConnectionStateListener() {
|
bs -> queryPendingPurchases(),
|
||||||
@Override
|
() -> Log.w(TAG, "[PAY-FLOW] initBillingService failed: billing not ready"));
|
||||||
public void onBillingSetupFinished(BillingResult result) {
|
}
|
||||||
Log.i(TAG, "[PAY-FLOW] onBillingSetupFinished: code=" + result.getResponseCode());
|
|
||||||
if (result.getResponseCode() == BillingResults.ResultCode.CONNECTION_SUCCESS_CODE) {
|
|
||||||
billingConnected = true;
|
|
||||||
queryPendingPurchases();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
private static void ensureBillingReady(Activity activity, String operation,
|
||||||
public void onBillingServiceDisconnected() {
|
BillingReadyOperation onReady, Runnable onFail) {
|
||||||
Log.w(TAG, "[PAY-FLOW] billing service disconnected");
|
ensureBillingReady(activity, operation, onReady, onFail, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ensureBillingReady(Activity activity, String operation,
|
||||||
|
BillingReadyOperation onReady, Runnable onFail,
|
||||||
|
boolean forceReconnect) {
|
||||||
|
if (activity != null) {
|
||||||
|
billingActivity = activity;
|
||||||
|
}
|
||||||
|
Activity targetActivity = activity != null ? activity : billingActivity;
|
||||||
|
if (targetActivity == null) {
|
||||||
|
Log.e(TAG, "[PAY-FLOW] ensureBillingReady failed: activity is null operation=" + operation);
|
||||||
|
if (onFail != null) onFail.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
targetActivity.runOnUiThread(() -> {
|
||||||
|
try {
|
||||||
|
if (billingService == null) {
|
||||||
|
Log.i(TAG, "[PAY-FLOW] create BillingService operation=" + operation);
|
||||||
|
billingService = FLX.createBillingService(targetActivity, purchasesListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ready = billingService != null && billingService.isReady();
|
||||||
|
Log.i(TAG, "[PAY-FLOW] ensureBillingReady operation=" + operation
|
||||||
|
+ " connected=" + billingConnected
|
||||||
|
+ " ready=" + ready
|
||||||
|
+ " connecting=" + billingConnecting
|
||||||
|
+ " forceReconnect=" + forceReconnect);
|
||||||
|
|
||||||
|
if (ready && !forceReconnect) {
|
||||||
|
billingConnected = true;
|
||||||
|
if (onReady != null) onReady.run(billingService);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
pendingBillingOperations.add(new PendingBillingOperation(operation, onReady, onFail));
|
||||||
|
if (billingConnecting) {
|
||||||
|
Log.i(TAG, "[PAY-FLOW] billing connection already in progress, queued operation=" + operation);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
billingConnecting = true;
|
||||||
|
Log.i(TAG, "[PAY-FLOW] billing reconnect start operation=" + operation);
|
||||||
|
billingService.startConnection(targetActivity, new ConnectionStateListener() {
|
||||||
|
@Override
|
||||||
|
public void onBillingSetupFinished(BillingResult result) {
|
||||||
|
int code = result.getResponseCode();
|
||||||
|
Log.i(TAG, "[PAY-FLOW] onBillingSetupFinished: code=" + code);
|
||||||
|
billingConnecting = false;
|
||||||
|
billingConnected = code == BillingResults.ResultCode.CONNECTION_SUCCESS_CODE;
|
||||||
|
drainBillingOperations(billingConnected);
|
||||||
|
if (billingConnected) {
|
||||||
|
queryPendingPurchases();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBillingServiceDisconnected() {
|
||||||
|
Log.w(TAG, "[PAY-FLOW] billing service disconnected");
|
||||||
|
billingConnected = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mainHandler.postDelayed(() -> {
|
||||||
|
if (!billingConnecting) return;
|
||||||
|
boolean timeoutReady = billingService != null && billingService.isReady();
|
||||||
|
Log.w(TAG, "[PAY-FLOW] billing reconnect timeout operation=" + operation
|
||||||
|
+ " ready=" + timeoutReady);
|
||||||
|
billingConnecting = false;
|
||||||
|
billingConnected = timeoutReady;
|
||||||
|
drainBillingOperations(timeoutReady);
|
||||||
|
}, 10000);
|
||||||
|
} catch (Exception e) {
|
||||||
|
Log.e(TAG, "[PAY-FLOW] ensureBillingReady exception operation=" + operation
|
||||||
|
+ " msg=" + e.getMessage());
|
||||||
|
billingConnecting = false;
|
||||||
billingConnected = false;
|
billingConnected = false;
|
||||||
|
drainBillingOperations(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void drainBillingOperations(boolean success) {
|
||||||
|
List<PendingBillingOperation> operations = new ArrayList<>(pendingBillingOperations);
|
||||||
|
pendingBillingOperations.clear();
|
||||||
|
Log.i(TAG, "[PAY-FLOW] drainBillingOperations success=" + success
|
||||||
|
+ " count=" + operations.size());
|
||||||
|
for (PendingBillingOperation operation : operations) {
|
||||||
|
if (success) {
|
||||||
|
if (operation.onReady != null) operation.onReady.run(billingService);
|
||||||
|
} else {
|
||||||
|
Log.w(TAG, "[PAY-FLOW] billing operation failed operation=" + operation.operation);
|
||||||
|
if (operation.onFail != null) operation.onFail.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void handlePurchase(Purchase purchase) {
|
private static void handlePurchase(Purchase purchase) {
|
||||||
Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState()
|
Log.i(TAG, "[PAY-FLOW] handlePurchase: state=" + purchase.getPurchaseState()
|
||||||
+ " orderId=" + purchase.getOrderId()
|
+ " orderId=" + purchase.getOrderId()
|
||||||
@@ -208,17 +310,15 @@ public class FlexionSDKService implements
|
|||||||
private static void queryPendingPurchases() {
|
private static void queryPendingPurchases() {
|
||||||
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService
|
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases start: billingService=" + billingService
|
||||||
+ " billingConnected=" + billingConnected);
|
+ " billingConnected=" + billingConnected);
|
||||||
if (billingService == null || !billingConnected) {
|
ensureBillingReady(null, "queryPendingPurchases",
|
||||||
Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected");
|
bs -> bs.queryPurchasesAsync(
|
||||||
return;
|
new QueryPurchasesParams("inapp"),
|
||||||
}
|
(result, purchases) -> {
|
||||||
billingService.queryPurchasesAsync(
|
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode()
|
||||||
new QueryPurchasesParams("inapp"),
|
+ " count=" + (purchases == null ? 0 : purchases.size()));
|
||||||
(result, purchases) -> {
|
if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p);
|
||||||
Log.i(TAG, "[PAY-FLOW] queryPendingPurchases result: code=" + result.getResponseCode()
|
}),
|
||||||
+ " count=" + (purchases == null ? 0 : purchases.size()));
|
() -> Log.w(TAG, "[PAY-FLOW] queryPendingPurchases skipped: billing not connected"));
|
||||||
if (purchases != null) for (Purchase p : purchases) cachePendingPurchase(p);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void cachePendingPurchase(Purchase purchase) {
|
private static void cachePendingPurchase(Purchase purchase) {
|
||||||
@@ -272,10 +372,33 @@ public class FlexionSDKService implements
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void queryProductDetails(List<String> productIds) {
|
@Override
|
||||||
billingService.queryProductDetailsAsync(
|
public void refreshBilling() {
|
||||||
|
Log.i(TAG, "[PAY-FLOW] refreshBilling requested");
|
||||||
|
billingConnected = false;
|
||||||
|
ensureBillingReady(null, "refreshBilling",
|
||||||
|
bs -> Log.i(TAG, "[PAY-FLOW] refreshBilling ready"),
|
||||||
|
() -> Log.w(TAG, "[PAY-FLOW] refreshBilling failed"),
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void queryProductDetails(BillingService service, List<String> productIds) {
|
||||||
|
final boolean[] completed = {false};
|
||||||
|
mainHandler.postDelayed(() -> {
|
||||||
|
if (completed[0]) return;
|
||||||
|
completed[0] = true;
|
||||||
|
Log.w(TAG, "[PAY-FLOW] queryProductDetails timeout count=" + productIds.size());
|
||||||
|
sendExtensionError("flexion_query_timeout");
|
||||||
|
}, 30000);
|
||||||
|
|
||||||
|
service.queryProductDetailsAsync(
|
||||||
new ProductDetailsParams("inapp", productIds),
|
new ProductDetailsParams("inapp", productIds),
|
||||||
(billingResult, productDetails) -> {
|
(billingResult, productDetails) -> {
|
||||||
|
if (completed[0]) {
|
||||||
|
Log.w(TAG, "[PAY-FLOW] queryProductDetails callback ignored after timeout");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
completed[0] = true;
|
||||||
try {
|
try {
|
||||||
JSONObject result = new JSONObject();
|
JSONObject result = new JSONObject();
|
||||||
if (billingResult.getResponseCode() == BillingResults.ResultCode.QUERY_PRODUCT_DETAILS_SUCCESS_CODE
|
if (billingResult.getResponseCode() == BillingResults.ResultCode.QUERY_PRODUCT_DETAILS_SUCCESS_CODE
|
||||||
@@ -338,15 +461,17 @@ public class FlexionSDKService implements
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static void consumePurchase(String token) {
|
public static void consumePurchase(String token) {
|
||||||
if (billingService == null || token == null || token.isEmpty()) {
|
if (token == null || token.isEmpty()) {
|
||||||
Log.w(TAG, "[PAY-FLOW] consumePurchase: billingService or token is null");
|
Log.w(TAG, "[PAY-FLOW] consumePurchase: token is null");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token);
|
Log.i(TAG, "[PAY-FLOW] consumePurchase: token=" + token);
|
||||||
billingService.consumeAsync(
|
ensureBillingReady(null, "consumePurchase",
|
||||||
new ConsumeParams(token),
|
bs -> bs.consumeAsync(
|
||||||
r -> Log.i(TAG, "[PAY-FLOW] consume result: " +
|
new ConsumeParams(token),
|
||||||
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail")));
|
r -> Log.i(TAG, "[PAY-FLOW] consume result: " +
|
||||||
|
(r.getResponseCode() == BillingResults.ResultCode.CONSUME_SUCCESS_CODE ? "ok" : "fail"))),
|
||||||
|
() -> Log.w(TAG, "[PAY-FLOW] consumePurchase skipped: billing not connected"));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cancelPurchase() {
|
public static void cancelPurchase() {
|
||||||
Binary file not shown.
@@ -34,7 +34,7 @@ dependencies {
|
|||||||
// Google Play In-App Review
|
// Google Play In-App Review
|
||||||
implementation 'com.google.android.play:review:2.0.1'
|
implementation 'com.google.android.play:review:2.0.1'
|
||||||
// Google Play Billing — tuyoosdk AAR references BillingManager during init
|
// Google Play Billing — tuyoosdk AAR references BillingManager during init
|
||||||
implementation 'com.android.billingclient:billing:6.0.1'
|
implementation 'com.android.billingclient:billing:7.0.0'
|
||||||
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
|
implementation 'net.aihelp:android-aihelp-aar:5.3.+'
|
||||||
|
|
||||||
implementation 'com.google.android.gms:play-services-auth:20.2.0'
|
implementation 'com.google.android.gms:play-services-auth:20.2.0'
|
||||||
@@ -52,7 +52,7 @@ dependencies {
|
|||||||
|
|
||||||
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
|
implementation 'com.miui.referrer:homereferrer:1.0.0.6'
|
||||||
|
|
||||||
implementation platform('com.google.firebase:firebase-bom:32.7.4')
|
implementation platform('com.google.firebase:firebase-bom:34.6.0')
|
||||||
implementation 'com.google.firebase:firebase-analytics'
|
implementation 'com.google.firebase:firebase-analytics'
|
||||||
|
|
||||||
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
|
implementation('com.facebook.android:facebook-android-sdk:16.2.0')
|
||||||
@@ -78,6 +78,7 @@ dependencies {
|
|||||||
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
|
implementation 'com.appsflyer:purchase-connector:2.2.0' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:8
|
||||||
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
|
implementation 'com.appsflyer:unity-wrapper:6.17.7' // Assets/AppsFlyer/Editor/AppsFlyerDependencies.xml:6
|
||||||
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
|
implementation 'com.google.android.ump:user-messaging-platform:2.1.0' // Assets/MaxSdk/AppLovin/Editor/Dependencies.xml:5
|
||||||
|
implementation 'com.tapjoy:tapjoy-android-unitybridge:14.5.0' // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:10
|
||||||
// Android Resolver Dependencies End
|
// Android Resolver Dependencies End
|
||||||
**DEPS**}
|
**DEPS**}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,10 @@ dependencyResolutionManagement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Android Resolver Repos Start
|
// Android Resolver Repos Start
|
||||||
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
|
def unityProjectPath = $/file:///**DIR_UNITYPROJECT**/$.replace("\\", "/")
|
||||||
|
maven {
|
||||||
|
url "https://sdk.tapjoy.com/" // Packages/com.unity.package-offerwall/Editor/TJPluginDependencies.xml:9
|
||||||
|
}
|
||||||
maven {
|
maven {
|
||||||
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
|
url "https://android-sdk.is.com/" // Assets/MaxSdk/Mediation/IronSource/Editor/Dependencies.xml:8
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,31 +55,17 @@ public class BuildTool
|
|||||||
BuildiOS(bparams);
|
BuildiOS(bparams);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("Tools/Build Android")]
|
[MenuItem("Tools/Build Android")]
|
||||||
public static void BuildAndroid()
|
public static void BuildAndroid()
|
||||||
{
|
{
|
||||||
|
ChannelBuildTool.Build_GooglePlay();
|
||||||
BuildParams bparams = new BuildParams()
|
}
|
||||||
{
|
|
||||||
outPath = "../Bin/tysdkTest.apk"
|
[MenuItem("Tools/Build Android With Debug")]
|
||||||
};
|
public static void BuildAndroidWithDebug()
|
||||||
|
{
|
||||||
BuildAndroid(bparams);
|
ChannelBuildTool.Build_GooglePlay_Debug();
|
||||||
}
|
}
|
||||||
|
|
||||||
[MenuItem("Tools/Build Android With Debug")]
|
|
||||||
public static void BuildAndroidWithDebug()
|
|
||||||
{
|
|
||||||
|
|
||||||
BuildParams bparams = new BuildParams()
|
|
||||||
{
|
|
||||||
outPath = "../Bin/tysdkTest_debug.apk",
|
|
||||||
};
|
|
||||||
|
|
||||||
var buildOptions = BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection;
|
|
||||||
|
|
||||||
BuildAndroid(bparams, buildOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CIBuildAndroid()
|
public static void CIBuildAndroid()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ public class ChannelBuildTool
|
|||||||
{
|
{
|
||||||
public static readonly string[] SupportedChannels = { "GooglePlay", "Rustore", "Flexion" };
|
public static readonly string[] SupportedChannels = { "GooglePlay", "Rustore", "Flexion" };
|
||||||
|
|
||||||
|
private const string DefaultChannel = "GooglePlay";
|
||||||
|
private const string DefaultAndroidBundleIdentifier = "com.arkgame.ft";
|
||||||
private const string ExtraCfgRoot = "../ExtraPluginCfgs/Android";
|
private const string ExtraCfgRoot = "../ExtraPluginCfgs/Android";
|
||||||
private const string TysdkPlugins = "Packages/tysdk/Plugins/Android";
|
private const string TysdkPlugins = "Packages/tysdk/Plugins/Android";
|
||||||
private const string TysdkFiles = "Packages/tysdk/Files";
|
private const string TysdkFiles = "Packages/tysdk/Files";
|
||||||
@@ -61,6 +63,8 @@ public class ChannelBuildTool
|
|||||||
File.Delete(oldJava);
|
File.Delete(oldJava);
|
||||||
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java"))
|
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKService.java"))
|
||||||
File.Delete(oldJava);
|
File.Delete(oldJava);
|
||||||
|
foreach (var oldJava in Directory.GetFiles(TysdkPlugins, "*SDKAdapter.java"))
|
||||||
|
File.Delete(oldJava);
|
||||||
var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java");
|
var oldConfig = Path.Combine(TysdkPlugins, "ConfigManager.java");
|
||||||
if (File.Exists(oldConfig)) File.Delete(oldConfig);
|
if (File.Exists(oldConfig)) File.Delete(oldConfig);
|
||||||
|
|
||||||
@@ -111,35 +115,51 @@ public class ChannelBuildTool
|
|||||||
|
|
||||||
// ==================== Build ====================
|
// ==================== Build ====================
|
||||||
|
|
||||||
|
private static void RestoreDefaultChannel()
|
||||||
|
{
|
||||||
|
Debug.Log($"[ChannelBuild] Restore default channel -> {DefaultChannel}");
|
||||||
|
SwitchChannel(DefaultChannel);
|
||||||
|
PlayerSettings.SetApplicationIdentifier(BuildTargetGroup.Android, DefaultAndroidBundleIdentifier);
|
||||||
|
Debug.Log($"[ChannelBuild] Restore bundle identifier -> {DefaultAndroidBundleIdentifier}");
|
||||||
|
AssetDatabase.SaveAssets();
|
||||||
|
}
|
||||||
|
|
||||||
private static void BuildAndroidForChannel(string channel, bool debug)
|
private static void BuildAndroidForChannel(string channel, bool debug)
|
||||||
{
|
{
|
||||||
SwitchChannel(channel);
|
try
|
||||||
|
|
||||||
string fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
|
|
||||||
string outPath = $"../Bin/{fileName}";
|
|
||||||
var buildOptions = debug
|
|
||||||
? BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection
|
|
||||||
: BuildOptions.None;
|
|
||||||
|
|
||||||
var buildPlayerOptions = new BuildPlayerOptions
|
|
||||||
{
|
{
|
||||||
scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(),
|
SwitchChannel(channel);
|
||||||
locationPathName = outPath,
|
|
||||||
target = BuildTarget.Android,
|
|
||||||
options = buildOptions,
|
|
||||||
};
|
|
||||||
|
|
||||||
Debug.Log($"[ChannelBuild] Building {channel} → {outPath}");
|
string fileName = debug ? $"tysdkTest_{channel}_debug.apk" : $"tysdkTest_{channel}.apk";
|
||||||
var buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
|
string outPath = $"../Bin/{fileName}";
|
||||||
|
var buildOptions = debug
|
||||||
|
? BuildOptions.Development | BuildOptions.AllowDebugging | BuildOptions.WaitForPlayerConnection
|
||||||
|
: BuildOptions.None;
|
||||||
|
|
||||||
if (buildReport?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
|
var buildPlayerOptions = new BuildPlayerOptions
|
||||||
{
|
{
|
||||||
Debug.Log($"[ChannelBuild] Success: {Path.GetFullPath(outPath)}");
|
scenes = EditorBuildSettings.scenes.Select(x => x.path).ToArray(),
|
||||||
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
|
locationPathName = outPath,
|
||||||
|
target = BuildTarget.Android,
|
||||||
|
options = buildOptions,
|
||||||
|
};
|
||||||
|
|
||||||
|
Debug.Log($"[ChannelBuild] Building {channel} → {outPath}");
|
||||||
|
var buildReport = BuildPipeline.BuildPlayer(buildPlayerOptions);
|
||||||
|
|
||||||
|
if (buildReport?.summary.result == UnityEditor.Build.Reporting.BuildResult.Succeeded)
|
||||||
|
{
|
||||||
|
Debug.Log($"[ChannelBuild] Success: {Path.GetFullPath(outPath)}");
|
||||||
|
AssetDatabase.Refresh(ImportAssetOptions.ForceSynchronousImport);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Debug.LogError($"[ChannelBuild] Failed: {buildReport?.summary}");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
finally
|
||||||
{
|
{
|
||||||
Debug.LogError($"[ChannelBuild] Failed: {buildReport?.summary}");
|
RestoreDefaultChannel();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,29 +177,7 @@ public class ChannelBuildTool
|
|||||||
public static void Switch_Flexion() => SwitchChannel("Flexion");
|
public static void Switch_Flexion() => SwitchChannel("Flexion");
|
||||||
|
|
||||||
// ==================== Menu: Build ====================
|
// ==================== Menu: Build ====================
|
||||||
|
|
||||||
[MenuItem("Tools/Build Android (Current Channel)")]
|
|
||||||
public static void Build_CurrentChannel()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(currentChannel))
|
|
||||||
{
|
|
||||||
Debug.LogError("[ChannelBuild] No channel selected. Use Tools/Switch Channel first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BuildAndroidForChannel(currentChannel, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Build Android (Current Channel) Debug")]
|
|
||||||
public static void Build_CurrentChannel_Debug()
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(currentChannel))
|
|
||||||
{
|
|
||||||
Debug.LogError("[ChannelBuild] No channel selected. Use Tools/Switch Channel first.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
BuildAndroidForChannel(currentChannel, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
[MenuItem("Tools/Build Android (Channel)/GooglePlay")]
|
[MenuItem("Tools/Build Android (Channel)/GooglePlay")]
|
||||||
public static void Build_GooglePlay() => BuildAndroidForChannel("GooglePlay", false);
|
public static void Build_GooglePlay() => BuildAndroidForChannel("GooglePlay", false);
|
||||||
|
|
||||||
|
|||||||
@@ -76,6 +76,10 @@ public class SDKManager {
|
|||||||
String getPendingPurchases();
|
String getPendingPurchases();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public interface IBillingLifecycleAdapter {
|
||||||
|
void refreshBilling();
|
||||||
|
}
|
||||||
|
|
||||||
private static IChannelSDKAdapter sdkAdapter;
|
private static IChannelSDKAdapter sdkAdapter;
|
||||||
|
|
||||||
public static void queryProductsByIDs(String productIds) {
|
public static void queryProductsByIDs(String productIds) {
|
||||||
@@ -100,9 +104,16 @@ public class SDKManager {
|
|||||||
return "[]";
|
return "[]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RefreshBillingService() {
|
||||||
|
Log.i(TAG, "RefreshBillingService");
|
||||||
|
if (sdkAdapter instanceof IBillingLifecycleAdapter) {
|
||||||
|
((IBillingLifecycleAdapter) sdkAdapter).refreshBilling();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void CancelBillingFlow() {
|
public static void CancelBillingFlow() {
|
||||||
Log.w(TAG, "CancelBillingFlow");
|
Log.w(TAG, "CancelBillingFlow");
|
||||||
// FlexionSDKService.cancelPurchase();
|
// FlexionSDKAdapter.cancelPurchase();
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------------
|
// -------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -322,6 +322,9 @@ namespace tysdk
|
|||||||
result.isSuccess = true;
|
result.isSuccess = true;
|
||||||
result.userId = _accountInfo.userId;
|
result.userId = _accountInfo.userId;
|
||||||
SetUserInfo();
|
SetUserInfo();
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
UnityBridgeFunc.RefreshBillingService();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
|
TYSDKCallbackManager.Instance.TryTriggerCallback(result);
|
||||||
@@ -414,6 +417,9 @@ namespace tysdk
|
|||||||
{
|
{
|
||||||
_accountInfo.AddLinkedAccount(accoutType);
|
_accountInfo.AddLinkedAccount(accoutType);
|
||||||
_accountInfo.Save();
|
_accountInfo.Save();
|
||||||
|
#if UNITY_ANDROID
|
||||||
|
UnityBridgeFunc.RefreshBillingService();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -160,10 +160,18 @@ namespace tysdk
|
|||||||
var result = new ProductListInfo();
|
var result = new ProductListInfo();
|
||||||
return result;
|
return result;
|
||||||
#elif UNITY_ANDROID || UNITY_IOS
|
#elif UNITY_ANDROID || UNITY_IOS
|
||||||
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>();
|
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(TimeSpan.FromSeconds(45));
|
||||||
UnityBridgeFunc.GetSKUList();
|
UnityBridgeFunc.GetSKUList();
|
||||||
var result = await task;
|
try
|
||||||
return result;
|
{
|
||||||
|
var result = await task;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[TYSdkFacade] GetSKUList timeout: {e.Message}");
|
||||||
|
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -176,10 +184,18 @@ namespace tysdk
|
|||||||
await Task.Yield();
|
await Task.Yield();
|
||||||
return new ProductListInfo();
|
return new ProductListInfo();
|
||||||
#elif UNITY_ANDROID
|
#elif UNITY_ANDROID
|
||||||
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>();
|
var task = TYSDKCallbackManager.Instance.RegisterCallback<ProductListInfo>(TimeSpan.FromSeconds(45));
|
||||||
UnityBridgeFunc.GetSKUListByIapIds(productIds);
|
UnityBridgeFunc.GetSKUListByIapIds(productIds);
|
||||||
var result = await task;
|
try
|
||||||
return result;
|
{
|
||||||
|
var result = await task;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
catch (TaskCanceledException e)
|
||||||
|
{
|
||||||
|
Debug.LogWarning($"[TYSdkFacade] GetSKUListByIapIds timeout: {e.Message}");
|
||||||
|
return new ProductListInfo() { code = 1, msg = "sku_callback_timeout" };
|
||||||
|
}
|
||||||
#else
|
#else
|
||||||
return new ProductListInfo() { code = -1, msg = "unsupported platform" };
|
return new ProductListInfo() { code = -1, msg = "unsupported platform" };
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ namespace tysdk
|
|||||||
|
|
||||||
public static string GetPendingPurchases() => "[]";
|
public static string GetPendingPurchases() => "[]";
|
||||||
|
|
||||||
|
public static void RefreshBillingService() { }
|
||||||
|
|
||||||
//打点
|
//打点
|
||||||
public static void SetGaUserInfo(string userId){}
|
public static void SetGaUserInfo(string userId){}
|
||||||
public static void SetGaCommonInfo(string SetGaCommonInfo){}
|
public static void SetGaCommonInfo(string SetGaCommonInfo){}
|
||||||
@@ -224,6 +226,15 @@ namespace tysdk
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void RefreshBillingService()
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.Log("UnityBridgeFunc.RefreshBillingService");
|
||||||
|
using(var sdkManager = new AndroidJavaClass(SDK_CLASS))
|
||||||
|
{
|
||||||
|
sdkManager.CallStatic("RefreshBillingService");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static void CancelBillingFlow()
|
public static void CancelBillingFlow()
|
||||||
{
|
{
|
||||||
UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow");
|
UnityEngine.Debug.Log("UnityBridgeFunc.CancelBillingFlow");
|
||||||
@@ -470,6 +481,8 @@ namespace tysdk
|
|||||||
|
|
||||||
public static string GetPendingPurchases() => "[]";
|
public static string GetPendingPurchases() => "[]";
|
||||||
|
|
||||||
|
public static void RefreshBillingService() { }
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user