From a4ff7262c42fc686bbba2cde96b937025f67687a Mon Sep 17 00:00:00 2001 From: gaorui Date: Thu, 22 Feb 2024 19:26:21 +0800 Subject: [PATCH] =?UTF-8?q?feat(app):=E6=96=B0=E5=A2=9E=E6=B5=B7=E5=A4=96?= =?UTF-8?q?=E8=BF=81=E7=A7=BBSDK=E5=8F=82=E8=80=83=E9=A1=B9=E7=9B=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 21 + demo/build.gradle | 122 +++ demo/dexknife.txt | 32 + demo/google-services.json | 55 ++ demo/proguard-rules.pro | 21 + demo/src/main/AndroidManifest.xml | 107 +++ demo/src/main/assets/access.config | 1 + demo/src/main/assets/access_localization.ini | 721 ++++++++++++++++ demo/src/main/assets/channel_config.json | 64 ++ demo/src/main/assets/develop.config | 1 + demo/src/main/assets/sdk_version.txt | 3 + .../java/com/hoolai/demo/GameActivity.java | 806 ++++++++++++++++++ .../java/com/hoolai/demo/GameApplication.java | 73 ++ .../demo/share/FacebookShareActivity.java | 229 +++++ .../com/hoolai/demo/share/ShareActivity.java | 139 +++ .../com/hoolai/demo/utils/DataReportUtil.java | 66 ++ .../java/com/hoolai/demo/utils/Utils.java | 115 +++ .../drawable-v24/ic_launcher_foreground.xml | 30 + .../res/drawable/ic_launcher_background.xml | 170 ++++ demo/src/main/res/drawable/icon.png | Bin 0 -> 40976 bytes demo/src/main/res/layout/activity_share.xml | 49 ++ demo/src/main/res/layout/hl_demo_game.xml | 185 ++++ demo/src/main/res/layout/hl_demo_login.xml | 27 + .../main/res/layout/productinfo_listview.xml | 14 + demo/src/main/res/layout/share_facebook.xml | 56 ++ .../res/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../mipmap-anydpi-v26/ic_launcher_round.xml | 5 + demo/src/main/res/mipmap-hdpi/ic_launcher.png | Bin 0 -> 3593 bytes .../res/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 5339 bytes demo/src/main/res/mipmap-mdpi/ic_launcher.png | Bin 0 -> 2636 bytes .../res/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 3388 bytes .../src/main/res/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 4926 bytes .../res/mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 7472 bytes .../main/res/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 7909 bytes .../res/mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 11873 bytes .../main/res/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 10652 bytes .../res/mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 16570 bytes demo/src/main/res/values/strings.xml | 8 + gradle.properties | 25 + gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 54417 bytes gradle/wrapper/gradle-wrapper.properties | 5 + gradlew | 172 ++++ gradlew.bat | 84 ++ keystorefile/access_global.keystore | Bin 0 -> 2198 bytes local.properties | 8 + sdk_version.txt | 3 + settings.gradle | 1 + 47 files changed, 3423 insertions(+) create mode 100644 build.gradle create mode 100644 demo/build.gradle create mode 100644 demo/dexknife.txt create mode 100644 demo/google-services.json create mode 100644 demo/proguard-rules.pro create mode 100644 demo/src/main/AndroidManifest.xml create mode 100644 demo/src/main/assets/access.config create mode 100644 demo/src/main/assets/access_localization.ini create mode 100644 demo/src/main/assets/channel_config.json create mode 100644 demo/src/main/assets/develop.config create mode 100644 demo/src/main/assets/sdk_version.txt create mode 100644 demo/src/main/java/com/hoolai/demo/GameActivity.java create mode 100644 demo/src/main/java/com/hoolai/demo/GameApplication.java create mode 100644 demo/src/main/java/com/hoolai/demo/share/FacebookShareActivity.java create mode 100644 demo/src/main/java/com/hoolai/demo/share/ShareActivity.java create mode 100644 demo/src/main/java/com/hoolai/demo/utils/DataReportUtil.java create mode 100644 demo/src/main/java/com/hoolai/demo/utils/Utils.java create mode 100644 demo/src/main/res/drawable-v24/ic_launcher_foreground.xml create mode 100644 demo/src/main/res/drawable/ic_launcher_background.xml create mode 100644 demo/src/main/res/drawable/icon.png create mode 100644 demo/src/main/res/layout/activity_share.xml create mode 100644 demo/src/main/res/layout/hl_demo_game.xml create mode 100644 demo/src/main/res/layout/hl_demo_login.xml create mode 100644 demo/src/main/res/layout/productinfo_listview.xml create mode 100644 demo/src/main/res/layout/share_facebook.xml create mode 100644 demo/src/main/res/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 demo/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml create mode 100644 demo/src/main/res/mipmap-hdpi/ic_launcher.png create mode 100644 demo/src/main/res/mipmap-hdpi/ic_launcher_round.png create mode 100644 demo/src/main/res/mipmap-mdpi/ic_launcher.png create mode 100644 demo/src/main/res/mipmap-mdpi/ic_launcher_round.png create mode 100644 demo/src/main/res/mipmap-xhdpi/ic_launcher.png create mode 100644 demo/src/main/res/mipmap-xhdpi/ic_launcher_round.png create mode 100644 demo/src/main/res/mipmap-xxhdpi/ic_launcher.png create mode 100644 demo/src/main/res/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 demo/src/main/res/mipmap-xxxhdpi/ic_launcher.png create mode 100644 demo/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 demo/src/main/res/values/strings.xml create mode 100644 gradle.properties create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100644 gradlew create mode 100644 gradlew.bat create mode 100644 keystorefile/access_global.keystore create mode 100644 local.properties create mode 100644 sdk_version.txt create mode 100644 settings.gradle diff --git a/build.gradle b/build.gradle new file mode 100644 index 0000000..849e121 --- /dev/null +++ b/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + repositories { + maven { + url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/' + } + } + dependencies { + classpath 'com.android.tools.build:gradle:4.0.2' + classpath 'com.google.gms:google-services:4.2.0' + } +} + +allprojects { + repositories { + maven { + url 'https://sdk.wdyxgames.com/nexus/repository/sdk-public/' + } + } +} diff --git a/demo/build.gradle b/demo/build.gradle new file mode 100644 index 0000000..adf4d72 --- /dev/null +++ b/demo/build.gradle @@ -0,0 +1,122 @@ +apply plugin: 'com.android.application' +apply plugin: 'com.google.gms.google-services' + +android { + compileSdkVersion 33 + + defaultConfig { + applicationId "com.hoolai.oversea.access" + minSdkVersion 26 + targetSdkVersion 33 + versionCode 10 + versionName "1.1.0" + multiDexEnabled true + multiDexKeepProguard file('dexknife.txt') + } + dexOptions { + jumboMode true + } + lintOptions { + abortOnError false + } + signingConfigs { + yourkeystore { + keyAlias 'access' + keyPassword 'access123' + storeFile file("${rootProject.projectDir.absolutePath}/keystorefile/access_global.keystore") + storePassword 'access123' + } + } + buildTypes { + debug { + minifyEnabled false //是否混淆 + signingConfig signingConfigs.yourkeystore + } + release { + minifyEnabled false //是否混淆 + signingConfig signingConfigs.yourkeystore + } + } + + sourceSets { + main { + jniLibs.srcDirs = ['libs'] + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + implementation fileTree(dir: "libs", include: ["*.jar"]) + implementation 'com.hoolai.access.open:fast-access:1.0.0.3' + implementation 'com.hoolai.access.open:hoolai-core:1.0.0.0-global12' + implementation 'com.hoolai.oversea:hl-oversea-global:1.0.0.0-dev5' + + /* implementation 'com.hoolai.open.fastaccess:fastaccess_sdk:2.3.16' + implementation files('libs\\hoolai_reyun_3.1.16.aar') + implementation files('libs\\oversea_base_3.2.3.aar') + implementation files('libs\\oversea_global_3.3.7.aar') + implementation files('libs\\oversea_login_3.2.1.aar') + implementation files('libs\\oversea_pay_3.2.1.aar') + + implementation 'androidx.appcompat:appcompat:1.3.0' + implementation 'androidx.recyclerview:recyclerview:1.2.0' + //implementation 'androidx.legacy:legacy-support-v13:1.0.0' + implementation 'com.google.android.material:material:1.1.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.multidex:multidex:2.0.1' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + + *//** + * google + *//* + implementation 'com.google.android.gms:play-services-base:17.0.0' + implementation 'com.google.android.gms:play-services-auth:20.4.0' +// implementation 'com.google.android.gms:play-services-games:19.0.0' + implementation "com.google.android.gms:play-services-games-v2:+" + implementation 'com.google.android.gms:play-services-analytics:17.0.0' + implementation 'com.android.billingclient:billing:6.0.0' + *//** + * firebase + *//* + implementation 'com.google.firebase:firebase-bom:31.1.1' + implementation 'com.google.firebase:firebase-analytics:21.2.0' + implementation 'com.google.firebase:firebase-messaging:23.1.1' + + *//** + * facebook + *//* + implementation 'com.facebook.android:facebook-share:15.2.0' + implementation 'com.facebook.android:facebook-login:15.2.0' + implementation 'com.facebook.android:facebook-applinks:14.1.0' + *//** + * twitter + *//* + implementation 'com.twitter.sdk.android:twitter:3.1.1' + + implementation 'com.squareup.retrofit2:retrofit:2.2.0' + implementation 'com.squareup.retrofit2:converter-gson:2.2.0' + implementation 'com.squareup.retrofit2:adapter-rxjava2:2.2.0' + implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' + implementation 'io.reactivex.rxjava2:rxjava:2.0.1' + implementation 'com.google.code.gson:gson:2.8.7' + *//** + * AIHelp + *//* + implementation 'net.aihelp:android-aihelp-aar:3.1.+' + implementation 'androidx.viewpager2:viewpager2:1.0.0' + + *//** + * af 广告 + *//* + implementation 'com.appsflyer:af-android-sdk:6.12.2' + //implementation 'com.appsflyer:oaid:6.1.1' + implementation 'com.android.installreferrer:installreferrer:2.2' + + //af 卸载衡量 + implementation 'com.google.firebase:firebase-messaging:23.0.3' + implementation 'com.google.firebase:firebase-core:20.1.2'*/ +} diff --git a/demo/dexknife.txt b/demo/dexknife.txt new file mode 100644 index 0000000..59eace6 --- /dev/null +++ b/demo/dexknife.txt @@ -0,0 +1,32 @@ +# 不进行dex分包, 直到 dex 的id数量超过 65536. +#-auto-maindex + +# dex 扩展参数, 例如 --set-max-idx-number=50000 +# 如果出现 DexException: Too many classes in --main-dex-list, main dex capacity exceeded,则需要调大数值 +-dex-param --set-max-idx-number=49000 + +# if you want to keep some classes in main dex, use '-keep'. +-keep android.support.multidex.** +-keep android.support.v4.** +-keep androidx.core.** +-keep com.hoolai.demo.** + +-keep org.jboss.netty.** +-keep com.hoolai.open.** +-keep com.hoolai.sdk.** +-keep com.hoolai.fatacess.** +-keep com.hoolai.overseas.sdk.Application.* +-keep com.adobe.** +-keep com.android.vending.billing.** +-keep com.google.android.gms.R.** +-keep com.google.android.gms.R$*.class + +-keep com.google.android.gms.common.R.** +-keep com.google.android.gms.common.R$*.class + +-keep org.OpenUDID.** +-keep com.alibaba.fastjson.** + +# this path will to be split to second dex. +#android.** +#-split android.** # same as android.** diff --git a/demo/google-services.json b/demo/google-services.json new file mode 100644 index 0000000..4920ef6 --- /dev/null +++ b/demo/google-services.json @@ -0,0 +1,55 @@ +{ + "project_info": { + "project_number": "242883477374", + "project_id": "access-f4133", + "storage_bucket": "access-f4133.appspot.com" + }, + "client": [ + { + "client_info": { + "mobilesdk_app_id": "1:242883477374:android:f7b5a4a9564fcd86e3bd2f", + "android_client_info": { + "package_name": "com.hoolai.oversea.access" + } + }, + "oauth_client": [ + { + "client_id": "242883477374-p0j6gg8pjibens17rfibf1tsd1d72tkm.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.hoolai.oversea.access", + "certificate_hash": "bc8591140a47250db392865765c4ce7d4e5b8b96" + } + }, + { + "client_id": "242883477374-s5a7d489td3s976hcgmcqq1nar7aqrie.apps.googleusercontent.com", + "client_type": 1, + "android_info": { + "package_name": "com.hoolai.oversea.access", + "certificate_hash": "c6889e4b5808728c7e1d82baed969e8351a2dd4e" + } + }, + { + "client_id": "242883477374-7u4f0fvqg2a6jrd7ro6sat6ms2ledtml.apps.googleusercontent.com", + "client_type": 3 + } + ], + "api_key": [ + { + "current_key": "AIzaSyAR9uWzd7yaXb6cDOY1HcDk46JZaEqMhgA" + } + ], + "services": { + "appinvite_service": { + "other_platform_oauth_client": [ + { + "client_id": "242883477374-7u4f0fvqg2a6jrd7ro6sat6ms2ledtml.apps.googleusercontent.com", + "client_type": 3 + } + ] + } + } + } + ], + "configuration_version": "1" +} \ No newline at end of file diff --git a/demo/proguard-rules.pro b/demo/proguard-rules.pro new file mode 100644 index 0000000..481bb43 --- /dev/null +++ b/demo/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/demo/src/main/AndroidManifest.xml b/demo/src/main/AndroidManifest.xml new file mode 100644 index 0000000..9c992a4 --- /dev/null +++ b/demo/src/main/AndroidManifest.xml @@ -0,0 +1,107 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/assets/access.config b/demo/src/main/assets/access.config new file mode 100644 index 0000000..085f265 --- /dev/null +++ b/demo/src/main/assets/access.config @@ -0,0 +1 @@ +F5OvkhmmnIQ+3ot6Y0R6mlEfM/pduDHyhPPJ4t9FsdEGstVHsBh13LCUjQZoOXb4f+11nTHw2S7gNJ5lVskOypSbR9GuujINqicrCO6ij2hoTNSW6ikKxik/d+a9hE/hEosC2h0loEE1IcAaQlgJ7QYakJIsXeQz2O2GRbVstklmbdnFrenerx5iKDfMA4Z1MS9JVYuwTcQDodrRXDq96mMh/nnDZRqeLSA+yaqrsnOnr6GXACaerHDYMNXcaUOlMiJLYYW7a9mJjBXFNdpq2zMgyyw77DJ29oKk0P5uSIZ9xDcfllnZZV07oJ5ikYwGUvw8JZSy40jXaEArW1G0UQRcUIzY4tSgCmQUe0vGaCittAE7e3/914N2NVAMuN3ZObmwTN+ufpJfdubnnAFqhSjIQse0lJ8twRn24/yCdacDVehke4nISTSm8A5CCFjmEDJeeLas1wvCoWaL2DxThIWJlebZA4GcNS/S0kHXY8FKZPRlPpXDUCdAOBoO3XzWMy4pkpRb7f0xMhQAEucmerX/g8TzilAXRjhGgBZLWbck3VdUDBec3Wb+yKObih3kmqTek0VkyXCRupZNHIlw/Df4yB1SBGaFpiTDFBdYIwfXYdxKTWeJTzXl7MhqcByxXMGIaVHyOKWq56/giUhTbBpcrEp+HztAsQleuP7bn6dnTZy6TFDyhyIQrVE5bNIZhBZxeHBLyfZK3q2wkEKMNb/JAmukaNcLmPGbsVKl3r9Wl1QdqdK3roAJAxz7o3nJ1yjwXPNpeFEiBvEPEX783ELcwcV0t2ojdN1vVMh7UFlralHzgUdEsrhM2dJfRBkrk/IF9BvVq1i5jpm9gIMJhGywWrkxLUG6yBFbBItKUxeYLTaNJKV7MrV+F0t1K1243QECrKQ6jB2tFoPiD5y9/OuOIkoLiX3h+2nQR3Zgh7yi4FvoapH+/zo91LLqmG2KZ+AP8wwEe2GrG04k974fCy4OzQWjw6qfmHN5r4W9nfR9ocQRs2N+M8FeeRDiK5HBI88CVKYdYFANDV63UfL6kCeZQ/+5b8ULbEr3zhx0ALY3daRcUcMmXPlgzWZiJiXm8PxbYPKUfrzzsUcpHqNh2kCTcIEtw4tV0/T11NxUCOMgkiO4QJoFE+DPcbrHkpTPLormp/BjgAlG9Ifjulf1LKD0bTIX6BBHS5XGSx6oUqjtG2oXTzytahtPPZKCR3mEjkPVwbLTgGlK+ZSHNTIGOs+sIthQbh3FHzN97JLHBIPvehJPXFUMuApF8Eju9nWgMiI/NUifyexbZidue0lzyrt9U3BYKSVNYLBAAjLb9fXVplEjhxKYPE+GGpsRHT7hqaSngpDntvsrrDZhF8OTbHCRrgbzpWa4TJw7ni3D8t3Lj8MXHhE6BRMDdPCZP96mK6cuESd19hqSe0N502pX3nVuNhJMUfLXILOEvqii4MWjFyyQBBgcsG1JNYeSMZJwv0walsjO7A7uYGW9yZmtz/u4OSua3g4noQru4IpErdAwkRjUYRSKz5hvrD9Y+wVhcHEObDjDR+V/4BBiBhdq96iqyonZhfrPomjM7ItVqK+Oyb7rF9jiphhdgxgaD+kQ0Hownz2jqAqcsWnugGwLuw8w60rrEx9EHs5ERDA/DmsvfjU/iQXDsiUQWaZws+Yar9S+nbIJ5jyVl8gq6Qx7ug9Xb1Et6RgZ4R+S+sr0nS0= \ No newline at end of file diff --git a/demo/src/main/assets/access_localization.ini b/demo/src/main/assets/access_localization.ini new file mode 100644 index 0000000..399c134 --- /dev/null +++ b/demo/src/main/assets/access_localization.ini @@ -0,0 +1,721 @@ +[default,zh-Hans] +NETWORK_ERROR = 网络异常 +ERROR = core 内通用非ok码 +DATA_ILLEGAL = 数据非法 +SANDBOX_OR_RESOURCE_EMPTY = 沙盒或资源目录为空 +INIT_CFG_ERROR = 初始化配置文件异常 +INIT_ACCOUNT_ERROR = 初始化账号列表异常 +INIT_PAYMENT_ERROR = 初始化支付系统异常 +NOT_START_SYSTEM = 没有启动系统 +ALREADY_INIT_SYSTEM = 系统已经初始化 +PARSE_RESPONSE_ERROR = 解析响应错误 +PARSE_INIT_INFO_ERROR = 解析初始化数据错误 +PARSE_LOGIN_INFO_ERROR = 解析登陆信息异常 +USERNAME_OR_PASSWORD_EMPTY = 用户名或密码为空 +ACCOUNT_ID_OR_CODE_EMPTY = 账户ID或账户码为空 +ACCOUNT_ALREADY_BIND_CHANNEL = 账号已经绑定当前渠道 +PHONE_OR_CODE_EMPTY = 手机号或验证码为空 +PHONE_NUMBER_EMPTY = 手机号码为空 +PHONE_FORMAT_ERROR = 手机号码格式错误 +QUICK_AUTH_TOKEN_EMPTY = 快速登陆授权Token为空 +NOT_SURE_ALL_INTERACTIVE = 未确认所有的对话框交互 +NOT_EXIST_SERVICE_DIALOG = 不存在业务对话框 +ALREADY_LOGIN = 已经登陆 +LOGIN_FAILED = 登录失败 +REMOVE_ACCOUNT_ERROR = 删除账号失败 +PAYMENT_ERROR = 支付异常 +CREATE_LOCAL_ORDER_ERROR = 创建本地订单异常 +EXIST_LOCAL_ORDER = 存在本地未完成订单 +REMOVE_LOCAL_ORDER_ERROR = 移除本地订单异常 +NOT_IN_INIT = 当前不在初始化状态 +NOT_IN_LOGIN = 当前不在登陆状态 +INIT_SYSTEM_ERROR = 初始化系统异常 +INIT_PREFERENCES_ERROR = 初始化用户偏好设置异常 +EXIST_VIEW_NOT_CLOSE = 存在未被关闭的view +CURRENT_VIEW_NOT_OPENED = 当前view未被打开 +FETCH_DESC_FAILED = 抓取异常描述失败 +NOT_EXIST_CHILD_ACCOUNT = 当前小号不存在 +NAME_OR_IDENTITY_EMPTY = 姓名或身份证为空 +ACCOUNT_INFO_NOT_EXIST = 账号信息不存在 +ORDER_INFO_NOT_EXIST = 订单信息不存在 +CHANNEL_NOT_SUPPORT_LOGIN = 渠道登陆不支持 +NOT_SUPPORT_OPERATOR = 当前操作不支持 +PLEASE_RESTART_UPDATE = 更新已完成,请重启 +LOCALIZATION_UPDATE_RESTART = 国际化文件已更新,重启生效 +CONFIGRATION_UPDATE_RESTART = 配置文件已更新,重启生效 +NOT_GENUINE_APP = 非正版应用,请下载正版应用 +CANCEL_REAL_NAME = 取消实名认证 +UNOFFICIAL_AUTHENTICATION = 非官方认证登录 +UNKNOWN = 未知异常 + +enterGame = 进入游戏 +policyPrivacy = 阅读并同意[隐私条款]及[用户协议] +otherLogin = 其它登录方式 +realAuth = 实名认证 +quickLogin = 快速登录 +phoneQuickLogin=本机号码一键登录 +pleaseInputPwd = 请输入密码 +pleaseInputAccount = 请输入账号 +getVerifyCode = 验证码 +pleaseInputVerifyCode = 请输入验证码 +pleaseInputPhone = 请输入手机号 +tip = 提示 +authorization = 认证 +cancel = 取消 +sure = 确定 +pleaseInputIdentifyId = 请输入身份证号 +pleaseInputName = 请输入姓名 +realNameRules = 根据国家新闻出版署《关于防止未成年沉迷网络游戏的通知》,未实名账号不可体验游戏,请尽快完成实名登记 +mainEnter = 主号进入 +chileEnter = 小号进入 +checkedPrivace = 请先勾选并同意相关政策条款再登录游戏 +checkPermissionTip = 由于您已拒绝%权限,开启该功能需要打开“设置”>“权限管理”进行授权 +realNameError = 您输入的姓名格式不正确 +idCardError = 您输入的身份证格式不正确 +phoneError = 手机号码格式不正确 +vcodeError = 验证码格式不正确 +pwdError = 密码格式不正确 +getVcodeFailed = 获取验证码失败 + +bindAccount = 绑定账号 +bindAccountTip = 当前渠道账号未绑定游客 +guestAccountTip = 当前账号为游客账号,请尽快绑定三方账号,避免账号丢失 +guestSwitchTip = 您当前为游客账号,切换账号将丢失当前游客账号的游戏进度 +bindingAccountTip = 当前账号已绑定%s账号 +accountId = 账号ID +accountCode = 账号码 +bindAccountIdPlaceholder = 请输入账号恢复Id +bindAccountCodePlaceholder = 请输入账号恢复码 +bindIdCodedescription = 当更换设备时可在切换账号页面中输入当前的恢复Id和恢复码,就可以使用原有账号角色数据继续游戏 +unbindIdCodedescription = 如果账号未绑定任何三方账号,可输入账号恢复Id和恢复码用来切换账号 +switchAccountTip = 当前登录的渠道账号已绑定其他游客账号,是否进行切换 +switchAccount = 切换账号 +switchAccountFailture = 切换账号失败 +sameAccountTip = 当前账号已登录 +loginSuccessTip = 用户%s,登录成功 + +orderValidate = 订单核验中... +startingPay = 开始购买 +purchaseing = 购买中... + +[en] +NETWORK_ERROR = Network exception +ERROR = General non-OK code within the core +DATA_ILLEGAL = Illegal data +SANDBOX_OR_RESOURCE_EMPTY = Sandbox or resource directory is empty +INIT_CFG_ERROR = Initialization configuration file exception +INIT_ACCOUNT_ERROR = Initialization account list exception +INIT_PAYMENT_ERROR = Initialization payment system exception +NOT_START_SYSTEM = System not started +ALREADY_INIT_SYSTEM = System already initialized +PARSE_RESPONSE_ERROR = Parse response error +PARSE_INIT_INFO_ERROR = Parse initialization data error +PARSE_LOGIN_INFO_ERROR = Parse login information exception +USERNAME_OR_PASSWORD_EMPTY = Username or password is empty +ACCOUNT_ID_OR_CODE_EMPTY = Account ID or account code is empty +ACCOUNT_ALREADY_BIND_CHANNEL = Account already bound to the current channel +PHONE_OR_CODE_EMPTY = Phone number or verification code is empty +PHONE_NUMBER_EMPTY = Phone number is empty +PHONE_FORMAT_ERROR = Phone number format is incorrect +QUICK_AUTH_TOKEN_EMPTY = Quick login authorization token is empty +NOT_SURE_ALL_INTERACTIVE = Not all dialog interactions are confirmed +NOT_EXIST_SERVICE_DIALOG = Service dialog does not exist +ALREADY_LOGIN = Already logged in +LOGIN_FAILED = Login failed +REMOVE_ACCOUNT_ERROR = Failed to remove account +PAYMENT_ERROR = Payment exception +CREATE_LOCAL_ORDER_ERROR = Exception occurred when creating a local order +EXIST_LOCAL_ORDER = Existing local incomplete order +REMOVE_LOCAL_ORDER_ERROR = Exception occurred when removing a local order +NOT_IN_INIT = Not in initialization state +NOT_IN_LOGIN = Not in login state +INIT_SYSTEM_ERROR = Initialization system exception +INIT_PREFERENCES_ERROR = Initialization user preference settings exception +EXIST_VIEW_NOT_CLOSE = Existing unclosed view +CURRENT_VIEW_NOT_OPENED = Current view not opened +FETCH_DESC_FAILED = Failed to fetch exception description +NOT_EXIST_CHILD_ACCOUNT = Current child account does not exist +NAME_OR_IDENTITY_EMPTY = Name or ID card is empty +ACCOUNT_INFO_NOT_EXIST = Account information does not exist +ORDER_INFO_NOT_EXIST = Order information does not exist +CHANNEL_NOT_SUPPORT_LOGIN = Channel login not supported +NOT_SUPPORT_OPERATOR = Current operation not supported +PLEASE_RESTART_UPDATE = Update completed, please restart +LOCALIZATION_UPDATE_RESTART = Localization files updated, restart required +CONFIGRATION_UPDATE_RESTART = Configuration files updated, restart required +NOT_GENUINE_APP = Non-genuine application, please download the official version +CANCEL_REAL_NAME = Cancel real-name authentication +UNOFFICIAL_AUTHENTICATION = Unofficial authentication login +UNKNOWN = Unknown exception + + +enterGame = Enter the game +policyPrivacy = Read and agree to [Privacy Policy] and [User Agreement] +otherLogin = Other login methods +realAuth = Real-name authentication +quickLogin = Quick login +phoneQuickLogin = Quick login with caller ID +pleaseInputPwd = Please enter the password +pleaseInputAccount = Please enter the account +getVerifyCode = Verification code +pleaseInputVerifyCode = Please enter the verification code +pleaseInputPhone = Please enter the phone number +tip = Tip +authorization = Authorization +cancel = Cancel +sure = Confirm +pleaseInputIdentifyId = Please enter the ID card number +pleaseInputName = Please enter the name +realNameRules = According to the requirements of the National Press and Publication Administration's "Notice on Preventing Minors from Being Addicted Online Games", unverified accounts cannot experience the game. Please complete real-name authentication as soon as possible. +mainEnter = Enter with main account +childEnter = Enter with child account +checkedPrivace = Please check and agree to the relevant policy terms before logging into the game +checkPermissionTip = As you have denied the % permission, enabling this feature requires opening "Settings" > "Permission Management" for authorization +realNameError = The name you entered is not in the correct format +idCardError = The ID card you entered is not in the correct format +phoneError = Incorrect phone number format +vcodeError = Incorrect verification code format +pwdError = Incorrect password format +getVcodeFailed = Failed to get verification code + + +bindAccount = Bind account +bindAccountTip = Current channel account is not bound to a guest account +guestAccountTip = Current account is a guest account, please bind to a third-party account as soon as possible in case of loss of account +guestSwitchTip = You are currently a guest account, Switching accounts will lose the game progress of the current guest account +bindingAccountTip = Current account is already bound to %s account +accountId = Account ID +accountCode = Account code +bindAccountIdPlaceholder = Please enter account recovery ID +bindAccountCodePlaceholder = Please enter account recovery code +bindIdCodedescription = When switching devices, you can input the current recovery ID and recovery code on the account switch page to continue using the original account character data +unbindIdCodedescription = If the account is not bound to any third-party account, you can enter the account recovery ID and recovery code to switch accounts +switchAccountTip = The current logged-in channel account is already bound to another guest account. Do you want to switch? +switchAccount = Switch account +switchAccountFailture = Failed to switch account +sameAccountTip = Current account is already logged in +loginSuccessTip = User %s logged in successfully + +orderValidate = Order validation in progress... +startingPay = Start purchasing +purchaseing = Purchasing... + +[es] +NETWORK_ERROR = Error de red +ERROR = Código no ok universal dentro del core +DATA_ILLEGAL = Datos inválido +SANDBOX_OR_RESOURCE_EMPTY = Entorno de pruebas o directorio de recursos vacío +INIT_CFG_ERROR = Error al inicializar archivo de configuración +INIT_ACCOUNT_ERROR = Error al inicializar lista de cuentas +INIT_PAYMENT_ERROR = Error al inicializar el sistema de pago +NOT_START_SYSTEM = Sistema no iniciado +ALREADY_INIT_SYSTEM = Sistema inicializado +PARSE_RESPONSE_ERROR = Error al analizar respuesta +PARSE_INIT_INFO_ERROR = Error al analizar datos de inicialización +PARSE_LOGIN_INFO_ERROR = Error al analizar información de inicio de sesión +USERNAME_OR_PASSWORD_EMPTY = Nombre d usuario o contraseña vacía +ACCOUNT_ID_OR_CODE_EMPTY = ID de cuenta o código de cuenta vacío +ACCOUNT_ALREADY_BIND_CHANNEL = Cuenta vinculada al canal actual +PHONE_OR_CODE_EMPTY = Número de teléfono móvil o código de verificación vacío +PHONE_NUMBER_EMPTY = Número de teléfono móvil vacío +PHONE_FORMAT_ERROR = Error de formato de teléfono móvil +QUICK_AUTH_TOKEN_EMPTY = Token d autorización de inicio de sesión rápido vacío +NOT_SURE_ALL_INTERACTIVE = Todas las interacciones del cuadro de diálogo no confirmadas +NOT_EXIST_SERVICE_DIALOG = No existe el cuadro de diálogo de negocios +ALREADY_LOGIN = Sesión iniciada +LOGIN_FAILED = Inicio de sesión fallida +REMOVE_ACCOUNT_ERROR = Error al eliminar cuenta +PAYMENT_ERROR = Error de pago +CREATE_LOCAL_ORDER_ERROR = Error al crear pedido local +EXIST_LOCAL_ORDER = Hay pedido local pendiente +CREATE_LOCAL_ORDER_ERROR = Error al retirar pedido local +NOT_IN_INIT = No en estado de inicialización +NOT_IN_LOGIN = No en estado de inicio de sesión +INIT_SYSTEM_ERROR = Error al inicializar el sistema +INIT_PREFERENCES_ERROR = Error al inicializar configuración de preferencias de usuario +EXIST_VIEW_NOT_CLOSE = Hay view no cerrado +CURRENT_VIEW_NOT_OPENED = view actual abierto +FETCH_DESC_FAILED = Error al capturar descripción de anomalía +USER_INFO_NOT_EXIST=La cuenta auxiliar actual no existe +NAME_OR_IDENTITY_EMPTY = Nombre o documento de identificación vacío +ACCOUNT_INFO_NOT_EXIST = La información de cuenta no existe +ORDER_INFO_NOT_EXIST = La información de pedido no existe +CHANNEL_NOT_SUPPORT_LOGIN = No admite inicio de sesión del canal actual +NOT_SUPPORT_OPERATOR = No admite la operación actual +PLEASE_RESTART_UPDATE = Actualización terminada, reinicie +LOCALIZATION_UPDATE_RESTART = Archivo internacional actualizado, reinicie para que surta efecto +CONFIGRATION_UPDATE_RESTART = Archivo de configuración actualizado, reinicie para que surta efecto +NOT_GENUINE_APP = Aplicación no genuina, descargue una genuina +CANCEL_REAL_NAME = Cancelar autentificación de nombre real +UNOFFICIAL_AUTHENTICATION = Inicio de sesión de autenticación no oficial +UNKNOWN = Error desconocido + + +enterGame = Entrar en el juego +policyPrivacy = Lea y acepte los [Términos de Privacidad] y [Acuerdo de Usuario] +otherLogin = Otros métodos de inicio de sesión +realAuth = Autentificación de nombre real +quickLogin = Inicio de sesión rápido +phoneQuickLogin=Iniciar sesión con un solo clic con el número del teléfono móvil +pleaseInputPwd = Ingrese la contraseña +pleaseInputAccount = Ingrese la cuenta +getVerifyCode = Código de verificación +pleaseInputVerifyCode = Ingrese el código de verificación +pleaseInputPhone = Ingrese el número de teléfono móvil +tip = Aviso +authorization = Autentificación +cancel = Cancelar +sure = Confirmar +pleaseInputIdentifyId = Ingrese el número de identificación +pleaseInputName = Ingrese el nombre +realNameRules = Conforme a los requisitos del "Aviso sobre la Prevención de la Adicción de Menores de Edad a los Juegos en Línea" de la Administración Nacional de Prensa y Publicaciones, no se puede experimentar el juego sin una cuenta de nombre real, complete el registro de nombre real lo antes posible. +mainEnter = Ingresar con cuenta principal +chileEnter = Ingresar con cuenta auxiliar +checkedPrivace = Marque y acepte los términos de la política pertinente antes de iniciar sesión en el juego. +checkPermissionTip = Como ha denegado el permiso%, debe habilitar esta función yendo a "Configuración" > "Gestión de permisos" para autorizar +realNameError = El formato del nombre ingrsado es incorrecto +idCardError = El formato del documento de identificación ingresado es incorrecto +phoneError = El formato del número de teléfono móvil es incorrecto +vcodeError = El formato del código de verificación es incorrecto +pwdError = El formato de la contraseña es incorrecto +getVcodeFailed = Error al obtener código de verificación + + +bindAccount = Vincular cuenta +bindAccountTip = La cuenta actual del canal no está vinculada a visitado +guestAccountTip = Es la cuenta de visitado, vincule la cuenta de un tercero lo antes posible para evitar la pérdida de la cuenta. +guestSwitchTip = Actualmente eres una cuenta de invitado, Al cambiar de cuenta, se perderá el progreso del juego de la cuenta de invitado actual. +bindAccountTip = La cuenta actual está vinculada a la cuenta %s +accountId = ID de cuenta +accountCode = Número de cuenta +bindAccountIdPlaceholder = Ingrese Id de recuperación de cuenta +bindAccountCodePlaceholder = Ingrese el código de recuperación de la cuenta +bindIdCodedescription = Al cambiar el dispositivo, puede introducir el Id de recuperación actual y el código de recuperación en la página de cambio de cuenta, y continuar el juego con los datos del personaje de la cuenta original. +unbindIdCodedescription = Si la cuenta no está vinculada a ninguna cuenta de tercero, puede introducir el ID de recuperación de la cuenta y el código de recuperación para cambiar la cuenta. +switchAccountTip = La cuenta del canal de inicio de sesión actual está vinculada a otra cuenta de visitado, ¿cambiar? +switchAccount = Cambiar cuenta +switchAccountFailture = Error al cambiar cuenta +sameAccountTip = La cuenta actual se ha iniciado sesión +loginSuccessTip = Usuario %s, inicio de sesión con éxito + +orderValidate = Verificando pedido... +startingPay = Comprar +purchaseing = Comprando... + +[fr] +NETWORK_ERROR = Anomalie réseau. +ERROR = code non-ok générique dans le core. +DATA_ILLEGAL = Données illégales. +SANDBOX_OR_RESOURCE_EMPTY = Le répertoire du bac à sable ou des ressources est vide +INIT_CFG_ERROR = Erreur de profil d'initialisation. +INIT_ACCOUNT_ERROR = Erreur d'initialisation de la liste des comptes +INIT_PAYMENT_ERROR = Erreur d'initialisation du système de paiement +NOT_START_SYSTEM = Le système n'a pas démarré +ALREADY_INIT_SYSTEM = Le système est déjà initialisé +PARSE_RESPONSE_ERROR = Erreur d'analyse de la réponse. +PARSE_INIT_INFO_ERROR = Erreur d'analyse des données d'initialisation. +PARSE_LOGIN_INFO_ERROR = Erreur d'analyse des informations de connexion. +USERNAME_OR_PASSWORD_EMPTY = Le nom d'utilisateur ou le mot de passe est vide. +ACCOUNT_ID_OR_CODE_EMPTY = ID de compte ou code de compte vide +ACCOUNT_ALREADY_BIND_CHANNEL = Le compte a été lié au canal actuel +PHONE_OR_CODE_EMPTY = Le numéro de téléphone mobile ou le code de vérification est vide. +PHONE_NUMBER_EMPTY = Le numéro de téléphone mobile est vide +PHONE_FORMAT_ERROR = Le format du numéro de téléphone mobile est incorrect. +QUICK_AUTH_TOKEN_EMPTY = Le token d'autorisation de connexion rapide est vide. +NOT_SURE_ALL_INTERACTIVE = Toutes les interactions de la boîte de dialogue ne sont pas confirmées. +NOT_EXIST_SERVICE_DIALOG = Aucun dialogue commercial n'existe. +ALREADY_LOGIN = Déjà connecté. +LOGIN_FAILED = Échec de la connexion. +REMOVE_ACCOUNT_ERROR = Échec de la suppression du compte. +PAYMENT_ERROR = Erreur de paiement +CREATE_LOCAL_ORDER_ERROR = Erreur de création d'une commande locale. +EXIST_LOCAL_ORDER = Il y a une commande locale non remplie. +REMOVE_LOCAL_ORDER_ERROR = Supprimer une erreur de commande locale. +NOT_IN_INIT = Actuellement pas dans l'état d'initialisation. +NOT_IN_LOGIN = Pas encore connecté. +INIT_SYSTEM_ERROR = Erreur du système d'initialisation. +INIT_PREFERENCES_ERROR = Erreur d'initialisation des préférences de l'utilisateur. +EXIST_VIEW_NOT_CLOSE = Un view n'a pas été fermé. +CURRENT_VIEW_NOT_OPENED = Le view actuel n'est pas ouvert. +FETCH_DESC_FAILED = Échec de la saisie de la description des erreurs. +NOT_EXIST_CHILD_ACCOUNT = Le compte enfant n'existe pas. +NAME_OR_IDENTITY_EMPTY = Le nom ou ID est vide. +ACCOUNT_INFO_NOT_EXIST = L'information sur le compte n'existe pas. +USER_INFO_NOT_EXIST = L'information sur la commande n'existe pas. +CHANNEL_NOT_SUPPORT_LOGIN = La connexion au canal est non supportée. +NOT_SUPPORT_OPERATOR = L'opération actuelle n'est pas supportée. +PLEASE_RESTART_UPDATE = La mise à jour est terminée, veuillez redémarrer. +LOCALIZATION_UPDATE_RESTART = Le fichier d'internationalisation a été mis à jour, veuillez redémarrer pour qu'il prenne effet. +CONFIGRATION_UPDATE_RESTART = Le fichier de configuration a été mis à jour, veuillez redémarrer pour qu'il prenne effet. +NOT_GENUINE_APP = Application non authentique, veuillez télécharger l'application authentique. +CANCEL_REAL_NAME = Annuler la vérification de l'identité. +UNOFFICIAL_AUTHENTICATION = Connexion d'authentification non officielle. +UNKNOWN = Erreur inconnue. + + +enterGame = Entrer dans le jeu. +policyPrivacy = Lire et accepter [Politique de confidentialité] et [Accord de l'utilisateur]. +otherLogin = Autres méthodes de connexion. +realAuth = La vérification de l'identité. +quickLogin = Connexion rapide. +phoneQuickLogin=Connexion en un clic avec le numéro local. +pleaseInputPwd = Veuillez saisir le mot de passe. +pleaseInputAccount = Veuillez saisir votre numéro de compte. +getVerifyCode = Code de vérification. +pleaseInputVerifyCode = Veuillez saisir le code de vérification. +pleaseInputPhone = Veuillez saisir le numéro de téléphone portable. +tip = Notice +authorization = Authentification +cancel = Annuler +sure = Confirmer +pleaseInputIdentifyId = Veuillez saisir le numéro d'identification. +pleaseInputName = Veuillez saisir votre nom. +realNameRules = Selon l’avis de l'Administration générale de la presse et des publications sur la prévention de l'accès des mineurs aux jeux en ligne, vous ne pouvez pas participer au jeu sans un compte à nom réel. Veuillez donc compléter l'enregistrement à nom réel dès que possible. +mainEnter = Entrée du compte main +chileEnter = Entrée du compte enfant +checkedPrivace = Veuillez cocher et accepter les conditions d'utilisation avant de vous connecter au jeu. +checkPermissionTip = Vous avez refusé la permission %, pour ouvrir cette fonction, vous devez ouvrir "Paramètres" > "Gestion des permissions" pour l'autorisation. +realNameError = Le nom que vous avez saisi n'est pas au bon format. +idCardError = Vous avez saisi un numéro d'identification incorrect. +phoneError = Le format de votre numéro de téléphone portable n'est pas correct. +vcodeError = Le code de vérification n'est pas dans le bon format. +pwdError = Le format du mot de passe est incorrect. +getVcodeFailed = Le code de vérification n'a pas été obtenu. + + +bindAccount = Lier le compte +bindAccountTip = Le compte de canal actuel n'est pas lié au visiteur. +guestAccountTip = Le compte actuel est un compte visiteur, veuillez lier un compte tiers dès que possible pour éviter une perte de compte. +guestSwitchTip = Vous êtes actuellement un compte invité. Changer de compte entraînera la perte de la progression du jeu du compte invité actuel. +bindingAccountTip = Le compte courant est lié au compte %s. +accountId = ID du compte +accountCode = Code du compte +bindAccountIdPlaceholder = Veuillez saisir le numéro de compte de recouvrement. +bindAccountIdPlaceholder = Veuillez saisir le code de recouvrement. +bindIdCodedescription = Lorsque vous changez d'appareil, vous pouvez saisir le numéro de compte et le code de recouvrement, puis vous pouvez utiliser les données de personnage du compte d'origine pour continuer le jeu. +unbindIdCodedescription = Si le compte n'est pas lié à un compte tiers, vous pouvez saisir le numéro de compte et le code de recouvrement pour changer de compte. +switchAccountTip = Le compte du canal actuellement connecté est lié à d'autres comptes visiteurs. Voulez-vous changer de compte ? +switchAccount = Changer de compte +switchAccountFailture = Échec du changement de compte +sameAccountTip = Le compte actuel est connecté +loginSuccessTip = Utilisateur%s, connexion réussie + +orderValidate = Vérification de la commande en cours... +startingPay = Commencer l'achat +purchaseing = Acheter... + +[it] +NETWORK_ERROR = Errore di rete +ERROR = In core cidice usato non ok +DATA_ILLEGAL = Dati non corretti +SANDBOX_OR_RESOURCE_EMPTY = Sandbox o elenco risorse vuoto +INIT_CFG_ERROR = Errore documenti impostazioni di inizializzazione +INIT_ACCOUNT_ERROR = Errore lista account inizializzazione +INIT_PAYMENT_ERROR = Errore di sistema pagamento inizializzazione +NOT_START_SYSTEM = Non è presente il sistema di avviamento +ALREADY_INIT_SYSTEM = Inizializzazione sistema avvenuta +PARSE_RESPONSE_ERROR = Errore risposta analisi +PARSE_INIT_INFO_ERROR = Errore dati inizializzazione analisi +PARSE_LOGIN_INFO_ERROR = Errore info accesso analisi +USERNAME_OR_PASSWORD_EMPTY = Nome utente o password vuoti +ACCOUNT_ID_OR_CODE_EMPTY = ID account o codice account vuoti +ACCOUNT_ALREADY_BIND_CHANNEL = Account già associato al canale attuale +PHONE_OR_CODE_EMPTY = Numero di telefono o codice di verifica vuoti +PHONE_NUMBER_EMPTY = Numero di telefono vuoto +PHONE_FORMAT_ERROR = Formato numero di telefono errato +QUICK_AUTH_TOKEN_EMPTY = Token permesso access rapido vuoto +NOT_SURE_ALL_INTERACTIVE = Non ancora confermata l’interazione di tutti i dialoghi +NOT_EXIST_SERVICE_DIALOG = Dialogo del servizio inesistente +ALREADY_LOGIN = Accesso già effettuato +LOGIN_FAILED = Accesso fallito +REMOVE_ACCOUNT_ERROR = Eliminazione account fallita +PAYMENT_ERROR = Errore pagamento +CREATE_LOCAL_ORDER_ERROR = Errore creazione ordine locale +EXIST_LOCAL_ORDER = È presente un ordine locale non completato +REMOVE_LOCAL_ORDER_ERROR = Errore rimozione ordine locale +NOT_IN_INIT = Al momento non si è nello stato di inizializzazione +NOT_IN_LOGIN = Al momento non si è nell stato di accesso +INIT_SYSTEM_ERROR = Errore inizializzazione di sistema +INIT_PREFERENCES_ERROR = Errore impostazioni preferite inizializzazione utente +EXIST_VIEW_NOT_CLOSE = È presente view non chiusa +CURRENT_VIEW_NOT_OPENED = View attuale non aperta +FETCH_DESC_FAILED = Fallimento descrizione errore fetch +NOT_EXIST_CHILD_ACCOUNT = Account secondario attuale inesistente +NAME_OR_IDENTITY_EMPTY = Nome o identità vuoti +ACCOUNT_NOT_EXIST=Info account inesistenti +ORDER_INFO_NOT_EXIST = Info ordine inesistenti +CHANNEL_NOT_SUPPORT_LOGIN = Accesso canale non supportato +NOT_SUPPORT_OPERATOR = Operazione attuale non supportata +PLEASE_RESTART_UPDATE = Aggiornamento completato, riavviare +LOCALIZATION_UPDATE_RESTART = Documento di localizzazione aggiornato, riavvia per attivare +CONFIGRATION_UPDATE_RESTART = Documento impostazioni aggiornato, riavvia per attivare +NOT_GENUINE_APP = Versione app non originale, scaricare versione ufficiale +CANCEL_REAL_NAME = Annulla verifica identità取消实名认证 +UNOFFICIAL_AUTHENTICATION = Accesso non ufficiale +UNKNOWN = Errore sconosciuto + + +enterGame = Entra nel gioco +policyPrivacy = Leggi e approva [normativa sulla privacy] e [contratto utente] +otherLogin = Altri metodi di accesso +realAuth = Verifica identità +quickLogin = Accessi rapido +phoneQuickLogin=Accesso rapido con questo numero +pleaseInputPwd = Inserire password +pleaseInputAccount = Inserire account +getVerifyCode = Codice di verifica +pleaseInputVerifyCode = Inserire codice di verifica +pleaseInputPhone = Inserire numero di telefono +tip = Consiglio +authorization = Autorizzazione +cancel = Annulla +sure = Conferma +pleaseInputIdentifyId = Inserire numero documento d’identità +pleaseInputName = Inserire nome +realNameRules = In base alle nuove informazioni pubblicate in “Avviso sulla prevenzione della dipendenza da giochi on-line dei minori”, gli account con identità non identificata non possono accedere al gioco, registra la tua identità al più presto. +mainEnter = Entrata account principale +chileEnter = Emtrata account secondario +checkedPrivace = Metti la spunta per approvare le normative necessarie per accedere al gioco +checkPermissionTip = Dato che hai negato il permesso di %, per attivare questa funzione è necessario aprire “impostazioni”>“gestione permessi” e attivare il permesso +realNameError = Formato nome e cognome inseriti errato +idCardError = Il formato di documento d’identità che hai inserito non è corretto +phoneError = Formato numero di telefono errato +vcodeError = Formato codice di verifca errato +pwdError = Formato password errato +getVcodeFailed = Ottenimento codice di verifica fallito + + +bindAccount = Associa account +bindAccountTip = L’ospite non ha associato l’account canale attuale +guestAccountTip = L’account attuale è un account ospite, associa al più presto un account di terze parti, così da evitare la perdita dell’account +guestSwitchTip = Attualmente sei un account ospite. Cambiare account farà perdere i progressi di gioco dell'account ospite corrente. +bindingAccountTip = Account attuale già associato all’account %s +accountId = ID account +accountCode = Codice account +bindAccountIdPlaceholder = Inserire Id di recupero account +bindAccountCodePlaceholder = Inserire codice di recupero account +bindIdCodedescription = Quando si cambia dispositivo si possono inserire i seguenti id di recupero e codice di recupero nella pagina di cambio account, così si possono utilizzare i dati del personaggio dell’account originale e proseguire il gioco +unbindIdCodedescription = Se l’account non ha account di terze parti associate, si possono inserire l’id di recupero e il codice di recupero per passare all’account +switchAccountTip = L’account del canale dell’accesso attuale è già associato a un’altro account di gioco, effettuare il cambio? +switchAccount = Cambio account +switchAccountFailture = Cambio account fallito +sameAccountTip = Accesso account attuale effettuato +loginSuccessTip = Utente %s, accesso riuscito + +orderValidate = Verifica ordine in corso... +startingPay = Inizia acquisto +purchaseing = Acquisto in corso... + +[ja] +NETWORK_ERROR = ネットワークエラー +ERROR = core 内共通の非okコード +DATA_ILLEGAL = 不適切なデータ +SANDBOX_OR_RESOURCE_EMPTY = サンドボックスまたはリソースディレクトリが空です +INIT_CFG_ERROR = 構成ファイルの初期化にエラーが発生しました +INIT_ACCOUNT_ERROR = アカウントリストの初期化にエラーが発生しました +INIT_PAYMENT_ERROR =支払いシステムの初期化にエラーが発生しました +NOT_START_SYSTEM = システムが起動していません +ALREADY_INIT_SYSTEM = システムは初期化されました +PARSE_RESPONSE_ERROR = レスポンスの解析にエラーが発生しました +PARSE_INIT_INFO_ERROR = 初期化データの解析にエラーが発生しました +PARSE_LOGIN_INFO_ERROR = ログイン情報の解析にエラーが発生しました +USERNAME_OR_PASSWORD_EMPTY = ユーザー名またはパスワードが空です +ACCOUNT_ID_OR_CODE_EMPTY = アカウントIDまたアカウントコードが空です +ACCOUNT_ALREADY_BIND_CHANNEL = アカウントは既にこのチャンネルと連携されています +PHONE_OR_CODE_EMPTY = 携帯番号または認証コードが空です +PHONE_NUMBER_EMPTY = 携帯番号が空です +PHONE_FORMAT_ERROR = 携帯番号の形式が間違っています +QUICK_AUTH_TOKEN_EMPTY = クイックログインの認証Tokenが空です +NOT_SURE_ALL_INTERACTIVE = すべてのダイアログインタラクションは確認されていません +NOT_EXIST_SERVICE_DIALOG = 業務ダイアログは存在しません +ALREADY_LOGIN = ログイン済み +LOGIN_FAILED = ログイン失敗 +REMOVE_ACCOUNT_ERROR = アカウントの削除に失敗しました +PAYMENT_ERROR = 支払いエラー +CREATE_LOCAL_ORDER_ERROR = ローカル注文の作成時にエラーが発生しました +EXIST_LOCAL_ORDER = 未完了のローカル注文があります +REMOVE_LOCAL_ORDER_ERROR = ローカル注文の削除時にエラーが発生しました +NOT_IN_INIT = 現在は初期化状態ではありません +NOT_IN_LOGIN = 現在はログイン状態ではありません +INIT_SYSTEM_ERROR = システムの初期化にエラーが発生しました +INIT_PREFERENCES_ERROR = ユーザーパーソナライズ設定の初期化にエラーが発生しました +EXIST_VIEW_NOT_CLOSE = 閉じられていないビューがあります +CURRENT_VIEW_NOT_OPENED = このビューは開いていません +FETCH_DESC_FAILED = エラーの説明の取得に失敗しました +NOT_EXIST_CHILD_ACCOUNT = このサブアカウントが存在しません +NAME_OR_IDENTITY_EMPTY = 名前または身分証明書が空です +ACCOUNT_INFO_NOT_EXIST = アカウント情報が存在しません +ORDER_INFO_NOT_EXIST = 注文情報が存在しません +CHANNEL_NOT_SUPPORT_LOGIN = チャンネルログインがサポートされていません +NOT_SUPPORT_OPERATOR = この操作がサポートされていません +PLEASE_RESTART_UPDATE = アップデートが完了しました。再起動してください +LOCALIZATION_UPDATE_RESTART = 国際化ファイルが更新されました。再起動すると有効になります。 +CONFIGRATION_UPDATE_RESTART = 構成ファイルが更新されました。再起動すると有効になります。 +NOT_GENUINE_APP = 正規版のアプリではありません。正規版をダウンロードしてください +CANCEL_REAL_NAME = 実名認証をキャンセルします +UNOFFICIAL_AUTHENTICATION = 非公式認証ログイン +UNKNOWN = 未知の異常 + + +enterGame = ゲームに入る +policyPrivacy = [プライバシーポリシー]と[利用規約]を読んで同意します +otherLogin = その他のログイン方法 +realAuth = 実名認証 +quickLogin = クイックログイン +phoneQuickLogin=この携帯番号でクイックログイン +pleaseInputPwd = パスワードを入力してください +pleaseInputAccount = アカウントを入力してください +getVerifyCode = 認証コード +pleaseInputVerifyCode = 認証コードを入力してください +pleaseInputPhone = 携帯番号を入力してください +tip = チップス +authorization = 認証 +cancel = キャンセル +sure = 確認 +pleaseInputIdentifyId = 身分証明書番号を入力してください +pleaseInputName = 名前を入力してください +realNameRules = 国家新聞出版署の『未成年者のオンラインゲーム依存防止に関する通知』の要件に従って、実名認証されていないアカウントはゲームを体験することができません。速やかに実名登録を完了してください +mainEnter = メインアカウントで入る +chileEnter = サブアカウントで入る +checkedPrivace = ゲームにログインする前に、関連するポリシー条項を確認し、同意してください。 +checkPermissionTip = %権限を拒否したため、この機能を有効にするには、「設定」 > 「権限管理」 を開いて許可する必要があります +realNameError = 入力した名前の形式が正しくありません +idCardError = 入力した身分証明書の形式が正しくありません +phoneError = 携帯番号の形式が正しくありません +vcodeError = 認証コードの形式が正しくありません +pwdError = パスワードの形式が正しくありません +getVcodeFailed = 認証コードの取得に失敗しました + + +bindAccount = アカウント連携 +bindAccountTip = このチャンネルアカウントはゲストと連携されていません +guestAccountTip = 現在のアカウントはゲストアカウントです。アカウントを失わないよう、できるだけ早く第三者アカウントに連携してください +guestSwitchTip = あなたは現在ゲスト アカウントです。アカウントを切り替えると、現在のゲスト アカウントのゲームの進行状況が失われます +bindingAccountTip = 現在のアカウントは%sアカウントに連携されています +accountId = アカウントID +accountCode = アカウントコード +bindAccountIdPlaceholder = アカウント回復IDを入力してください +bindAccountCodePlaceholder = アカウント回復コードを入力してください +bindIdCodedescription = デバイスを変更した場合、アカウント切り替え画面で現在の回復IDと回復コードを入力すると、元のアカウントのキャラクターデータでゲームを継続することができます。 +unbindIdCodedescription = アカウントが第三者アカウントに連携されていない場合は、アカウント回復IDと回復コードを入力することでアカウントを切り替えることができます。 +switchAccountTip = 現在ログインしているチャンネルアカウントは別のゲストアカウントに連携されています。切り替えますか? +switchAccount = アカウント切り替え +switchAccountFailture = アカウントの切り替えに失敗しました +sameAccountTip = このアカウントはログインしています +loginSuccessTip = ユーザー%s、ログインに成功しました + +orderValidate = 注文確認中... +startingPay = 購入を開始します +purchaseing = 購入中... + +[pt] +NETWORK_ERROR = Erro de rede +ERROR = Código de erro geral das funções principais +DATA_ILLEGAL = Dados inválidos +SANDBOX_OR_RESOURCE_EMPTY = Não há recursos na pasta de recursos +INIT_CFG_ERROR = Ocorreu um erro ao inicializar o arquivo de configuração +INIT_ACCOUNT_ERROR = Ocorreu um erro ao inicializar a lista de contas +INIT_PAYMENT_ERROR = Ocorreu um erro ao inicializar o sistema de pagamento +NOT_START_SYSTEM = O sistema não foi inicializado +ALREADY_INIT_SYSTEM = O sistema foi inicializado +PARSE_RESPONSE_ERROR = Ocorre um erro ao analisar a resposta +PARSE_INIT_INFO_ERROR = Ocorre um erro ao analisar dados inicializados +PARSE_LOGIN_INFO_ERROR = Falha ao analisar as informações de login +USERNAME_OR_PASSWORD_EMPTY = Nome do usuário e senha devem ser preenchidos +ACCOUNT_ID_OR_CODE_EMPTY = ID da conta e código da conta devem ser preenchidos +ACCOUNT_ALREADY_BIND_CHANNEL = A conta já está vinculada ao canal atual +PHONE_OR_CODE_EMPTY = Número do celular e código de verificação devem ser preenchidos +PHONE_NUMBER_EMPTY = Número do celular deve ser preenchido +PHONE_FORMAT_ERROR = Formato do número de celular incorreto +QUICK_AUTH_TOKEN_EMPTY = A identidade do jogador não pode ser autorizado com bug de Token +NOT_SURE_ALL_INTERACTIVE = Todos os diálogos ainda não foram confirmados +NOT_EXIST_SERVICE_DIALOG = Não há diálogos de negócios +ALREADY_LOGIN = Já está logado +LOGIN_FAILED = Falha no login +REMOVE_ACCOUNT_ERROR = Falha ao excluir a conta +PAYMENT_ERROR = Erro de pagamento +CREATE_LOCAL_ORDER_ERROR = Criação do pedido local falhou +EXIST_LOCAL_ORDER = Há um pedido local pendente +REMOVE_LOCAL_ORDER_ERROR = Falha ao remover o pedido local +NOT_IN_INIT = O sistema ainda não foi inicializado +NOT_IN_LOGIN = Você ainda não fez login +INIT_SYSTEM_ERROR = Ocorreu um erro ao inicializar o sistema +INIT_PREFERENCES_ERROR = Ocorreu um erro ao inicializar a configuração de preferências do usuário +EXIST_VIEW_NOT_CLOSE = Tem View pendente +CURRENT_VIEW_NOT_OPENED = View Atual ainda não foi aberto +FETCH_DESC_FAILED = Falha ao obter as informações do erro +NOT_EXIST_CHILD_ACCOUNT = A conta secundária não existe +NAME_OR_IDENTITY_EMPTY = O nome e o número do RG devem ser preenchidos +ACCOUNT_INFO_NOT_EXIST = As informações da conta não foram encontradas +ORDER_INFO_NOT_EXIST = As informações do pedido não foram encontradas +CHANNEL_NOT_SUPPORT_LOGIN = Este canal não suporta login +NOT_SUPPORT_OPERATOR = A operação atual não é suportada +PLEASE_RESTART_UPDATE = A atualização foi concluída. Reinicie o aplicativo +LOCALIZATION_UPDATE_RESTART = Atualização do arquivo de internacionalização concluída. Reinicie o aplicativo para ver as alterações +CONFIGRATION_UPDATE_RESTART = Atualização do arquivo de configuração concluída. Reinicie o aplicativo para ver as alterações +NOT_GENUINE_APP = Aplicativo duvidoso, baixe aplicativo de confiança +CANCEL_REAL_NAME = Cancelar a verificação do nome real +UNOFFICIAL_AUTHENTICATION = Método de login inválido +UNKNOWN = Erro desconhecido + + +enterGame = Entrar +policyPrivacy = Li e aceito a [Política de Privacidade] e o [Contrato do Usuário] +otherLogin = Outros métodos de login +realAuth = Verificação do nome real +quickLogin = Login rápido +phoneQuickLogin=Login rápido com número do celular local +pleaseInputPwd = Insira a senha +pleaseInputAccount = Insira a conta +getVerifyCode = Código de verificação +pleaseInputVerifyCode = Insira o código de verificação +pleaseInputPhone = Insira o número do celular +tip = Dica +authorization = Autorização +cancel = Cancelar +sure = Confirmar +pleaseInputIdentifyId = Insira seu número do RG +pleaseInputName = Insira seu nome +realNameRules = Conforme os requisitos do “Aviso para Impedir que Menores de Idade Joguem Online” publicado pela Administração Nacional de Imprensa e Publicação, os usuários não verificados não podem experimentar esse jogo. Por favor, conclua o registro do nome real o mais rápido possível +mainEnter = Entrar com conta principal +chileEnter = Entrar com conta secundária +checkedPrivace = Marque e aceite os termos da política antes de entrar no jogo +checkPermissionTip = Como você não autorizou a permissão de %, para ativar essa função, é necessário abrir “Configurações” > “Gerenciamento de permissões” e autorizar +realNameError = Você inseriu o nome em um formato incorreto +idCardError = Você inseriu o número do RG em um formato incorreto +phoneError = Formato do número de celular incorreto +vcodeError = Formato do código de verificação incorreto +pwdError = Formato da senha incorreto +getVcodeFailed = Falha ao obter o código de verificação + + +bindAccount = Vincular conta +bindAccountTip = A conta atual deste canal ainda não vincula um convidado +guestAccountTip = A conta atual é uma conta de convidado. Vincule a conta de terceiros o mais rápido possível para evitar a perda da conta +guestSwitchTip = Você é atualmente uma conta de convidado. Ao trocar de conta, você perderá o progresso do jogo da conta de convidado atual +bindingAccountTip = A conta atual já está vinculada à conta %s +accountId = ID da conta +accountCode = Código da conta +bindAccountIdPlaceholder = Insira o ID de recuperação da conta +bindAccountCodePlaceholder = Insira o código de recuperação da conta +bindIdCodedescription = Se você trocar de dispositivo, não se preocupe! Basta inserir o ID de recuperação e o código de recuperação atuais na página de troca de conta. Assim, você poderá continuar jogando com os dados de personagem da conta original +unbindIdCodedescription = Se você não estiver vinculado a nenhuma conta de terceiros, você poderá inserir o ID de recuperação da conta e o código de recuperação para trocar de conta +switchAccountTip = A conta deste canal que está usando agora já está vinculada a outra conta de convidado. Deseja trocar de conta? +switchAccount = Trocar de conta +switchAccountFailture = Falha ao trocar de conta +sameAccountTip = A conta atual está logada +loginSuccessTip = Usuário %s fez login com sucesso + +orderValidate = Verificando pedido... +startingPay = Começar a comprar +purchaseing = Comprando... + +[system] +TIMESTAMPT = 1701326040 +background_color_view = DFE0E5 +button_background_default_color = 008CD6 +button_background_unable_color = CFCFD1 +button_background_pressed_color = 009FEC +button_background_color = 008CD6 +button_border_color = D80020 +label_font_color = D80020 diff --git a/demo/src/main/assets/channel_config.json b/demo/src/main/assets/channel_config.json new file mode 100644 index 0000000..cda0c0b --- /dev/null +++ b/demo/src/main/assets/channel_config.json @@ -0,0 +1,64 @@ +{ + "accessDomainSign":"cfC0wG/8ffRLnAGCANdv03kKHLNLvD4bu0WYuoIIJ3LbGgRNJ6ogD0dZFHpcxK0KXhuGXLUg26ia9HNScreiIwICv+NnZ9tuVCjDMtT12GHOD527EpqrWaXhbj+kcXFkRoWW895lNFeZqjshbdOKEE/xRTKcta1SEVLgaTvXlpQNxZiFtNgEkm1XJKjy5squxh+fJad9BfdYaynaZAVJOeEQ795rHO/k/YfssZqa11/TMlcRdQis49MsYrSEqU2JvjwdbVPQYeb9A09jO7UKJPMNui3v6bMiteqSR3MD5DI4biZcQUfs5zESLwvFXNdGK+chaauNpizXfF1vADFzMQ==", + "accessOpenApiUrl":"http://global.hoolai.com/access_open_api", + "biCollectionApiUrl":"http://overseascollect.hoolai.com/tracking/", + "buildPackageId":1, + "channelInfo":{ + "betaType":0, + "biSwitch":"true", + "channel":"hoolai_global", + "channelName":"Access2.0", + "customHost":"", + "description":"", + "extendInfo":"{\"rsaPublicKey\":\"MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnVHxEG9YBnFnMgldw9ekme/8Jv4FdUdW3lAj7lDkd9IbwxqZ9P0RlXkSe6Pk3TUtdc6ZfiSEaYmORid+34+7L3pcgofWVQrX8fNlRiOYSrvbH1PIxSlhZPHdVf8Ovju2MzQDP0VZ1wzsvON2QVytHhqTAd4nJ5sDxvI2LvjvuDCrWf/TTRnSHRO1XLgtvw35M4mhcrNor4tvtccqh1blU8i5HoAIoQJfR+uWdXhGAbyK1iho5pTg12atMF1R6D8cmCCC056I9ONs85tWAsR5S2mKHAgl8U6HRZNT1pAb4KTUMBEQJB6Kf9Cak5NkGQlGg1KrVi60MVefGjWLukTKxQIDAQAB\",\"showIGameBuyPay\":false,\"showTT\":false,\"customText\":false,\"appsflyer_debug\":\"false\",\"showGG\":false,\"showmol\":false,\"showAihelp\":false,\"showUnipinPay\":false,\"showCatappultPay\":false,\"updateService\":\"1\",\"showhw\":false,\"showPaypalH5Pay\":false,\"showFBL\":false,\"showFW\":true,\"showPaypalPay\":false,\"oaidCertPem\":\"\",\"smileoneTest\":true,\"showJollyMaxH5Pay\":false,\"showIGameBuyWebPay\":false,\"showPagsmileH5Pay\":false,\"showYJM\":true,\"paypalH5Test\":true,\"adjust_debug\":\"false\",\"showGL\":true,\"vklType\":1,\"enablePgs\":true,\"adjustDebug\":false,\"paypalTest\":true,\"privacyAgreementUrl\":\"\",\"showSmileOneH5Pay\":false,\"showxl\":false,\"oaidAccess\":\"0\",\"isSandBox\":\"1\",\"uitype\":\"hoolai\",\"facebookNeedEmail\":false,\"googleClientId\":\"242883477374-7u4f0fvqg2a6jrd7ro6sat6ms2ledtml.apps.googleusercontent.com\",\"showCodashopPay\":false,\"showbp\":false,\"showmycard\":false,\"userPrivaceAgreementUrl\":\"\",\"showMobPay\":false,\"pgsProjectId\":\"111\",\"vkAppId\":\"0\",\"sdkType\":true,\"showcoda\":false,\"showGooglePay\":true,\"pagsmileTest\":true,\"privacyAgreement\":\"-1\",\"jollymaxTest\":true,\"showPayermaxH5Pay\":false}", + "fzSwitch":"false", + "icon":"/2023/07/21/fff3ef16-421b-4f74-8c3f-e52bfe3fa820.png", + "id":6403, + "newreyunAdAppid":"", + "nodeId":0, + "packageName":"com.hoolai.oversea.access", + "payClose":false, + "productId":6, + "registerClose":false, + "reyunAppid":"", + "reyunSwitch":"false", + "signId":415, + "userHttps":false, + "webUrl":"" + }, + "chargeDomainSign":"cfC0wG/8ffRLnAGCANdv03kKHLNLvD4bu0WYuoIIJ3LbGgRNJ6ogD0dZFHpcxK0KXhuGXLUg26ia9HNScreiIwICv+NnZ9tuVCjDMtT12GHOD527EpqrWaXhbj+kcXFkRoWW895lNFeZqjshbdOKEE/xRTKcta1SEVLgaTvXlpQNxZiFtNgEkm1XJKjy5squxh+fJad9BfdYaynaZAVJOeEQ795rHO/k/YfssZqa11/TMlcRdQis49MsYrSEqU2JvjwdbVPQYeb9A09jO7UKJPMNui3v6bMiteqSR3MD5DI4biZcQUfs5zESLwvFXNdGK+chaauNpizXfF1vADFzMQ==", + "chargeOpenApiUrl":"http://global.hoolai.com/charge_open_api", + "clientLogUrl":"http://global.hoolai.com/client_log", + "community_back":"http://community.hoolai.com/community_open_api", + "community_front":"http://community.hoolai.com/community_frontend", + "customGameid":"", + "customServiceApiUrl":"http://api.kf.hulai.com/v1/", + "developerUid":22633, + "forumOpenApiUrl":"http://global.hoolai.com/community_web", + "iconPosition":0, + "iosUpdateCDNUrl":"http://iossdkupdate-10009868.file.myqcloud.com", + "localType":"oversea", + "notifyUrl":"http://global.hoolai.com/charge_open_api/validate_hoolai_global.hl", + "productId":6, + "single":false, + "snid":300, + "targetSdkVersion":29, + "tripartitleAccount":{ + "androidCstAppId":"qqq", + "androidCstAppKey":"www", + "androidXgAppid":"2100076124", + "androidXgAppkey":"A3H6E4YW55ZA", + "fzDebug":"0", + "iosBuglyAppid":"900003937", + "iosCstAppId":"qq", + "iosCstAppKey":"qq", + "iosXgAppid":"2200140989", + "iosXgAppkey":"IY56MQ4PX69H", + "uid":"22633" + }, + "useBugly":true, + "useCustomerService":false, + "usePush":false, + "userService":false, + "version":"1" +} \ No newline at end of file diff --git a/demo/src/main/assets/develop.config b/demo/src/main/assets/develop.config new file mode 100644 index 0000000..3130c51 --- /dev/null +++ b/demo/src/main/assets/develop.config @@ -0,0 +1 @@ +Nylq2KwJc9zyQHHorv7V1k9OQlVmZRldJ7pYyX3SBh0n80IkSOshbI1GpwwpPnfN1pIzICsQe8SNodtNxtOFo+5+2GwSop3KQ9L8huM6Q+uLvbmV77hS+lHX9l3c4qqsgUqC/V8hTAWVFMNCCwjYZX5FZsPrsjsaimPJita5FoC5Xakapt5bP9fEf/t5eetVFxQRTCaU1kJvbaZc0lyP9IDEabEAgbecqx6ldQQxfucII8s2L/61ZosgzoGRuCizpNTyRotsLyeRJCDZup1MK8k2NqwngizHcndiAPi71cUSdoZN8HSCkruPvlIpVPE9hNE/1dx65rQSCuabsvKeHA== \ No newline at end of file diff --git a/demo/src/main/assets/sdk_version.txt b/demo/src/main/assets/sdk_version.txt new file mode 100644 index 0000000..88d9058 --- /dev/null +++ b/demo/src/main/assets/sdk_version.txt @@ -0,0 +1,3 @@ +版本:v3.3.3_google +更新时间:2023-10-25 + diff --git a/demo/src/main/java/com/hoolai/demo/GameActivity.java b/demo/src/main/java/com/hoolai/demo/GameActivity.java new file mode 100644 index 0000000..0478739 --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/GameActivity.java @@ -0,0 +1,806 @@ +package com.hoolai.demo; + +import android.app.Activity; +import android.app.AlertDialog; +import android.content.ClipboardManager; +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.res.Configuration; +import android.os.Bundle; +import android.text.TextUtils; +import android.util.Log; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View; +import android.widget.AdapterView; +import android.widget.ArrayAdapter; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ListView; +import android.widget.TextView; +import android.widget.Toast; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.hoolai.access.log.LogUtils; +import com.hoolai.demo.share.FacebookShareActivity; +import com.hoolai.demo.share.ShareActivity; +import com.hoolai.demo.utils.DataReportUtil; +import com.hoolai.fastsdk.demo.R; +import com.hoolai.open.fastaccess.channel.ExitCallback; +import com.hoolai.open.fastaccess.channel.FastSdk; +import com.hoolai.open.fastaccess.channel.InitCallback; +import com.hoolai.open.fastaccess.channel.PayCallback; +import com.hoolai.open.fastaccess.channel.PayParams; +import com.hoolai.open.fastaccess.channel.UserExtDataKeys; +import com.hoolai.open.fastaccess.channel.UserLoginResponse; +import com.hoolai.open.fastaccess.channel.callback.CommonCallback; +import com.hoolai.open.fastaccess.channel.callback.LoginCallback; +import com.hoolai.open.fastaccess.channel.callback.PayProduct; +import com.hoolai.open.fastaccess.channel.callback.ProductCallback; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; + +public class GameActivity extends Activity implements View.OnClickListener { + + public static final String TAG = "fastaccessGameDemo"; + + Button gameLoginBtn; + Button LanguageBtn; + private String msg; + private String callbackInfo = "0|6867485|nw_001"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + //SysUtil.setFuLLScreen(this); + setContentView(R.layout.hl_demo_login); + initData(); + } + + /** + * 设置回调信息 + */ + private void initData() { + //设置初始化回调 + InitCallback initCallback = new InitCallback() { + + @Override + public void onInitSuccess(String s) { + LogUtils.e("初始化成功:" + FastSdk.getChannelInfo().getInitParams()); + //sdk初始化成功 + //成功回调信息为json串,其中包含channel、biChannel,channelId和productId + //注意此时返回的channel不能用于server端登录验证时的channel,登录验证使用的是登录成功后回调的channel值 + setLoginCallback(); + paintLogin(); + + // funCode = 120(信鸽初始化,只有此功能必接,其他信鸽功能选接) + if (FastSdk.hasFunctionCode(120)) { + FastSdk.invokeSpecialFunction(120, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + if (objects[0] instanceof org.json.JSONObject) { + JSONObject result = (JSONObject) objects[0]; + } + } + }); + } + } + + @Override + public void onInitFail(String s) { + //sdk初始化失败,初始化失败不能调用FastSdk其他接口 + } + }; + FastSdk.onCreate(this, initCallback); + +// // 获取当前SDK版本号接口 + String sdkVersion = FastSdk.getSdkVersion(); + //Log.i(AbstractChannelInterfaceImpl.TAG, "当前SDK版本号:" + sdkVersion); + } + + /** + * 设置登录回调 + */ + private void setLoginCallback() { + FastSdk.setLoginCallback(new LoginCallback() { + @Override + public void onLoginFailed(String s) { + // onLoginFailed 登录失败(取消登录框)回调,并不是每个渠道都会有这个回调,所以建议在登录界面上放一个登陆按钮。 + //避免取消登录框以后无法登录的情况。 + Log.d(TAG, "登陆失败:" + s); + } + + @Override + public void onLoginSuccess(UserLoginResponse userLoginResponse) { + LogUtils.e(TAG, "登陆成功:" + JSON.toJSONString(userLoginResponse)); + try { + //模拟报送数据 + sendDataReport(userLoginResponse); + // 自定义指标打点报送 + //userExtData.put(UserExtDataKeys.ACTION, "zdy"); + //userExtData.put(UserExtDataKeys.PHYLUM, "5"); + //userExtData.put(UserExtDataKeys.CLASSFIELD, "zdy555"); + //FastSdk.setUserExtData(GameActivity.this, userExtData); + + //渠道登录成功 + Integer productId = FastSdk.getChannelInfo().getProductId(); + String accessToken = userLoginResponse.getAccessToken(); + Long uid = userLoginResponse.getUid();//接入平台的用户id + //channel对应的各渠道值查询http://access.hoolai.com/access_web/channelEnumList.do + String channel = userLoginResponse.getChannel();//渠道 + String channelUid = userLoginResponse.getChannelUid();//渠道的用户id + //将以上参数,传给产品的服务器端, 产品的服务器端去接入平台的服务器去验证当前用户登录是否有效, + //参考服务端接入http://access.hoolai.com/doc/2016/02/16/%E6%9C%8D%E5%8A%A1%E7%AB%AF%E6%8E%A5%E5%8 + + String name = userLoginResponse.getNickName(); + Integer channelId = FastSdk.getChannelInfo().getId(); + String biChannel = FastSdk.getChannelInfo().getBiChannel(); +// String udid = AccessHttpService.getUDID(); + // 如果是YSDK渠道,判断是QQ还是微信登录 + if (FastSdk.getChannelInfo().getChannel().equals("hoolaiysdk")) { + Boolean isQQ = (Boolean) userLoginResponse.getExtendInfo().get("isQQ"); + } + // 如果是胡莱官网渠道 + if (FastSdk.getChannelInfo().getChannel().equals("hoolai")) { + Boolean isBindPhone = (Boolean) userLoginResponse.getExtendInfo().get("isBindPhone"); + Boolean isBindEmail = (Boolean) userLoginResponse.getExtendInfo().get("isBindEmail"); + } + + msg = "hoolai_uid:" + uid + "\n名称:" + name + "\n产品ID:" + productId + "\n渠道ID:" + channelId + "\nchannelUid:" + channelUid + "\nchannel:" + channel + + "\nbiChannel:" + biChannel + "\nUDID:" ;//+ udid; + + // 验证通过进入游戏 + paintGame(); + } catch (Exception e) { + Log.e(TAG, "验证access出现异常", e); + } + } + + @Override + public void onLogout(Object... objects) { + //登出回调逻辑请重点关注,这里是接入完成以后,测试的时候最容易出问题的地方 + //调用FastSdk.logout(this,null)会触发该回调方法 + //某些sdk的浮标里有切换账号 注销账号的时候也会调用该接口 + + //这里请不要直接调用sdk的login接口 + //渠道要求在游戏运行的任何时刻均可正常登出帐号,所以游戏需要具备完善的登出逻辑, + //当收到渠道或者游戏自己的切换帐号或登出通知时,能中断游戏逻辑,清空游戏角色信息,返回登录界面,供用户切换其他帐号登录。 + //登出逻辑属于用户系统逻辑,认为随着游戏SDK的不断成熟,未来渠道均会提供切换帐号或登出逻辑。 + //目前有悬浮窗的渠道大部分会在悬浮窗中提供切换帐号功能,但剩余的渠道有部分仍需游戏自己处理, + //所以强烈建议游戏中添加登出或切换帐号的按钮。 + paintLogin(); + } + }); + } + + private void sendDataReport(UserLoginResponse userLoginResponse) { + try { + Map userExtData = new HashMap(); + userExtData.put(UserExtDataKeys.ROLE_ID, userLoginResponse.getChannelUid()); + userExtData.put(UserExtDataKeys.ROLE_NAME, userLoginResponse.getChannelUid()); + userExtData.put(UserExtDataKeys.ROLE_LEVEL, "1"); + userExtData.put(UserExtDataKeys.ROLE_ID, "11182"); + userExtData.put(UserExtDataKeys.PARTYNAME, "同福碗粥"); + userExtData.put(UserExtDataKeys.BALANCE, "0"); + userExtData.put(UserExtDataKeys.VIP, "0"); + userExtData.put(UserExtDataKeys.ZONE_ID, "1"); + userExtData.put(UserExtDataKeys.ZONE_NAME, "1区-雄霸天下"); + + //基础打点报送模拟 + DataReportUtil.basisData(this, userExtData); + //af_开头打点报送模拟 + DataReportUtil.afDataSend(this, userExtData); + //普通扩展数据模拟报送 + DataReportUtil.DataSend(this, userExtData); + + } catch (Exception e) { + LogUtils.e("report Data send Exception:" + e.getMessage()); + } + + } + + /** + * 启动登录界面 + */ + private void paintLogin() { + setContentView(R.layout.hl_demo_login); + gameLoginBtn = (Button) findViewById(R.id.gameLoginBtn); + + gameLoginBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 调用SDK登录功能 + FastSdk.login(GameActivity.this, null); + } + }); + + LanguageBtn = (Button) findViewById(R.id.gameLanguageBtn); + LanguageBtn.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // 国际化设置语言 + final Map supportLanguages = FastSdk.getSupportLanguages(GameActivity.this); +// LogUtils.i(JSON.toJSONString(supportLanguages)); + final String[] items = new String[supportLanguages.size()]; + int index = 0; + for (Map.Entry e : supportLanguages.entrySet()) { + items[index++] = e.getKey() + "--" + e.getValue(); + } + new AlertDialog.Builder(GameActivity.this) + .setTitle("语言切换") + .setItems(items, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + String item = items[i]; + FastSdk.switchLanguage(GameActivity.this, item.split("--")[0]); + } + }).show(); + } + }); + } + + /** + * 启动游戏主界面 + */ + private void paintGame() { + this.setContentView(R.layout.hl_demo_game); + TextView textView = (TextView) this.findViewById(R.id.showMessage); + textView.setText(msg); + this.findViewById(R.id.btnCopy).setOnClickListener(this); + this.findViewById(R.id.btnCallbackLength).setOnClickListener(this); + this.findViewById(R.id.btnCallbackText).setOnClickListener(this); + this.findViewById(R.id.btnGetMetaData).setOnClickListener(this); + this.findViewById(R.id.btnGetBalance).setOnClickListener(this); + this.findViewById(R.id.btnEnterPay).setOnClickListener(this); + this.findViewById(R.id.btnLogout).setOnClickListener(this); + this.findViewById(R.id.btnExitSDK).setOnClickListener(this); + this.findViewById(R.id.btnOpenService).setOnClickListener(this); + this.findViewById(R.id.crash).setOnClickListener(this); + this.findViewById(R.id.anr).setOnClickListener(this); + this.findViewById(R.id.error).setOnClickListener(this); + findViewById(R.id.btnGetSpecialFuncCodes).setOnClickListener(this); + findViewById(R.id.btnInvokeSpecialFunc).setOnClickListener(this); + findViewById(R.id.btnFbShare).setOnClickListener(this); + + Button btnAccountManage = (Button) this.findViewById(R.id.btnAccountManage); + + if (FastSdk.hasAccountManage()) { + btnAccountManage.setVisibility(View.VISIBLE); + btnAccountManage.setOnClickListener(this); + } else { + btnAccountManage.setVisibility(View.GONE); + } + } + + @Override + public void onClick(View v) { + int id = v.getId(); + if (id == R.id.btnCopy) {// 复制 + copyClipboard(msg); + } else if (id == R.id.btnCallbackLength) { + setGameCallback(); + } else if (id == R.id.btnCallbackText) { + setGameCallbackText(); + } else if (id == R.id.btnOpenService) { + //打开客服(可选) + FastSdk.customerService(this); + } else if (id == R.id.btnGetMetaData) { + getMetaData(); + } else if (id == R.id.btnEnterPay) {// 定额支付 + getProductInfo(); + } else if (id == R.id.btnExitSDK) {// 退出游戏 + exit(); + } else if (id == R.id.btnGetSpecialFuncCodes) { + getSpecialFuncCodes(); + } else if (id == R.id.btnInvokeSpecialFunc) { + invokeSpecialFun(); + } else if (id == R.id.btnAccountManage) {// 账号管理 + FastSdk.accountManage(GameActivity.this, null); + } else if (id == R.id.btnLogout) { + FastSdk.logout(this, ""); + } else if (id == R.id.btnFbShare) { + startActivity(new Intent(this, ShareActivity.class)); + } else if (id == R.id.crash) { + int b = 100 / 0; + int a = 1 / 0; + LogUtils.i("崩溃异常"); + } else if (id == R.id.anr) { + try { + Thread.sleep(100000); + startActivity(new Intent(this, FacebookShareActivity.class)); + LogUtils.i("出现ANR"); + } catch (InterruptedException e) { + e.printStackTrace(); + } + } else if (id == R.id.error) { + try { + int a = 2 / 0; + } catch (Exception e) { + FastSdk.crashReport(this, e); + LogUtils.i("捕捉异常"); + e.printStackTrace(); + } + } + } + + /** + * 特殊功能吗调用 + */ + private void invokeSpecialFun() { + final EditText numberText = new EditText(GameActivity.this); + numberText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); + numberText.setHint("FunctionCode"); +// com.google.android.gms.measurement.PackageMeasurementTaskService +// com.google.android.gms.clearcut.uploader.QosUploaderService + new AlertDialog.Builder(GameActivity.this).setTitle("请输入功能码").setIcon(android.R.drawable.ic_dialog_info).setView(numberText) + .setPositiveButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String moneyStr = numberText.getText().toString(); + if (TextUtils.isEmpty(moneyStr)) { + Toast.makeText(GameActivity.this, "请输入功能码", Toast.LENGTH_SHORT).show(); + return; + } + int funCode = Integer.parseInt(moneyStr); + specialInvoke(funCode); + } + }).setNegativeButton("取消", null).show(); + } + + /** + * 查看该渠道都有那些特殊功能 + */ + private void getSpecialFuncCodes() { + for (Map.Entry entry : FastSdk.getSpecialFunctionCodes().entrySet()) { + LogUtils.w("FunctionCode=" + entry.getKey() + ",Desc=" + entry.getValue()); + Toast.makeText(GameActivity.this, "FunCode=" + entry.getKey() + ",Desc=" + entry.getValue(), Toast.LENGTH_SHORT).show(); + } + } + + /** + * 获取商品信息 + */ + private void getProductInfo() { + //TODO:注意此处回调在子线程,如需操作UI界面需要切换线程 + FastSdk.getProductInfo(null, new ProductCallback() { + @Override + public void onProductInfo(final List list) { + final List arrayList = new ArrayList(); + for (PayProduct payProduct : list) { + arrayList.add(JSON.toJSONString(payProduct)); + } + LogUtils.i("Demo收到的商品信息返回arrayList:" + arrayList.toString()); + runOnUiThread(new Runnable() { + + @Override + public void run() { + AlertDialog.Builder builder = new AlertDialog.Builder(GameActivity.this); + ListView listView = new ListView(GameActivity.this); + listView.setAdapter(new ArrayAdapter(GameActivity.this, android.R.layout.simple_list_item_1, android.R.id.text1, arrayList)); + listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { + @Override + public void onItemClick(AdapterView parent, View view, int position, long id) { +// Toast.makeText(MainActivity.this,"你点击了支付"+position,Toast.LENGTH_SHORT).show(); + String payInfo = (String) arrayList.get(position); + PayProduct payProduct = JSONObject.parseObject(payInfo, PayProduct.class); + PayParams payParams = new PayParams(); + payParams.setItemId(payProduct.getItemId()); + payParams.setItemName(payProduct.getPriceTag()); + payParams.setAmount(Integer.parseInt(payProduct.getPrice())); + payParams.setCount(1); + payParams.setCallbackInfo(callbackInfo + "支付扩展信息"); + payParams.addExtendParam("isSupportThirdPayment", "true"); + payParams.addExtendParam("payType", "1"); + payParams.addExtendParam("currency", payProduct.getPriceLocal()); + FastSdk.pay(GameActivity.this, payParams, new PayCallback() { + @Override + public void onSuccess(String s) { + LogUtils.i("Demo 提示:支付成功"); + Toast.makeText(GameActivity.this, "Demo 提示:支付成功" + s, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onFail(String s) { + LogUtils.i("Demo 提示:支付失败"); + Toast.makeText(GameActivity.this, "Demo 提示:支付失败" + s, Toast.LENGTH_SHORT).show(); + } + }); + } + }); + builder.setView(listView); + builder.show(); + } + }); + } + + @Override + public void onFail(String str) { + Context context = GameActivity.this; + StringBuilder stringBuilder = new StringBuilder(); + stringBuilder.append("商品列表获取失败:"); + stringBuilder.append(str); + Toast.makeText(context, stringBuilder.toString(), Toast.LENGTH_SHORT).show(); + + } + }); + } + + /** + * 获取声明在AndroidManifest.xml中meta标签中value值,注意value为数字获取为空 + */ + private void getMetaData() { + final EditText et = new EditText(GameActivity.this); + et.setHint("请输入Key"); + new AlertDialog.Builder(GameActivity.this).setTitle("请输入").setView(et).setPositiveButton("获取", new DialogInterface.OnClickListener() { + + @Override + public void onClick(DialogInterface dialog, int which) { + String key = et.getText().toString(); + if (TextUtils.isEmpty(key)) { + Toast.makeText(GameActivity.this, "请输入Key", Toast.LENGTH_SHORT).show(); + return; + } + String result = FastSdk.getMetaDataConfig(GameActivity.this, key); + Toast.makeText(GameActivity.this, result, Toast.LENGTH_SHORT).show(); + } + }).show(); + } + + /** + * 设置回调内容 + */ + private void setGameCallbackText() { + final EditText numberText = new EditText(GameActivity.this); + numberText.setInputType(android.text.InputType.TYPE_CLASS_TEXT); + numberText.setHint("回调信息"); + numberText.setText(callbackInfo); + + new AlertDialog.Builder(GameActivity.this).setTitle("请输入").setIcon(android.R.drawable.ic_dialog_info).setView(numberText) + .setPositiveButton("设置", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String str = numberText.getText().toString(); + if (TextUtils.isEmpty(str)) { + return; + } + callbackInfo = str; + } + }).setNegativeButton("取消", null).show(); + } + + /** + * 设置支付回调 + */ + private void setGameCallback() { + final EditText numberText = new EditText(GameActivity.this); + numberText.setInputType(android.text.InputType.TYPE_CLASS_NUMBER); + numberText.setHint("回调信息长度"); + numberText.setText("" + callbackInfo.length()); + + new AlertDialog.Builder(GameActivity.this).setTitle("请输入").setIcon(android.R.drawable.ic_dialog_info).setView(numberText) + .setPositiveButton("设置", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + String str = numberText.getText().toString(); + if (TextUtils.isEmpty(str)) { + return; + } + String result = "A"; + for (int i = 1; i < Integer.parseInt(str); i++) { + result += new Random().nextInt(10); + } + callbackInfo = result; + } + }).setNegativeButton("取消", null).show(); + } + + /** + * 复制到粘贴板 + * + * @param msg + */ + private void copyClipboard(String msg) { + ClipboardManager cmb = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); + cmb.setText(msg); + Toast.makeText(GameActivity.this, "成功复制到剪切板!", Toast.LENGTH_SHORT).show(); + } + + /** + * 渠道特殊功能码调用 + * + * @param funCode + */ + private void specialInvoke(int funCode) { + /** + * 华为三方绑定 + */ + if (FastSdk.getChannelInfo().getChannel().equals("huawei")) { + if (FastSdk.hasFunctionCode(funCode)) { + FastSdk.invokeSpecialFunction(funCode, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + String msg = ""; + if (objects[0] instanceof String) { + msg = (String) objects[0]; + } + Toast.makeText(GameActivity.this, "绑定结果:" + i + ",b:" + b + ", msg:" + msg, Toast.LENGTH_SHORT).show(); + } + }); + } + } + + /** + * 冰鸟 + */ + if (FastSdk.getChannelInfo().getChannel().equals("bnoversea")) { + FastSdk.invokeSpecialFunction(funCode, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + + if (i == 121) { + if (b) { + Toast.makeText(GameActivity.this, "安卓冰鸟海外邮箱绑定成功!", Toast.LENGTH_SHORT).show(); + } else if (objects != null) { + Toast.makeText(GameActivity.this, "安卓冰鸟海外万能接口接入错误,请检查!", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(GameActivity.this, "安卓冰鸟海外邮箱绑定失败!", Toast.LENGTH_SHORT).show(); + } + } + + if (i == 122) { + if (b) { + Toast.makeText(GameActivity.this, "安卓冰鸟海外fb share成功!", Toast.LENGTH_SHORT).show(); + } else if (objects != null) { + Toast.makeText(GameActivity.this, "安卓冰鸟海外万能接口接入错误,请检查!", Toast.LENGTH_SHORT).show(); + } else { + Toast.makeText(GameActivity.this, "安卓冰鸟海外fb share失败!", Toast.LENGTH_SHORT).show(); + } + } + + } + }); + } + //信鸽推送 + if (FastSdk.hasFunctionCode(funCode)) { + FastSdk.invokeSpecialFunction(funCode, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + String msg = ""; + if (objects[0] instanceof String) { + msg = (String) objects[0]; + } + LogUtils.i("信鸽调用类型:" + funCode); + LogUtils.i("信鸽调用结果:" + msg); + } + }); + } + + //Aihelp自定义导入数据到CRM系统(非必接) + if (FastSdk.hasFunctionCode(funCode)) { + Map userExtData = new HashMap(); + userExtData.put("ROLE_ID", "123123"); + userExtData.put("ROLE_NAME", "J"); + userExtData.put("SERVER_ID", "101"); + userExtData.put("VIP", "15"); + userExtData.put("ROLE_LEVEL", "200"); + userExtData.put("ZONE_NAME", "基地1"); + userExtData.put("BALANCE", "10"); + Boolean importCrm = true;//是否导入Ai-help CRM系统,ture导入,false不导入 + FastSdk.invokeSpecialFunction(funCode, userExtData, importCrm); + } + } + + private void doPay(int amount, String itemName, String itemId) { + // 调用SDK支付功能 + PayParams payParams = new PayParams(); + payParams.setItemId(itemId); + payParams.setItemName(itemName); + payParams.setAmount(amount); + payParams.setCount(123); + payParams.setCallbackInfo(callbackInfo); + //payParams.addExtendParam(PayParams.NOTIFY_URL_KEY, "http://a.b.com");//游戏自定义支付回调地址,不设置默认使用后台配置 + payParams.addExtendParam("isSupportThirdPayment", "false");//是否显示三方支付 true显示 false不显示 + + FastSdk.pay(GameActivity.this, payParams, new PayCallback() { + @Override + public void onSuccess(String param) { + //支付成功后,该回调不能用作判定支付成功,只能判定支付客户端行为完成 + //支付是否成功要以服务端回调为准 + } + + @Override + public void onFail(String param) { + //有可能是支付失败,取消支付界面等情况 + //支付失败(取消支付框回调)回调,并不是每个渠道都会有这个回调,所以建议不要完全依赖该回调实现一些功能。 + //(例如只有收到支付失败回调才会关闭loding框或者蒙板,这样会导致一些渠道会有问题) + } + }); + } + + private void exit() { + // 调用SDK退出功能 + FastSdk.exit(GameActivity.this, null, new ExitCallback() { + @Override + public void onExitSuccess(String s) { + // 渠道存在退出界面,如91、360等,此处只需进行退出逻辑即可,无需再弹游戏退出界面 + // 实现退出逻辑,请勿直接照搬demo +// System.exit(0); + moveTaskToBack(true); + finish(); + // // 退出程序 + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(Intent.CATEGORY_HOME); + intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + startActivity(intent); + android.os.Process.killProcess(android.os.Process.myPid()); + +// FastSdk.exit(); + } + + @Override + public void onCustomExit(final String s) { + // 渠道不存在退出界面,如百度移动游戏等,此时需在此处弹出游戏退出确认界面,否则会出现渠道审核不通过情况 + // 游戏实现自己的退出界面 ,实现退出逻辑,请勿直接照搬demo + // 根据需要加二次确认框 + new AlertDialog.Builder(GameActivity.this).setTitle("游戏二次确认").setNegativeButton("取消", null).setPositiveButton("确定", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + onExitSuccess(s); + } + }).setCancelable(false).show(); + } + }); + } + + @Override + protected void onStart() { + super.onStart(); + FastSdk.onStart(this); + } + + @Override + protected void onRestart() { + super.onRestart(); + FastSdk.onRestart(this); + } + + @Override + protected void onResume() { + super.onResume(); + FastSdk.onResume(this); + } + + @Override + protected void onPause() { + super.onPause(); + FastSdk.onPause(this); + } + + @Override + protected void onStop() { + super.onStop(); + FastSdk.onStop(this); + } + + @Override + protected void onDestroy() { + try { + super.onDestroy(); + FastSdk.onDestroy(this); + } catch (Exception e) { + Log.e(TAG, "onDestroy occured exception", e); + } + // funCode = 120(信鸽初始化,如果使用信鸽功能只有此功能必接,其他信鸽功能选接) + if (FastSdk.hasFunctionCode(120)) { + FastSdk.invokeSpecialFunction(120, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + if (objects[0] instanceof org.json.JSONObject) { + JSONObject result = (JSONObject) objects[0]; + } + } + }); + } + // funCode = 121(XG更新标签) + if (FastSdk.hasFunctionCode(121)) { + String[] addTags = {"tag1", "tag2"};//要添加的标签 + FastSdk.invokeSpecialFunction(121, (Object) addTags); + } + // funCode = 122(XG删除标签) + if (FastSdk.hasFunctionCode(122)) { + String[] delTags = {"tag1", "tag2"};//要删除的标签 + FastSdk.invokeSpecialFunction(122, (Object) delTags); + } + // funCode = 123(XG查询标签) + if (FastSdk.hasFunctionCode(123)) { + FastSdk.invokeSpecialFunction(123, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + if (objects[0] instanceof String) { + String result = (String) objects[0]; + } + } + }); + } + // funCode = 124(XG清空标签) + if (FastSdk.hasFunctionCode(124)) { + FastSdk.invokeSpecialFunction(124); + } + // funCode = 125(XG取消推送) + if (FastSdk.hasFunctionCode(125)) { + FastSdk.invokeSpecialFunction(125); + } + // funCode = 126(XG上传日志) + if (FastSdk.hasFunctionCode(126)) { + FastSdk.invokeSpecialFunction(126); + } + // funCode = 127(XG获取token) + if (FastSdk.hasFunctionCode(127)) { + FastSdk.invokeSpecialFunction(127, new CommonCallback() { + @Override + public void process(int i, boolean b, Object... objects) { + if (objects[0] instanceof String) { + String token = (String) objects[0]; + } + } + }); + } + // funCode = 128(XG设置CRM用户信息) + if (FastSdk.hasFunctionCode(128)) { + Map map = new HashMap<>(); + Boolean importCrm = true; + FastSdk.invokeSpecialFunction(128, map, importCrm); + } + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + FastSdk.onConfigurationChanged(this, newConfig); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + FastSdk.onActivityResult(this, requestCode, resultCode, intent); + } + + @Override + protected void onNewIntent(Intent intent) { + super.onNewIntent(intent); + FastSdk.onNewIntent(intent); + } + + @Override + protected void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + FastSdk.onSaveInstanceState(outState); + } + + @Override + public void onBackPressed() { + FastSdk.onBackPressed(); + exit(); + } + + @Override + public boolean dispatchKeyEvent(KeyEvent event) { + return FastSdk.dispatchKeyEvent(event) || super.dispatchKeyEvent(event); + } + + @Override + public boolean dispatchTouchEvent(MotionEvent ev) { + return FastSdk.dispatchTouchEvent(ev) || super.dispatchTouchEvent(ev); + } + + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + FastSdk.onRequestPermissionsResult(requestCode, permissions, grantResults); + } + +} diff --git a/demo/src/main/java/com/hoolai/demo/GameApplication.java b/demo/src/main/java/com/hoolai/demo/GameApplication.java new file mode 100644 index 0000000..b913183 --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/GameApplication.java @@ -0,0 +1,73 @@ +package com.hoolai.demo; + +import android.app.Application; +import android.content.Context; +import android.content.res.Configuration; +import android.os.Process; +import android.text.TextUtils; + + +import com.hoolai.open.fastaccess.channel.HoolaiApplication; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileReader; + +public class GameApplication extends Application { + + @Override + public void onCreate() { + super.onCreate(); + //如使用了Multidex 需要指定 hoolaisdk相关代码到主dex中 + //-keep com.hoolai.** + HoolaiApplication.onApplicationCreate(this); + } + + @Override + public void onTerminate() { + super.onTerminate(); + HoolaiApplication.onApplicationTerminate(this); + } + + @Override + public void onConfigurationChanged(Configuration newConfig) { + super.onConfigurationChanged(newConfig); + HoolaiApplication.onConfigurationChanged(this, newConfig); + + } + + @Override + protected void attachBaseContext(Context context) { + super.attachBaseContext(context); + HoolaiApplication.attachBaseContext(this, context); + + } + + + /** + * 多进程判断,sdk已经做了处理,游戏可不做处理 + * @param application + * @return + */ + public static boolean getProcessPerform(Application application){ + String processName = getProcessName(); + int pid = Process.myPid(); + if(!TextUtils.isEmpty(processName) && processName.equals(application.getPackageName())){ + return true; + } + return false; + } + + public static String getProcessName() { + try { + File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline"); + BufferedReader mBufferedReader = new BufferedReader(new FileReader(file)); + String processName = mBufferedReader.readLine().trim(); + mBufferedReader.close(); + return processName; + } catch (Exception e) { + e.printStackTrace(); + return null; + } + } +} diff --git a/demo/src/main/java/com/hoolai/demo/share/FacebookShareActivity.java b/demo/src/main/java/com/hoolai/demo/share/FacebookShareActivity.java new file mode 100644 index 0000000..7a789b8 --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/share/FacebookShareActivity.java @@ -0,0 +1,229 @@ +package com.hoolai.demo.share; + +import android.annotation.TargetApi; +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.text.TextUtils; +import android.view.View; +import android.widget.EditText; +import android.widget.Toast; +import com.hoolai.access.log.LogUtils; +import com.hoolai.fastsdk.demo.R; +import com.hoolai.open.fastaccess.channel.FastSdk; +import com.hoolai.open.fastaccess.channel.callback.FacebookShareCallback; +import com.hoolai.open.fastaccess.channel.callback.FacebookShareType; +import com.hoolai.open.fastaccess.channel.pojo.FaceBookShareData; + +/** + * @author gaor + * @className: FacebookShareActivity + * @description TODO 描述 Facebook分享 + * @date 2022/1/17 17:17 + */ +public class FacebookShareActivity extends Activity { + + private static final int FILE_SELECT_IMAGE_CODE = 010110; + private static final int REQUEST_WRITE_STORAGE_PERMISSION = 010111; + private static final int REQUEST_CODE_OTHER_FILE = 010112; + private EditText et; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.share_facebook); + et = findViewById(R.id.et_text_fb); + et.setText("http://www.baidu.com"); + + requestPermission(); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (resultCode == RESULT_OK ){ + Uri uri = data.getData(); + LogUtils.e("文件分享路径:"+uri.toString()); + if (requestCode == FILE_SELECT_IMAGE_CODE){ + shareImage(uri.toString()); + } + + if (requestCode == REQUEST_CODE_OTHER_FILE){ + shareVideo(uri.toString()); + } + } + } + + /** + * FacebookShareType:要分享的类型,FacebookShareType.LINK + * String url:要分享的链接(必须是网络连接) + * String quote:引文 + * String hashTag:必须以#号开头,类似微博的标签/话题(非必传) + * FacebookShareCallback:回调 + * @param view + */ + public void facebookShareLink(View view){ + String url = et.getText().toString(); + if (TextUtils.isEmpty(url)){ + Toast.makeText(FacebookShareActivity.this,"分享连接不能为空!",Toast.LENGTH_SHORT).show(); + return; + } + FaceBookShareData data = new FaceBookShareData(); + data.setHashTag("#"+this.getAppName(this)); + data.setQuote(url); + data.setUrl(url); + FastSdk.facebookShare(FacebookShareType.LINK,data,new FacebookShareCallback() { + @Override + public void onSuccess(String s) { + Toast.makeText(FacebookShareActivity.this,"分享成功"+s,Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCancel() { + Toast.makeText(FacebookShareActivity.this,"取消分享",Toast.LENGTH_SHORT).show(); + } + + @Override + public void onError(String s) { + Toast.makeText(FacebookShareActivity.this,"分享失败"+s,Toast.LENGTH_SHORT).show(); + } + }); + } + + public void facebookShareImage(View view){ + openFileChooser(); + } + + /** + * FacebookShareType:要分享的类型, + * FacebookShareType.IMAGE + * String photoUrl:要分享的图片地址(必须是本地图片路径),可分享1-6张图片 + * String hashTag:必须以#号开头,类似微博的标签/话题(非必传) + * FacebookShareCallback:回调 + * @param path + */ + public void shareImage(String path){ + if (path==null){ + Toast.makeText(this,"Uri路径不能为空!",Toast.LENGTH_SHORT).show(); + return; + } + FaceBookShareData data = new FaceBookShareData(); + +// List listBitMap = new ArrayList<>(); +// listBitMap.add(path); + + data.setHashTag("#demo"); +// data.setBitMap(listBitMap); + data.addBitMap(path); +// data.addBitMap(photoUrl1); +// data.addBitMap(photoUrl2);//最多可添加6个图片... + FastSdk.facebookShare(FacebookShareType.IMAGE,data,new FacebookShareCallback() { + @Override + public void onSuccess(String s) { + Toast.makeText(FacebookShareActivity.this,"分享成功"+s,Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCancel() { + Toast.makeText(FacebookShareActivity.this,"取消分享",Toast.LENGTH_SHORT).show(); + } + + @Override + public void onError(String s) { + Toast.makeText(FacebookShareActivity.this,"分享失败"+s,Toast.LENGTH_SHORT).show(); + } + }); + } + + + /** + * 选择图片 + */ + private void openFileChooser() { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + intent.addCategory(Intent.CATEGORY_OPENABLE); + try { + startActivityForResult(Intent.createChooser(intent, "选择文件"), FILE_SELECT_IMAGE_CODE); + overridePendingTransition(0, 0); + } catch (Exception ex) { + LogUtils.e("openFile Chooser:"+ex); + // Potentially direct the user to the Market with OnProgressChangeListener Dialog + Toast.makeText(this, "请先安装文件管理器", Toast.LENGTH_SHORT).show(); + } + } + + /** + * 选择视频 + * @param view + */ + public void facebookShareVideo(View view){ + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.addCategory(Intent.CATEGORY_OPENABLE); + intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);// 支持多选 + intent.setType("*/*"); + startActivityForResult(Intent.createChooser(intent, "选择文件"), REQUEST_CODE_OTHER_FILE); + } + + + @TargetApi(24) + public void shareVideo(String path){ + if (path == null){ + Toast.makeText(this,"文件路径不能为空!"+path,Toast.LENGTH_SHORT).show(); + return; + } + FaceBookShareData data = new FaceBookShareData(); + data.setHashTag("#"+this.getAppName(this)); + data.setVideoPath(path); + FastSdk.facebookShare(FacebookShareType.VIDEO,data,new FacebookShareCallback() { + @Override + public void onSuccess(String s) { + Toast.makeText(FacebookShareActivity.this,"分享成功"+s,Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCancel() { + Toast.makeText(FacebookShareActivity.this,"取消分享",Toast.LENGTH_SHORT).show(); + } + + @Override + public void onError(String s) { + Toast.makeText(FacebookShareActivity.this,"分享失败"+s,Toast.LENGTH_SHORT).show(); + } + }); + } + + + /** + * 获取应用程序名称 + */ + public static synchronized String getAppName(Context context) { + try { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo( + context.getPackageName(), 0); + int labelRes = packageInfo.applicationInfo.labelRes; + return context.getResources().getString(labelRes); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + + /** + * 获取存储权限 + */ + private void requestPermission() { + /*if (this.checkSelfPermission(FacebookShareActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_WRITE_STORAGE_PERMISSION); + } else { + Toast.makeText(this, "缺少文件读写权限,可能会造成无法分享文件", Toast.LENGTH_SHORT).show(); + } + }*/ + } +} diff --git a/demo/src/main/java/com/hoolai/demo/share/ShareActivity.java b/demo/src/main/java/com/hoolai/demo/share/ShareActivity.java new file mode 100644 index 0000000..a9e5dfa --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/share/ShareActivity.java @@ -0,0 +1,139 @@ +package com.hoolai.demo.share; + +import android.app.Activity; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageInfo; +import android.content.pm.PackageManager; +import android.net.Uri; +import android.os.Bundle; +import android.provider.MediaStore; +import android.view.View; +import android.widget.Toast; +import com.hoolai.access.open.fastaccess.utils.Logger; +import com.hoolai.fastsdk.demo.R; +import com.hoolai.open.fastaccess.channel.FastSdk; +import com.hoolai.open.fastaccess.channel.callback.FacebookShareCallback; +import com.hoolai.open.fastaccess.channel.callback.FacebookShareType; +import com.hoolai.open.fastaccess.channel.pojo.FaceBookShareData; +import java.util.ArrayList; +import java.util.List; + +/** + * @author gaor + * @date 2023/12/14 20:22 + * @description 分享 + */ +public class ShareActivity extends Activity { + + private static int REQUEST_PHOTO_PICKER_SINGLE_SELECT = 1008611; + private static int REQUEST_VIDEO_PICKER_SINGLE_SELECT = 1008612; + private FacebookShareCallback facebookShare = new FacebookShareCallback() { + @Override + public void onSuccess(String s) { + Toast.makeText(ShareActivity.this, "分享成功" + s, Toast.LENGTH_SHORT).show(); + } + + @Override + public void onCancel() { + Toast.makeText(ShareActivity.this, "取消分享", Toast.LENGTH_SHORT).show(); + } + + @Override + public void onError(String s) { + Toast.makeText(ShareActivity.this, "分享失败" + s, Toast.LENGTH_SHORT).show(); + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_share); + + } + + public void shareText(View view) { + String title = "系统文字分享"; + String contentText = "分享内容"; + com.hoolai.open.fastaccess.channel.FastSdk.invokeSpecialFunction(112, title, contentText); + } + + public void shareVideo(View view) { + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + startActivityForResult(intent, REQUEST_VIDEO_PICKER_SINGLE_SELECT); + } + + public void sharePhoto(View view) { + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + startActivityForResult(intent, REQUEST_PHOTO_PICKER_SINGLE_SELECT); + } + + public void fbShareText(View view) { + FaceBookShareData data = new FaceBookShareData(); + data.setHashTag("#" + getAppName(this)); + data.setQuote("http://www.baidu.com"); + data.setUrl("http://www.baidu.com"); + FastSdk.facebookShare(FacebookShareType.LINK, data, facebookShare); + } + + public void fbPhotoShare(View view) { + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + startActivityForResult(intent, REQUEST_PHOTO_PICKER_SINGLE_SELECT); + } + + public void fbShareVideo(View view) { + Intent intent = new Intent(MediaStore.ACTION_PICK_IMAGES); + startActivityForResult(intent, REQUEST_VIDEO_PICKER_SINGLE_SELECT); + } + + @Override + protected void onActivityResult(int requestCode, int resultCode, Intent data) { + super.onActivityResult(requestCode, resultCode, data); + if (data == null) { + return; + } + Uri uri = data.getData(); + if (uri == null) { + return; + } + Logger.i("shareActivity onActivityResult:" + uri.toString()); + if (requestCode == REQUEST_PHOTO_PICKER_SINGLE_SELECT) { + /* String title = "系统图片分享"; + com.hoolai.open.fastaccess.channel.FastSdk.invokeSpecialFunction(113, title, data1);*/ + + //facebook图片分享 + FaceBookShareData faceBookShareData = new FaceBookShareData(); + List listBitMap = new ArrayList<>(); + listBitMap.add(uri.toString()); + faceBookShareData.setHashTag("#demo"); + faceBookShareData.setBitMap(listBitMap); + FastSdk.facebookShare(FacebookShareType.IMAGE, faceBookShareData, facebookShare); + + } else if (requestCode == REQUEST_VIDEO_PICKER_SINGLE_SELECT) { + //系统分享视频 + //com.hoolai.open.fastaccess.channel.FastSdk.invokeSpecialFunction(115, "系统视频分享", uri); + + //Facebook视频分享 + FaceBookShareData faceBookShareData = new FaceBookShareData(); + faceBookShareData.setHashTag("#" + getAppName(this)); + faceBookShareData.setVideoPath(uri.toString()); + FastSdk.facebookShare(FacebookShareType.VIDEO, faceBookShareData, facebookShare); + } + } + + /** + * 获取应用程序名称 + */ + public static synchronized String getAppName(Context context) { + try { + PackageManager packageManager = context.getPackageManager(); + PackageInfo packageInfo = packageManager.getPackageInfo( + context.getPackageName(), 0); + int labelRes = packageInfo.applicationInfo.labelRes; + return context.getResources().getString(labelRes); + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/demo/src/main/java/com/hoolai/demo/utils/DataReportUtil.java b/demo/src/main/java/com/hoolai/demo/utils/DataReportUtil.java new file mode 100644 index 0000000..af8d0e5 --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/utils/DataReportUtil.java @@ -0,0 +1,66 @@ +package com.hoolai.demo.utils; + +import android.content.Context; + +import com.hoolai.open.fastaccess.channel.FastSdk; +import com.hoolai.open.fastaccess.channel.UserExtDataKeys; + +import java.util.HashMap; +import java.util.Map; + +/** + * 模拟数据报送 + */ +public class DataReportUtil { + + /** + * //基础打点模拟报送 必须 + * @param context + * @param userExtData + */ + public static void basisData(Context context, Map userExtData){ + // 创建角色 + userExtData.put(UserExtDataKeys.ACTION, UserExtDataKeys.ACTION_CREATE_ROLE); + FastSdk.setUserExtData(context, new HashMap(userExtData)); + // 角色升级 + userExtData.put(UserExtDataKeys.ACTION, UserExtDataKeys.ACTION_LEVEL_UP); + FastSdk.setUserExtData(context, new HashMap(userExtData)); + + // 进入服务器 + userExtData.put(UserExtDataKeys.ACTION, UserExtDataKeys.ACTION_ENTER_SERVER); + FastSdk.setUserExtData(context, new HashMap(userExtData)); + } + + /** + * af数据模拟报送 + * @param context + * @param userExtData + */ + public static void afDataSend(Context context, Map userExtData){ + userExtData.put(UserExtDataKeys.ACTION,"af_vip_1"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "vip_1"); + FastSdk.setUserExtData(context, userExtData); + + userExtData.put(UserExtDataKeys.ACTION,"af_gethero_14201"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "af_gethero_14201"); + FastSdk.setUserExtData(context, userExtData); + + userExtData.put(UserExtDataKeys.ACTION,"af_seiya_recharge4999"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "seiya_recharge49.99"); + FastSdk.setUserExtData(context, userExtData); + + userExtData.put(UserExtDataKeys.ACTION,"af_click_0dot99"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "click_0dot99"); + FastSdk.setUserExtData(context, userExtData); + } + + public static void DataSend(Context context, Map userExtData) { + userExtData.put(UserExtDataKeys.ACTION,"guild_join"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "guild_join"); + FastSdk.setUserExtData(context, userExtData); + + userExtData.put(UserExtDataKeys.ACTION,"loginreward_day_7"); + userExtData.put(UserExtDataKeys.CLASSFIELD, "loginreward_day_7"); + FastSdk.setUserExtData(context, userExtData); + } +} diff --git a/demo/src/main/java/com/hoolai/demo/utils/Utils.java b/demo/src/main/java/com/hoolai/demo/utils/Utils.java new file mode 100644 index 0000000..477ffa3 --- /dev/null +++ b/demo/src/main/java/com/hoolai/demo/utils/Utils.java @@ -0,0 +1,115 @@ +package com.hoolai.demo.utils; + +import android.annotation.SuppressLint; +import android.content.ContentUris; +import android.content.Context; +import android.database.Cursor; +import android.net.Uri; +import android.os.Build; +import android.provider.DocumentsContract; +import android.provider.MediaStore; + +/** + * Uri tools + */ +public class Utils { + /** + * 根据Uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + public static String getRealPathFromUri(Context context, Uri uri) { + int sdkVersion = Build.VERSION.SDK_INT; + if (sdkVersion >= 19) { // api >= 19 + return getRealPathFromUriAboveApi19(context, uri); + } else { // api < 19 + return getRealPathFromUriBelowAPI19(context, uri); + } + } + + /** + * 适配api19以下(不包括api19),根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + private static String getRealPathFromUriBelowAPI19(Context context, Uri uri) { + return getDataColumn(context, uri, null, null); + } + + /** + * 适配api19及以上,根据uri获取图片的绝对路径 + * + * @param context 上下文对象 + * @param uri 图片的Uri + * @return 如果Uri对应的图片存在, 那么返回该图片的绝对路径, 否则返回null + */ + @SuppressLint("NewApi") + private static String getRealPathFromUriAboveApi19(Context context, Uri uri) { + String filePath = null; + if (DocumentsContract.isDocumentUri(context, uri)) { + // 如果是document类型的 uri, 则通过document id来进行处理 + String documentId = DocumentsContract.getDocumentId(uri); + if (isMediaDocument(uri)) { // MediaProvider + // 使用':'分割 + String id = documentId.split(":")[1]; + + String selection = MediaStore.Images.Media._ID + "=?"; + String[] selectionArgs = {id}; + filePath = getDataColumn(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, selectionArgs); + } else if (isDownloadsDocument(uri)) { // DownloadsProvider + Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(documentId)); + filePath = getDataColumn(context, contentUri, null, null); + } + } else if ("content".equalsIgnoreCase(uri.getScheme())) { + // 如果是 content 类型的 Uri + filePath = getDataColumn(context, uri, null, null); + } else if ("file".equals(uri.getScheme())) { + // 如果是 file 类型的 Uri,直接获取图片对应的路径 + filePath = uri.getPath(); + } + return filePath; + } + + /** + * 获取数据库表中的 _data 列,即返回Uri对应的文件路径 + * + */ + private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { + String path = null; + + String[] projection = new String[]{MediaStore.Images.Media.DATA}; + Cursor cursor = null; + try { + cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); + if (cursor != null && cursor.moveToFirst()) { + int columnIndex = cursor.getColumnIndexOrThrow(projection[0]); + path = cursor.getString(columnIndex); + } + } catch (Exception e) { + if (cursor != null) { + cursor.close(); + } + } + return path; + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is MediaProvider + */ + private static boolean isMediaDocument(Uri uri) { + return "com.android.providers.media.documents".equals(uri.getAuthority()); + } + + /** + * @param uri the Uri to check + * @return Whether the Uri authority is DownloadsProvider + */ + private static boolean isDownloadsDocument(Uri uri) { + return "com.android.providers.downloads.documents".equals(uri.getAuthority()); + } +} diff --git a/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml b/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..2b068d1 --- /dev/null +++ b/demo/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,30 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demo/src/main/res/drawable/ic_launcher_background.xml b/demo/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..07d5da9 --- /dev/null +++ b/demo/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/demo/src/main/res/drawable/icon.png b/demo/src/main/res/drawable/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..0b0b9f8344518bde78e6d7aa3f0b534fc5f57aac GIT binary patch literal 40976 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 XeDNq~>tk9|7P64sE) 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 U= + + +