Huawei Developers
Published in

Huawei Developers

Create a Game App with HMS

1) Project Introduction

Hi everyone,

In this article i will explain to you how i developed a game application with using Huawei mobile Services step by step.

I used HMS’s Account Kit, Game Service, IAP, Analytics Kit, Drive Kit and Push Kit in this project and also i published this app on App Gallery.

First of all i want to briefly tell you about the kits i use.

Account Kit

HUAWEI Account Kit provides developers with simple, secure, and quick sign-in and authorization functions. Instead of entering accounts and passwords and waiting for authorization, users can just tap the Sign In with HUAWEI ID button to quickly and securely sign in to your app. I used an account kit so that users can join the game services with their own account.

Game Service

With HUAWEI Game Service, you will have access to a range of development capabilities to help you develop your games more efficiently:

  • Real-name authentication
  • Bulletins
  • Achievements
  • Events
  • Leaderboards
  • Saved games
  • Player statistics
  • You can promote your game quickly and efficiently to Huawei’s vast user base by having users sign in using their HUAWEI IDs. For details about the countries and regions supported by HUAWEI Game Service, please refer to Countries and Regions Supported by HUAWEI Account Kit.
  • You can implement features like achievements and events faster, so you can quickly build the foundations for your game.
  • You can perform in-depth operations tailored to your game content and your users.

In-App Purchases

Huawei’s In-App Purchases (IAP) service allows you to offer in-app purchases and facilitates in-app payment. Users can purchase a variety of virtual products, including one-time virtual products and subscriptions, directly within your app. I used this service for the extra chance in my app.

Analytics Kit

Analytics Kit offers you a range of preset analytics models so you can gain a deeper insight into your users, products, and content. With this insight, you can take a data-driven approach to market your apps and optimize your products.

Drive Kit

HUAWEI Drive Kit (“Drive Kit”) allows you to create apps that use the HUAWEI Drive Kit service. Drive Kit provides cloud storage for your apps, enabling users to store files that are created while using your apps, including photos, videos, and documents, in HUAWEI Drive (“Drive”) as well as download, synchronize, share, search for, and comment on these files and reply to comments. Drive Kit also provides comprehensive data protection, empowering users to manage their data securely and conveniently. I integrated it to save screenshots and share them with other users.

Push Kit

HUAWEI Push Kit is a messaging service provided by Huawei for developers. It establishes a messaging channel from the cloud to devices. By integrating HUAWEI Push Kit, developers can send messages to apps on users’ devices in real time. This helps developers maintain closer ties with users and increases user awareness and engagement. The following figure shows the process of sending messages from the cloud to a device.

2) How to Implement

First, you need to open a huawei developer account and add the android studio project you created to the network console. https://developer.huawei.com/consumer/en/codelab/HMSPreparation/index.html#0

I. Account Kit Integration

After the implement AGC Core in your project add compile dependencies to “dependencies" and replace {version} with the current latest HMS SDK version number and Sync your project.

dependencies {
implementation 'com.huawei.hms:hwid:{version}'}

and then we need to configure proguard file

-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.**{*;}

In the opening of my application, Users can login to the application with Huawei id. I added drive kit scopes to signinoptions because I used drive kit in my app

private void signIn(){
HuaweiIdSignInOptions signInOptions = new HuaweiIdSignInOptions.Builder(HuaweiIdSignInOptions.DEFAULT_SIGN_IN)
.requestAccessToken()
.requestIdToken("")
.requestScopes(HuaweiId.HUAEWEIID_BASE_SCOPE)
.requestScopes(new Scope(DriveScopes.DRIVE))
.requestScopes(new Scope(DriveScopes.DRIVE_METADATA))
.requestScopes(new Scope(DriveScopes.DRIVE_METADATA_READONLY))
.requestScopes(new Scope(DriveScopes.DRIVE_READONLY))
.build();
HuaweiIdSignInClient client = HuaweiIdSignIn.getClient(MainActivity.this, signInOptions);
startActivityForResult(client.getSignInIntent(), REQUEST_SIGN_IN_LOGIN);
}

