SDK 介绍
腾讯浏览服务已支持以下基础文档格式的本地打开,接入 SDK 可支持打开文档格式:doc、docx、rtf、ppt、pptx、xls、xlsx、xlsm、csv、pdf、txt、epub、dwg、chm。
其他兼容了 office 格式、遵循 office 标准的文件,可以尝试把文件后缀名转成以上格式,再调用 SDK 打开。
SDK 接入说明
隐私政策
请开发者及终端用户认真阅读上述规则。如您是开发者,请您确认充分了解并同意本规则后再集成 SDK 产品,如果您不同意上述规则及按照本规则履行对应的用户个人信息保护义务,应立即停止接入及使用 SDK 产品。
为了保证腾讯浏览服务 SDK 的正常使用,SDK 需要在
AndroidManifest.xml
声明了如下权限:权限类型 | 权限使用说明 |
android.permission.INTERNET | 文档能力使用前的授权鉴权功能 (离线版本忽略此项) |
| android.permission.ACCESS_NETWORK_STATE |
在 SDK 的功能使用期间,SDK 需要获取以下信息数据以支持 SDK 基础功能的正常运行。
信息类型 | 目的 | 时机 | 处理方式 |
获取设备信息(操作系统版本、CPU 类型、屏幕宽高、屏幕方向、屏幕像素) | 加载文档引擎必要参数 | SDK 初始化获取 | 内存内使用,退出程序即销毁 |
应用信息(宿主应用包名,版本号) | 加载文档引擎必要参数 | SDK 初始化获取 | 通过标识化、加密传输和处理的安全处理方式 |
为实现 SDK 的基础功能,SDK 打开文档可能涉及使用到以下权限,但 SDK 不会主动申请权限,也不会判断应用是否有权限,在需要使用功能的同时,宿主根据业务的判断自己决定是否授予,权限的控制由宿主自主决定。下表列举了各个权限使用涉及的功能及影响。
操作系统 | 权限名称 | 使用目的及功能场景(申请时机) | 是否必选 |
Android | 网络权限 | 通过 SDK 打开文档时,需要网络保持连接,支持授权鉴权能力。开发者在调用需要该权限的 SDK 功能时进行调用。 | 必选 |
| 存储权限 | 若接入方将文档存储在非 App 私有目录下(例如 SD 卡公共目录),则需要申请存储权限,SDK 才能访问并打开文档;若接入方将文档存储在 App 私有目录下,则无需申请存储权限。开发者在调用需要该权限的 SDK 功能时进行调用。 | 可选 |
| 剪切板权限 | 用户主动操作文档内容复制粘贴,需要访问剪切板。开发者在调用需要该权限的 SDK 功能时进行调用。 | 可选 |
注意:
请务必确保用户同意隐私政策后,再调用 SDK iniEngine 接口进行初始化。
SDK 接入流程
1. 下载官网 SDK(按照需要选择一种),集成到项目中。例如:在工程根目录下创建 libs 目录,将 aar 文件放到 libs 目录下。
2. 在 App 模块的 build.gradle 中引入 SDK,例如:
implementation fileTree(dir: 'libs', include: ['TbsFileSdk_xxxx.aar'])
3. 配置混淆,为了功能的正常使用,需要在 proguard-rules.pro 文件中添加如下配置:
-dontwarn com.tencent.tbs.reader.**-keep class com.tencent.tbs.reader.** {*;}
4. 参考下文 接入示例-自定义 layout 方式 实现文档打开代码。
接口介绍
腾讯浏览服务 SDK 不使用 TbsReaderView,统一使用 TbsFileInterfaceImpl 下的接口:
LicenseKey 初始化接口
场景描述:用户同意隐私政策后,首先调用该接口设置 LicenseKey,才能初始化 SDK。
public static void setLicenseKey(String licenseStr)
SDK 初始化接口
功能介绍:初始化文件 SDK。
场景描述:该方法每次 App 启动后仅需调用一次,只有成功初始化 SDK 才能调用打开文档的其他接口。
/*** 同步初始化SDK*/public static int initEngine(Context appContext)/*** 异步初始化SDK*/public static void initEngineAsync(Context appContext, final ITbsReaderCallback callBackListener)
initEngineAsync 相关回调值:
actionType | args | result | 描述 |
OPEN_FILEREADER_ASYNC_LOAD_READER_ENTRY_CALLBACK | 0:加载 SDK 成功 非0:加载 SDK 失败 | / | 异步加载 SDK |
格式判断接口
功能介绍:判断该文件的格式是否是腾讯浏览服务 SDK 支持打开的文件格式。
场景描述:在打开文件前调用。
/*** @param fileExt 文件后缀名,如文件名为test.pdf,则只需要传入"pdf"* @return boolean*/public static boolean canOpenFileExt(String fileExt)
打开文档接口
功能介绍:打开本地文档。
public int openFileReader(Context cx, Bundle param, ITbsReaderCallback callback, FrameLayout layout)
参数配置:
layout:
传入 null 则使用默认的 dialog 方式,使用默认标题栏。
传入一个自定义的 layout,则没有标题栏,layout 内只显示文档内容,宿主可以自己实现标题栏及其功能(和 TbsReaderView 方式相同)。
param:
必要参数
参数名称 | 参数类型 | 参数描述 |
filePath | String | 本地文件路径,SDK 只支持打开一个本地文档,服务器文档需要先下载到本地再调 SDK 打开 |
fileExt | String | 文件后缀名,例如文件名为 test.pdf,则只需要传入"pdf" |
tempPath | String | 文件临时目录路径(文件打开过程中缓存目录,包括记录上次文档打开位置等,打开文档结束后不会自动删除,如有需要可自行删除,建议指定在沙盒目录下) |
可选参数
参数名称 | 参数类型 | 参数描述 |
file_reader_enable_long_press_menu | Boolean | 开启长按菜单复制功能,支持 PDF、DOCX、XLSX 和 TXT |
file_reader_is_ppt_page_mode_default | Boolean | 设置 PPT 打开为翻页模式 |
file_reader_stream_mode | Boolean | 设置为文件流打开模式 |
file_reader_goto_last_pos | Boolean | 打开文件自动跳转到上次浏览结束位置 |
file_reader_content_bg_color | String | 设置文档视图的背景颜色,支持 PDF、DOCX、PPTX,如"#ffffff" |
默认 dialog 模式特定参数
默认 dialog 模式提供一些自定义标题栏的参数:
参数名称 | 参数类型 | 参数描述 |
file_reader_top_bar_bg_color | String | 设置顶 bar 的颜色,如"#ffffff" |
file_reader_top_bar_hight | Int | 设置顶 bar 的高度 |
自定义 layout 方式特定参数(老 SDK 的 TbsReaderView 方式)
如果宿主实现了固定高度的标题栏,需要传入额外的参数设置文档内容显示区域的高度(例如屏幕高度-标题栏高度):
参数名称 | 参数类型 | 描述 |
set_content_view_height | Int | 设置文档内容的高度(默认为整个屏幕高度) |
openFileReader 相关回调值:
actionType | args | result | 描述 |
OPEN_FILEREADER_STATUS_UI_CALLBACK | 打开文件: Bundle[{typeId=0, typeDes=fileReaderOpened}] 关闭文件: Bundle[{typeId=1, typeDes=fileReaderClosed}] | / | 文件打开关闭事件 |
NOTIFY_CANDISPLAY | / | / | 文档引擎渲染成功,即将显示文档 |
READER_EVENT_CLICK | / | / | 单击事件 |
READER_EVENT_SCROLL_BEGIN | / | / | 滑动开始事件 |
READER_EVENT_SCROLL_END | / | / | 滑动结束事件 |
READER_EVENT_SCALE_BEGIN | / | / | 缩放开始事件 |
READER_EVENT_SCALE_END | / | / | 缩放结束事件 |
READER_PAGE_TOAST | Bundle[{cur_page=当前页码, page_count=总页码}] | / | 获取当前页码和总页码 |
关闭文档接口
功能介绍:关闭文档,释放相关资源。
public void closeFileReader()
页面跳转接口
功能介绍:支持 PDF、DOCX、PPTX 跳转到指定页面。
public void gotoPosition(Bundle b)
使用示例:
int curPageIndex = -1;ITbsReaderCallback callback = new ITbsReaderCallback() {@Overridepublic void onCallBackAction(Integer actionType, Object args, Object result) {if (ITbsReader.READER_PAGE_TOAST == actionType) {// cur_page获取到的是当前真实的页码,需要-1才能得到页面对应的数组下标curPageIndex = ((Bundle) args).getInt("cur_page") - 1;}}};// 上一页Button pre = view.findViewById(R.id.iv_document_previous_page);pre.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Bundle b = new Bundle();b.putInt("progress", curPageIndex - 1);TbsFileInterfaceImpl.getInstance().gotoPosition(b); // 传入页面对应的数组下标}});// 下一页Button next = view.findViewById(R.id.iv_document_next_page);next.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {Bundle b = new Bundle();b.putInt("progress", curPageIndex + 1);TbsFileInterfaceImpl.getInstance().gotoPosition(b);}});
其他功能
横屏
在
AndroidManifest.xml
设置打开文件的 activity 属性:android:configChanges="orientation|keyboardHidden|navigation|screenSize"
自定义 layout 方式需要主动调用接口适配横屏,默认 dialog 模式无需调用该接口:
/*** @param width layout宽度* @param height layout高度*/public void onSizeChanged(int width, int height)
长按菜单
腾讯浏览服务文档引擎(PDF、DOCX、XLSX、TXT)支持长按复制文本功能 ,见打开文档接口可选参数。
PPT 翻页模式
参考打开文档接口可选参数,设置 PPT 打开为翻页模式(默认是滑动模式);参考页面跳转接口设置 PPT 翻页模式页面跳转。
接入示例
腾讯浏览服务 SDK 不使用 TbsReaderView,统一使用 TbsFileInterfaceImpl 下的接口,提供以下两种接入方式:
默认 Dialog 方式
openFileReader 接口 layout 传入 null。
//设置回调ITbsReaderCallback callback = new ITbsReaderCallback() {@Overridepublic void onCallBackAction(Integer actionType, Object args, Object result) {Log.i(TAG, "actionType=" + actionType + ",args=" + args + ",result=" + result);if (ITbsReader.OPEN_FILEREADER_STATUS_UI_CALLBACK == actionType) {if (args instanceof Bundle) {int id = ((Bundle) args).getInt("typeId");if (ITbsReader.TBS_READER_TYPE_STATUS_UI_SHUTDOWN == id) {TbsFileInterfaceImpl.getInstance().closeFileReader(); //关闭fileReader}}}}};// 初始化Engine,见下文int ret = initEngine();if(ret != 0) {Log.i(TAG, "initEngine失败, 不要调用其他接口,ret = " + ret);return;}//点击打开文档findViewById(R.id.btn_dialog).setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {ret = openFileReader(fileName);}});private int openFileReader(String fileName) {//设置参数Bundle param = new Bundle();param.putString("filePath", filePath); //文件路径param.putString("fileExt", extName); // 文件后缀名,如文件名为test.pdf,则只需要传入"pdf"param.putString("tempPath", getExternalFilesDir("temp").getAbsolutePath());//调用openFileReader打开文档if (TbsFileInterfaceImpl.canOpenFileExt(extName)) { //tbs支持的文档类型int ret = TbsFileInterfaceImpl.getInstance().openFileReader(this, param, callback, null);if (ret != 0) {Log.i(TAG, "openFileReader失败, ret = " + ret);}} else {//tbs不支持的文档类型//...}}
自定义 Layout 方式
openFileReader 接口传入一个 layout。
public class MainActivity extends AppCompatActivity {private final String TAG = "FileSdkDemo";private static boolean isInit = false;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initUI();// 初始化Engine,见下文int ret = initEngine();if(ret != 0) {Toast.makeText(getApplicationContext(), "初始化Engine失败 ret = " + ret, Toast.LENGTH_SHORT).show();} else {Toast.makeText(getApplicationContext(), "初始化Engine成功", Toast.LENGTH_SHORT).show();isInit = true;}}private void initUI() {findViewById(R.id.btn_open_file).setOnClickListener(view -> openExternalFilesDirDocument(filePath,extName));}private void openExternalFilesDirDocument(String filePath, String extName) {if(isInit) {if(TbsFileInterfaceImpl.canOpenFileExt(fileExt)) {PreviewActivity.start(this, filePath, extName);} else {// tbs不支持打开的类型}} else {Toast.makeText(getApplicationContext(), "Engine未初始化成功,无法打开文件", Toast.LENGTH_SHORT).show();}}
public class PreviewActivity extends AppCompatActivity {private FrameLayout mFlRoot; //内容显示区域private RelativeLayout mRl; //自定义标题栏private boolean isDestroyed = false;public static void start(Context context, String filePath,String fileExt) {context = context.getApplicationContext();Intent starter = new Intent(context, PreviewActivity.class);starter.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);starter.putExtra("filePath", filePath);starter.putExtra("fileExt", fileExt);context.startActivity(starter);}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_preview);String filePath = getIntent().getStringExtra("filePath");String fileExt = getIntent().getStringExtra("fileExt");initView();openFile(filePath, fileExt);}private void initView() {mFlRoot = findViewById(R.id.content);mRl = findViewById(R.id.reader_top);}private void openFile(String filePath, String fileExt) {//设置回调ITbsReaderCallback callback = new ITbsReaderCallback() {@Overridepublic void onCallBackAction(Integer actionType, Object args, Object result) {Log.i(TAG, "actionType=" + actionType + ",args=" + args + ",result=" + result);if (ITbsReader.OPEN_FILEREADER_STATUS_UI_CALLBACK == actionType) {if (args instanceof Bundle) {int id = ((Bundle) args).getInt("typeId");if (ITbsReader.TBS_READER_TYPE_STATUS_UI_SHUTDOWN == id) {finish(); // 加密文档弹框取消需关闭activity}}}}};//设置参数Bundle param = new Bundle();param.putString("filePath", filePath);param.putString("fileExt", fileExt); // 文件后缀名,如文件名为test.pdf,则只需要传入"pdf"param.putString("tempPath", getExternalFilesDir("temp").getAbsolutePath());//调用openFileReader打开文件mFlRoot.post(new Runnable() {@Overridepublic void run() {int height = mFlRoot.getHeight();// 自定义layout模式必须设置这个值,否则可能导致文档内容显示不全param.putInt("set_content_view_height", height);int ret = TbsFileInterfaceImpl.getInstance().openFileReader(PreviewActivity.this, param, callback, mFlRoot);}});}// 销毁资源使用onpause + ondestroy的方式。避免onDestroy延迟回调private void destroy() {if (isDestroyed) {return;}// 回收资源mFlRoot.removeAllViews(); //移除内容显示区域layoutTbsFileInterfaceImpl.getInstance().closeFileReader(); //关闭fileReaderisDestroyed = true;}@Overrideprotected void onPause() {super.onPause();if (isFinishing()) {destroy();}}@Overridepublic void onDestroy() {super.onDestroy();destroy();}//横竖屏切换@Overridepublic void onConfigurationChanged(@NonNull Configuration newConfig) {super.onConfigurationChanged(newConfig);mFlRoot.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { mFlRoot.getViewTreeObserver().removeOnGlobalLayoutListener(this); int w = mFlRoot.getWidth(); int h = mFlRoot.getHeight(); TbsFileInterfaceImpl.getInstance().onSizeChanged(w, h); } });}}
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical">// 自定义标题栏<RelativeLayoutandroid:id="@+id/reader_top"android:layout_width="match_parent"android:layout_height="50dp"android:background="#576B95"></RelativeLayout>// 内容显示区域<FrameLayoutandroid:id="@+id/content"android:layout_width="match_parent"android:layout_height="match_parent"/></LinearLayout>
<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"android:layout_height="match_parent"><Buttonandroid:id="@+id/btn_open_file"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="打开文件"android:layout_centerInParent="true"/></RelativeLayout>
同步初始化 Engine
public int initEngine() {//设置licenseKeyTbsFileInterfaceImpl.setLicenseKey("your licenseKey");int ret = -1;//初始化Engineif (!TbsFileInterfaceImpl.isEngineLoaded()) {ret = TbsFileInterfaceImpl.initEngine(this);}return ret;}
异步初始化 Engine
public void initEngineAsync() {//设置licenseKeyTbsFileInterfaceImpl.setLicenseKey("your licenseKey");ITbsReaderCallback callback = new ITbsReaderCallback() {@Overridepublic void onCallBackAction(Integer actionType, Object args, Object result) {Log.i(TAG, "actionType=" + actionType + ",args=" + args + ",result=" + result);// ITbsReader.OPEN_FILEREADER_ASYNC_LOAD_READER_ENTRY_CALLBACK 的值为 7002,不是错误码if (ITbsReader.OPEN_FILEREADER_ASYNC_LOAD_READER_ENTRY_CALLBACK == actionType) {int ret = (int)args; // 错误码为actionType == 7002时 args的值if (ret == 0) {// 初始化成功} else {// 初始化失败}}}};if (!TbsFileInterfaceImpl.isEngineLoaded()) {TbsFileInterfaceImpl.initEngineAsync(this, callback);}}
打开文件流
ITbsReaderCallback callback = new ITbsReaderCallback() {@Overridepublic void onCallBackAction(Integer actionType, Object args, Object result) {// 使用文件流模式会收到该actionType回调if (ITbsReader.OPEN_FILEREADER_STREAM_MODE_CALLBACK == actionType) {if (args instanceof ITbsReaderStream) {ITbsReaderStream stream = (ITbsReaderStream) args;FileInputStream fin = null;try {// 实际需要打开的文件流File realFile = new File(this.getExternalFilesDir("docs") + "/RealTbsTestFile.docx");fin = new FileInputStream(realFile);// 文件流打开真正的错误码int ret = stream.write(fin);fin.close();} catch (IOException e) {e.printStackTrace();}}}}};Bundle param = new Bundle();param.putBoolean("file_reader_stream_mode", true); // 传入该参数表示使用文件流模式打开param.putString("filePath", filePath); // 该参数必须设置,模拟文件路径。例如:"sdcard/Andoird/data/package_name/files/docs/MockTbsTestFile.docx"// 本地必须创建一个模拟文件,可以为空文件File file = new File(filePath);File fileParent = file.getParentFile();if(!fileParent.exists()){fileParent.mkdirs();}file.createNewFile();param.putString("fileExt", fileExt); // 该参数必须设置,后缀名必须和传入后续要打开的文件流类型一致。如:打开docx文件流,就要传入"docx"param.putString("tempPath", ""); // 该参数无需设置if (TbsFileInterfaceImpl.canOpenFileExt(fileExt)) {// 使用文件流模式,ret会返回-8。int ret = TbsFileInterfaceImpl.getInstance().openFileReader(this, param, callback, null);}