根据国家新闻出版署的最新规定,发布中国大陆的所有网络游戏必须接入国家新闻出版署网络游戏防沉迷实名验证系统,并且仅可在周五、周六、周日和法定节假日每日20时至21时向未成年人提供1小时网络游戏服务,其他时间均不得以任何形式向未成年人提供网络游戏服务,并且游戏付费服务需要对未成年人支付充值金额设限。
华为平台已为您实现了整套防沉迷的控制逻辑,您只需编写少量代码即可实现防沉迷。本codelab针对发布中国大陆的游戏,演示如何通过华为帐号登录游戏,并快速高效实现防沉迷功能。
在本次codelab中,您将建立一个具备简单游戏交互功能的Android应用程序,您的应用程序将包含:
通过这个Codelab,您将学到:
集成HUAWEI HMS Core能力,需要完成以下准备工作:
Android Studio的代码库配置在Gradle插件7.0以下版本、7.0版本和7.1及以上版本有所不同。请根据您当前的Gradle插件版本,选择对应的配置过程。
7.0以下版本 | 7.0版本 | 7.1及以上版本 |
allprojects {
repositories {
google()
jcenter()
// 配置如下地址
maven {url 'https://developer.huawei.com/repo/'}
}
}
buildscript {
repositories {
google()
jcenter()
// 配置如下地址
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
// 配置如下地址(推荐您使用最新版本的agcp插件)
classpath 'com.huawei.agconnect:agcp:1.6.3.300'
}
}
buildscript {
repositories {
google()
jcenter()
// 配置如下地址
maven {url 'https://developer.huawei.com/repo/'}
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
// 配置如下地址(推荐您使用最新版本的agcp插件)
classpath 'com.huawei.agconnect:agcp:1.6.3.300'
}
}
dependencyResolutionManagement {
...
repositories {
google()
jcenter()
// 配置HMS Core SDK的Maven仓地址。
maven {url 'https://developer.huawei.com/repo/'}
}
}
buildscript {
dependencies {
classpath 'com.android.tools.build:gradle:3.5.4'
// 配置如下地址(推荐您使用最新版本的agcp插件)
classpath 'com.huawei.agconnect:agcp:1.6.3.300'
}
}
c) 打开项目级"settings.gradle"文件,配置HMS Core SDK的Maven仓地址。
pluginManagement {
repositories {
gradlePluginPortal()
google()
mavenCentral()
// 配置HMS Core SDK的Maven仓地址。
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencyResolutionManagement {
...
repositories {
google()
mavenCentral()
// 配置HMS Core SDK的Maven仓地址。
maven { url 'https://developer.huawei.com/repo/' }
}
}
dependencies {
implementation 'com.huawei.hms:hwid:{version}'
implementation 'com.huawei.hms:game:{version}'
}
apply plugin: 'com.huawei.agconnect'
方式二:在plugins中添加如下配置。
plugins {
id 'com.android.application'
// 添加如下配置
id 'com.huawei.agconnect'
}
点击界面上的"Sync Now"同步已完成的配置。
编译APK前需要添加混淆配置,避免功能异常。配置步骤如下:
-ignorewarnings
-keepattributes *Annotation*
-keepattributes Exceptions
-keepattributes InnerClasses
-keepattributes Signature
-keepattributes SourceFile,LineNumberTable
-keep class com.hianalytics.android.**{*;}
-keep class com.huawei.updatesdk.**{*;}
-keep class com.huawei.hms.**{*;}
-keep class com.huawei.gamebox.plugin.gameservice.**{*;}
-keep interface com.huawei.hms.analytics.type.HAEventType{*;}
-keep interface com.huawei.hms.analytics.type.HAParamType{*;}
-keep class com.huawei.hms.analytics.HiAnalyticsInstance{*;}
-keep class com.huawei.hms.analytics.HiAnalyticsInstance{*;}
-keep class com.huawei.hms.analytics.HiAnalytics{*;}
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools"
tools:keep="@layout/hms_download_progress,@drawable/screen_off,@layout/upsdk*,@drawable/c_buoycircle*,@drawable/hms_game*,@layout/c_buoycircle*,@layout/hms_game*,@strings/hms_game*,@strings/c_buoycircle*"
tools:shrinkMode="strict" />
您可以参考本Codelab在Android Studio工程中创建如下布局页面。其中,Init按钮点击后可触发初始化接口,SignIn按钮点击后可以触发登录接口,GetGamePlayer按钮用来触发获取玩家信息接口。
<!--布局代码-->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<TextView
android:id="@+id/init_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:gravity="center"
android:text="@string/init_text"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size"/>
<TextView
android:id="@+id/login_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:layout_marginTop="@dimen/margin_top"
android:gravity="center"
android:text="@string/login_text"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size" />
<TextView
android:id="@+id/player1_btn"
android:layout_width="match_parent"
android:layout_height="@dimen/btn_height"
android:background="@color/btn_background"
android:gravity="center"
android:layout_marginTop="@dimen/margin_top"
android:text="@string/game_player"
android:textColor="@color/text_color"
android:textSize="@dimen/button_size" />
</LinearLayout>
如果您希望游戏对接国家版署,您可向当地的新闻出版局申请接入网络游戏防沉迷实名认证系统,并获取"bizID(游戏备案识别码)",再将bizID配置到AppGallery Connect,华为会自动对接国家新闻出版署的实名认证系统并开启强制实名认证,无需您进行额外的开发,具体操作请参见版署实名认证申请。
代码如下:
public class MyApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
}
代码如下:
public void init() {
AccountAuthParams params = AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME;
JosAppsClient appsClient = JosApps.getJosAppsClient(this);
// Set the anti-addiction prompt context, this line must be added
// 设置防沉迷提示语的Context
ResourceLoaderUtil.setmContext(this);
Task<Void> initTask = appsClient.init(new AppParams(params, new AntiAddictionCallback() {
@Override
public void onExit() {
// Implement the game addiction prevention function, such as saving the game and invoking the account exit interface.
// 在此处实现游戏防沉迷功能,如保存游戏、调用帐号退出接口
showLog("onExit");
}
}));
initTask.addOnSuccessListener(new OnSuccessListener<Void>() {
@Override
public void onSuccess(Void aVoid) {
showLog("init success");
playersClient = Games.getPlayersClient(MainActivity.this);
// The login interface can be invoked only after the init is successful. Otherwise, error code 7018 is displayed.
// 一定要在init成功后,才可以调用登录接口,否则登录会提示7018错误码
// signIn();
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
showLog("init failed, " + e.getMessage());
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
int statusCode = apiException.getStatusCode();
// Error code 7401 indicates that the user did not agree to Huawei joint operations privacy agreement
// 错误码为7401时表示用户未同意华为联运隐私协议
if (statusCode == JosStatusCodes.JOS_PRIVACY_PROTOCOL_REJECTED) {
showLog("has reject the protocol");
// You can exit the game or re-call the init interface.
// 在此处实现退出游戏或者重新调用初始化接口
}
// Handle other error codes.
// 在此处实现其他错误码的处理
}
}
});
}
/**
* Log output
* 日志输出
*/
public void showLog(String logLine) {
StringBuffer sbLog = new StringBuffer();
DateFormat format = new java.text.SimpleDateFormat("yyyy-MM-dd HH:mm:SS", Locale.ENGLISH);
String time = format.format(new Date());
sbLog.append(time).append(":").append(logLine);
Log.i(TAG, sbLog.toString());
}
/**
* 游戏登录需要先调用华为帐号认证登录完成之后,再调用getCurrentPlayer或者getGamePlayer接口获取游戏玩家信息
* 1.先调用华为帐号登录接口的静默登录接口,此接口对于已经授权登录过的应用不会再次拉起登录页面。
* 2.静默登录失败一般是由于登录需要授权,此时在回调中调用显示登录接口拉起登录授权页面进行登录认证。
* 登录结果会在onActivityResult回调中获取,可在此时调用游戏获取玩家信息接口。
*/
public void signIn() {
Task<AuthAccount> authAccountTask = AccountAuthManager.getService(this, getHuaweiIdParams()).silentSignIn();
authAccountTask.addOnSuccessListener(
authAccount -> {
Toast.makeText(MainActivity.this, "signIn success", Toast.LENGTH_LONG).show();
showLog("signIn success");
})
.addOnFailureListener(
new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
ApiException apiException = (ApiException) e;
Toast.makeText(MainActivity.this, "signIn failed:" + apiException.getStatusCode(), Toast.LENGTH_LONG).show();
showLog("signIn failed:" + apiException.getStatusCode());
signInNewWay();
}
}
});
}
/**
* 获取到华为帐号登录授权页面的Intent,并通过调用startActivityForResult(Intent, int)打开华为帐号登录授权页面。
*/
public void signInNewWay() {
Intent intent = AccountAuthManager.getService(MainActivity.this, getHuaweiIdParams()).getSignInIntent();
startActivityForResult(intent, SIGN_IN_INTENT);
}
public AccountAuthParams getHuaweiIdParams() {
return new AccountAuthParamsHelper(AccountAuthParams.DEFAULT_AUTH_REQUEST_PARAM_GAME).createParams();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == SIGN_IN_INTENT) {
if (null == data) {
showLog("signIn intent is null");
return;
}
String jsonSignInResult = data.getStringExtra("HUAWEIID_SIGNIN_RESULT");
if (TextUtils.isEmpty(jsonSignInResult)) {
showLog("signIn result is empty");
return;
}
try {
HuaweiIdAuthResult signInResult = new HuaweiIdAuthResult().fromJson(jsonSignInResult);
if (0 == signInResult.getStatus().getStatusCode()) {
showLog("signIn success");
} else {
showLog("signIn failed: " + signInResult.getStatus().getStatusCode());
}
} catch (JSONException var7) {
showLog("Failed to convert json from signInResult.");
}
}
}
在华为帐号登录成功后,游戏调用PlayersClient.getGamePlayer()获取玩家信息。
/**
* Obtains user information about gamers.
* 获取游戏玩家用户信息
*/
public void getGamePlayer() {
playersClient = Games.getPlayersClient(this);
Task<Player> playerTask = playersClient.getGamePlayer();
playerTask.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
showLog("getPlayerInfo Success");
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
// 获取玩家信息失败
if (e instanceof ApiException) {
showLog("getPlayerInfo failed, status: " + ((ApiException) e).getStatusCode());
if (7400 == ((ApiException) e).getStatusCode() || 7018 == ((ApiException) e).getStatusCode()) {
// error code 7400 indicates that the user has not agreed to the joint operations privacy agreement
// error code 7018 indicates that the init API is not called.
// 7400表示用户未签署联运协议,需要继续调用init接口
// 7018表示初始化失败,需要继续调用init接口
init();
}else if (GamesStatusCodes.GAME_STATE_NETWORK_ERROR == ((ApiException) e).getStatusCode()) {
//Error code 7002 indicates that the network is abnormal. You can prompt the player to check the network.
// 错误码7002表示网络异常,此处您可提示玩家检查网络
showLog( "Network error");
}
}
}
});
}
祝贺您,您已经成功完成了Codelab,并学到了:
您可以阅读下面链接,了解更多相关的信息。
本Codelab中所用API请参考游戏服务的相关API介绍
源码下载地址:源码下载