We handle the entry process in onActivityResult method

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (SIGN_IN_INTENT == requestCode) {
Task<SignInHuaweiId> signInHuaweiIdTask = HuaweiIdSignIn.getSignedInAccountFromIntent(data);
if (signInHuaweiIdTask.isSuccessful()) {
((FrameLayout) findViewById(R.id.container)).removeAllViews();
handleSignInResult(data);
SignInHuaweiId huaweiAccount = signInHuaweiIdTask.getResult();
accessToken = huaweiAccount.getAccessToken();
unionId = huaweiAccount.getUnionId();
}

II. Game Service Integration

For using game services firstly we need to enable game and iap api. We need to convert our account to merchant account.

In the build.gradle file in the app directory of your Android Studio project, add the following configurations, and replace {version} with the current latest HMS SDK version number.

dependencies {
implementation 'com.huawei.hms:iap:{version}'
implementation 'com.huawei.hms:game:{version}'
}

Then we need to add following code to proguard file

-keep class com.huawei.gamebox.plugin.gameservice.**{*;}

Add a callback listener for the registered Activity in the onCreate method of the Application.

public class MyApplication extends android.app.Application {
@Override
public void onCreate() {
super.onCreate();
HuaweiMobileServicesUtil.setApplication(this);
}
@Override
public void onTerminate() {
super.onTerminate();
}
}

Initialize the game during game launch.

private void init(){
JosAppsClient appsClient = JosApps.getJosAppsClient(Objects.requireNonNull(getActivity()), mSignInHuaweiId);
appsClient.init();
}

We do not need to add anything to the login process because we have integrated the account kit before. I used the method below to obtain and use the user’s information.

private void getCurrentPlayer() {
PlayersClient client = Games.getPlayersClient(Objects.requireNonNull(getActivity()), mSignInHuaweiId);
Task<Player> task = client.getCurrentPlayer();
task.addOnSuccessListener(new OnSuccessListener<Player>() {
@Override
public void onSuccess(Player player) {
String result = "displayName:" + player.getDisplayName() + "\n" + "playerId:" + player.getPlayerId()
+ "\n" + "playerlevel:" + player.getLevelInfo().getCurrentLevel().getLevelNumber() + "\n" + "ts:"
+ player.getSignTs() + "\n" + "playerSign:" + player.getPlayerSign();
userName.setText(player.getDisplayName());
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
}
}
});
}

Currently, our application uses game service effectively. Let’s add achievements to our application!

Integrate Achievements

You get achievements after reaching various levels in my application. You need to define Achievments on AG Console and relate them with your application. With related achievement ids, you can unlock achievements according to the logic of your application.

private void unlock(String achievementId) {
Games.getAchievementsClient(Objects.requireNonNull(getActivity()),
((MainActivity) Objects.requireNonNull(getActivity())).getSignInHuaweiId()).unlock(achievementId);
}

For showing all the achievemnts in app I created a diffrent fragment in app. This is my Adapter Class for the Achievement fragment.

public class AchievementListAdapter extends RecyclerView.Adapter<AchievementListAdapter.ViewHolder> {
private static final String TAG = "AchievementListAdapter";
private final Context context;
private List<Achievement> achievementList;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView achievementImage;
CheckBox cBox;
TextView achievementName;
TextView achievementDes;
TextView achivementStat;

ViewHolder(View view) {
super(view);
achievementImage = view.findViewById(R.id.achievement_image);
achievementName = view.findViewById(R.id.achievement_name);
achievementDes = view.findViewById(R.id.achievement_des);
achivementStat = view.findViewById(R.id.achieveStat);
}
}
AchievementListAdapter(Context mContext, List<Achievement> achievements) {
context = mContext;
achievementList = achievements;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.achievement_list_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(@NonNull
final ViewHolder holder, final int position) {
final Achievement achievement = achievementList.get(position);
int type = achievement.getType();
final String achievementId = achievement.getAchievementId();
Glide.with(context).load(achievement.getUnlockedImageUri()).into(holder.achievementImage);
holder.achievementName.setText(achievement.getName());
holder.achievementDes.setText(achievement.getDescription());
holder.achivementStat.setText("Level : "+String.valueOf(achievement.getState()));
}
private boolean allBtnInvisibility(TextView achievementUnlock, TextView achievementReveal,
TextView achievementIncrement, TextView achievementStep) {
return achievementUnlock.getVisibility() == View.GONE && achievementReveal.getVisibility() == View.GONE
&& achievementIncrement.getVisibility() == View.GONE && achievementStep.getVisibility() == View.GONE;
}
@Override
public int getItemCount() {
return achievementList.size();
}
}

Achievement Fragment

