#import "BluetoothPrintPlugin.h" #import "ConnecterManager.h" #import "EscCommand.h" #import "TscCommand.h" @interface BluetoothPrintPlugin () @property(nonatomic, retain) NSObject *registrar; @property(nonatomic, retain) FlutterMethodChannel *channel; @property(nonatomic, retain) BluetoothPrintStreamHandler *stateStreamHandler; @property(nonatomic, assign) int stateID; @property(nonatomic) NSMutableDictionary *scannedPeripherals; @end @implementation BluetoothPrintPlugin + (void)registerWithRegistrar:(NSObject*)registrar { FlutterMethodChannel* channel = [FlutterMethodChannel methodChannelWithName:NAMESPACE @"/methods" binaryMessenger:[registrar messenger]]; FlutterEventChannel* stateChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/state" binaryMessenger:[registrar messenger]]; BluetoothPrintPlugin* instance = [[BluetoothPrintPlugin alloc] init]; instance.channel = channel; instance.scannedPeripherals = [NSMutableDictionary new]; // STATE BluetoothPrintStreamHandler* stateStreamHandler = [[BluetoothPrintStreamHandler alloc] init]; [stateChannel setStreamHandler:stateStreamHandler]; instance.stateStreamHandler = stateStreamHandler; [registrar addMethodCallDelegate:instance channel:channel]; } - (void)handleMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { NSLog(@"call method -> %@", call.method); if ([@"state" isEqualToString:call.method]) { result([NSNumber numberWithInt:self.stateID]); } else if([@"isAvailable" isEqualToString:call.method]) { result(@(YES)); } else if([@"isConnected" isEqualToString:call.method]) { bool isConnected = self.stateID == 1; result(@(isConnected)); } else if([@"isOn" isEqualToString:call.method]) { result(@(YES)); }else if([@"startScan" isEqualToString:call.method]) { NSLog(@"getDevices method -> %@", call.method); [self.scannedPeripherals removeAllObjects]; if (Manager.bleConnecter == nil) { [Manager didUpdateState:^(NSInteger state) { switch (state) { case CBCentralManagerStateUnsupported: NSLog(@"The platform/hardware doesn't support Bluetooth Low Energy."); break; case CBCentralManagerStateUnauthorized: NSLog(@"The app is not authorized to use Bluetooth Low Energy."); break; case CBCentralManagerStatePoweredOff: NSLog(@"Bluetooth is currently powered off."); break; case CBCentralManagerStatePoweredOn: [self startScan]; NSLog(@"Bluetooth power on"); break; case CBCentralManagerStateUnknown: default: break; } }]; } else { [self startScan]; } result(nil); } else if([@"stopScan" isEqualToString:call.method]) { [Manager stopScan]; result(nil); } else if([@"connect" isEqualToString:call.method]) { NSDictionary *device = [call arguments]; @try { NSLog(@"connect device begin -> %@", [device objectForKey:@"name"]); CBPeripheral *peripheral = [_scannedPeripherals objectForKey:[device objectForKey:@"address"]]; self.state = ^(ConnectState state) { [self updateConnectState:state]; }; [Manager connectPeripheral:peripheral options:nil timeout:2 connectBlack: self.state]; result(nil); } @catch(FlutterError *e) { result(e); } } else if([@"disconnect" isEqualToString:call.method]) { @try { [Manager close]; result(nil); } @catch(FlutterError *e) { result(e); } } else if([@"print" isEqualToString:call.method]) { @try { result(nil); } @catch(FlutterError *e) { result(e); } } else if([@"printReceipt" isEqualToString:call.method]) { @try { NSDictionary *args = [call arguments]; [Manager write:[self mapToEscCommand:args]]; result(nil); } @catch(FlutterError *e) { result(e); } } else if([@"printLabel" isEqualToString:call.method]) { @try { NSDictionary *args = [call arguments]; [Manager write:[self mapToTscCommand:args]]; result(nil); } @catch(FlutterError *e) { result(e); } }else if([@"printTest" isEqualToString:call.method]) { @try { result(nil); } @catch(FlutterError *e) { result(e); } } } -(NSData *)mapToTscCommand:(NSDictionary *) args { NSDictionary *config = [args objectForKey:@"config"]; NSMutableArray *list = [args objectForKey:@"data"]; NSNumber *width = ![config objectForKey:@"width"]?@"48" : [config objectForKey:@"width"]; NSNumber *height = ![config objectForKey:@"height"]?@"80" : [config objectForKey:@"height"]; NSNumber *gap = ![config objectForKey:@"gap"]?@"2" : [config objectForKey:@"gap"]; TscCommand *command = [[TscCommand alloc]init]; // 设置标签尺寸宽高,按照实际尺寸设置 单位mm [command addSize:[width intValue] :[height intValue]]; // 设置标签间隙,按照实际尺寸设置,如果为无间隙纸则设置为0 单位mm [command addGapWithM:[gap intValue] withN:0]; // 设置原点坐标 [command addReference:0 :0]; // 撕纸模式开启 [command addTear:@"ON"]; // 开启带Response的打印,用于连续打印 [command addQueryPrinterStatus:ON]; // 清除打印缓冲区 [command addCls]; for(NSDictionary *m in list){ NSString *type = [m objectForKey:@"type"]; NSString *content = [m objectForKey:@"content"]; NSNumber *x = ![m objectForKey:@"x"]?@0 : [m objectForKey:@"x"]; NSNumber *y = ![m objectForKey:@"y"]?@0 : [m objectForKey:@"y"]; if([@"text" isEqualToString:type]){ [command addTextwithX:[x intValue] withY:[y intValue] withFont:@"TSS24.BF2" withRotation:0 withXscal:1 withYscal:1 withText:content]; }else if([@"barcode" isEqualToString:type]){ [command add1DBarcode:[x intValue] :[y intValue] :@"CODE128" :100 :1 :0 :2 :2 :content]; }else if([@"qrcode" isEqualToString:type]){ [command addQRCode:[x intValue] :[y intValue] :@"L" :5 :@"A" :0 :content]; }else if([@"image" isEqualToString:type]){ NSData *decodeData = [[NSData alloc] initWithBase64EncodedString:content options:0]; UIImage *image = [UIImage imageWithData:decodeData]; [command addBitmapwithX:[x intValue] withY:[y intValue] withMode:0 withWidth:300 withImage:image]; } } [command addPrint:1 :1]; return [command getCommand]; } -(NSData *)mapToEscCommand:(NSDictionary *) args { NSDictionary *config = [args objectForKey:@"config"]; NSMutableArray *list = [args objectForKey:@"data"]; EscCommand *command = [[EscCommand alloc]init]; [command addInitializePrinter]; [command addPrintAndFeedLines:3]; for(NSDictionary *m in list){ NSString *type = [m objectForKey:@"type"]; NSString *content = [m objectForKey:@"content"]; NSNumber *align = ![m objectForKey:@"align"]?@0 : [m objectForKey:@"align"]; NSNumber *size = ![m objectForKey:@"size"]?@4 : [m objectForKey:@"size"]; NSNumber *weight = ![m objectForKey:@"weight"]?@0 : [m objectForKey:@"weight"]; NSNumber *width = ![m objectForKey:@"width"]?@0 : [m objectForKey:@"width"]; NSNumber *height = ![m objectForKey:@"height"]?@0 : [m objectForKey:@"height"]; NSNumber *underline = ![m objectForKey:@"underline"]?@0 : [m objectForKey:@"underline"]; NSNumber *linefeed = ![m objectForKey:@"linefeed"]?@0 : [m objectForKey:@"linefeed"]; //内容居左(默认居左) [command addSetJustification:[align intValue]]; if([@"text" isEqualToString:type]){ Byte mode = PrintModeEnumDefault; if ([weight intValue] == 1) mode = mode | PrintModeEnumBold; if ([underline intValue] == 1) mode = mode | PrintModeEnumUnderline; [command addPrintMode: mode]; Byte size = CharacterSizeEnumDefault; if ([height intValue] == 1) size = size | CharacterSizeEnumDoubleHeight; if ([width intValue] == 1) size = size | CharacterSizeEnumDoubleWidth; [command addSetCharcterSize: size]; [command addText:content]; [command addPrintMode: PrintModeEnumDefault]; [command addSetCharcterSize: CharacterSizeEnumDefault]; }else if([@"barcode" isEqualToString:type]){ [command addSetBarcodeWidth:2]; [command addSetBarcodeHeight:60]; [command addSetBarcodeHRPosition:2]; [command addCODE128:'B' : content]; }else if([@"qrcode" isEqualToString:type]){ // This code snippet has been copied from https://stackoverflow.com/q/34608340/1993514 int store_len = (int )content.length + 3; int pl = (store_len % 256); [command addQRCodeSizewithpL:0 withpH:0 withcn:49 withyfn:67 withn:[size intValue]]; [command addQRCodeLevelwithpL:0 withpH:0 withcn:49 withyfn:69 withn:[size intValue]]; [command addQRCodeSavewithpL:pl withpH:0 withcn:49 withyfn:80 withm:48 withData:[content dataUsingEncoding:NSASCIIStringEncoding]]; [command addQRCodePrintwithpL:3 withpH:pl+3 withcn:49 withyfn:81 withm:48]; }else if([@"image" isEqualToString:type]){ NSData *decodeData = [[NSData alloc] initWithBase64EncodedString:content options:0]; UIImage *image = [UIImage imageWithData:decodeData]; [command addOriginrastBitImage:image width:0]; } if([linefeed isEqualToNumber:@1]){ [command addPrintAndLineFeed]; } } [command addPrintAndFeedLines:4]; return [command getCommand]; } -(void)startScan { [Manager scanForPeripheralsWithServices:nil options:nil discover:^(CBPeripheral * _Nullable peripheral, NSDictionary * _Nullable advertisementData, NSNumber * _Nullable RSSI) { if (peripheral.name != nil) { NSLog(@"find device -> %@", peripheral.name); [self.scannedPeripherals setObject:peripheral forKey:[[peripheral identifier] UUIDString]]; NSDictionary *device = [NSDictionary dictionaryWithObjectsAndKeys:peripheral.identifier.UUIDString,@"address",peripheral.name,@"name",nil,@"type",nil]; [_channel invokeMethod:@"ScanResult" arguments:device]; } }]; } -(void)updateConnectState:(ConnectState)state { dispatch_async(dispatch_get_main_queue(), ^{ NSNumber *ret = @0; switch (state) { case CONNECT_STATE_CONNECTING: NSLog(@"status -> %@", @"连接状态:连接中...."); ret = @0; self.stateID = 0; break; case CONNECT_STATE_CONNECTED: NSLog(@"status -> %@", @"连接状态:连接成功"); ret = @1; self.stateID = 1; break; case CONNECT_STATE_FAILT: NSLog(@"status -> %@", @"连接状态:连接失败"); ret = @0; break; case CONNECT_STATE_DISCONNECT: NSLog(@"status -> %@", @"连接状态:断开连接"); ret = @0; self.stateID = -1; break; default: NSLog(@"status -> %@", @"连接状态:连接超时"); ret = @0; self.stateID = -1; break; } NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:ret,@"id",nil]; if(_stateStreamHandler.sink != nil) { self.stateStreamHandler.sink([dict objectForKey:@"id"]); } }); } @end @implementation BluetoothPrintStreamHandler - (FlutterError*)onListenWithArguments:(id)arguments eventSink:(FlutterEventSink)eventSink { self.sink = eventSink; return nil; } - (FlutterError*)onCancelWithArguments:(id)arguments { self.sink = nil; return nil; } @end