一、前言
之前做过USB打印机,但是现在需求变了,不是使用收银台进行打印机的连接了,而是使用手机与打印机进行打印。手机又无法像收银机一样直接使用USB直接与打印机直接连接进行打印。所以只能使用蓝牙打印机或者wifi打印机了,但是项目的需求中需要使用到语音播报的功能,配备的是蓝牙音箱,故只剩下了wifi打印机了。因为之前使用过佳博的打印机,所以也就直接推荐公司买了佳博的wifi打印机。一直想整理下写一篇博文记录一下,但是一直没有时间,也就不了了之了,趁着现在需求少一点,赶紧撸一个,不多说,直接开撸。
二、佳博打印机配置教程
在上一篇博文硬件系列(一)----------WIFI打印机之使用socket进行打印机搜索中,介绍了wifi打印机的搜索,在这里就不多说了,想了解的就去看另外一篇博文吧。
2.1、配置文件配置
<uses-permission android:name="android.permission.INTERNET" />
<!--wifi打印机权限-->
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.hardware.usb.accessory" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permissionandroid:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.GET_TASKS" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
<uses-permissionandroid:name="android.permission.WRITE_SETTINGS"tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /><uses-feature android:name="android.hardware.usb.host" />
<serviceandroid:name="com.gprinter.service.GpPrintService"android:enabled="true"android:exported="true"android:label="GpPrintService"><intent-filter><action android:name="com.gprinter.aidl.GpPrintService" /></intent-filter>
</service>
<service android:name="com.gprinter.service.AllService" />
2.2、添加aidl文件
新建包,包名为com.gprinter.aidl,向包中添加文件,文件名为GpService.aidl内容为:注意,包名要一样哦!!!如下图所示
package com.gprinter.aidl;
interface GpService{ int openPort(int PrinterId,int PortType,String DeviceName,int PortNumber);void closePort(int PrinterId);int getPrinterConnectStatus(int PrinterId);int printeTestPage(int PrinterId); void queryPrinterStatus(int PrinterId,int Timesout,int requestCode);int getPrinterCommandType(int PrinterId);int sendEscCommand(int PrinterId, String b64);int sendLabelCommand(int PrinterId, String b64);void isUserExperience(boolean userExperience);String getClientID();int setServerIP(String ip, int port);
}
2.3、启动并绑定 启动并绑定PrinterPrintService服务
PrinterPrintService我就一起放在打印工具类里面了,如下:
/*** @author Freak* @date 2019/8/13.*/public class PrinterUtil {private PrinterServiceConnection conn;private BroadCastReceiver mBroadcastReceiver;private GpService mGpService;private Context context;private boolean isBind;class PrinterServiceConnection implements ServiceConnection {@Overridepublic void onServiceConnected(ComponentName name, IBinder service) {mGpService = GpService.Stub.asInterface(service);}@Overridepublic void onServiceDisconnected(ComponentName name) {Log.i("ServiceConnection", "onServiceDisconnected() called");mGpService = null;}}public PrinterUtil(Context context) {this.context = context;mBroadcastReceiver = new BroadCastReceiver();connection();doRegisterReceiver();}/*** 连接 开启服务** @param*/private void connection() {LogUtil.e("开启打印服务");conn = new PrinterServiceConnection();Intent intent = new Intent(context, GpPrintService.class);// bindServiceisBind = context.bindService(intent, conn, Context.BIND_AUTO_CREATE);}/*** 注册广播** @param*/public void doRegisterReceiver() {// 注册实时状态查询广播context.registerReceiver(mBroadcastReceiver, new IntentFilter(GpCom.ACTION_DEVICE_REAL_STATUS));/*** 票据模式下,可注册该广播,在需要打印内容的最后加入addQueryPrinterStatus(),在打印完成后会接收到* action为GpCom.ACTION_DEVICE_STATUS的广播,特别用于连续打印,* 可参照该sample中的sendReceiptWithResponse方法与广播中的处理**/context.registerReceiver(mBroadcastReceiver, new IntentFilter(GpCom.ACTION_RECEIPT_RESPONSE));/*** 标签模式下,可注册该广播,在需要打印内容的最后加入addQueryPrinterStatus(RESPONSE_MODE mode)* ,在打印完成后会接收到,action为GpCom.ACTION_LABEL_RESPONSE的广播,特别用于连续打印,* 可参照该sample中的sendLabelWithResponse方法与广播中的处理**/context.registerReceiver(mBroadcastReceiver, new IntentFilter(GpCom.ACTION_LABEL_RESPONSE));}/*** 连接打印机*/public void connectToDevice(String ip, int port) {int rel = 0;try {rel = mGpService.openPort(0, 3, ip, port);LogUtil.e("开始连接");} catch (RemoteException e) {e.printStackTrace();}GpCom.ERROR_CODE r = GpCom.ERROR_CODE.values()[rel];if (r == GpCom.ERROR_CODE.SUCCESS) {LogUtil.e("打印机连接成功");} else {LogUtil.e("连接失败原因--》" + GpCom.getErrorText(r));}LogUtil.e("打印机连接状态--》" + GpCom.getErrorText(r));}/*** 断开连接、关闭端口*/public void disConnectToDevice() {try {mGpService.closePort(0);LogUtil.e("关闭端口");} catch (RemoteException e) {e.printStackTrace();}}/**** 查询打印机连接状态*/public void checkPrintLinStatus() {try {int printStatus = mGpService.getPrinterConnectStatus(0);switch (printStatus) {case 0:LogUtil.e("连接断开");break;case 1:LogUtil.e("监听状态");break;case 2:LogUtil.e("正在连接");break;case 3:LogUtil.e("已连接");break;default:break;}} catch (RemoteException e1) {// TODO Auto-generated catch blocke1.printStackTrace();}}/*** 后台控制打印格式方式打印测试** @param arg*/public void printFormat(String arg) {Map<String, Object> styleMap = new HashMap<>();styleMap.put("<center>", "CENTER");styleMap.put("<center_bold>", "CENTER_BOLD");styleMap.put("<title>", "TITLE");styleMap.put("<left>", "LEFT");styleMap.put("<left_bold>", "LEFT_BOLD");styleMap.put("<right>", "RIGHT");styleMap.put("<right_bold>", "RIGHT_BOLD");styleMap.put("<bold>", "BOLD");Stack stack = new Stack<String>();StringBuffer sb = new StringBuffer();int pos = 0;boolean flag;int start = 0;int end = 0;String type = "";List<String> typeList = new ArrayList<>();List<String> printTextList = new ArrayList<>();while (arg.length() > pos) {flag = true;if (arg.charAt(pos) == '<') {for (Object key : styleMap.keySet()) {if (arg.length() > (pos + key.toString().length()) && arg.substring(pos, pos + key.toString().length()).equals(key)) {type = styleMap.get(key).toString();stack.push(styleMap.get(key));pos += key.toString().length();start = pos;flag = false;break;}}if (flag) {pos = arg.indexOf('>', pos) + 1;if (!stack.isEmpty()) {int length = stack.lastElement().toString().length();end = pos - length - 4;typeList.add(type);printTextList.add(arg.substring(start, end + 1));stack.pop();}}} else {sb.append(arg.charAt(pos));pos++;}}Logger.e(sb.toString());typeList.add("end");printTextList.add("-------------------------------");startPrint(typeList, printTextList);}public void startPrint(List<String> type, List<String> printText) {EscCommand escCommand = new EscCommand();escCommand.addInitializePrinter();for (int i = 0; i < type.size(); i++) {if ("CENTER_BOLD".equals(type.get(i))) {//设置打印居中escCommand.addSelectJustification(EscCommand.JUSTIFICATION.CENTER);//设置加粗escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.ON, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);//是否加重escCommand.addTurnDoubleStrikeOnOrOff(EscCommand.ENABLE.ON);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("TITLE".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.CENTER);//设置不加粗escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.ON, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 2);} else if ("CENTER".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.CENTER);//设置不加粗escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("LEFT".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.LEFT);//设置不加粗escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("LEFT_BOLD".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.LEFT);//设置不加粗escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.ON, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);//是否加重escCommand.addTurnDoubleStrikeOnOrOff(EscCommand.ENABLE.ON);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("RIGHT".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.RIGHT);escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("RIGHT_BOLD".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.RIGHT);escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.ON, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addTurnDoubleStrikeOnOrOff(EscCommand.ENABLE.ON);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);} else if ("END".equals(type.get(i))) {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.LEFT);escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 2);} else {escCommand.addSelectJustification(EscCommand.JUSTIFICATION.LEFT);escCommand.addSelectPrintModes(EscCommand.FONT.FONTA, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF, EscCommand.ENABLE.OFF);escCommand.addText(printText.get(i));escCommand.addPrintAndFeedLines((byte) 1);}}escCommand.addPrintAndLineFeed();// 加入查询打印机状态,打印完成后,此时会接收到GpCom.ACTION_DEVICE_STATUS广播escCommand.addQueryPrinterStatus();// 发送数据Vector<Byte> data = escCommand.getCommand();byte[] bytes = GpUtils.ByteTo_byte(data);String sss = Base64.encodeToString(bytes, Base64.DEFAULT);int rs;try {rs = mGpService.sendEscCommand(0, sss);GpCom.ERROR_CODE r = GpCom.ERROR_CODE.values()[rs];if (r != GpCom.ERROR_CODE.SUCCESS) {disConnectToDevice();} else {disConnectToDevice();mHandler.sendEmptyMessage(0);}} catch (RemoteException e) {// TODO Auto-generated catch blocke.printStackTrace();}}public void printerOnDestroy() {if (conn != null) {// unBindService//解决14254 java.lang.IllegalArgumentException Service not registered错误 此错误在执行解绑成功之后在进行解绑导致的错误if (isBind) {context.unbindService(conn);context.unregisterReceiver(mBroadcastReceiver);isBind = false;LogUtil.e("解绑服务");}}}/*** 查询打印机状态并返回** @return*/public int isLin() {int printStatus = 0;try {printStatus = mGpService.getPrinterConnectStatus(0);} catch (RemoteException e) {e.printStackTrace();}return printStatus;}@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what) {case 0:LogUtil.e("打印成功");ToastUtil.show("打印成功");break;default:break;}}};
}
2.4、BroadCastReceiver类
/*** @author Freak* @date 2019/8/13.*/public class BroadCastReceiver extends BroadcastReceiver {private static final int REQUEST_PRINT_RECEIPT = 0xfc;@Overridepublic void onReceive(Context context, Intent intent) {String action = intent.getAction();LogUtil.e("Action-->" + action);// GpCom.ACTION_DEVICE_REAL_STATUS 为广播的IntentFilterif (action.equals(GpCom.ACTION_DEVICE_REAL_STATUS)) {// 业务逻辑的请求码,对应哪里查询做什么操作int requestCode = intent.getIntExtra(GpCom.EXTRA_PRINTER_REQUEST_CODE, -1);// 判断请求码,是则进行业务操作if (requestCode == REQUEST_PRINT_RECEIPT) {int status = intent.getIntExtra(GpCom.EXTRA_PRINTER_REAL_STATUS, 16);String str;if (status == GpCom.STATE_NO_ERR) {str = "打印机正常";} else {str = "打印机 ";if ((byte) (status & GpCom.STATE_OFFLINE) > 0) {str += "脱机";}if ((byte) (status & GpCom.STATE_PAPER_ERR) > 0) {str += "缺纸";}if ((byte) (status & GpCom.STATE_COVER_OPEN) > 0) {str += "打印机开盖";}if ((byte) (status & GpCom.STATE_ERR_OCCURS) > 0) {str += "打印机出错";}if ((byte) (status & GpCom.STATE_TIMES_OUT) > 0) {str += "查询超时";}}LogUtil.e("打印机状态--》" + str);ToastUtil.show(str);}}}
}
2.4、打印测试方法
public void checkLinStatusBeforePrint(final String ip, final int port) {int mLin = App.getPrinterUtilInstance().isLin();LogUtil.e("连接返回状态--》" + mLin);switch (mLin) {//连接断开case 0:mHandler.sendEmptyMessage(0);break;//监听状态case 1:break;//正在连接,延迟一秒再次查询状态case 2://延迟1秒后再检查连接状态if (count > 20) {ToastUtil.show("无法连接打印机,请检查打印机是否正常工作,稍后重试");} else {new Handler().postDelayed(new Runnable() {@Overridepublic void run() {mHandler.sendEmptyMessage(2);}}, 100);count++;}break;//连接成功case 3:count = 0;ThreadPoolProxyFactory.getNormalThreadPoolProxy().execute(new Runnable() {@Overridepublic void run() {App.getPrinterUtilInstance().printFormat("<center_bold>打印测试</center_bold>" +"<left>测试金额:66686.3</left>");}});break;default:break;}
}
/*** 查找打印Handel、检查打印机状态Handel*/
@SuppressLint("HandlerLeak")
private Handler mHandler = new Handler() {@Overridepublic void handleMessage(Message msg) {switch (msg.what) {//断开连接case 0://sp文件保存的是否开启打印boolean isOpenPrinter = SharedPreferencesUtils.getBoolean(getActivity(), SharedPreferencesUtils.Printer_Open_Status);if (isOpenPrinter) {if (TextUtils.isEmpty(getIpAddress())) {ToastUtil.show("请先设置打印机ip地址");} else {App.getPrinterUtilInstance().connectToDevice(getIpAddress(), 9100);checkLinStatusBeforePrint(getIpAddress(), 9100);}} else {ToastUtil.show("打印机未打开,请到打印机设置中打开打印机");}break;//搜索结束case 1:setSharedPreferencesIpAddress();break;//正在连接case 2://sp文件保存的是否开启打印boolean mIsOpenPrinter = SharedPreferencesUtils.getBoolean(getActivity(), SharedPreferencesUtils.Printer_Open_Status);if (mIsOpenPrinter) {if (TextUtils.isEmpty(getIpAddress())) {ToastUtil.show("请先设置打印机ip地址");} else {checkLinStatusBeforePrint(getIpAddress(), 9100);}} else {ToastUtil.show("打印机未打开,请到打印机设置中打开打印机");}break;case 4:ToastUtil.show("连接成功");break;case 5:mPrinterSettingAdapter.setNewData(mList);break;case 6:ToastUtil.show("连接失败");break;default:break;}}
};private void setSharedPreferencesIpAddress() {for (int i = 0; i < mList.size(); i++) {if (mList.get(i).isSelect()) {SharedPreferencesUtils.save(getActivity(), SharedPreferencesUtils.Printer_IP_Address, mList.get(i).getIp());} else {SharedPreferencesUtils.save(getActivity(), SharedPreferencesUtils.Printer_IP_Address, "");}}
}
到此,佳博wifi打印机的打印就完成了,当然,这个是使用sdk进行打印的,sdk可以在下载中进行下载。