public class AchievementListFragment extends Fragment {
private SignInHuaweiId signInHuaweiId;
private RecyclerView recyclerView;
private AchievementsClient client;
private ArrayList<Achievement> achievements = new ArrayList<>();
private Context context;
private ProgressBar progressBarAchieve;
public AchievementListFragment() {
}
public static AchievementListFragment newInstance(String param1, String param2) {
AchievementListFragment fragment = new AchievementListFragment();
Bundle args = new Bundle();
fragment.setArguments(args);
return fragment;
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_achievment_list, container, false);
}
@Override
public void onViewCreated(@NonNull final View view, @Nullable Bundle savedInstanceState) {
signInHuaweiId =((MainActivity) Objects.requireNonNull(getActivity())).getSignInHuaweiId();
recyclerView= getActivity().findViewById(R.id.items_recycler);
context=view.getContext();
progressBarAchieve=getActivity().findViewById(R.id.progressBarAchieve);
initData();
requestData();
}
private void requestData() {
Task<AnnotatedData<AchievementBuffer>> task = client.load(true);
task.addOnSuccessListener(new OnSuccessListener<AnnotatedData<AchievementBuffer>>() {
@Override
public void onSuccess(AnnotatedData<AchievementBuffer> data) {
AchievementBuffer achievementBuffer = data.get();
if (achievementBuffer == null) {
return;
}
Iterator<Achievement> iterator = achievementBuffer.iterator();
achievements.clear();
while (iterator.hasNext()) {
Achievement achievement = iterator.next();
achievements.add(achievement);
}
LinearLayoutManager layoutManager = new LinearLayoutManager(context);
recyclerView.setLayoutManager(layoutManager);
AchievementListAdapter adapter = new AchievementListAdapter(context, achievements);
recyclerView.setAdapter(adapter);
progressBarAchieve.setVisibility(View.GONE);
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
if (e instanceof ApiException) {
String result = "rtnCode:" + ((ApiException) e).getStatusCode();
}
}
});
}
private void initData() {
client = Games.getAchievementsClient(Objects.requireNonNull(getActivity()), signInHuaweiId);
progressBarAchieve.setVisibility(View.VISIBLE);
}
}

Game service integration is completed. Let’s see how I use IAP in my app.

III)In-App Purchases Integration

First of all, we need to define the products we will offer to the users on the console. We can create 3 types of products: consumable, non-consumable and subscription.

private void initProduct() {
beforeInitProduct();
IapClient iapClient = Iap.getIapClient(Objects.requireNonNull(getActivity()));
Task<SkuDetailResult> task = iapClient.getSkuDetail(createSkuDetailReq());
task.addOnSuccessListener(new OnSuccessListener<SkuDetailResult>() {
@Override
public void onSuccess(SkuDetailResult result) {
if (result != null && !result.getSkuList().isEmpty()) {
showProduct(result.getSkuList());
getBuyIntent(getActivity(), "testc", Constant.PRODUCT_TYPE_NON_CONSUMABLE);
}
}
}).addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private void beforeInitProduct(){
Task<GetPurchasesResult> task = Iap.getIapClient(Objects.requireNonNull(getActivity()))
.getPurchases(createGetPurchaseReq());
task.addOnSuccessListener(new OnSuccessListener<GetPurchasesResult>() {
@Override
public void onSuccess(GetPurchasesResult getPurchasesResult) {
if(getPurchasesResult.getReturnCode() == 0 && !getPurchasesResult.getInAppPurchaseDataList().isEmpty()){
for(String inAppPurchaseData : getPurchasesResult.getInAppPurchaseDataList()){
consumePurchase(getActivity(), inAppPurchaseData);
}
}
}
});
task.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(Exception e) {
}
});
}
private GetPurchaseReq createGetPurchaseReq(){
GetPurchaseReq getPurchaseReq = new GetPurchaseReq();
getPurchaseReq.priceType = Constant.PRODUCT_TYPE_CONSUMABLE;
return getPurchaseReq;
}
private void showProduct(List<SkuDetail> skuDetailList) {
Log.d(TAG, "showProduct: ");
getBuyIntent(getActivity(), skuDetailList.get(0).productId);
}

IV)Analytics Kit Integration

In my application, I added the analytics kit to send the players scores to the analytics console.

Add build dependencies to dependencies.

