前段时间app需要实现一个直接打开office文档的功能,在这里记录一下,方便以后使用,所需的jar包在https://download.csdn.net/download/fishandbean/11987785
wps集成打开文档的功能,有两种实现方式。第一种是通过第三方启动方式直接打开文件,第二种是使用AIDL方式启动WPS,通过绑定Service,调用WPS对外暴露的接口,直接操作WPS的功能。
两种方式优缺点:使用第三方启动方式,简单方便,但是仅仅能控制一些打开文档的显示方式;使用AIDL方式打开可以操控的功能比较多,但是实现较为复杂。
下面介绍一下两种集成方式的具体实现:
首先提供两种实现方式都需要的一个工具类
public class Define
{
public static final String PREFS_NAME = "MyPrefsFile"; //用于存取参数的文件名
public static final String KEY = "PackageName"; //第三方包名
public static final String READ_ONLY = "ReadOnly"; //只读模式
public static final String NORMAL = "Normal"; //正常模式
public static final String READ_MODE = "ReadMode"; //打开文件直接进入阅读器模式
public static final String EDIT_MODE = "EditMode"; //打开文件直接进入编辑模式
public static final String SAVE_ONLY = "SaveOnly"; //仅仅用来另存文件
public static final String VIEW_SCALE = "ViewScale"; //视图比例
public static final String VIEW_PROGRESS = "ViewProgress"; //查看进度百分比
public static final String VIEW_SCROLL_X = "ViewScrollX"; //显示的x坐标
public static final String VIEW_SCROLL_Y = "ViewScrollY"; //显示的y坐标
public static final String CLOSE_FILE = "CloseFile"; //关闭的文件
public static final String OPEN_FILE = "OpenFile"; //打开的文件
public static final String THIRD_PACKAGE = "ThirdPackage"; //第三方的包名
public static final String SAVE_PATH = "SavePath"; //文件保存的路径
public static final String CLEAR_BUFFER = "ClearBuffer"; //清除缓冲区,默认为true
public static final String CLEAR_TRACE = "ClearTrace"; //清除使用痕迹,默认为false
public static final String CLEAR_FILE = "ClearFile"; //删除文件自身,默认为false
public static final String CHECK_PACKAGE_NAME = "CheckPackageName"; //企业版华为不固定的应用包名
public static final String IS_SCREEN_SHOTFORBID = "isScreenshotForbid"; // 禁止截屏
// 以下是自己重新定义的
public static final String USER_NAME = "UserName";
public static final String SEND_CLOSE_BROAD = "SendCloseBroad"; //关闭文件时是否发送广播,默认不发送
public static final String SEND_SAVE_BROAD = "SendSaveBroad"; //关闭保存时是否发送广播,默认不发送
public static final String IS_VIEW_SCALE = "IsViewScale"; //view scale
public static final String OPEN_MODE = "OpenMode"; //阅读器模式
public static final String AUTO_JUMP = "AutoJump"; //第三方打开文件时是否自动跳转
public static final String IS_CLEAR_BUFFER = "IsClearBuffer"; //清除缓冲区,默认为true
public static final String IS_CLEAR_TRACE = "IsClearTrace"; //清除使用痕迹,默认为false
public static final String IS_CLEAR_FILE = "IsClearFile"; //删除文件自身,默认为false
public static final String HOME_KEY_DOWN = "HomeKeyDown"; //Home 按钮
public static final String BACK_KEY_DOWN = "BackKeyDown"; //Back 按钮
public static final String CACHE_FILE_INVISIBLE = "CacheFileInvisible"; //缓存文件是否可见,默认可见
public static final String ENTER_REVISE_MODE = "EnterReviseMode"; //以修订模式打开文档
public static final String ENCRYPT_FILE = "EncrptFile"; //加密方式操作文档
public static final String MENU_XML = "MenuXML"; //xml菜单
public static final String REVISION_NOMARKUP = "RevisionNoMarkup";//修订模式的无标记
public static final String DISPLAY_OPEN_FILE_NAME = "DisplayOpenFileName"; // 第三方设置文档显示名称
public static final String SHOW_REVIEWING_PANE_RIGHT_DEFAULT = "ShowReviewingPaneRightDefault"; //打开文档默认侧边栏不显示/显示 默认false
public static final String WATERMASK_TEXT = "WaterMaskText";//水印文字
public static final String WATERMASK_COLOR = "WaterMaskColor";//水印文字颜色
public static final String PACKAGENAME_KING_PRO = "com.kingsoft.moffice_pro";
public static final String CLASSNAME = "cn.wps.moffice.documentmanager.PreStartActivity2"; //wps类名,标准版本
// public static final String CLASSNAME = "cn.wps.moffice.emm.EmmOpenFileActivity"; //wps类名,EMM版本
public static final String OFFICE_SERVICE_ACTION = "cn.wps.moffice.service.OfficeService";
public static final String PRO_OFFICE_SERVICE_ACTION = "cn.wps.moffice.service.ProOfficeService";
public static final String OFFICE_ACTIVITY_NAME = "cn.wps.moffice.service.MOfficeWakeActivity";
public static final String OFFICE_READY_ACTION = "cn.wps.moffice.service.startup";
public static final String WPS_OPEN_MODE = "WPSOPENMODE";
public static final String WPS_OPEN_AIDL = "AIDL";
public static final String WPS_OPEN_THIRD = "THIRD";
public static final String FAIR_COPY = "FairCopy"; //清稿
public static final String FAIR_COPY_PW = "FairCopyPw"; //清稿密码
public static final String IS_SHOW_VIEW = "IsShowView"; //是否显示wps界面
//自动播放控制
public static final String AUTO_PLAY = "AutoPlay"; // PPT直接进入自动播放
public static final String AUTO_PLAY_INTERNAL = "AutoPlayInternal"; // PPT自动播放间隔
//编辑
public static final int INVALID_EDITPARAM = -1;
public static final String AT_SAVE = "AT_SAVE"; //保存
public static final String AT_SAVEAS = "AT_SAVEAS"; //另存为
public static final String AT_COPY = "AT_COPY"; //复制
public static final String AT_CUT = "AT_CUT"; //剪切
public static final String AT_PASTE = "AT_PASTE"; //粘贴
public static final String AT_SHARE = "AT_SHARE"; //分享
public static final String AT_PRINT = "AT_PRINT"; //输出
public static final String AT_SPELLCHECK = "AT_SPELLCHECK"; //拼写检查
public static final String AT_QUICK_CLOSE_REVISEMODE = "AT_QUICK_CLOSE_REVISEMODE"; //快速关闭修订
public static final String AT_MULTIDOCCHANGE = "AT_MULTIDOCCHANGE"; //多文档切换
public static final String AT_EDIT_REVISION = "AT_EDIT_REVISION";
public static final String AT_CURSOR_MODEL = "AT_CURSOR_MODEL";
public static final String AT_PATH = "at_path"; //编辑路径
public static final String AT_CHANGE_COMMENT_USER = "AT_CHANGE_COMMENT_USER";
public static final String AT_SHARE_PLAY = "AT_SHARE_PLAY";
public static final String AT_GRID_BACKBOARD = "AT_GRID_BACKBOARD";
public static final String SERIAL_NUMBER_OTHER = "SerialNumberOther"; // android 激活外部传入序列号
第一种,只需要以下方法即可:
private void openFile(final BaseChatActivity mContext, File file, final MessageBase msg) {
Intent intent = new Intent();
final Bundle bundle = new Bundle();
//打开模式,只读模式
bundle.putString(Define.OPEN_MODE, Define.READ_ONLY);
//关闭时是否发送广播true为发送
bundle.putBoolean(Define.SEND_CLOSE_BROAD, true);
//打开文件时,在最近列表不显示该打开记录
if (msg.getDirection() == MessageBase.DIRECTION_SEND) {
bundle.putBoolean(Define.CLEAR_FILE, false);
} else {
bundle.putBoolean(Define.CLEAR_FILE, true);
}
bundle.putBoolean(Define.CLEAR_TRACE, true);
bundle.putBoolean(Define.CLEAR_BUFFER, true);
bundle.putBoolean(Define.IS_SCREEN_SHOTFORBID, true);//禁止截屏
//第三方应用的包名,用于对该应用合法性的验证
bundle.putString(Define.THIRD_PACKAGE, mContext.getPackageName());
//按返回键时是否发送广播true为发送
bundle.putBoolean(Define.BACK_KEY_DOWN, true);
//按home键时是否发送广播true为发送
bundle.putBoolean(Define.HOME_KEY_DOWN, true);
bundle.putBoolean(Define.CACHE_FILE_INVISIBLE, true);
bundle.putBoolean("DisplayView", false);
//正式版wps激活码,购买wps官方应用的时候,wps会提供
bundle.putString(Define.SERIAL_NUMBER_OTHER,"938268264276");
//设置wps显示的水印文字
bundle.putString(Define.WATERMASK_TEXT,"我的应用"));
//设置wps显示的水印颜色
bundle.putInt(Define.WATERMASK_COLOR, Color.argb(40,170,170,170));
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_HISTORY);
intent.setAction(android.content.Intent.ACTION_VIEW);
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
//检查手机是否安装了wps正式版,没有安装就提示下载
if (WpsUtil.checkPackage(mContext, Define.PACKAGENAME_KING_PRO)) {
intent.setClassName(Define.PACKAGENAME_KING_PRO, Define.CLASSNAME);
} else {
ToastUtils.showShortToast(mContext, R.string.remind_user_install_wps);
return false;
}
//传递权限,由于7.0以上的手机需要具有文件权限才能打开,不加权限会崩溃
Uri uri;
if (Build.VERSION.SDK_INT >= 24) {
uri = FileProvider.getUriForFile(mContext.getApplicationContext(), "com.bankcomm.jrt.fileprovider", file);
} else {
uri = Uri.fromFile(file);
}
intent.putExtras(bundle);
String type = WpsUtil.getMIMEType(file);
intent.setDataAndType(uri, type);
if (file == null || !file.exists()) {
ToastUtils.showShortToast(mContext, R.string.feinno_str_file_not_exist_download);
return false;
}
try {
//打开文档
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
ToastUtils.showShortToast(mContext, "文件打开失败请稍后重试");
e.printStackTrace();
}
}
第二种实现方式:
1.如果需要使用AIDL方式操作wps,需要将moffice-aidl.jar和moffice-event-interface.jar两个jar包导入到自己的工程中,并在eclipse中选择Java Build Path的Order and Export中选中两个jar包,这样编出的第三方应用程序才能正常操作控制wps。
2.清单文件中的配置。两个serVice的acticion的name,一定要设置的独特一点,不然如果手机上装了多个可以打开wps的应用的话就会冲突,导致某一个打不开。
实现打开文档的service:
package com.feinnoui.library.wps.client;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import com.feinnoui.library.utils.ToastUtils;
import com.feinnoui.library.wps.util.Define;
import com.feinnoui.library.wps.util.WpsUtil;
import cn.wps.moffice.client.OfficeServiceClient;
import cn.wps.moffice.service.OfficeService;
import cn.wps.moffice.service.doc.Document;
import cn.wps.moffice.service.pdf.PDFReader;
import cn.wps.moffice.service.presentation.Presentation;
import cn.wps.moffice.service.spreadsheet.Workbook;
public class MOfficeClientService extends Service {
private static MOfficeClientService mOfficeClientService;
protected final OfficeServiceClient.Stub mBinder = new OfficeServiceClientImpl(this);
private static String docPath;
public static boolean isBound = false; //是否绑定,为了在关闭wps接收到广播后解绑
private static Presentation mPresentation = null;
private static Workbook mWorkBook = null;
private static PDFReader mPdfReader = null;
private static Document mDoc = null;
private static Context mContext;
public static OfficeService mService;
private static ServiceConnection connection ;
public MOfficeClientService() {
}
@Override
public void onCreate() {
mOfficeClientService = this;
mContext = this;
connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mService = OfficeService.Stub.asInterface(service);
isBound = true;
if (mService!=null)
openFile(mService);
else
ToastUtils.showShortToast(MOfficeClientService.this,"wps服务开启失败,请重试!");
}
@Override
public void onServiceDisconnected(ComponentName name) {
mService = null;
isBound = false;
}
};
bindOfficeService();//绑定wps的服务
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
bindOfficeService();
}
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
if (connection != null) {
unbindService(connection);
}
super.onUnbind(intent);
return true;
}
@Override
public void onStart(Intent intent, int startId) {
}
private boolean bindOfficeService() {
final Intent intent = new Intent();
intent.putExtra("DisplayView", true);
intent.setAction(Define.OFFICE_SERVICE_ACTION);
if (Build.VERSION.SDK_INT > 21) {
intent.setPackage(Define.PACKAGENAME_KING_PRO);
}
if (!bindService(intent, connection, Service.BIND_AUTO_CREATE)) {
unbindService(connection);
isBound = false;
return false;
}
return true;
}
@Override
public void onDestroy() {
isBound = false;
if (connection != null){
try {
unbindService(connection);
connection = null;
} catch (Exception e) {
e.printStackTrace();
}
}
this.stopSelf();
try {
closeFile();
} catch (Exception e) {
e.printStackTrace();
}
mService = null;
super.onDestroy();
}
//打开文档
public void openFile(OfficeService mService) {
if (!isBound|| MOfficeClientService.mService == null) {
ToastUtils.showShortToast(MOfficeClientService.this, "wps服务绑定失败,请手动开启wps自启动权限");
return;
}
if (WpsUtil.isPptFile(docPath)) {//ppt文档
LoadPresentationThread pptFileThread = new LoadPresentationThread(docPath, MOfficeClientService.mService);
pptFileThread.start();
} else if (WpsUtil.isExcelFile(docPath)) {//excel文档
LoadWorkBookThread excelFileThread = new LoadWorkBookThread(docPath, MOfficeClientService.mService);
excelFileThread.start();
} else if (WpsUtil.isPDFFile(docPath)) {//pdf文档
LoadPDFDocThread pdfFileThread = new LoadPDFDocThread(docPath, MOfficeClientService.mService);
pdfFileThread.start();
} else {//doc文档
LoadDocThread docFileThread = new LoadDocThread(docPath, MOfficeClientService.mService);
docFileThread.start();
}
}
public static void setDocPath(String path) {
docPath = path;
}
public void stopService() {
isBound = false;
if (connection != null) {
unbindService(connection);
}
stopSelf();
try {
closeFile();
} catch (Exception e) {
e.printStackTrace();
}
mService = null;
}
public static MOfficeClientService getInstance() {
return mOfficeClientService;
}
class LoadPresentationThread extends Thread// 内部类
{
String path;
OfficeService service;
public LoadPresentationThread(String path, OfficeService service) {
this.path = path;
this.service = service;
}
public void run() {
// 打开文档
if (service == null || !bindOfficeService()) {
return;
}
try {
Intent intent = new Intent();
Bundle bundle = WpsUtil.getOpenFileBundle(MOfficeClientService.this);
intent.putExtras(bundle);
mPresentation = mService.openPresentation(path, "", intent);
} catch (RemoteException e) {
e.printStackTrace();
mPresentation = null;
}
}
}
class LoadWorkBookThread extends Thread// 内部类
{
String path;
OfficeService service;
public LoadWorkBookThread(String path, OfficeService service) {
this.path = path;
this.service = service;
}
public void run() {
// 打开文档
if (service == null || !bindOfficeService()) {
return;
}
try {
Intent intent = new Intent();
Bundle bundle = WpsUtil.getOpenFileBundle(MOfficeClientService.this);
intent.putExtras(bundle);
mWorkBook = mService.getWorkbooks().openBookEx(path, "", intent);
} catch (RemoteException e) {
e.printStackTrace();
mWorkBook = null;
}
}
}
class LoadPDFDocThread extends Thread// 内部类
{
String path;
OfficeService service;
public LoadPDFDocThread(String path, OfficeService service) {
this.path = path;
this.service = service;
}
public void run() {
// 打开文档
if (service == null || !bindOfficeService()) {
return;
}
try {
Intent intent = new Intent();
Bundle bundle = WpsUtil.getOpenFileBundle(MOfficeClientService.this);
intent.putExtras(bundle);
mPdfReader = mService.openPDFReader(path, "", intent);
} catch (RemoteException e) {
e.printStackTrace();
mPdfReader = null;
}
}
}
class LoadDocThread extends Thread// 内部类
{
String path;
OfficeService service;
public LoadDocThread(String path, OfficeService service) {
this.path = path;
this.service = service;
}
public void run() {
// 打开文档
if (service == null || !bindOfficeService()) {
return;
}
try {
Intent intent = new Intent();
Bundle bundle = WpsUtil.getOpenFileBundle(MOfficeClientService.this);
intent.putExtras(bundle);
mDoc = mService.openWordDocument(path, "", intent);
} catch (Exception e) {
e.printStackTrace();
mDoc = null;
}
}
}
public static void closeFile() throws RemoteException {
if (mPresentation != null) {
mPresentation.close();
mPresentation = null;
} else if (mWorkBook != null) {
mWorkBook.close();
mWorkBook = null;
} else if ( mPdfReader != null) {
mPdfReader.close();
mPdfReader = null;
} else {
if (mDoc != null) {
mDoc.close();
mDoc = null;
}
}
}
}
wpsUtil的工具类:
package com.feinnoui.library.wps.util;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.graphics.Color;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.FileProvider;
import android.text.TextUtils;
import android.widget.Toast;
import com.feinnoui.library.utils.PreferenceUtil;
import java.io.File;
import java.io.InputStream;
public class WpsUtil {
/**
* 判断是否是wps能打开的文件
*
* @param file
* @return
*/
public static boolean IsWPSFile(File file) {
if(null == file){
return false;
}
String end = file.getName().substring(file.getName().lastIndexOf(".") + 1,
file.getName().length()).toLowerCase();
if (end.equals("doc") || end.equals("dot") || end.equals("wps")
|| end.equals("wpt") || end.equals("docx") || end.equals("dotx")
|| end.equals("docm") || end.equals("dotm") || end.equals("rtf")
|| end.equals("xls") || end.equals("xlt") || end.equals("et")
|| end.equals("ett") || end.equals("xlsx") || end.equals("xltx")
|| end.equals("csv") || end.equals("xlsb") || end.equals("xlsm")
|| end.equals("xml") || end.equals("html") || end.equals("htm")
|| end.equals("ppt") || end.equals("pptx") || end.equals("dps")
|| end.equals("pot") || end.equals("pps") || end.equals("dpt")
|| end.equals("potx") || end.equals("ppsx") || end.equals("pptm")
|| end.equals("txt") || end.equals("pdf")
|| end.equals("potm") || end.equals("ppsm")) {
return true;
} else {
return false;
}
}
public static boolean isPDFFile(String filePath) {
if(TextUtils.isEmpty(filePath)){
return false;
}
String path = filePath.toLowerCase();
return path.endsWith(".pdf");
}
public static boolean isPptFile(String filePath) {
if(TextUtils.isEmpty(filePath)){
return false ;
}
filePath = filePath.toLowerCase();
return filePath.endsWith(".ppt")
|| filePath.endsWith("pptx");
}
public static boolean isExcelFile(String filePath) {
if(TextUtils.isEmpty(filePath)){
return false;
}
filePath = filePath.toLowerCase();
return filePath.endsWith(".xls")
|| filePath.endsWith(".xlsx")
|| filePath.endsWith(".et");
}
zz
public static Bundle getOpenFileBundle(Context context) {
Bundle bundle = new Bundle();
bundle.putString(Define.OPEN_MODE, Define.READ_ONLY); //打开模式
bundle.putBoolean(Define.HOME_KEY_DOWN, true);
bundle.putBoolean(Define.BACK_KEY_DOWN, true);
bundle.putBoolean(Define.SEND_CLOSE_BROAD, true);
bundle.putString(Define.THIRD_PACKAGE, context.getPackageName());
bundle.putBoolean(Define.CLEAR_FILE, true);
bundle.putBoolean(Define.CLEAR_TRACE, true);
bundle.putBoolean(Define.CLEAR_BUFFER, true);
bundle.putBoolean(Define.CACHE_FILE_INVISIBLE, true);
bundle.putString(Define.SERIAL_NUMBER_OTHER,"934611435276");
bundle.putBoolean(Define.FAIR_COPY,true);
bundle.putBoolean(Define.SHOW_REVIEWING_PANE_RIGHT_DEFAULT,false);
bundle.putBoolean("DisplayView", true);//如果用的是标准版的wps的话,这行代码不能少,否则打不开文档
bundle.putBoolean("isScreenshotForbid", true);
//agentClassName一定要与清单文件中配置的acticion的name保持一致,否则将会导致在OfficeEventListenerImpl中禁用功能的代码不生效。
bundle.putString("agentClassName","cn.wps.moffice.agent.OfficeServiceAgent3");
bundle.putString(Define.WATERMASK_TEXT, PreferenceUtil.getString("user_mobile","交融通"));
bundle.putInt(Define.WATERMASK_COLOR, Color.argb(40,170,170,170));
return bundle;
}
}
需要打开文件的时候调用此方法:
private void openWpsFile(Context mContext, String docPath) {
MOfficeClientService.setDocPath(docPath);
if (MOfficeClientService.isBound){
MOfficeClientService.getInstance().openFile(MOfficeClientService.mService);
}else {
Intent serviceIntent = new Intent();
serviceIntent.setClass(mContext, MOfficeClientService.class);
serviceIntent.setAction("cn.wps.moffice.client.OfficeServiceClient3");
mContext.startService(serviceIntent);
}
}
如果你的应用想控制WPS的部分功能是否可以使用,也是可以实现的,wps提供了现成的方法。WPS在执行被限制的功能前,会通过连接好的Service,调用Client的 isActionAllowed(String path, ActionType type) 方法,通过该方法的返回值来决定该功能是否执行。所以如果想使得、那个功能不可使用,直接返回false即可。
/**下面以限制复制功能为例 */
@Override
public boolean isActionAllowed(String path, ActionType type) throws RemoteException
{
//如果是复制类型的事件,则返回false,即不允许复制
If (type.equals(ActionType.AT_CUT))
Return false;
Return true;
}
isViewForbidden方法是为了实现功能是否被禁用而存在的,但是仅word文档有用,与isActionAllowed相反。
@Override
public boolean isViewForbidden(String arg0, ViewType type) throws RemoteException {
//仅word文档有用
if (type.equals(ViewType.VT_MENU_FILE))
{
return true;
}
return false;
}
isViewForbidden方法是控制功能入口是否显示的,但是仅word文档有用
@Override
public boolean isViewInVisible(String arg0, ViewType type) throws RemoteException {
//仅word文档有用
if (type.equals(ViewType.VT_MENU_VIEW)){
return false;
}
return true;
}