唤起更新开发指南
TapTap 开发者服务为游戏和玩家提供唤起 TapTap 客户端更新游戏的功能。
当游戏发布了新版本,且需要玩家进行更新才能体验新版本时,在游戏内绘制一个界面告知玩家并提供「更新」按钮。
玩家点击按钮调用 updateGameAndFailToWebInTapTap
接口,此时跳转到 TapTap 客户端内的游戏详情页,玩家在 TapTap 内完成更新。
唤起更新只提供「唤起 TapTap 客户端,跳转至游戏详情页」这一简单功能。
TapTap 上游戏的版本号来自开发者在构建新版本时上传的 APK,唤起更新功能并不提供「检查 TapTap 商店是否有新版本」接口,游戏需自行实现判断版本的功能。
SDK 获取
- Unity
- Android
- iOS
SDK 可以通过 Unity Package Manager 导入或手动导入,二者任选其一。
如果选择 UPM 导入,可以在项目的 Packages/manifest.json
文件中添加:
"dependencies":{
"com.taptap.tds.common":"https://github.com/TapTap/TapCommon-Unity.git#3.29.4",
"com.leancloud.storage": "https://github.com/leancloud/csharp-sdk-upm.git#storage-2.3.0",
}
如果选择手动导入:
- 在 下载页 找到 TapSDK Unity 下载地址,下载 TapSDK-UnityPackage.zip 然后解压,导入其中的
TapTap_Common
模块。 - 下载 LeanCloud-SDK-Storage-Unity.zip,解压后为 Plugins 文件夹,拖拽至 Unity 即可。
在 下载页 获得 TapSDK,添加 TapCommon
模块。
repositories{
flatDir {
dirs 'libs'
}
}
dependencies {
...
implementation (name:'TapCommon_3.29.4', ext:'aar')
}
在 下载页 获得 TapSDK,添加 TapCommon
模块。
TapCommonSDK.framework
唤起 TapTap 检查更新
自 TapSDK 3.3.0 版本开始,针对更新功能做了逻辑优化,唤起 TapTap 客户端更新游戏失败时可以跳转到自定义网页。TapSDK 3.3.0 版本对之前的版本向下兼容。 该版本一般情况下不需要在使用以下 API 前特别检查是否安装 TapTap 客户端,自 TapSDK 3.3.0 版本开始推荐使用新接口。
- Unity
- Android
- iOS
在 TapTap 客户端更新游戏,失败时通过浏览器打开 TapTap 网站对应的游戏页面:
// 适用于中国大陆
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapTap(string appId);
// 适用于其他国家或地区
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapGlobal(string appId);
唤起 TapTap 客户端更新游戏失败,跳转到自定义网页:
// 适用于中国大陆
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapTap(string appId, string webUrl);
// 适用于其他国家或地区
bool isSuccess = await TapCommon.UpdateGameAndFailToWebInTapGlobal(string appId, string webUrl);
在 TapTap 客户端更新游戏,失败时通过浏览器打开 TapTap 网站对应的游戏页面:
// 适用于中国大陆
TapGameUtil.updateGameAndFailToWebInTapTap(context, "your app id");
// 适用于其他国家或地区
TapGameUtil.updateGameAndFailToWebInTapGlobal(context, "your app id");
唤起 TapTap 客户端更新游戏失败,跳转到自定义网页:
// 适用于中国大陆
TapGameUtil.updateGameAndFailToWebInTapTap(context, "your app id", "your website url");
// 适用于其他国家或地区
TapGameUtil.updateGameAndFailToWebInTapGlobal(context, "your app id", "your website url");
// 受限于苹果政策,iOS 平台的 TapTap 客户端不提供游戏更新功能
点击展开 TapSDK 3.3.0 之前的相关接口
TapSDK 3.3.0 及之后版本不必参考这一节,推荐使用上面「唤起 TapTap 检查更新」的 API。
检查 TapTap 客户端是否安装:
- Unity
- Android
- iOS
// 适用于中国大陆
TapCommon.IsTapTapInstalled(installed =>
{
if (installed) {
Debug.Log("TapTap 已经安装");
}
});
// 适用于其他国家或地区
TapCommon.IsTapTapGlobalInstalled(installed =>
{
if (installed) {
Debug.Log("TapTap 已经安装");
}
});
需要导入 TapCommon.aar
包,接口在 TapGameUtil
中。
import com.tds.common.utils.TapGameUtil;
// 适用于中国大陆
if(TapGameUtil.isTapTapInstalled(this)){
Log.d(TAG, "已经安装 TapTap 客户端");
}
// 适用于其他国家或地区
if(TapGameUtil.isTapGlobalInstalled(this)){
Log.d(TAG, "已经安装 TapTap 客户端");
}
需要导入 TapCommon.framework
库文件,在 info.plist
里配置 LSApplicationQueriesSchemes
增加 tapsdk
、taptap
和 tapiosdk
。
#import <TapCommonSDK/TapCommonSDK.h>
// 适用于中国大陆
BOOL isInstalled = [TapGameUtil isTapTapInstalled];
// 适用于其他国家或地区
BOOL isInstalled = [TapGameUtil isTapGlobalInstalled];
唤起 TapTap 更新游戏:
- Unity
- Android
- iOS
TapCommon.UpdateGameInTapTap("appid", callSuccess =>
{
if (callSuccess) {
Debug.Log("TapTap 唤起成功");
}
});
if(TapGameUtil.updateGameInTapTap(this,"appid")){
Log.d(TAG, "唤起 TapTap 客户端成功");
}
// 受限于苹果政策,iOS 平台的 TapTap 客户端不提供游戏更新功能
常见问题:Android 11 或更高版本无法拉起 TapTap 客户端
Android 11(API level 30)之后加强了隐私保护策略,引入了大量变更和限制,其中一个重要变更——软件包可见性,将会导致第三方应用无法拉起 TapTap 客户端,从而影响 TapTap 相关功能的正常使用,包括但不限于更新唤起 TapTap、购买验证等功能。
如果没有完成适配,Android 版本为 11 及更高版本的客户端打开游戏会提示「本游戏需要最新版 TapTap 服务支持」,无法正常进入游戏。异常呈现如下图所示:
对此提供如下两种适配方案:
方案一:
编译时将 targetSdkVersion
改为 29(目前设置成 >= 30 会触发该问题)。
方案二:
将 gradle build tools 改为 4.1.0+:
classpath 'com.android.tools.build:gradle:4.1.0'
在 AndroidManifest.xml 里添加
<queries>
标签中的内容:<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="string">
<application
. . .
</application>
<!--添加如下 queries 标签的内容,直接在 manifest 标签下-->
<queries>
<package android:name="com.taptap" />
<package android:name="com.taptap.pad" />
<package android:name="com.taptap.global" />
</queries>
</manifest>
打开游戏评论区
- Unity
- Android
- iOS
// 适用于中国大陆
TapCommon.OpenReviewInTapTap(appId, openSuccess =>
{
if (openSuccess) {
Debug.Log("打开游戏评论区成功");
}
});
// 适用于其他国家或地区
TapCommon.OpenReviewInTapGlobal(appId, openSuccess =>
{
if (openSuccess) {
Debug.Log("打开游戏评论区成功");
}
});
// 适用于中国大陆
if(TapGameUtil.openReviewInTapTap(this,"appid")){
Log.d(TAG, "打开评论区成功");
}
// 适用于其他国家或地区
if(TapGameUtil.openReviewInTapGlobal(this,"appid")){
Log.d(TAG, "打开评论区成功");
}
// 未支持
appid:游戏在 TapTap 商店的唯一身份标识。
例如:https://www.taptap.cn/app/187168
,其中 187168
是 appid
。
常见问题
未接入 TapSDK,如何唤起 TapTap 客户端更新游戏
受限于苹果政策,iOS 平台的 TapTap 客户端不提供游戏更新功能,以下方案仅限于 Android 平台使用。
未接入 TapSDK、使用旧版 TapSDK 难以升级的游戏,可以通过以下方案进行手动唤起 TapTap 客户端更新游戏:
根据玩家设备是否安装 TapTap 客户端来对应打开 URL:
- 如果玩家设备安装 TapTap 客户端则直接唤起 TapTap 客户端到游戏详情页进行更新;
- 如果玩家设备没有安装 TapTap 客户端,则以 Web 形式打开游戏详情页,根据页面底部提示引导玩家下载 TapTap 客户端,安装成功后打开 TapTap 客户端,玩家根据提示选择在 TapTap 客户端里打开游戏详情页进行更新。
未安装 TapTap 客户端对应的 URL:
- 适用于中国大陆:
https://l.taptap.cn/5d1NGyET?subc1=AppID
- 适用于其他国家或地区:
https://l.taptap.io/GNYwFaZr?subc1=AppID
已安装 TapTap 客户端对应的 URL:
- 适用于中国大陆:
taptap://taptap.cn/app?app_id=AppID&source=outer|update
- 适用于其他国家或地区:
tapglobal://taptap.tw/app?app_id=游戏商店id&source=outer|update
注意替换其中的 AppID
。AppID
是游戏在 TapTap 商店的唯一身份标识,例如:https://www.taptap.cn/app/187168
,其中 187168
是 AppID。
注意,除了打开 URL 外,还需要检测设备是否已经安装 TapTap 客户端,以及处理唤起失败的逻辑,这些代码都需要自行编写。
下面提供 TapSDK 唤起更新的代码供参考。
参考代码
适用于中国大陆:
package com.tds.common.utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.Locale;
public class TapGameUtil {
private static final String TAG = TapGameUtil.class.getName();
public static final String PACKAGE_NAME_TAPTAP = "com.taptap";
public static final String CLIENT_URI_TAPTAP = "taptap://taptap.cn";
public static final String DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP = "https://l.taptap.cn/5d1NGyET";
// 这里更新的时候不检查 Tap 客户端,一是因为特定 schema 没被应用注册的话大概率是直接返回 error 的,在这里被 try catch 后返回 false 可以近似等于客户端不存在
// 二是因为 Android 11 开始检查客户端需要游戏做特殊配置,这个配置无法在 SDK 内做好,因为和编译工具版本强绑定,无法做前后版本兼容。
public static boolean updateGameAndFailToWebInTapTap(Activity activity, String appId) {
return updateGameInTapTap(activity, appId) || openWebDownloadUrlOfTapTap(activity, appId);
}
public static boolean updateGameAndFailToWebInTapTap(Activity activity, String appId, String webUrl) {
if (TextUtils.isEmpty(webUrl)) {
return updateGameAndFailToWebInTapTap(activity, appId);
}
return updateGameInTapTap(activity, appId) || openWebDownloadUrl(activity, webUrl);
}
public static boolean isTapTapInstalled(Context context) {
return isTapClientInstalled(context, PACKAGE_NAME_TAPTAP);
}
public static boolean isTapClientInstalled(Context context, String clientPackageName) {
if (context != null && !TextUtils.isEmpty(clientPackageName)) {
boolean TapTapInstalled = false;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(clientPackageName, 0);
if (null != packageInfo) {
TapTapInstalled = true;
}
} catch (Exception e) {
Log.e(TAG, clientPackageName + " isInstalled=false");
}
return TapTapInstalled;
}
return false;
}
public static boolean updateGameInTapTap(Activity activity, String appId) {
return updateGameInTapClient(activity, appId, CLIENT_URI_TAPTAP);
}
public static boolean updateGameInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?app_id=%s&source=outer|update", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
public static boolean openReviewInTapTap(Activity activity, String appId) {
return openReviewInTapClient(activity, appId, CLIENT_URI_TAPTAP);
}
public static boolean openReviewInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?tab_name=review&app_id=%s", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
public static boolean openWebDownloadUrlOfTapTap(Activity activity, String appId) {
return openWebDownloadUrl(activity, String.format(Locale.US, DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP + "?subc1=%s", appId));
}
public static boolean openWebDownloadUrl(Activity activity, String url) {
if (activity != null && !TextUtils.isEmpty(url)) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openWebUrl fail");
return false;
}
return true;
}
return false;
}
}
适用于其他国家或地区:
package com.tds.common.utils;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
import java.util.Locale;
public class TapGameUtil {
private static final String TAG = TapGameUtil.class.getName();
public static final String PACKAGE_NAME_TAPTAP_GLOBAL = "com.taptap.global";
public static final String CLIENT_URI_TAPTAP_GLOBAL = "tapglobal://taptap.tw";
public static final String DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP_GLOBAL = "https://l.taptap.io/GNYwFaZr";
// 这里更新的时候不检查 Tap 客户端,一是因为特定 schema 没被应用注册的话大概率是直接返回 error 的,在这里被 try catch 后返回 false 可以近似等于客户端不存在
// 二是因为 Android 11 开始检查客户端需要游戏做特殊配置,这个配置无法在 SDK 内做好,因为和编译工具版本强绑定,无法做前后版本兼容。
public static boolean updateGameAndFailToWebInTapGlobal(Activity activity, String appId) {
return updateGameInTapGlobal(activity, appId) || openWebDownloadUrlOfTapGlobal(activity, appId);
}
public static boolean updateGameAndFailToWebInTapGlobal(Activity activity, String appId, String webUrl) {
if (TextUtils.isEmpty(webUrl)) {
return updateGameAndFailToWebInTapGlobal(activity, appId);
}
return updateGameInTapGlobal(activity, appId) || openWebDownloadUrl(activity, webUrl);
}
public static boolean isTapGlobalInstalled(Context context) {
return isTapClientInstalled(context, PACKAGE_NAME_TAPTAP_GLOBAL);
}
public static boolean isTapClientInstalled(Context context, String clientPackageName) {
if (context != null && !TextUtils.isEmpty(clientPackageName)) {
boolean TapTapInstalled = false;
try {
PackageInfo packageInfo = context.getPackageManager().getPackageInfo(clientPackageName, 0);
if (null != packageInfo) {
TapTapInstalled = true;
}
} catch (Exception e) {
Log.e(TAG, clientPackageName + " isInstalled=false");
}
return TapTapInstalled;
}
return false;
}
public static boolean updateGameInTapGlobal(Activity activity, String appId) {
return updateGameInTapClient(activity, appId, CLIENT_URI_TAPTAP_GLOBAL);
}
public static boolean updateGameInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?app_id=%s&source=outer|update", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "updateGameInTapTap failed");
return false;
}
public static boolean openReviewInTapGlobal(Activity activity, String appId) {
return openReviewInTapClient(activity, appId, CLIENT_URI_TAPTAP_GLOBAL);
}
public static boolean openReviewInTapClient(Activity activity, String appId, String clientUri) {
if (activity != null && !TextUtils.isEmpty(appId) && !TextUtils.isEmpty(clientUri)) {
try {
Uri uri = Uri.parse(String.format(Locale.US,
"%s/app?tab_name=review&app_id=%s", clientUri, appId));
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
return true;
}
Log.e(TAG, clientUri + "openTapTapReview failed");
return false;
}
public static boolean openWebDownloadUrlOfTapGlobal(Activity activity, String appId) {
return openWebDownloadUrl(activity, String.format(Locale.US, DEFAULT_CLIENT_DOWNLOAD_URL_TAPTAP_GLOBAL + "?subc1=%s", appId));
}
public static boolean openWebDownloadUrl(Activity activity, String url) {
if (activity != null && !TextUtils.isEmpty(url)) {
try {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setData(Uri.parse(url));
activity.startActivity(intent);
} catch (Exception e) {
Log.e(TAG, "openWebUrl fail");
return false;
}
return true;
}
return false;
}
}