dependencies {
implementation 'com.huawei.hms:hianalytics:4.0.3.300'}
import static com.huawei.hms.analytics.type.HAEventType.HA_POST_SCORE;
import static com.huawei.hms.analytics.type.HAParamType.HA_SCORE;
private HiAnalyticsInstance instance;HiAnalyticsTools.enableLog();
instance=HiAnalytics.getInstance(getActivity());
instance.setAnalyticsCollectionEnabled(true);

The method I sent the score

private void postScore() {
Bundle bundle = new Bundle();
bundle.putLong(HA_SCORE, trueAnswer);
instance.logEvent(HA_POST_SCORE, bundle);
}

V)Drive Kit Integration

I used a drive kit so that users can share screenshots of their scores.

Add build dependencies to dependencies.

dependencies {
implementation 'com.huawei.hms:drive:4.0.3.300'
implementation 'com.huawei.hms:hwid:4.0.0.300'}

We need to initilaze the Drive service connection in the relevant method

private void initDrive(){
new Thread(new Runnable() {
@Override
public void run() {
try {
if (accessToken == null) {
return;
}
if (drive == null){
int initCode = DriveManager.getInstance().init(context, unionId, accessToken, new DriveCredential.AccessMethod() {
@Override
public String refreshToken() throws IOException {
return accessToken;
}
});
if (DriveCode.SUCCESS != initCode) {
} else {
drive = new Drive.Builder(DriveManager.getInstance().getCredential(), context).build();
sendScreenshot();
}
}
} catch (Exception ex) {
Logger.e("init drive", ex);
}
}
}).start();
}
private void sendScreenshot(){
new Thread(new Runnable() {
@Override
public void run() {
try {
if (accessToken == null) {
return;
}
if (drive == null){
int initCode = DriveManager.getInstance().init(Objects.requireNonNull(getActivity()), unionId, accessToken, new DriveCredential.AccessMethod() {
@Override
public String refreshToken() throws IOException {
return accessToken;
}
});
if (DriveCode.SUCCESS != initCode) {
return;
}
else {
drive = new Drive.Builder(DriveManager.getInstance().getCredential(), getActivity()).build();
}
}
com.huawei.api.services.drive.model.File file = new com.huawei.api.services.drive.model.File();
file.setName("Presentation Directory" + System.currentTimeMillis()).setMimeType("application/vnd.huawei-apps.folder");
directoryCreated = drive.files().create(file).execute();
com.huawei.api.services.drive.model.File content = new com.huawei.api.services.drive.model.File()
.setName("test")
.setMimeType("image/jpeg")
.setParents(Collections.singletonList(directoryCreated.getId()));
java.io.File fileObject = new java.io.File(mPath);
drive.files().create(content, fileObject).execute();
} catch (Exception ex) {
Logger.e("upload", ex);
}
}
}).start();
}

VI)Push Kit Integration

I integrated the push kit to send users notifications about the game.

Add build dependencies to dependencies.

implementation 'com.huawei.hms:push:4.0.3.301'

We need to edit our manifest file and add the huawei push service class to the application.

Modify

<service
android:name=".MyPushService"
android:exported="false">
<intent-filter>
<action android:name="com.huawei.push.action.MESSAGING_EVENT" />
</intent-filter></service>

Create a class

public class MyPushService extends HmsMessageService {
private static final String TAG = "PushDemoLog";
@Override
public void onNewToken(String token) {
super.onNewToken(token);
Log.i(TAG, "receive token:" + token);
}
@Override
public void onMessageReceived(RemoteMessage var1) {
var1.getNotification();
}
@Override
public void onMessageSent(String var1) {
}
}

And we need to trigger getToken method at the project begining

private void getToken() {
Log.i(TAG, "get token: begin");
new Thread() {
@Override
public void run() {
try {
String appId = AGConnectServicesConfig.fromContext(MainActivity.this).getString("client/app_id");
String pushToken = HmsInstanceId.getInstance(MainActivity.this).getToken(appId, "HCM");
if(!TextUtils.isEmpty(pushToken)) {
Log.i(TAG, "get token:" + pushToken);
}
else
Log.i(TAG, "null token");
} catch (Exception e) {
Log.i(TAG,"getToken failed, " + e);
}
}
}.start();
}

In my application, I used push notifications only to send via console. It can send notifications to all users via AG console. You can create custom audience groups and send push messages only to a certain audience.

3)Final

I explained to you how I developed the game application. For any questions, please feel free to contact me.

Thanks for reading!

--

--

As Huawei Developers, our Medium publication where we share information about the use of Huawei Ecosystem

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store