提交 5198289b authored 作者: houziyu's avatar houziyu

Initial commit

上级
.DS_Store
.dart_tool/
.idea
.packages
.pub/
build/
#Thu May 09 15:59:56 ICT 2019
gradle.version=5.1.1
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
channel: stable
project_type: plugin
## 1.1.8
* Fix bug on ios build
## 1.1.7
* dartdoc comments and Dart formatter
## 1.1.6
* Add print3Column method
* Add print4Column method
* Support format string
## 1.1.5
* Migrate to Android embedding v2
## 1.1.4
* Fix Error Null-Safety
## 1.1.3
* Fix Cast Error on getBondedDevices
## 1.1.2
* Migrate to null-safety
## 1.1.1
* Support different charset in print methods, thanks to danilof
## 1.1.0
* Add print image bytes method, thanks to mvanvu
## 1.0.9
* Avoiding activity null pointer, thanks to wmattei
## 1.0.8
* fix crash with "Methods marked with @UiThread must be executed on the main thread, thanks to ricardochen
## 1.0.7
* change documentation
## 1.0.6
* add size to printleftright
## 1.0.5
* fix bug
## 1.0.4
* fix bug
## 1.0.3
* Add print left + right
## 1.0.2
* Add Endline in QRCODE
## 1.0.1
* Add Doc.
## 1.0.0
* initial release.
THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS COMMON PUBLIC
LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
1. DEFINITIONS
"Contribution" means:
a) in the case of the initial Contributor, the initial code and
documentation distributed under this Agreement, and
b) in the case of each subsequent Contributor:
i) changes to the Program, and
ii) additions to the Program;
where such changes and/or additions to the Program originate from and are
distributed by that particular Contributor. A Contribution 'originates' from a
Contributor if it was added to the Program by such Contributor itself or anyone
acting on such Contributor's behalf. Contributions do not include additions to
the Program which: (i) are separate modules of software distributed in
conjunction with the Program under their own license agreement, and (ii) are not
derivative works of the Program.
"Contributor" means any person or entity that distributes the Program.
"Licensed Patents " mean patent claims licensable by a Contributor which are
necessarily infringed by the use or sale of its Contribution alone or when
combined with the Program.
"Program" means the Contributions distributed in accordance with this Agreement.
"Recipient" means anyone who receives the Program under this Agreement,
including all Contributors.
2. GRANT OF RIGHTS
a) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free copyright license to
reproduce, prepare derivative works of, publicly display, publicly perform,
distribute and sublicense the Contribution of such Contributor, if any, and such
derivative works, in source code and object code form.
b) Subject to the terms of this Agreement, each Contributor hereby grants
Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed
Patents to make, use, sell, offer to sell, import and otherwise transfer the
Contribution of such Contributor, if any, in source code and object code form.
This patent license shall apply to the combination of the Contribution and the
Program if, at the time the Contribution is added by the Contributor, such
addition of the Contribution causes such combination to be covered by the
Licensed Patents. The patent license shall not apply to any other combinations
which include the Contribution. No hardware per se is licensed hereunder.
c) Recipient understands that although each Contributor grants the licenses
to its Contributions set forth herein, no assurances are provided by any
Contributor that the Program does not infringe the patent or other intellectual
property rights of any other entity. Each Contributor disclaims any liability to
Recipient for claims brought by any other entity based on infringement of
intellectual property rights or otherwise. As a condition to exercising the
rights and licenses granted hereunder, each Recipient hereby assumes sole
responsibility to secure any other intellectual property rights needed, if any.
For example, if a third party patent license is required to allow Recipient to
distribute the Program, it is Recipient's responsibility to acquire that license
before distributing the Program.
d) Each Contributor represents that to its knowledge it has sufficient
copyright rights in its Contribution, if any, to grant the copyright license set
forth in this Agreement.
3. REQUIREMENTS
A Contributor may choose to distribute the Program in object code form under its
own license agreement, provided that:
a) it complies with the terms and conditions of this Agreement; and
b) its license agreement:
i) effectively disclaims on behalf of all Contributors all warranties and
conditions, express and implied, including warranties or conditions of title and
non-infringement, and implied warranties or conditions of merchantability and
fitness for a particular purpose;
ii) effectively excludes on behalf of all Contributors all liability for
damages, including direct, indirect, special, incidental and consequential
damages, such as lost profits;
iii) states that any provisions which differ from this Agreement are offered
by that Contributor alone and not by any other party; and
iv) states that source code for the Program is available from such
Contributor, and informs licensees how to obtain it in a reasonable manner on or
through a medium customarily used for software exchange.
When the Program is made available in source code form:
a) it must be made available under this Agreement; and
b) a copy of this Agreement must be included with each copy of the Program.
Contributors may not remove or alter any copyright notices contained within the
Program.
Each Contributor must identify itself as the originator of its Contribution, if
any, in a manner that reasonably allows subsequent Recipients to identify the
originator of the Contribution.
4. COMMERCIAL DISTRIBUTION
Commercial distributors of software may accept certain responsibilities with
respect to end users, business partners and the like. While this license is
intended to facilitate the commercial use of the Program, the Contributor who
includes the Program in a commercial product offering should do so in a manner
which does not create potential liability for other Contributors. Therefore, if
a Contributor includes the Program in a commercial product offering, such
Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
every other Contributor ("Indemnified Contributor") against any losses, damages
and costs (collectively "Losses") arising from claims, lawsuits and other legal
actions brought by a third party against the Indemnified Contributor to the
extent caused by the acts or omissions of such Commercial Contributor in
connection with its distribution of the Program in a commercial product
offering. The obligations in this section do not apply to any claims or Losses
relating to any actual or alleged intellectual property infringement. In order
to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
Contributor in writing of such claim, and b) allow the Commercial Contributor to
control, and cooperate with the Commercial Contributor in, the defense and any
related settlement negotiations. The Indemnified Contributor may participate in
any such claim at its own expense.
For example, a Contributor might include the Program in a commercial product
offering, Product X. That Contributor is then a Commercial Contributor. If that
Commercial Contributor then makes performance claims, or offers warranties
related to Product X, those performance claims and warranties are such
Commercial Contributor's responsibility alone. Under this section, the
Commercial Contributor would have to defend claims against the other
Contributors related to those performance claims and warranties, and if a court
requires any other Contributor to pay any damages as a result, the Commercial
Contributor must pay those damages.
5. NO WARRANTY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
Recipient is solely responsible for determining the appropriateness of using and
distributing the Program and assumes all risks associated with its exercise of
rights under this Agreement, including but not limited to the risks and costs of
program errors, compliance with applicable laws, damage to or loss of data,
programs or equipment, and unavailability or interruption of operations.
6. DISCLAIMER OF LIABILITY
EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
7. GENERAL
If any provision of this Agreement is invalid or unenforceable under applicable
law, it shall not affect the validity or enforceability of the remainder of the
terms of this Agreement, and without further action by the parties hereto, such
provision shall be reformed to the minimum extent necessary to make such
provision valid and enforceable.
If Recipient institutes patent litigation against a Contributor with respect to
a patent applicable to software (including a cross-claim or counterclaim in a
lawsuit), then any patent licenses granted by that Contributor to such Recipient
under this Agreement shall terminate as of the date such litigation is filed. In
addition, if Recipient institutes patent litigation against any entity
(including a cross-claim or counterclaim in a lawsuit) alleging that the Program
itself (excluding combinations of the Program with other software or hardware)
infringes such Recipient's patent(s), then such Recipient's rights granted under
Section 2(b) shall terminate as of the date such litigation is filed.
All Recipient's rights under this Agreement shall terminate if it fails to
comply with any of the material terms or conditions of this Agreement and does
not cure such failure in a reasonable period of time after becoming aware of
such noncompliance. If all Recipient's rights under this Agreement terminate,
Recipient agrees to cease use and distribution of the Program as soon as
reasonably practicable. However, Recipient's obligations under this Agreement
and any licenses granted by Recipient relating to the Program shall continue and
survive.
Everyone is permitted to copy and distribute copies of this Agreement, but in
order to avoid inconsistency the Agreement is copyrighted and may only be
modified in the following manner. The Agreement Steward reserves the right to
publish new versions (including revisions) of this Agreement from time to time.
No one other than the Agreement Steward has the right to modify this Agreement.
IBM is the initial Agreement Steward. IBM may assign the responsibility to serve
as the Agreement Steward to a suitable separate entity. Each new version of the
Agreement will be given a distinguishing version number. The Program (including
Contributions) may always be distributed subject to the version of the Agreement
under which it was received. In addition, after a new version of the Agreement
is published, Contributor may elect to distribute the Program (including its
Contributions) under the new version. Except as expressly stated in Sections
2(a) and 2(b) above, Recipient receives no rights or licenses to the
intellectual property of any Contributor under this Agreement, whether
expressly, by implication, estoppel or otherwise. All rights in the Program not
expressly granted under this Agreement are reserved.
This Agreement is governed by the laws of the State of New York and the
intellectual property laws of the United States of America. No party to this
Agreement will bring a legal action under this Agreement more than one year
after the cause of action arose. Each party waives its rights to a jury trial in
any resulting litigation.
\ No newline at end of file
# blue_thermal_printer
https://pub.dev/packages/blue_thermal_printer
A new Flutter plugin for connecting to thermal printer via bluetooth
(Android Only),
this plugin is still under development
Note: Migrate your project to AndroidX
## Getting Started
1. Depend on it
Add this to your package's pubspec.yaml file:
dependencies:
blue_thermal_printer: ^any
2. Install it
You can install packages from the command line:
with Flutter:
$ flutter packages get
Alternatively, your editor might support flutter packages get. Check the docs for your editor to learn more.
3. Import it
Now in your Dart code, you can use:
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
EXAMPLE:
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
BlueThermalPrinter bluetooth = BlueThermalPrinter.instance;
List<BluetoothDevice> _devices = [];
BluetoothDevice _device;
bool _connected = false;
bool _pressed = false;
String pathImage;
@override
void initState() {
super.initState();
initPlatformState();
initSavetoPath();
}
initSavetoPath()async{
//read and write
//image max 300px X 300px
final filename = 'yourlogo.png';
var bytes = await rootBundle.load("assets/images/yourlogo.png");
String dir = (await getApplicationDocumentsDirectory()).path;
writeToFile(bytes,'$dir/$filename');
setState(() {
pathImage='$dir/$filename';
});
}
Future<void> initPlatformState() async {
List<BluetoothDevice> devices = [];
try {
devices = await bluetooth.getBondedDevices();
} on PlatformException {
// TODO - Error
}
bluetooth.onStateChanged().listen((state) {
switch (state) {
case BlueThermalPrinter.CONNECTED:
setState(() {
_connected = true;
_pressed = false;
});
break;
case BlueThermalPrinter.DISCONNECTED:
setState(() {
_connected = false;
_pressed = false;
});
break;
default:
print(state);
break;
}
});
if (!mounted) return;
setState(() {
_devices = devices;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Blue Thermal Printer'),
),
body: Container(
child: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Device:',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
DropdownButton(
items: _getDeviceItems(),
onChanged: (value) => setState(() => _device = value),
value: _device,
),
RaisedButton(
onPressed:
_pressed ? null : _connected ? _disconnect : _connect,
child: Text(_connected ? 'Disconnect' : 'Connect'),
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 50),
child: RaisedButton(
onPressed: _connected ? _tesPrint : null,
child: Text('TesPrint'),
),
),
],
),
),
),
);
}
List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
List<DropdownMenuItem<BluetoothDevice>> items = [];
if (_devices.isEmpty) {
items.add(DropdownMenuItem(
child: Text('NONE'),
));
} else {
_devices.forEach((device) {
items.add(DropdownMenuItem(
child: Text(device.name),
value: device,
));
});
}
return items;
}
void _connect() {
if (_device == null) {
show('No device selected.');
} else {
bluetooth.isConnected.then((isConnected) {
if (!isConnected) {
bluetooth.connect(_device).catchError((error) {
setState(() => _pressed = false);
});
setState(() => _pressed = true);
}
});
}
}
void _disconnect() {
bluetooth.disconnect();
setState(() => _pressed = true);
}
//write to app path
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
void _tesPrint() async {
//SIZE
// 0- normal size text
// 1- only bold text
// 2- bold with medium text
// 3- bold with large text
//ALIGN
// 0- ESC_ALIGN_LEFT
// 1- ESC_ALIGN_CENTER
// 2- ESC_ALIGN_RIGHT
bluetooth.isConnected.then((isConnected) {
if (isConnected) {
bluetooth.printNewLine();
bluetooth.printCustom("HEADER",3,1);
bluetooth.printNewLine();
bluetooth.printImage(pathImage); //path of your image/logo
bluetooth.printNewLine();
//bluetooth.printImageBytes(bytes.buffer.asUint8List(bytes.offsetInBytes, bytes.lengthInBytes));
bluetooth.printLeftRight("LEFT", "RIGHT",0);
bluetooth.printLeftRight("LEFT", "RIGHT",1);
bluetooth.printLeftRight("LEFT", "RIGHT",1,format: "%-15s %15s %n");
bluetooth.printNewLine();
bluetooth.printLeftRight("LEFT", "RIGHT",2);
bluetooth.printLeftRight("LEFT", "RIGHT",3);
bluetooth.printLeftRight("LEFT", "RIGHT",4);
bluetooth.printNewLine();
bluetooth.print3Column("Col1", "Col2", "Col3",1);
bluetooth.print3Column("Col1", "Col2", "Col3",1,format: "%-10s %10s %10s %n");
bluetooth.printNewLine();
bluetooth.print4Column("Col1","Col2","Col3","Col4",1);
bluetooth.print4Column("Col1","Col2","Col3","Col4",1,format: "%-8s %7s %7s %7s %n" );
bluetooth.printNewLine();
String testString = " čĆžŽšŠ-H-ščđ";
bluetooth.printCustom(testString, 1, 1, charset: "windows-1250");
bluetooth.printLeftRight("Številka:", "18000001", 1, charset: "windows-1250");
bluetooth.printCustom("Body left",1,0);
bluetooth.printCustom("Body right",0,2);
bluetooth.printNewLine();
bluetooth.printCustom("Thank You",2,1);
bluetooth.printNewLine();
bluetooth.printQRcode("Insert Your Own Text to Generate", 200, 200, 1);
bluetooth.printNewLine();
bluetooth.printNewLine();
bluetooth.paperCut();
}
});
}
Future show(
String message, {
Duration duration: const Duration(seconds: 3),
}) async {
await new Future.delayed(new Duration(milliseconds: 100));
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text(
message,
style: new TextStyle(
color: Colors.white,
),
),
duration: duration,
),
);
}
}
If you like my content, please consider buying me a coffee. Thank you for your support!
[![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/Z8Z6656JW)
<a href="https://www.buymeacoffee.com/QP1rCmf5L" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png" alt="Buy Me A Coffee" style="height: 41px !important;width: 174px !important;box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;-webkit-box-shadow: 0px 3px 2px 0px rgba(190, 190, 190, 0.5) !important;" ></a>
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
group 'id.kakzaki.blue_thermal_printer'
version '1.1.3'
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
}
}
rootProject.allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
mavenCentral()
}
}
apply plugin: 'com.android.library'
android {
compileSdkVersion 30
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
defaultConfig {
minSdkVersion 18
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
lintOptions {
disable 'InvalidPackage'
}
buildToolsVersion '29.0.2'
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.android.support:multidex:1.0.3'
implementation 'com.journeyapps:zxing-android-embedded:3.6.0@aar'
implementation 'com.github.bingoogolapple.BGAQRCode-Android:zxing:1.3.8'
implementation 'com.blankj:utilcode:1.30.1'
implementation files('libs/gprintersdkv2.jar')
}
org.gradle.jvmargs=-Xmx1536M
rootProject.name = 'blue_thermal_printer'
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="id.kakzaki.blue_thermal_printer">
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<!-- Request legacy Bluetooth permissions on older devices. -->
<uses-permission android:name="android.permission.BLUETOOTH"
tools:remove="android:maxSdkVersion" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN"
tools:remove="android:maxSdkVersion" />
<!-- Needed only if your app looks for Bluetooth devices.
You must add an attribute to this permission, or declare the
ACCESS_FINE_LOCATION permission, depending on the results when you
check location usage in your app. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- Needed only if your app makes the device discoverable to Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
<!-- Needed only if your app communicates with already-paired Bluetooth
devices. -->
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
</manifest>
package id.kakzaki.blue_thermal_printer;
import android.Manifest;
import android.app.Activity;
import android.app.Application;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.BluetoothSocket;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import androidx.annotation.NonNull;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import android.util.Log;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import cn.bingoogolapple.qrcode.zxing.QRCodeEncoder;
import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;
import io.flutter.plugin.common.EventChannel.StreamHandler;
import io.flutter.plugin.common.EventChannel.EventSink;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
import io.flutter.plugin.common.MethodChannel.Result;
import io.flutter.plugin.common.PluginRegistry;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugin.common.PluginRegistry.RequestPermissionsResultListener;
import com.google.zxing.BarcodeFormat;
import com.google.zxing.MultiFormatWriter;
import com.google.zxing.WriterException;
import com.google.zxing.common.BitMatrix;
import com.gprinter.command.LabelCommand;
import com.journeyapps.barcodescanner.BarcodeEncoder;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import com.blankj.utilcode.util.LogUtils;
public class BlueThermalPrinterPlugin implements FlutterPlugin, ActivityAware, MethodCallHandler, RequestPermissionsResultListener {
private static final String TAG = "BThermalPrinterPlugin";
private static final String NAMESPACE = "blue_thermal_printer";
private static final int REQUEST_COARSE_LOCATION_PERMISSIONS = 1451;
private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
private static ConnectedThread THREAD = null;
private BluetoothAdapter mBluetoothAdapter;
private ExecutorService executorService;
private Result pendingResult;
private EventSink readSink;
private EventSink statusSink;
private FlutterPluginBinding pluginBinding;
private ActivityPluginBinding activityBinding;
private Object initializationLock = new Object();
private Context context;
private MethodChannel channel;
private EventChannel stateChannel;
private EventChannel readChannel;
private BluetoothManager mBluetoothManager;
private Application application;
private Activity activity;
private String lastDevice = "";
// private Handler handler;
public static void registerWith(Registrar registrar) {
final BlueThermalPrinterPlugin instance = new BlueThermalPrinterPlugin();
//registrar.addRequestPermissionsResultListener(instance);
Activity activity = registrar.activity();
Application application = null;
instance.setup(registrar.messenger(), application, activity, registrar, null);
}
public BlueThermalPrinterPlugin() {
}
@Override
public void onAttachedToEngine(@NonNull FlutterPluginBinding binding) {
pluginBinding = binding;
}
@Override
public void onDetachedFromEngine(@NonNull FlutterPluginBinding binding) {
pluginBinding = null;
}
@Override
public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
activityBinding = binding;
executorService = Executors.newFixedThreadPool(1);
setup(
pluginBinding.getBinaryMessenger(),
(Application) pluginBinding.getApplicationContext(),
activityBinding.getActivity(),
null,
activityBinding);
}
@Override
public void onDetachedFromActivityForConfigChanges() {
onDetachedFromActivity();
}
@Override
public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
onAttachedToActivity(binding);
}
@Override
public void onDetachedFromActivity() {
detach();
}
private void setup(
final BinaryMessenger messenger,
final Application application,
final Activity activity,
final PluginRegistry.Registrar registrar,
final ActivityPluginBinding activityBinding) {
synchronized (initializationLock) {
Log.i(TAG, "setup");
this.activity = activity;
this.application = application;
this.context = application;
channel = new MethodChannel(messenger, NAMESPACE + "/methods");
channel.setMethodCallHandler(this);
stateChannel = new EventChannel(messenger, NAMESPACE + "/state");
stateChannel.setStreamHandler(stateStreamHandler);
readChannel = new EventChannel(messenger, NAMESPACE + "/read");
readChannel.setStreamHandler(readResultsHandler);
mBluetoothManager = (BluetoothManager) application.getSystemService(Context.BLUETOOTH_SERVICE);
mBluetoothAdapter = mBluetoothManager.getAdapter();
if (registrar != null) {
// V1 embedding setup for activity listeners.
registrar.addRequestPermissionsResultListener(this);
} else {
// V2 embedding setup for activity listeners.
activityBinding.addRequestPermissionsResultListener(this);
}
// handler = new Handler(Looper.getMainLooper());
}
}
private void detach() {
Log.i(TAG, "detach");
context = null;
activityBinding.removeRequestPermissionsResultListener(this);
activityBinding = null;
channel.setMethodCallHandler(null);
channel = null;
stateChannel.setStreamHandler(null);
stateChannel = null;
mBluetoothAdapter = null;
mBluetoothManager = null;
application = null;
}
// MethodChannel.Result wrapper that responds on the platform thread.
private static class MethodResultWrapper implements Result {
private Result methodResult;
private Handler handler;
MethodResultWrapper(Result result) {
methodResult = result;
handler = new Handler(Looper.getMainLooper());
}
@Override
public void success(final Object result) {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.success(result);
}
});
}
@Override
public void error(final String errorCode, final String errorMessage, final Object errorDetails) {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.error(errorCode, errorMessage, errorDetails);
}
});
}
@Override
public void notImplemented() {
handler.post(new Runnable() {
@Override
public void run() {
methodResult.notImplemented();
}
});
}
}
@Override
public void onMethodCall(MethodCall call, Result rawResult) {
Result result = new MethodResultWrapper(rawResult);
if (mBluetoothAdapter == null && !"isAvailable".equals(call.method)) {
result.error("bluetooth_unavailable", "the device does not have bluetooth", null);
return;
}
final Map<String, Object> arguments = call.arguments();
switch (call.method) {
case "isAvailable":
result.success(mBluetoothAdapter != null);
break;
case "isOn":
try {
assert mBluetoothAdapter != null;
result.success(mBluetoothAdapter.isEnabled());
} catch (Exception ex) {
result.error("Error", ex.getMessage(), exceptionToString(ex));
}
break;
case "isConnected":
result.success(THREAD != null);
break;
case "getLastDevice":
result.success(lastDevice);
break;
case "openSettings":
ContextCompat.startActivity(context, new Intent(android.provider.Settings.ACTION_BLUETOOTH_SETTINGS),
null);
result.success(true);
break;
case "getBondedDevices":
try {
if (ContextCompat.checkSelfPermission(activity,
Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(activity,
new String[]{Manifest.permission.ACCESS_COARSE_LOCATION}, REQUEST_COARSE_LOCATION_PERMISSIONS);
pendingResult = result;
break;
}
getBondedDevices(result);
} catch (Exception ex) {
result.error("Error", ex.getMessage(), exceptionToString(ex));
}
break;
case "connect":
if (arguments.containsKey("address")) {
String address = (String) arguments.get("address");
String name = (String) arguments.get("name");
Log.d(TAG, "连接蓝牙名字:" + name);
lastDevice = name;
connect(result, address, name);
} else {
result.error("invalid_argument", "argument 'address' not found", null);
}
break;
case "disconnect":
disconnect(result);
break;
case "writeBytes":
if (arguments.containsKey("message")) {
byte[] message = (byte[]) arguments.get("message");
writeBytes(result, message);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "printTSCImage":
if (arguments.containsKey("config") && arguments.containsKey("data")) {
final Map<String, Object> config = (Map<String, Object>) arguments.get("config");
final List<Map<String, Object>> list = (List<Map<String, Object>>) arguments.get("data");
printTSCImage(result, config, list);
}
break;
case "printNewLine":
printNewLine(result);
break;
case "paperCut":
paperCut(result);
break;
case "printImage":
if (arguments.containsKey("pathImage")) {
String pathImage = (String) arguments.get("pathImage");
printImage(result, pathImage);
} else {
result.error("invalid_argument", "argument 'pathImage' not found", null);
}
break;
case "printImageBytes":
if (arguments.containsKey("bytes")) {
byte[] bytes = (byte[]) arguments.get("bytes");
printImageBytes(result, bytes);
} else {
result.error("invalid_argument", "argument 'bytes' not found", null);
}
break;
case "printQRcode":
if (arguments.containsKey("textToQR")) {
String textToQR = (String) arguments.get("textToQR");
int width = (int) arguments.get("width");
int height = (int) arguments.get("height");
int align = (int) arguments.get("align");
int light = (int) arguments.get("light");
int weight = (int) arguments.get("weight");
if (light == 0 && weight == 0) {
printQRcode(result, textToQR, width, height, align);
} else {
printQRcode(result, textToQR, width, height, light, weight);
}
} else {
result.error("invalid_argument", "argument 'textToQR' not found", null);
}
break;
case "printBarCode":
if (arguments.containsKey("textToBar")) {
String textToQR = (String) arguments.get("textToBar");
int width = (int) arguments.get("width");
int height = (int) arguments.get("height");
int align = (int) arguments.get("align");
int light = (int) arguments.get("light");
int weight = (int) arguments.get("weight");
if (light == 0 && weight == 0) {
printBarCode(result, textToQR, width, height, align);
} else {
printBarCode(result, textToQR, width, height, light, weight);
}
} else {
result.error("invalid_argument", "argument 'textToQR' not found", null);
}
break;
case "printLeftRight":
if (arguments.containsKey("string1")) {
String string1 = (String) arguments.get("string1");
String string2 = (String) arguments.get("string2");
int size = (int) arguments.get("size");
String charset = (String) arguments.get("charset");
String format = (String) arguments.get("format");
printLeftRight(result, string1, string2, size, charset, format);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "print3Column":
if (arguments.containsKey("string1")) {
String string1 = (String) arguments.get("string1");
String string2 = (String) arguments.get("string2");
String string3 = (String) arguments.get("string3");
int size = (int) arguments.get("size");
String charset = (String) arguments.get("charset");
String format = (String) arguments.get("format");
print3Column(result, string1, string2, string3, size, charset, format);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "print4Column":
if (arguments.containsKey("string1")) {
String string1 = (String) arguments.get("string1");
String string2 = (String) arguments.get("string2");
String string3 = (String) arguments.get("string3");
String string4 = (String) arguments.get("string4");
int size = (int) arguments.get("size");
String charset = (String) arguments.get("charset");
String format = (String) arguments.get("format");
print4Column(result, string1, string2, string3, string4, size, charset, format);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "initPrinter":
initPrinter(result);
break;
case "printMarginLeft":
if (arguments.containsKey("left")) {
int left = (int) arguments.get("left");
printMarginLeft(result, left);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "getQRcode":
if (arguments.containsKey("textToQR")) {
String textToQR = (String) arguments.get("textToQR");
int width = (int) arguments.get("width");
int height = (int) arguments.get("height");
int align = (int) arguments.get("align");
int light = (int) arguments.get("light");
int weight = (int) arguments.get("weight");
try {
getQRcode(result, textToQR, width, height, align);
} catch (Exception ex) {
result.error("Error", ex.getMessage(), exceptionToString(ex));
}
}
break;
case "getBarCode":
if (arguments.containsKey("textToBar")) {
String textToBar = (String) arguments.get("textToBar");
int width = (int) arguments.get("width");
int height = (int) arguments.get("height");
int align = (int) arguments.get("align");
int light = (int) arguments.get("light");
int weight = (int) arguments.get("weight");
try {
getBarCode(result, textToBar, width, height, align);
} catch (Exception ex) {
result.error("Error", ex.getMessage(), exceptionToString(ex));
}
}
break;
case "write":
if (arguments.containsKey("message")) {
String message = (String) arguments.get("message");
write(result, message);
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
case "printText":
if (arguments.containsKey("message")) {
String message = (String) arguments.get("message");
int size = (int) arguments.get("size");
int align = (int) arguments.get("align");
boolean bold = (boolean) arguments.get("bold");
String charset = (String) arguments.get("charset");
String format = (String) arguments.get("format");
int light = (int) arguments.get("light");
int weight = (int) arguments.get("weight");
if (light == 0 && weight == 0) {
printText(result, message, size, bold, align, charset, format);
} else {
printText(result, message, size, bold, light, weight, charset, format);
}
} else {
result.error("invalid_argument", "argument 'message' not found", null);
}
break;
default:
result.notImplemented();
break;
}
}
/**
* @param requestCode requestCode
* @param permissions permissions
* @param grantResults grantResults
* @return boolean
*/
@Override
public boolean onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
if (requestCode == REQUEST_COARSE_LOCATION_PERMISSIONS) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
getBondedDevices(pendingResult);
} else {
pendingResult.error("no_permissions", "this plugin requires location permissions for scanning", null);
pendingResult = null;
}
return true;
}
return false;
}
private void initPrinter(Result result) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.initPrinter());
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printMarginLeft(Result result, int left) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.printMarginLeft(left));
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
/**
* @param result result
*/
private void getBondedDevices(Result result) {
List<Map<String, Object>> list = new ArrayList<>();
for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
Map<String, Object> ret = new HashMap<>();
ret.put("address", device.getAddress());
ret.put("name", device.getName());
ret.put("type", device.getType());
int majorDevice = device.getBluetoothClass().getMajorDeviceClass();
if (majorDevice == 1536) {
list.add(ret);
}
}
result.success(list);
}
private String exceptionToString(Exception ex) {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
ex.printStackTrace(pw);
return sw.toString();
}
private void connect(Result result, String address, String name) {
if (THREAD != null) {
THREAD.cancel();
THREAD = null;
lastDevice = "";
result.error("connect_error", "already connected", null);
statusSink.success(0);
return;
}
executorService.submit(() -> {
try {
BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
boolean b = device.fetchUuidsWithSdp();
LogUtils.i("进来了,device连接方法++++++");
if (device == null) {
if(THREAD != null)
THREAD.cancel();
THREAD = null;
lastDevice = "";
result.error("connect_error", "device not found", null);
// sendStatus(3);
return;
}
BluetoothSocket socket = device.createRfcommSocketToServiceRecord(MY_UUID);
if (socket == null) {
// sendStatus(3);
if(THREAD != null)
THREAD.cancel();
THREAD = null;
lastDevice = "";
result.error("connect_error", "socket connection not established", null);
return;
}
// Cancel bt discovery, even though we didn't start it
mBluetoothAdapter.cancelDiscovery();
try {
LogUtils.i("进来了,连接方法++++++");
socket.connect();
THREAD = new ConnectedThread(socket);
THREAD.start();
lastDevice = name;
result.success(true);
} catch (IOException ex) {
Log.e(TAG, ex.getMessage(), ex);
if(THREAD != null)
THREAD.cancel();
THREAD = null;
lastDevice = "";
LogUtils.i("异常了,连接方法++++++");
// sendStatus(3);
result.error("connect_error", ex.getMessage(), exceptionToString(ex));
}
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
if(THREAD != null)
THREAD.cancel();
THREAD = null;
lastDevice = "";
LogUtils.i("异常了,连接方法++++++");
// sendStatus(3);
result.error("connect_error", ex.getMessage(), exceptionToString(ex));
}
});
}
/**
* @param result result
*/
private void disconnect(Result result) {
if (THREAD == null) {
result.error("disconnection_error", "not connected", null);
return;
}
executorService.submit(() -> {
try {
if(THREAD != null)
THREAD.cancel();
THREAD = null;
lastDevice = "";
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("disconnection_error", ex.getMessage(), exceptionToString(ex));
}
});
}
// private void sendStatus(int event) {
// handler.post(() -> statusSink.success(event));
// }
/**
* @param result result
* @param message message
*/
private void write(Result result, String message) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(message.getBytes());
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void writeBytes(Result result, byte[] message) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
result.success(true);
THREAD.write(message);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printText(Result result, String message, int size, boolean bold,
int align, String charset, String format) {
// Print config "mode"
byte[] cc = new byte[]{0x1B, 0x21, 0x03}; // 0- normal size text
// byte[] cc1 = new byte[]{0x1B,0x21,0x00}; // 0- normal size text
byte[] bb = new byte[]{0x1B, 0x21, 0x08}; // 1- only bold text
byte[] bb2 = new byte[]{0x1B, 0x21, 0x20}; // 2- bold with medium text
byte[] bb3 = new byte[]{0x1B, 0x21, 0x10}; // 3- bold with large text
byte[] bb4 = new byte[]{0x1B, 0x21, 0x30}; // 4- strong text
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (size) {
case 0:
THREAD.write(cc);
break;
case 1:
THREAD.write(bb);
break;
case 2:
THREAD.write(bb2);
break;
case 3:
THREAD.write(bb3);
break;
case 4:
THREAD.write(bb4);
break;
}
switch (align) {
case 0:
// left align
THREAD.write(PrinterCommands.ESC_ALIGN_LEFT);
break;
case 1:
// center align
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
break;
case 2:
// right align
THREAD.write(PrinterCommands.ESC_ALIGN_RIGHT);
break;
}
if (format != null) {
message = String.format(format, message);
}
if (charset != null) {
THREAD.write(message.getBytes(charset));
} else {
THREAD.write(message.getBytes());
}
THREAD.write(PrinterCommands.FEED_LINE);
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printText(Result result, String message, int size, boolean bold, int light,
int weight, String charset, String format) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.fontSizeSetBig(size));
THREAD.write(PrinterCommands.bold(bold));
THREAD.write(PrinterCommands.printLocation(light, weight));
if (format != null) {
message = String.format(format, message);
}
if (charset != null) {
THREAD.write(message.getBytes(charset));
} else {
THREAD.write(message.getBytes());
}
THREAD.write(PrinterCommands.FEED_LINE);
result.success(true);
} catch (
Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printLeftRight(Result result, String msg1, String msg2, int size, String
charset, String format) {
byte[] cc = new byte[]{0x1B, 0x21, 0x03}; // 0- normal size text
// byte[] cc1 = new byte[]{0x1B,0x21,0x00}; // 0- normal size text
byte[] bb = new byte[]{0x1B, 0x21, 0x08}; // 1- only bold text
byte[] bb2 = new byte[]{0x1B, 0x21, 0x20}; // 2- bold with medium text
byte[] bb3 = new byte[]{0x1B, 0x21, 0x10}; // 3- bold with large text
byte[] bb4 = new byte[]{0x1B, 0x21, 0x30}; // 4- strong text
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (size) {
case 0:
THREAD.write(cc);
break;
case 1:
THREAD.write(bb);
break;
case 2:
THREAD.write(bb2);
break;
case 3:
THREAD.write(bb3);
break;
case 4:
THREAD.write(bb4);
break;
}
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
String line = String.format("%-15s %15s %n", msg1, msg2);
if (format != null) {
line = String.format(format, msg1, msg2);
}
if (charset != null) {
THREAD.write(line.getBytes(charset));
} else {
THREAD.write(line.getBytes());
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void print3Column(Result result, String msg1, String msg2, String msg3,
int size, String charset, String format) {
byte[] cc = new byte[]{0x1B, 0x21, 0x03}; // 0- normal size text
// byte[] cc1 = new byte[]{0x1B,0x21,0x00}; // 0- normal size text
byte[] bb = new byte[]{0x1B, 0x21, 0x08}; // 1- only bold text
byte[] bb2 = new byte[]{0x1B, 0x21, 0x20}; // 2- bold with medium text
byte[] bb3 = new byte[]{0x1B, 0x21, 0x10}; // 3- bold with large text
byte[] bb4 = new byte[]{0x1B, 0x21, 0x30}; // 4- strong text
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (size) {
case 0:
THREAD.write(cc);
break;
case 1:
THREAD.write(bb);
break;
case 2:
THREAD.write(bb2);
break;
case 3:
THREAD.write(bb3);
break;
case 4:
THREAD.write(bb4);
break;
}
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
String line = String.format("%-10s %10s %10s %n", msg1, msg2, msg3);
if (format != null) {
line = String.format(format, msg1, msg2, msg3);
}
if (charset != null) {
THREAD.write(line.getBytes(charset));
} else {
THREAD.write(line.getBytes());
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void print4Column(Result result, String msg1, String msg2, String msg3, String msg4,
int size, String charset, String format) {
byte[] cc = new byte[]{0x1B, 0x21, 0x03}; // 0- normal size text
// byte[] cc1 = new byte[]{0x1B,0x21,0x00}; // 0- normal size text
byte[] bb = new byte[]{0x1B, 0x21, 0x08}; // 1- only bold text
byte[] bb2 = new byte[]{0x1B, 0x21, 0x20}; // 2- bold with medium text
byte[] bb3 = new byte[]{0x1B, 0x21, 0x10}; // 3- bold with large text
byte[] bb4 = new byte[]{0x1B, 0x21, 0x30}; // 4- strong text
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (size) {
case 0:
THREAD.write(cc);
break;
case 1:
THREAD.write(bb);
break;
case 2:
THREAD.write(bb2);
break;
case 3:
THREAD.write(bb3);
break;
case 4:
THREAD.write(bb4);
break;
}
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
String line = String.format("%-8s %7s %7s %7s %n", msg1, msg2, msg3, msg4);
if (format != null) {
line = String.format(format, msg1, msg2, msg3, msg4);
}
if (charset != null) {
THREAD.write(line.getBytes(charset));
} else {
THREAD.write(line.getBytes());
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printNewLine(Result result) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.FEED_LINE);
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void paperCut(Result result) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.FEED_PAPER_AND_CUT);
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printImage(Result result, String pathImage) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
Bitmap bmp = BitmapFactory.decodeFile(pathImage);
if (bmp != null) {
byte[] command = Utils.decodeBitmap(bmp);
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
THREAD.write(command);
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printImageBytes(Result result, byte[] bytes) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
if (bmp != null) {
byte[] command = Utils.decodeBitmap(bmp);
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
THREAD.write(command);
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void getQRcode(Result result, String textToQR, int width, int height, int align) {
try {
byte[] positionBytes = new byte[3];
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
switch (align) {
case 0:
// left align
positionBytes = PrinterCommands.ESC_ALIGN_LEFT;
break;
case 1:
// center align
positionBytes = PrinterCommands.ESC_ALIGN_CENTER;
break;
case 2:
// right align
positionBytes = PrinterCommands.ESC_ALIGN_RIGHT;
break;
}
BitMatrix bitMatrix = multiFormatWriter.encode(textToQR, BarcodeFormat.QR_CODE, width, height);
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bmp = barcodeEncoder.createBitmap(bitMatrix);
byte[] bmpBytes = Utils.decodeBitmap(bmp);
byte[] bytes = Utils.byteMerger(positionBytes, bmpBytes);
result.success(bytes);
} catch (WriterException ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("getQRcode_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printQRcode(Result result, String textToQR, int width, int height, int align) {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (align) {
case 0:
// left align
THREAD.write(PrinterCommands.ESC_ALIGN_LEFT);
break;
case 1:
// center align
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
break;
case 2:
// right align
THREAD.write(PrinterCommands.ESC_ALIGN_RIGHT);
break;
}
BitMatrix bitMatrix = multiFormatWriter.encode(textToQR, BarcodeFormat.QR_CODE, width, height);
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bmp = barcodeEncoder.createBitmap(bitMatrix);
if (bmp != null) {
byte[] command = Utils.decodeBitmap(bmp);
THREAD.write(command);
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printQRcode(Result result, String textToQR, int width, int height, int light,
int weight) {
MultiFormatWriter multiFormatWriter = new MultiFormatWriter();
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.printLocation(light, weight));
BitMatrix bitMatrix = multiFormatWriter.encode(textToQR, BarcodeFormat.QR_CODE, width, height);
BarcodeEncoder barcodeEncoder = new BarcodeEncoder();
Bitmap bmp = barcodeEncoder.createBitmap(bitMatrix);
if (bmp != null) {
byte[] command = Utils.decodeBitmap(bmp);
THREAD.write(command);
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void getBarCode(Result result, String textToBar, int width, int height, int align) {
byte[] positionBytes = new byte[3];
switch (align) {
case 0:
// left align
positionBytes = PrinterCommands.ESC_ALIGN_LEFT;
break;
case 1:
// center align
positionBytes = PrinterCommands.ESC_ALIGN_CENTER;
break;
case 2:
// right align
positionBytes = PrinterCommands.ESC_ALIGN_RIGHT;
break;
}
Bitmap bmp = QRCodeEncoder.syncEncodeBarcode(textToBar, width, height, 0);
byte[] bmpBytes = Utils.decodeBitmap(bmp);
byte[] bytes = Utils.byteMerger(positionBytes, bmpBytes);
result.success(bytes);
}
/**
* 标签打印对象转换
*/
public void printTSCImage(Result result, Map<String, Object> config, List<Map<String, Object>> list) {
LabelCommand tsc = new LabelCommand();
int width = (int) (config.get("width") == null ? 60 : config.get("width")); // 单位:mm
int height = (int) (config.get("height") == null ? 75 : config.get("height")); // 单位:mm
int gap = (int) (config.get("gap") == null ? 0 : config.get("gap")); // 单位:mm
Log.d(TAG, "mapToLabel: width = " + width + ", height = " + height + ", gap = " + gap);
// 设置标签尺寸宽高,按照实际尺寸设置 单位mm
tsc.addSize(width, height);
// 设置标签间隙,按照实际尺寸设置,如果为无间隙纸则设置为0 单位mm
tsc.addGap(gap);
// // 设置打印方向
// tsc.addDirection(LabelCommand.DIRECTION.FORWARD, LabelCommand.MIRROR.NORMAL);
// // 开启带Response的打印,用于连续打印
// tsc.addQueryPrinterStatus(LabelCommand.RESPONSE_MODE.ON);
// // 设置原点坐标
// tsc.addReference(0, 0);
// //设置浓度
// tsc.addDensity(LabelCommand.DENSITY.DNESITY4);
// // 撕纸模式开启
// tsc.addTear(EscCommand.ENABLE.ON);
// 清除打印缓冲区
tsc.addCls();
// {type:'text|barcode|qrcode|image', content:'', x:0,y:0}
for (Map<String, Object> m : list) {
Log.d(TAG, "printTSCImage: "+m);
String type = (String) m.get("type");
String content = (String) m.get("content");
int x = (int) (m.get("x") == null ? 0 : m.get("x")); //dpi: 1mm约为8个点
int y = (int) (m.get("y") == null ? 0 : m.get("y"));
int contentWidth = (int) (m.get("width") == null ? 300 : m.get("width"));
if ("text".equals(type)) {
// 绘制简体中文
tsc.addText(x, y, LabelCommand.FONTTYPE.SIMPLIFIED_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1, content);
//打印繁体
//tsc.addUnicodeText(10,32, LabelCommand.FONTTYPE.TRADITIONAL_CHINESE, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,"BIG5碼繁體中文字元","BIG5");
//打印韩文
//tsc.addUnicodeText(10,60, LabelCommand.FONTTYPE.KOREAN, LabelCommand.ROTATION.ROTATION_0, LabelCommand.FONTMUL.MUL_1, LabelCommand.FONTMUL.MUL_1,"Korean 지아보 하성","EUC_KR");
} else if ("barcode".equals(type)) {
tsc.add1DBarcode(x, y, LabelCommand.BARCODETYPE.CODE128, 100, LabelCommand.READABEL.EANBEL, LabelCommand.ROTATION.ROTATION_0, content);
} else if ("qrcode".equals(type)) {
tsc.addQRCode(x, y, LabelCommand.EEC.LEVEL_L, 5, LabelCommand.ROTATION.ROTATION_0, content);
} else if ("image".equals(type)) {
byte[] bytes = Base64.decode(content, Base64.DEFAULT);
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
tsc.addBitmap(x, y, LabelCommand.BITMAP_MODE.OVERWRITE, contentWidth, bitmap);
}
}
// 打印标签
tsc.addPrint(1, 1);
// 打印标签后 蜂鸣器响
tsc.addSound(2, 100);
//开启钱箱
// tsc.addCashdrwer(LabelCommand.FOOT.F5, 255, 255);
// 发送数据
Byte[] objects = tsc.getCommand().toArray(new Byte[0]);
writeBytes(result, toPrimitives(objects));
}
byte[] toPrimitives(Byte[] oBytes) {
byte[] bytes = new byte[oBytes.length];
for (int i = 0; i < oBytes.length; i++) {
bytes[i] = oBytes[i];
}
return bytes;
}
private void printBarCode(Result result, String textToBar, int width, int height, int align) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
switch (align) {
case 0:
// left align
THREAD.write(PrinterCommands.ESC_ALIGN_LEFT);
break;
case 1:
// center align
THREAD.write(PrinterCommands.ESC_ALIGN_CENTER);
break;
case 2:
// right align
THREAD.write(PrinterCommands.ESC_ALIGN_RIGHT);
break;
}
Bitmap bmp = QRCodeEncoder.syncEncodeBarcode(textToBar, width, height, 0);
if (bmp != null) {
THREAD.write(Utils.parseBmpToByte(bmp));
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private void printBarCode(Result result, String textToBar, int width, int height, int light,
int weight) {
if (THREAD == null) {
result.error("write_error", "not connected", null);
return;
}
try {
THREAD.write(PrinterCommands.printLocation(light, weight));
Bitmap bmp = QRCodeEncoder.syncEncodeBarcode(textToBar, width, height, 0);
if (bmp != null) {
THREAD.write(Utils.parseBmpToByte(bmp));
} else {
Log.e("Print Photo error", "the file isn't exists");
}
result.success(true);
} catch (Exception ex) {
Log.e(TAG, ex.getMessage(), ex);
result.error("write_error", ex.getMessage(), exceptionToString(ex));
}
}
private class ConnectedThread extends Thread {
private final BluetoothSocket mmSocket;
private final InputStream inputStream;
private final OutputStream outputStream;
ConnectedThread(BluetoothSocket socket) {
mmSocket = socket;
InputStream tmpIn = null;
OutputStream tmpOut = null;
try {
tmpIn = socket.getInputStream();
tmpOut = socket.getOutputStream();
} catch (IOException e) {
e.printStackTrace();
}
inputStream = tmpIn;
outputStream = tmpOut;
}
public void run() {
byte[] buffer = new byte[1024];
int bytes;
while (true) {
try {
bytes = inputStream.read(buffer);
readSink.success(new String(buffer, 0, bytes));
} catch (NullPointerException e) {
break;
} catch (IOException e) {
break;
}
}
}
public void write(byte[] bytes) {
try {
outputStream.write(bytes);
} catch (IOException e) {
e.printStackTrace();
}
}
public void cancel() {
try {
outputStream.flush();
outputStream.close();
inputStream.close();
mmSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
private final StreamHandler stateStreamHandler = new StreamHandler() {
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.d(TAG, "蓝牙广播:" + action);
if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) {
THREAD = null;
statusSink.success(intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1));
} else if (BluetoothDevice.ACTION_ACL_CONNECTED.equals(action)) {
statusSink.success(1);
} else if (BluetoothDevice.ACTION_ACL_DISCONNECTED.equals(action)) {
THREAD = null;
statusSink.success(0);
}
}
};
@Override
public void onListen(Object o, EventSink eventSink) {
statusSink = eventSink;
Log.d(TAG, "注册蓝牙广播:");
context.registerReceiver(mReceiver, new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED));
context.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_CONNECTED));
context.registerReceiver(mReceiver, new IntentFilter(BluetoothDevice.ACTION_ACL_DISCONNECTED));
}
@Override
public void onCancel(Object o) {
statusSink = null;
Log.d(TAG, "取消注册蓝牙广播:");
context.unregisterReceiver(mReceiver);
}
};
private final StreamHandler readResultsHandler = new StreamHandler() {
@Override
public void onListen(Object o, EventSink eventSink) {
Log.d(TAG, "回调蓝牙广播:");
readSink = eventSink;
}
@Override
public void onCancel(Object o) {
readSink = null;
}
};
}
\ No newline at end of file
package id.kakzaki.blue_thermal_printer;
/**
* Created by https://goo.gl/UAfmBd on 2/6/2017.
*/
public class PrinterCommands {
public static final byte HT = 0x9;
public static final byte LF = 0x0A;
public static final byte CR = 0x0D;
public static final byte ESC = 0x1B;
public static final byte DLE = 0x10;
public static final byte GS = 0x1D;
public static final byte FS = 0x1C;
public static final byte STX = 0x02;
public static final byte US = 0x1F;
public static final byte CAN = 0x18;
public static final byte CLR = 0x0C;
public static final byte EOT = 0x04;
public static final byte[] INIT = {27, 64};
public static byte[] FEED_LINE = {10};
public static byte[] SELECT_FONT_A = {20, 33, 0};
public static byte[] SET_BAR_CODE_HEIGHT = {29, 104, 100};
public static byte[] PRINT_BAR_CODE_1 = {29, 107, 2};
public static byte[] SEND_NULL_BYTE = {0x00};
public static byte[] SELECT_PRINT_SHEET = {0x1B, 0x63, 0x30, 0x02};
public static byte[] FEED_PAPER_AND_CUT = {0x1D, 0x56, 66, 0x00};
public static byte[] SELECT_CYRILLIC_CHARACTER_CODE_TABLE = {0x1B, 0x74, 0x11};
public static byte[] SELECT_BIT_IMAGE_MODE = {0x1B, 0x2A, 33, -128, 0};
public static byte[] SET_LINE_SPACING_24 = {0x1B, 0x33, 24};
public static byte[] SET_LINE_SPACING_30 = {0x1B, 0x33, 30};
public static byte[] TRANSMIT_DLE_PRINTER_STATUS = {0x10, 0x04, 0x01};
public static byte[] TRANSMIT_DLE_OFFLINE_PRINTER_STATUS = {0x10, 0x04, 0x02};
public static byte[] TRANSMIT_DLE_ERROR_STATUS = {0x10, 0x04, 0x03};
public static byte[] TRANSMIT_DLE_ROLL_PAPER_SENSOR_STATUS = {0x10, 0x04, 0x04};
public static final byte[] ESC_FONT_COLOR_DEFAULT = new byte[]{0x1B, 'r', 0x00};
public static final byte[] FS_FONT_ALIGN = new byte[]{0x1C, 0x21, 1, 0x1B,
0x21, 1};
public static final byte[] ESC_ALIGN_LEFT = new byte[]{0x1b, 'a', 0x00};
public static final byte[] ESC_ALIGN_RIGHT = new byte[]{0x1b, 'a', 0x02};
public static final byte[] ESC_ALIGN_CENTER = new byte[]{0x1b, 'a', 0x01};
public static final byte[] ESC_CANCEL_BOLD = new byte[]{0x1B, 0x45, 0};
/*********************************************/
public static final byte[] ESC_HORIZONTAL_CENTERS = new byte[]{0x1B, 0x44, 20, 28, 00};
public static final byte[] ESC_CANCLE_HORIZONTAL_CENTERS = new byte[]{0x1B, 0x44, 00};
/*********************************************/
public static final byte[] ESC_ENTER = new byte[]{0x1B, 0x4A, 0x40};
public static final byte[] PRINTE_TEST = new byte[]{0x1D, 0x28, 0x41};
public static final byte[] position = new byte[]{0x1B, 0x24, 20, 1};
public static final byte[] reset = new byte[]{0x1b, 0x40};
public static final byte[] print = new byte[]{0x0a};
public static byte[] initPrinter(){
byte[] result = new byte[2];
result[0] = ESC;
result[1] = 64;
return result;
}
/**
* 设置左边距
*/
public static byte[] printMarginLeft(int left) {
byte[] result = new byte[4];
result[0] = 0x1D;
result[1] = 0x4C;
result[2] = (byte) left;
result[3] = 0;
return result;
}
/**
* 设置位置
*/
public static byte[] printLocation(int light, int weight) {
byte[] result = new byte[4];
result[0] = 0x1D;
result[1] = 0x4C;
result[2] = (byte) light;
result[3] = (byte)weight;
return result;
}
/**
* 字体变大为标准的n倍
*/
public static byte[] fontSizeSetBig(int num) {
byte realSize = 0;
switch (num) {
case 1:
realSize = 0;
break;
case 2:
realSize = 17;
break;
case 3:
realSize = 34;
break;
case 4:
realSize = 51;
break;
case 5:
realSize = 68;
break;
case 6:
realSize = 85;
break;
case 7:
realSize = 102;
break;
case 8:
realSize = 119;
break;
}
byte[] result = new byte[3];
result[0] = 29;
result[1] = 33;
result[2] = realSize;
return result;
}
/**
* 是否加粗
*/
public static byte[] bold(boolean flag) {
byte[] result = new byte[3];
if(flag){
result[0] = ESC;
result[1] = 69;
result[2] = 0xF;
}else {
result[0] = ESC;
result[1] = 69;
result[2] = 0;
}
return result;
}
}
\ No newline at end of file
package id.kakzaki.blue_thermal_printer;
import android.graphics.Bitmap;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
public class Utils {
// UNICODE 0x23 = #
public static final byte[] UNICODE_TEXT = new byte[]{0x23, 0x23, 0x23,
0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23, 0x23,
0x23, 0x23, 0x23};
private static String hexStr = "0123456789ABCDEF";
private static String[] binaryArray = {"0000", "0001", "0010", "0011",
"0100", "0101", "0110", "0111", "1000", "1001", "1010", "1011",
"1100", "1101", "1110", "1111"};
public static byte[] decodeBitmap(Bitmap bmp) {
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
List<String> list = new ArrayList<String>(); //binaryString list
StringBuffer sb;
int bitLen = bmpWidth / 8;
int zeroCount = bmpWidth % 8;
String zeroStr = "";
if (zeroCount > 0) {
bitLen = bmpWidth / 8 + 1;
for (int i = 0; i < (8 - zeroCount); i++) {
zeroStr = zeroStr + "0";
}
}
for (int i = 0; i < bmpHeight; i++) {
sb = new StringBuffer();
for (int j = 0; j < bmpWidth; j++) {
int color = bmp.getPixel(j, i);
int r = (color >> 16) & 0xff;
int g = (color >> 8) & 0xff;
int b = color & 0xff;
// if color close to white,bit='0', else bit='1'
if (r > 160 && g > 160 && b > 160)
sb.append("0");
else
sb.append("1");
}
if (zeroCount > 0) {
sb.append(zeroStr);
}
list.add(sb.toString());
}
List<String> bmpHexList = binaryListToHexStringList(list);
String commandHexString = "1D763000";
String widthHexString = Integer
.toHexString(bmpWidth % 8 == 0 ? bmpWidth / 8
: (bmpWidth / 8 + 1));
if (widthHexString.length() > 10) {
Log.e("decodeBitmap error", " width is too large");
return null;
} else if (widthHexString.length() == 1) {
widthHexString = "0" + widthHexString;
}
widthHexString = widthHexString + "00";
String heightHexString = Integer.toHexString(bmpHeight);
if (heightHexString.length() > 10) {
Log.e("decodeBitmap error", " height is too large");
return null;
} else if (heightHexString.length() == 1) {
heightHexString = "0" + heightHexString;
}
heightHexString = heightHexString + "00";
List<String> commandList = new ArrayList<String>();
commandList.add(commandHexString + widthHexString + heightHexString);
commandList.addAll(bmpHexList);
return hexList2Byte(commandList);
}
public static List<String> binaryListToHexStringList(List<String> list) {
List<String> hexList = new ArrayList<String>();
for (String binaryStr : list) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < binaryStr.length(); i += 8) {
String str = binaryStr.substring(i, i + 8);
String hexString = myBinaryStrToHexString(str);
sb.append(hexString);
}
hexList.add(sb.toString());
}
return hexList;
}
public static String myBinaryStrToHexString(String binaryStr) {
String hex = "";
String f4 = binaryStr.substring(0, 4);
String b4 = binaryStr.substring(4, 8);
for (int i = 0; i < binaryArray.length; i++) {
if (f4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
for (int i = 0; i < binaryArray.length; i++) {
if (b4.equals(binaryArray[i]))
hex += hexStr.substring(i, i + 1);
}
return hex;
}
public static byte[] hexList2Byte(List<String> list) {
List<byte[]> commandList = new ArrayList<byte[]>();
for (String hexStr : list) {
commandList.add(hexStringToBytes(hexStr));
}
byte[] bytes = sysCopy(commandList);
return bytes;
}
public static byte[] hexStringToBytes(String hexString) {
if (hexString == null || hexString.equals("")) {
return null;
}
hexString = hexString.toUpperCase();
int length = hexString.length() / 2;
char[] hexChars = hexString.toCharArray();
byte[] d = new byte[length];
for (int i = 0; i < length; i++) {
int pos = i * 2;
d[i] = (byte) (charToByte(hexChars[pos]) << 4 | charToByte(hexChars[pos + 1]));
}
return d;
}
public static byte[] sysCopy(List<byte[]> srcArrays) {
int len = 0;
for (byte[] srcArray : srcArrays) {
len += srcArray.length;
}
byte[] destArray = new byte[len];
int destLen = 0;
for (byte[] srcArray : srcArrays) {
System.arraycopy(srcArray, 0, destArray, destLen, srcArray.length);
destLen += srcArray.length;
}
return destArray;
}
private static byte charToByte(char c) {
return (byte) "0123456789ABCDEF".indexOf(c);
}
/*
* 将数据 按照最大长度分段
*
* n 表示按照最大max_length 可以分隔成的段数
* m 表示余数
* */
public static List<byte[]> separatorBytes(byte[] bytes) {
List<byte[]> list = new ArrayList<>();
// 每次传输最大的字节数 超过600可能会导致打印不出来
// int maxLength = 146;
// if (Platform.isAndroid) {
int maxLength = 500;
// }
int length = bytes.length;
int n = length / maxLength;
int m = length % maxLength;
if (m > 0) {
n += 1;
}
for (int i = 0; i < n; i++) {
int start = i * maxLength;
int end = start + maxLength;
if (end > length) {
end = length;
}
byte[] temBytes = new byte[end - start];
System.arraycopy(bytes, start, temBytes, 0, temBytes.length);
list.add(temBytes);
}
return list;
}
/**
* @param bitmap
* @return 将图片转数组
*/
public static byte[] parseBmpToByte(Bitmap bitmap) {
int scaleHeight = bitmap.getHeight();
//宽度要是8的倍数
int bitWidth = (bitmap.getWidth() + 7) / 8 * 8;
int width = bitmap.getWidth();
int data[] = new int[width * scaleHeight];
byte dataVec[] = new byte[bitWidth * scaleHeight / 8 + 8];
long start = System.currentTimeMillis();
dataVec[0] = 29;
dataVec[1] = 118;
dataVec[2] = 48;
dataVec[3] = 0;
dataVec[4] = (byte) (bitWidth / 8 % 256);
dataVec[5] = (byte) (bitWidth / 8 / 256);
dataVec[6] = (byte) (scaleHeight % 256);
dataVec[7] = (byte) (scaleHeight / 256);
int k = 8;
bitmap.getPixels(data, 0, width, 0, 0, width, scaleHeight);
for (int h = 0; h < scaleHeight; h++) {
for (int w = 0; w < bitWidth; w += 8) {
int value = 0;
for (int i = 0; i < 8; i++) {
int index = h * width + w + i;
if (w + i >= width) {
// 超度图片的大小零填充
value |= 0;
} else {
//这里就是高低在前低位在后
value |= px2Byte(data[index]) << (7 - i);
}
}
dataVec[k++] = (byte) value;
}
}
return dataVec;
}
public static byte px2Byte(int pixel) {
byte b;
int red = (pixel & 0x00ff0000) >> 16; // 取高两位
int green = (pixel & 0x0000ff00) >> 8; // 取中两位
int blue = pixel & 0x000000ff; // 取低两位
int gray = rgb2gray(red, green, blue);
if (gray < 127) {
b = 1;
} else {
b = 0;
}
return b;
}
public static int rgb2gray(int r, int g, int b) {
return (int) Math.round(0.299 * r + 0.587 * g + 0.114 * b);
}
//System.arraycopy()方法
public static byte[] byteMerger(byte[] bt1, byte[] bt2){
byte[] bt3 = new byte[bt1.length+bt2.length];
System.arraycopy(bt1, 0, bt3, 0, bt1.length);
System.arraycopy(bt2, 0, bt3, bt1.length, bt2.length);
return bt3;
}
}
<?xml version="1.0" encoding="UTF-8"?>
<module external.linked.project.id="blue_thermal_printer" external.linked.project.path="$MODULE_DIR$" external.root.project.path="$MODULE_DIR$" external.system.id="GRADLE" type="JAVA_MODULE" version="4">
<component name="FacetManager">
<facet type="java-gradle" name="Java-Gradle">
<configuration>
<option name="BUILD_FOLDER_PATH" value="$MODULE_DIR$/build" />
<option name="BUILDABLE" value="false" />
</configuration>
</facet>
</component>
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.gradle" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/blue_thermal_printer/example/ios/.symlinks/plugins/blue_thermal_printer/example/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/build" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/example/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks/plugins/path_provider_ios/example/build" />
</content>
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/lib" isTestSource="false" />
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
<excludeFolder url="file://$MODULE_DIR$/.idea" />
<excludeFolder url="file://$MODULE_DIR$/.pub" />
<excludeFolder url="file://$MODULE_DIR$/build" />
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
<excludeFolder url="file://$MODULE_DIR$/example/build" />
</content>
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Dart Packages" level="project" />
<orderEntry type="library" name="Dart SDK" level="project" />
<orderEntry type="library" name="Flutter Plugins" level="project" />
</component>
</module>
\ No newline at end of file
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# Visual Studio Code related
.vscode/
# Flutter/Dart/Pub related
**/doc/api/
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.packages
.pub-cache/
.pub/
/build/
# Android related
**/android/**/gradle-wrapper.jar
**/android/.gradle
**/android/captures/
**/android/gradlew
**/android/gradlew.bat
**/android/local.properties
**/android/**/GeneratedPluginRegistrant.java
# iOS/XCode related
**/ios/**/*.mode1v3
**/ios/**/*.mode2v3
**/ios/**/*.moved-aside
**/ios/**/*.pbxuser
**/ios/**/*.perspectivev3
**/ios/**/*sync/
**/ios/**/.sconsign.dblite
**/ios/**/.tags*
**/ios/**/.vagrant/
**/ios/**/DerivedData/
**/ios/**/Icon?
**/ios/**/Pods/
**/ios/**/.symlinks/
**/ios/**/profile
**/ios/**/xcuserdata
**/ios/.generated/
**/ios/Flutter/App.framework
**/ios/Flutter/Flutter.framework
**/ios/Flutter/Generated.xcconfig
**/ios/Flutter/app.flx
**/ios/Flutter/app.zip
**/ios/Flutter/flutter_assets/
**/ios/ServiceDefinitions.json
**/ios/Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!**/ios/**/default.mode1v3
!**/ios/**/default.mode2v3
!**/ios/**/default.pbxuser
!**/ios/**/default.perspectivev3
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: 7a4c33425ddd78c54aba07d86f3f9a4a0051769b
channel: stable
project_type: app
# blue_thermal_printer_example
Demonstrates how to use the blue_thermal_printer plugin.
## Getting Started
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
BlueThermalPrinter bluetooth = BlueThermalPrinter.instance;
List<BluetoothDevice> _devices = [];
BluetoothDevice _device;
bool _connected = false;
bool _pressed = false;
String pathImage;
@override
void initState() {
super.initState();
initPlatformState();
initSavetoPath();
}
initSavetoPath()async{
//read and write
//image max 300px X 300px
final filename = 'yourlogo.png';
var bytes = await rootBundle.load("assets/images/yourlogo.png");
String dir = (await getApplicationDocumentsDirectory()).path;
writeToFile(bytes,'$dir/$filename');
setState(() {
pathImage='$dir/$filename';
});
}
Future<void> initPlatformState() async {
List<BluetoothDevice> devices = [];
try {
devices = await bluetooth.getBondedDevices();
} on PlatformException {
// TODO - Error
}
bluetooth.onStateChanged().listen((state) {
switch (state) {
case BlueThermalPrinter.CONNECTED:
setState(() {
_connected = true;
_pressed = false;
});
break;
case BlueThermalPrinter.DISCONNECTED:
setState(() {
_connected = false;
_pressed = false;
});
break;
default:
print(state);
break;
}
});
if (!mounted) return;
setState(() {
_devices = devices;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Blue Thermal Printer'),
),
body: Container(
child: ListView(
children: <Widget>[
Padding(
padding: const EdgeInsets.fromLTRB(10.0, 10.0, 10.0, 0.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(
'Device:',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
DropdownButton(
items: _getDeviceItems(),
onChanged: (value) => setState(() => _device = value),
value: _device,
),
RaisedButton(
onPressed:
_pressed ? null : _connected ? _disconnect : _connect,
child: Text(_connected ? 'Disconnect' : 'Connect'),
),
],
),
),
Padding(
padding: const EdgeInsets.only(left: 10.0, right: 10.0, top: 50),
child: RaisedButton(
onPressed: _connected ? _tesPrint : null,
child: Text('TesPrint'),
),
),
],
),
),
),
);
}
List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
List<DropdownMenuItem<BluetoothDevice>> items = [];
if (_devices.isEmpty) {
items.add(DropdownMenuItem(
child: Text('NONE'),
));
} else {
_devices.forEach((device) {
items.add(DropdownMenuItem(
child: Text(device.name),
value: device,
));
});
}
return items;
}
void _connect() {
if (_device == null) {
show('No device selected.');
} else {
bluetooth.isConnected.then((isConnected) {
if (!isConnected) {
bluetooth.connect(_device).catchError((error) {
setState(() => _pressed = false);
});
setState(() => _pressed = true);
}
});
}
}
void _disconnect() {
bluetooth.disconnect();
setState(() => _pressed = true);
}
//write to app path
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
void _tesPrint() async {
//SIZE
// 0- normal size text
// 1- only bold text
// 2- bold with medium text
// 3- bold with large text
//ALIGN
// 0- ESC_ALIGN_LEFT
// 1- ESC_ALIGN_CENTER
// 2- ESC_ALIGN_RIGHT
bluetooth.isConnected.then((isConnected) {
if (isConnected) {
bluetooth.printCustom("HEADER",3,1);
bluetooth.printNewLine();
bluetooth.printImage(pathImage);
bluetooth.printNewLine();
bluetooth.printLeftRight("LEFT", "RIGHT",0);
bluetooth.printLeftRight("LEFT", "RIGHT",1);
bluetooth.printNewLine();
bluetooth.printLeftRight("LEFT", "RIGHT",2);
bluetooth.printCustom("Body left",1,0);
bluetooth.printCustom("Body right",0,2);
bluetooth.printNewLine();
bluetooth.printCustom("Terimakasih",2,1);
bluetooth.printNewLine();
bluetooth.printQRcode("Insert Your Own Text to Generate");
bluetooth.printNewLine();
bluetooth.printNewLine();
bluetooth.paperCut();
});
}
Future show(
String message, {
Duration duration: const Duration(seconds: 3),
}) async {
await new Future.delayed(new Duration(milliseconds: 100));
Scaffold.of(context).showSnackBar(
new SnackBar(
content: new Text(
message,
style: new TextStyle(
color: Colors.white,
),
),
duration: duration,
),
);
}
}
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at
# https://dart-lang.github.io/linter/lints/index.html.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
# Remember to never publicly share your keystore.
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
key.properties
**/*.keystore
**/*.jks
def localProperties = new Properties()
def localPropertiesFile = rootProject.file('local.properties')
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader('UTF-8') { reader ->
localProperties.load(reader)
}
}
def flutterRoot = localProperties.getProperty('flutter.sdk')
if (flutterRoot == null) {
throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
}
def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
if (flutterVersionCode == null) {
flutterVersionCode = '1'
}
def flutterVersionName = localProperties.getProperty('flutter.versionName')
if (flutterVersionName == null) {
flutterVersionName = '1.0'
}
apply plugin: 'com.android.application'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android {
compileSdkVersion 30
lintOptions {
disable 'InvalidPackage'
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "id.kakzaki.blue_thermal_printer_example"
minSdkVersion 18
targetSdkVersion 30
multiDexEnabled true
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
buildToolsVersion '29.0.2'
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
flutter {
source '../..'
}
dependencies {
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.3.0-alpha02'
implementation 'com.android.support:multidex:1.0.3'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0-alpha02'
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="id.kakzaki.blue_thermal_printer_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="id.kakzaki.blue_thermal_printer_example">
<uses-permission android:name="android.permission.INTERNET"/>
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:label="blue_thermal_printer_example"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in @style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
</manifest>
package id.kakzaki.blue_thermal_printer_example;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}
package id.kakzaki.bluethermalprinter_example
import io.flutter.embedding.android.FlutterActivity
class MainActivity: FlutterActivity() {
}
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@android:color/white" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
<!-- Theme applied to the Android Window as soon as the process has started.
This theme determines the color of the Android Window while your
Flutter UI initializes, as well as behind your Flutter UI while its
running.
This Theme is only used starting with V2 of Flutter's Android embedding. -->
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
Flutter draws its first frame -->
<item name="android:windowBackground">@drawable/launch_background</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="id.kakzaki.blue_thermal_printer_example">
<!-- Flutter needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.5.0'
}
}
allprojects {
repositories {
google()
jcenter()
}
}
rootProject.buildDir = '../build'
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(':app')
}
task clean(type: Delete) {
delete rootProject.buildDir
}
android.enableJetifier=true
android.useAndroidX=true
org.gradle.jvmargs=-Xmx1536M
android.enableR8=true
\ No newline at end of file
#Fri Aug 30 12:26:22 BRT 2019
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
include ':app'
def flutterProjectRoot = rootProject.projectDir.parentFile.toPath()
def plugins = new Properties()
def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins')
if (pluginsFile.exists()) {
pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) }
}
plugins.each { name, path ->
def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile()
include ":$name"
project(":$name").projectDir = pluginDirectory
}
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
<key>MinimumOSVersion</key>
<string>9.0</string>
</dict>
</plist>
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
# Uncomment this line to define a global platform for your project
# platform :ios, '9.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
use_modular_headers!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
target.build_configurations.each do |config|
config.build_settings['ENABLE_BITCODE'] = 'NO'
end
end
end
PODS:
- blue_thermal_printer (0.0.1):
- Flutter
- Flutter (1.0.0)
- path_provider_ios (0.0.1):
- Flutter
DEPENDENCIES:
- blue_thermal_printer (from `.symlinks/plugins/blue_thermal_printer/ios`)
- Flutter (from `Flutter`)
- path_provider_ios (from `.symlinks/plugins/path_provider_ios/ios`)
EXTERNAL SOURCES:
blue_thermal_printer:
:path: ".symlinks/plugins/blue_thermal_printer/ios"
Flutter:
:path: Flutter
path_provider_ios:
:path: ".symlinks/plugins/path_provider_ios/ios"
SPEC CHECKSUMS:
blue_thermal_printer: 5702a6b03759a69281a7f27f0001130089c584f4
Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a
path_provider_ios: 14f3d2fd28c4fdb42f44e0f751d12861c43cee02
PODFILE CHECKSUM: e4aed95c7aa3e0620ec6419ead01fe8977619d0a
COCOAPODS: 1.11.3
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 50;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
48B6B50EF7BDF68AD9BE3DD3 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8BEF83EA25F4065C8374E741 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
499AE47CC5974DAE43E30C83 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
8BEF83EA25F4065C8374E741 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
D009E91431D56FC687D3E57E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
DCF2A4EADD0D8B28FF458EFF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
48B6B50EF7BDF68AD9BE3DD3 /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
03A0E3611FA46F9457B9438F /* Pods */ = {
isa = PBXGroup;
children = (
DCF2A4EADD0D8B28FF458EFF /* Pods-Runner.debug.xcconfig */,
499AE47CC5974DAE43E30C83 /* Pods-Runner.release.xcconfig */,
D009E91431D56FC687D3E57E /* Pods-Runner.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
1100AE48B1453DCDD213F352 /* Frameworks */ = {
isa = PBXGroup;
children = (
8BEF83EA25F4065C8374E741 /* Pods_Runner.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
03A0E3611FA46F9457B9438F /* Pods */,
1100AE48B1453DCDD213F352 /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
97C146F11CF9000F007C117D /* Supporting Files */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
97C146F11CF9000F007C117D /* Supporting Files */ = {
isa = PBXGroup;
children = (
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
3890B2043A501D009B2313EB /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
3117903BD074CF076C82D12A /* [CP] Embed Pods Frameworks */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 1300;
ORGANIZATIONNAME = "The Chromium Authors";
TargetAttributes = {
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
DevelopmentTeam = JYL43SXW4K;
LastSwiftMigration = 0910;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
English,
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
3117903BD074CF076C82D12A /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh",
"${BUILT_PRODUCTS_DIR}/blue_thermal_printer/blue_thermal_printer.framework",
"${BUILT_PRODUCTS_DIR}/path_provider_ios/path_provider_ios.framework",
);
name = "[CP] Embed Pods Frameworks";
outputPaths = (
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/blue_thermal_printer.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/path_provider_ios.framework",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
3890B2043A501D009B2313EB /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JYL43SXW4K;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = id.kakzaki.blueThermalPrinterExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 9.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JYL43SXW4K;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = id.kakzaki.blueThermalPrinterExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = JYL43SXW4K;
ENABLE_BITCODE = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
INFOPLIST_FILE = Runner/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
LIBRARY_SEARCH_PATHS = (
"$(inherited)",
"$(PROJECT_DIR)/Flutter",
);
PRODUCT_BUNDLE_IDENTIFIER = id.kakzaki.blueThermalPrinterExample;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_SWIFT3_OBJC_INFERENCE = On;
SWIFT_VERSION = 4.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1300"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
</Testables>
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
language = ""
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
<AdditionalOptions>
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import UIKit
import Flutter
@UIApplicationMain
@objc class AppDelegate: FlutterAppDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?
) -> Bool {
GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="12089"/>
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
</imageView>
</subviews>
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>blue_thermal_printer_example</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>
<key>NSBluetoothAlwaysUsageDescription</key>
<string>Allow App use bluetooth?</string>
<key>NSBluetoothPeripheralUsageDescription</key>
<string>蓝牙打印机功能需要您开启蓝牙,并允许该应用使用。</string>
<key>UIBackgroundModes</key>
<array>
<string>bluetooth-central</string>
<string>bluetooth-peripheral</string>
</array>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
<string>UIInterfaceOrientationLandscapeLeft</string>
<string>UIInterfaceOrientationLandscapeRight</string>
</array>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>io.flutter.embedded_views_preview</key>
<true/>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
\ No newline at end of file
import 'dart:io';
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
import 'package:blue_thermal_printer_example/testprint.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
}
class _MyAppState extends State<MyApp> {
BlueThermalPrinter bluetooth = BlueThermalPrinter.instance;
List<BluetoothDevice> _devices = [];
BluetoothDevice _device;
bool _connected = false;
String pathImage;
TestPrint testPrint;
@override
void initState() {
super.initState();
initPlatformState();
testPrint = TestPrint();
}
Future<void> initPlatformState() async {
bool isConnected = await bluetooth.isConnected;
List<BluetoothDevice> devices = [];
try {
devices = await bluetooth.getBondedDevices();
} on PlatformException {
// TODO - Error
}
bluetooth.onStateChanged().listen((state) {
print("蓝牙状态:$state");
switch (state) {
case BlueThermalPrinter.CONNECTED:
setState(() {
_connected = true;
});
break;
case BlueThermalPrinter.DISCONNECTED:
setState(() {
_connected = false;
});
break;
default:
print(state);
break;
}
});
bluetooth.onRead().listen((event) {
print("打印状态:$event");
});
if (!mounted) return;
setState(() {
_devices = devices;
});
if (isConnected) {
setState(() {
_connected = true;
});
}
if (defaultTargetPlatform == TargetPlatform.iOS) {
await bluetooth.repeatScan();
}
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Blue Thermal Printer'),
),
body: Container(
child: Padding(
padding: const EdgeInsets.all(8.0),
child: ListView(
children: <Widget>[
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(
width: 10,
),
Text(
'Device:',
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
SizedBox(
width: 30,
),
Expanded(
child: DropdownButton(
items: _getDeviceItems(),
onChanged: (value) => setState(() => _device = value),
value: _device,
),
),
],
),
SizedBox(
height: 10,
),
Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.brown),
onPressed: () {
initPlatformState();
bluetooth.getLastDevice().then((value) {
print("上台设备:$value");
});
},
child: Text(
'刷新',
style: TextStyle(color: Colors.white),
),
),
SizedBox(
width: 20,
),
ElevatedButton(
style: ElevatedButton.styleFrom(
primary: _connected ? Colors.red : Colors.green),
onPressed: _connected ? _disconnect : _connect,
child: Text(
_connected ? '断开' : '连接',
style: TextStyle(color: Colors.white),
),
),
],
),
Padding(
padding:
const EdgeInsets.only(left: 10.0, right: 10.0, top: 50),
child: ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.brown),
onPressed: () {
testPrint.sample();
},
child: Text('打印', style: TextStyle(color: Colors.white)),
),
),
Padding(
padding:
const EdgeInsets.only(left: 10.0, right: 10.0, top: 10),
child: ElevatedButton(
style: ElevatedButton.styleFrom(primary: Colors.brown),
onPressed: () {
testPrint.sample1();
},
child: Text('打印(tsc)', style: TextStyle(color: Colors.white)),
),
),
],
),
),
),
),
);
}
List<DropdownMenuItem<BluetoothDevice>> _getDeviceItems() {
List<DropdownMenuItem<BluetoothDevice>> items = [];
if (_devices.isEmpty) {
items.add(DropdownMenuItem(
child: Text('NONE'),
));
} else {
_devices.forEach((device) {
items.add(DropdownMenuItem(
child: Text(device.name),
value: device,
));
});
}
return items;
}
void _connect() {
if (_device == null) {
show('No device selected.');
} else {
bluetooth.isConnected.then((isConnected) {
if (!isConnected) {
bluetooth.connect(_device).catchError((error) {
setState(() => _connected = false);
});
setState(() => _connected = true);
}
});
}
}
void _disconnect() {
bluetooth.disconnect();
print("断开");
setState(() => _connected = true);
}
//write to app path
Future<void> writeToFile(ByteData data, String path) {
final buffer = data.buffer;
return new File(path).writeAsBytes(
buffer.asUint8List(data.offsetInBytes, data.lengthInBytes));
}
Future show(
String message, {
Duration duration: const Duration(seconds: 3),
}) async {
await new Future.delayed(new Duration(milliseconds: 100));
ScaffoldMessenger.of(context).showSnackBar(
new SnackBar(
content: new Text(
message,
style: new TextStyle(
color: Colors.white,
),
),
duration: duration,
),
);
}
}
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
import 'package:dio/dio.dart';
class TestPrint {
BlueThermalPrinter bluetooth = BlueThermalPrinter.instance;
sample() async {
bluetooth.isConnected.then((isConnected) async {
if (isConnected) {
// for (var i = 0; i < 5; i++) {
List<int> data = [];
var barcode =
await bluetooth.getBarCode("02345678901234567890", 300, 120, 0);
data.addAll(PrintData.initPrinter());
data.addAll(PrintData.printText("采样编码", size: 3, bold: true, space: 1));
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printText("VIP添加样", size: 2));
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printText("硫/量热仪/灰分"));
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printMarginLeft(11));
data.addAll(PrintData.printQRCode("02345678901234567890"));
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printMarginLeft(5));
data.addAll(barcode);
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printText("马上来货运"));
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
data.addAll(PrintData.printNewLine());
bluetooth.writeBytes(Uint8List.fromList(data));
await Future.delayed(Duration(milliseconds: 500));
// }
}
});
}
sample1() async {
bluetooth.isConnected.then((isConnected) async {
if (isConnected) {
Map<String, dynamic> config = Map();
config['width'] = 100; // 标签宽度,单位mm
config['height'] = 150; // 标签高度,单位mm
config['gap'] = 2; // 标签间隔,单位mm
List<Map<String, dynamic>> data = [];
//ByteData imageByte = await rootBundle.load("assets/images/WechatIMG91.png");
// List<int> imageBytes = imageByte.buffer.asUint8List(imageByte.offsetInBytes, imageByte.lengthInBytes);
// String base64Image = base64Encode(imageBytes);
// 下载远程图片,转换为字节流
var str = "https://img1.baidu.com/it/u=873106765,2587410047&fm=253&fmt=auto&app=138&f=JPEG?w=530&h=500";
Response<List<int>> rs = await Dio().get<List<int>>(
str,
options: Options(
responseType: ResponseType.bytes,
),
);
String base64Image = base64Encode(rs.data);
//打印配置
Map<String, dynamic> image = Map();
image['type'] = 'image';
image['x'] = 0;
image['y'] = 0;
image['width'] = 800;
image['content'] = base64Image;
data.add(image);
bluetooth.printTSCImage(config, data);
await Future.delayed(Duration(milliseconds: 500));
}
});
}
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.2"
blue_thermal_printer:
dependency: "direct dev"
description:
path: ".."
relative: true
source: path
version: "1.3.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.16.0"
dio:
dependency: "direct main"
description:
name: dio
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.6"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
fast_gbk:
dependency: transitive
description:
name: fast_gbk
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
ffi:
dependency: transitive
description:
name: ffi
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.1"
file:
dependency: transitive
description:
name: file
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.1.4"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
http_parser:
dependency: transitive
description:
name: http_parser
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.0.1"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.4"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.11"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.20"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.11"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.7"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.0.4"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.3"
platform:
dependency: "direct main"
description:
name: platform
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.3"
process:
dependency: transitive
description:
name: process
url: "https://pub.flutter-io.cn"
source: hosted
version: "4.2.4"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.2"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.9"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.2"
win32:
dependency: transitive
description:
name: win32
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.2.0+2"
sdks:
dart: ">=2.17.0 <3.0.0"
flutter: ">=3.0.0"
name: blue_thermal_printer_example
description: Demonstrates how to use the blue_thermal_printer plugin.
publish_to: https://pub.dev/packages/blue_thermal_printer
environment:
sdk: ">=2.1.0 <3.0.0"
dependencies:
flutter:
sdk: flutter
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
path_provider: ^2.0.5
platform: ^3.1.0
dio: ^4.0.6
dev_dependencies:
flutter_test:
sdk: flutter
blue_thermal_printer:
path: ../
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
# The following section is specific to Flutter.
flutter:
# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true
# To add assets to your application, add an assets section, like this:
assets:
- assets/images/yourlogo.png
- assets/images/bluetooth_print.png
- assets/images/WechatIMG72.jpeg
- assets/images/WechatIMG74.png
- assets/images/WechatIMG76.png
- assets/images/WechatIMG77.png
- assets/images/WechatIMG78.png
- assets/images/WechatIMG79.png
- assets/images/1663660235169.jpg
- assets/images/WechatIMG80.png
- assets/images/WechatIMG81.png
- assets/images/WechatIMG91.png
- assets/images/WechatIMG92.png
- assets/images/WechatIMG95.png
# - images/a_dot_ham.jpeg
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# For details regarding adding assets from package dependencies, see
# https://flutter.dev/assets-and-images/#from-packages
# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.dev/custom-fonts/#from-packages
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:blue_thermal_printer_example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) =>
widget is Text && widget.data.startsWith('Running on:'),
),
findsOneWidget,
);
});
}
<!DOCTYPE html>
<html>
<head>
<!--
If you are serving your web app in a path other than the root, change the
href value below to reflect the base path you are serving from.
The path provided below has to start and end with a slash "/" in order for
it to work correctly.
For more details:
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base
This is a placeholder for base href that will be replaced by the value of
the `--base-href` argument provided to `flutter build`.
-->
<base href="$FLUTTER_BASE_HREF">
<meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="Demonstrates how to use the bluethermalprinter plugin.">
<!-- iOS meta tags & icons -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="bluethermalprinter_example">
<link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/>
<title>bluethermalprinter_example</title>
<link rel="manifest" href="manifest.json">
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
application. For more information, see:
https://developers.google.com/web/fundamentals/primers/service-workers -->
<script>
var serviceWorkerVersion = null;
var scriptLoaded = false;
function loadMainDartJs() {
if (scriptLoaded) {
return;
}
scriptLoaded = true;
var scriptTag = document.createElement('script');
scriptTag.src = 'main.dart.js';
scriptTag.type = 'application/javascript';
document.body.append(scriptTag);
}
if ('serviceWorker' in navigator) {
// Service workers are supported. Use them.
window.addEventListener('load', function () {
// Wait for registration to finish before dropping the <script> tag.
// Otherwise, the browser will load the script multiple times,
// potentially different versions.
var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;
navigator.serviceWorker.register(serviceWorkerUrl)
.then((reg) => {
function waitForActivation(serviceWorker) {
serviceWorker.addEventListener('statechange', () => {
if (serviceWorker.state == 'activated') {
console.log('Installed new service worker.');
loadMainDartJs();
}
});
}
if (!reg.active && (reg.installing || reg.waiting)) {
// No active web worker and we have installed or are installing
// one for the first time. Simply wait for it to activate.
waitForActivation(reg.installing || reg.waiting);
} else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {
// When the app updates the serviceWorkerVersion changes, so we
// need to ask the service worker to update.
console.log('New service worker available.');
reg.update();
waitForActivation(reg.installing);
} else {
// Existing service worker is still good.
console.log('Loading app from service worker.');
loadMainDartJs();
}
});
// If service worker doesn't succeed in a reasonable amount of time,
// fallback to plaint <script> tag.
setTimeout(() => {
if (!scriptLoaded) {
console.warn(
'Failed to load app from service worker. Falling back to plain <script> tag.',
);
loadMainDartJs();
}
}, 4000);
});
} else {
// Service workers not supported. Just drop the <script> tag.
loadMainDartJs();
}
</script>
</body>
</html>
{
"name": "bluethermalprinter_example",
"short_name": "bluethermalprinter_example",
"start_url": ".",
"display": "standalone",
"background_color": "#0175C2",
"theme_color": "#0175C2",
"description": "Demonstrates how to use the bluethermalprinter plugin.",
"orientation": "portrait-primary",
"prefer_related_applications": false,
"icons": [
{
"src": "icons/Icon-192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "icons/Icon-512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "icons/Icon-maskable-192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "icons/Icon-maskable-512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
]
}
android.enableJetifier=true
android.useAndroidX=true
\ No newline at end of file
.idea/
.vagrant/
.sconsign.dblite
.svn/
.DS_Store
*.swp
profile
DerivedData/
build/
GeneratedPluginRegistrant.h
GeneratedPluginRegistrant.m
.generated/
*.pbxuser
*.mode1v3
*.mode2v3
*.perspectivev3
!default.pbxuser
!default.mode1v3
!default.mode2v3
!default.perspectivev3
xcuserdata
*.moved-aside
*.pyc
*sync/
Icon?
.tags*
/Flutter/Generated.xcconfig
#import <Flutter/Flutter.h>
#import <CoreBluetooth/CoreBluetooth.h>
#define NAMESPACE @"blue_thermal_printer"
@interface BlueThermalPrinterPlugin : NSObject<FlutterPlugin, CBCentralManagerDelegate, CBPeripheralDelegate>
@end
@interface BluetoothPrintStreamHandler : NSObject<FlutterStreamHandler>
@property FlutterEventSink sink;
@end
#import "BlueThermalPrinterPlugin.h"
#import <blue_thermal_printer/blue_thermal_printer-Swift.h>
#import "HLBLEManager.h"
#import "HLPrinter.h"
#import "TscCommand.h"
@interface BlueThermalPrinterPlugin ()
@property(nonatomic, retain) NSObject<FlutterPluginRegistrar> *registrar;
@property(nonatomic, retain) FlutterMethodChannel *channel;
@property(nonatomic, retain) BluetoothPrintStreamHandler *stateStreamHandler;
@property(nonatomic) NSMutableDictionary *scannedPeripherals;
@property (nonatomic, strong) HLBLEManager *manager;
@property (nonatomic, strong) NSMutableArray *deviceArray; /**< 蓝牙设备>*/
@property (nonatomic, strong) NSString *lastDevice;
@property (nonatomic, assign) BOOL isConnected;
@property (assign, nonatomic) NSTimeInterval timeout; /**< 默认超时时间 */
@end
@implementation BlueThermalPrinterPlugin
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar {
//[SwiftBlueThermalPrinterPlugin registerWithRegistrar:registrar];
FlutterMethodChannel* channel = [FlutterMethodChannel
methodChannelWithName:NAMESPACE @"/methods"
binaryMessenger:[registrar messenger]];
FlutterEventChannel* stateChannel = [FlutterEventChannel eventChannelWithName:NAMESPACE @"/state" binaryMessenger:[registrar messenger]];
BlueThermalPrinterPlugin* instance = [[BlueThermalPrinterPlugin alloc] init];
instance.channel = channel;
instance.scannedPeripherals = [NSMutableDictionary new];
// 初始化数据
[instance setupData];
// 蓝牙模块
[instance initBluetoothToScan];
// 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);
NSDictionary *arguments = call.arguments;
__weak BlueThermalPrinterPlugin *weakSelf = self;
if ([@"state" isEqualToString:call.method]) {
result(nil);
}
else if([@"isAvailable" isEqualToString:call.method]) {
result(@(YES));
}
else if([@"isOn" isEqualToString:call.method]) {
result(@(YES));
}
else if([@"isConnected" isEqualToString:call.method]) {
result(@(weakSelf.isConnected));
}
else if([@"getLastDevice" isEqualToString:call.method]) {
NSString *lastDevice = [HLBLEManager UUIDStringForLastPeripheral];
@try {
result(lastDevice != nil ? lastDevice:@"");
} @catch (FlutterError *e) {
result(e);
}
}
else if([@"openSettings" isEqualToString:call.method]) {
}
else if([@"getBondedDevices" isEqualToString:call.method]) {
result(weakSelf.deviceArray);
}
else if([@"connect" isEqualToString:call.method]) {
@try {
if ([arguments.allKeys containsObject:@"address"]) {
NSString *name = arguments[@"name"];
weakSelf.lastDevice = name;
NSLog(@"连接蓝牙名称:%@", name);
CBPeripheral *peripheral = [_scannedPeripherals objectForKey:[arguments objectForKey:@"address"]];
// 查询连接外围设备
[weakSelf connectBLEDevice:peripheral];
}
result(nil);
} @catch(FlutterError *e) {
result(e);
}
}
else if([@"disconnect" isEqualToString:call.method]) {
@try {
[weakSelf.manager cancelPeripheralConnection];
weakSelf.isConnected = NO;
result(nil);
} @catch(FlutterError *e) {
result(e);
}
}
else if([@"printSampleCode" isEqualToString:call.method]) {
@try {
[weakSelf sendPrintDataEvent:arguments];
result(nil);
} @catch(FlutterError *e) {
result(e);
}
}
else if([@"repeatScan" isEqualToString:call.method]) {
[weakSelf startScanPerpheralTimeout:10];
result(nil);
}
else if([@"writeBytes" isEqualToString:call.method]) {
@try {
if ([arguments.allKeys containsObject:@"message"]) {
FlutterStandardTypedData *bytesList = arguments[@"message"];
[weakSelf writeBytes:bytesList.data];
}
result(nil);
} @catch (FlutterError *e) {
result(e);
}
}
else if([@"getBarCode" isEqualToString:call.method]) {
@try {
if ([arguments.allKeys containsObject:@"textToBar"]) {
[weakSelf getBarCode:result arguments:arguments];
}
result(nil);
} @catch (FlutterError *e) {
result(e);
}
}
else if([@"printTSCImage" isEqualToString:call.method]) {
@try {
NSDictionary *args = [call arguments];
[weakSelf writeBytes:[self mapToTscCommand:args]];
result(nil);
} @catch(FlutterError *e) {
result(e);
}
}
}
#pragma mark - 打印 method
- (void)writeBytes:(NSData *)mainData {
[self.manager sendPrintData:mainData completion:^(CBPeripheral *connectPerpheral, BOOL completion, NSString *error) {
NSLog(@"写入结:%d---错误:%@",completion,error);
}];
}
- (void)sendPrintDataEvent:(NSDictionary *)arguments {
HLPrinter *printer = [self getPrinter:arguments];
if (printer == nil) {
return;
}
NSData *mainData = [printer getFinalData];
[self.manager sendPrintData:mainData completion:^(CBPeripheral *connectPerpheral, BOOL completion, NSString *error) {
NSLog(@"写入结:%d---错误:%@",completion,error);
}];
}
- (HLPrinter *)getPrinter:(NSDictionary *)dict {
HLPrinter *printer = [[HLPrinter alloc] init];
NSString *qrcode = dict[@"qrcode"];
NSString *barcode = dict[@"barcode"];
NSString *middleTitle = dict[@"title"]; //小票title
NSString *subTitle = dict[@"subTitle"]; //小票subTitle
NSString *smallSubTitle = dict[@"smallSubTitle"]; //小票smallSubTitle
NSString *footerInfo = dict[@"bottomContent"]; //小票底部文案
if (qrcode.length <= 0 || barcode.length <= 0) { return nil; }
/*
* 制作小票代码
*/
if (middleTitle.length > 0) {
NSUInteger lengthT = [self stringEncode:middleTitle];
if (lengthT <= 16) {
if (lengthT % 2 == 0) { // 偶数
NSInteger offset = ceil((16 - lengthT)/2) * (48/2);
[printer appendTitle:@"" value:middleTitle valueOffset:offset fontSize:HLFontSizeTitleMiddle];
} else {
NSInteger offset = ceil((16 - lengthT)/2) * (54/2);
[printer appendTitle:@"" value:middleTitle valueOffset:offset fontSize:HLFontSizeTitleMiddle];
}
} else {
[printer appendTitle:@"" value:middleTitle valueOffset:0 fontSize:HLFontSizeTitleMiddle];
}
}
if (subTitle.length > 0) {
NSUInteger lengthT = [self stringEncode:subTitle];
if (lengthT <= 32) {
NSInteger offset = (32 - lengthT)/2 * (25/2);
[printer appendTitle:@"" value:subTitle valueOffset:offset fontSize:HLFontSizeTitleSmalle];
} else {
[printer appendTitle:@"" value:subTitle valueOffset:0 fontSize:HLFontSizeTitleSmalle];
}
}
if (smallSubTitle.length > 0) {
NSUInteger lengthT2 = [self stringEncode:smallSubTitle];
if (lengthT2 <= 32) {
NSInteger offset = (32 - lengthT2)/2 * (25/2);
[printer appendTitle:@"" value:smallSubTitle valueOffset:offset fontSize:HLFontSizeTitleSmalle];
} else {
[printer appendTitle:@"" value:smallSubTitle valueOffset:0 fontSize:HLFontSizeTitleSmalle];
}
}
[printer appendQRCodeWithInfo:qrcode size:8 alignment:HLTextAlignmentLeft offset:90];
[printer appendBarCodeWithInfo:barcode alignment:HLTextAlignmentLeft maxWidth:300];
[printer appendFooter:footerInfo];
return printer;
}
- (NSUInteger)stringEncode:(NSString *)s {
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [s dataUsingEncoding:enc];
NSUInteger lengthT = [data length];
return lengthT;
}
- (void)getBarCode:(FlutterResult)result arguments:(NSDictionary *)arguments {
NSString *textToBar = arguments[@"textToBar"];
NSInteger width = [arguments[@"width"] integerValue];
NSInteger height = [arguments[@"height"] integerValue];
NSInteger align = [arguments[@"align"] integerValue];
HLTextAlignment alignment;
switch (align) {
case 0:
{
alignment = HLTextAlignmentLeft;
} break;
case 1:
{
alignment = HLTextAlignmentCenter;
} break;
case 2:
{
alignment = HLTextAlignmentRight;
} break;
default:
alignment = HLTextAlignmentCenter;
break;
}
if (textToBar.length <= 0) { result(nil); return; }
HLPrinter *printer = [[HLPrinter alloc] init];
[printer getBarCodeWithInfo:textToBar
alignment:alignment
offset:40
width:width
height:height];
NSData *mainData = [printer getFinalData];
FlutterStandardTypedData *dataByte = [FlutterStandardTypedData typedDataWithBytes:mainData];
result(dataByte);
}
- (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];
}
#pragma mark -
#pragma mark - setup 初始化数据
- (void)setupData {
self.isConnected = NO;
self.timeout = 30;
}
- (void)startScanPerpheralTimeout:(NSTimeInterval)timeout {
// 清空字典
[self.scannedPeripherals removeAllObjects];
[self.deviceArray removeAllObjects];
self.timeout = timeout;
// 不重复扫描已发现设备
// CBCentralManagerScanOptionAllowDuplicatesKey设置为NO表示不重复扫瞄已发现设备,为YES就是允许。
// CBCentralManagerOptionShowPowerAlertKey设置为YES就是在蓝牙未打开的时候显示弹框
NSDictionary *option = @{
CBCentralManagerScanOptionAllowDuplicatesKey : [NSNumber numberWithBool:NO],
CBCentralManagerOptionShowPowerAlertKey:[NSNumber numberWithBool:YES]};
__weak BlueThermalPrinterPlugin *weakSelf = self;
[self.manager scanForPeripheralsWithServiceUUIDs:nil options:option didDiscoverPeripheral:^(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI) {
NSLog(@"扫描的外围设备名称 -> name: %@", peripheral.name);
if (peripheral.name.length <= 0) { return ; }
if (self.deviceArray.count == 0) {
NSDictionary *dict = @{
@"name":peripheral.name,
@"address":peripheral.identifier.UUIDString,
};
[weakSelf.deviceArray addObject:dict];
} else {
BOOL isExist = NO;
for (int i = 0; i < self.deviceArray.count; i++) {
NSDictionary *dict = [weakSelf.deviceArray objectAtIndex:i];
NSString *address = dict[@"address"];
if ([address isEqualToString:peripheral.identifier.UUIDString]) {
isExist = YES;
NSDictionary *dict = @{
@"name":peripheral.name,
@"address":peripheral.identifier.UUIDString,
};
[weakSelf.deviceArray replaceObjectAtIndex:i withObject:dict];
}
}
if (!isExist) {
NSDictionary *dict = @{
@"name":peripheral.name,
@"address":peripheral.identifier.UUIDString,
};
[weakSelf.deviceArray addObject:dict];
}
}
[weakSelf.scannedPeripherals setObject:peripheral forKey:[[peripheral identifier] UUIDString]];
NSLog(@"扫描的蓝牙外设数量 -> %lu", (unsigned long)weakSelf.deviceArray.count);
}];
}
// 开始扫描
- (void)initBluetoothToScan {
__weak BlueThermalPrinterPlugin *weakSelf = self;
self.manager.stateUpdateBlock = ^(CBCentralManager *central) {
NSString *info = nil;
switch (central.state) {
case CBCentralManagerStatePoweredOn:{
info = @"蓝牙已打开,并且可用";
[weakSelf startScanPerpheralTimeout:10];
break;
}
case CBCentralManagerStatePoweredOff:
info = @"蓝牙可用,未打开";
break;
case CBCentralManagerStateUnsupported:
info = @"SDK不支持";
break;
case CBCentralManagerStateUnauthorized:
info = @"程序未授权";
break;
case CBCentralManagerStateResetting:
info = @"CBCentralManagerStateResetting";
break;
case CBCentralManagerStateUnknown:
info = @"CBCentralManagerStateUnknown";
break;
}
NSLog(@"扫描中心设备状态 -> state:%@", info);
};
}
// 连接外围设备
- (void)connectBLEDevice:(CBPeripheral *)perpheral {
if (perpheral.name.length <= 0) {
return;
}
//HLBLEManager *manager = [HLBLEManager sharedInstance];
__weak BlueThermalPrinterPlugin *weakSelf = self;
[self.manager connectPeripheral:perpheral
connectOptions:@{CBConnectPeripheralOptionNotifyOnDisconnectionKey:@(YES)}
stopScanAfterConnected:YES
servicesOptions:nil
characteristicsOptions:nil
completeBlock:^(HLOptionStage stage, CBPeripheral *peripheral, CBService *service, CBCharacteristic *character, NSError *error) {
switch (stage) {
case HLOptionStageConnection:
{
NSLog(@"连接的外设状态 -> state: %ld", (long)peripheral.state);
if (error) {
NSLog(@"连接失败");
weakSelf.isConnected = NO;
} else { // 状态变化
[weakSelf updateConnectState:peripheral.state];
}
break;
}
case HLOptionStageSeekServices:
{
if (error) {
NSLog(@"查找服务失败");
} else {
NSLog(@"查找服务成功");
}
break;
}
case HLOptionStageSeekCharacteristics:
{
// 该block会返回多次,每一个服务返回一次
if (error) {
NSLog(@"查找特性失败");
} else {
NSLog(@"查找特性成功");
}
break;
}
case HLOptionStageSeekdescriptors:
{
// 该block会返回多次,每一个特性返回一次
if (error) {
NSLog(@"查找特性的描述失败");
} else {
NSLog(@"查找特性的描述成功");
}
break;
}
default:
break;
}
}];
}
- (void)updateConnectState:(CBPeripheralState)state {
__weak BlueThermalPrinterPlugin *weakSelf = self;
dispatch_async(dispatch_get_main_queue(), ^{
NSNumber *ret = @0;
switch (state) {
case CBPeripheralStateConnecting:
{
NSLog(@"update status -> %@", @"连接状态:连接中....");
ret = @0;
weakSelf.isConnected = NO;
break;
}
case CBPeripheralStateConnected:
{
NSLog(@"update status -> %@", @"连接状态:连接成功");
ret = @1;
weakSelf.isConnected = YES;
break;
}
case CBPeripheralStateDisconnecting:
{
NSLog(@"update status -> %@", @"连接状态:连接失败");
ret = @0;
weakSelf.isConnected = NO;
break;
}
case CBPeripheralStateDisconnected:
{
NSLog(@"update status -> %@", @"连接状态:断开连接");
ret = @0;
weakSelf.isConnected = NO;
break;
}
default:
NSLog(@"update status -> %@", @"连接状态:连接超时");
ret = @0;
weakSelf.isConnected = NO;
break;
}
NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:ret,@"id",nil];
if(weakSelf.stateStreamHandler.sink != nil) {
weakSelf.stateStreamHandler.sink([dict objectForKey:@"id"]);
}
});
}
#pragma mark -
#pragma mark - 懒加载
- (NSMutableArray *)deviceArray {
if (!_deviceArray) {
_deviceArray = [[NSMutableArray alloc] init];
}
return _deviceArray;
}
- (HLBLEManager *)manager {
if (!_manager) {
_manager = [HLBLEManager sharedInstance];
}
return _manager;
}
- (void)setTimeout:(NSTimeInterval)timeout {
_timeout = timeout;
if (_timeout > 0) {
[self performSelector:@selector(timeoutAction) withObject:nil afterDelay:timeout];
}
}
- (void)timeoutAction {
[self.manager stopScan];
}
@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
//
// HLBLEConst.h
// HLBluetoothDemo
//
// Created by Harvey on 16/4/29.
// Copyright © 2016年 Halley. All rights reserved.
//
#ifndef HLBLEConst_h
#define HLBLEConst_h
typedef NS_ENUM(NSInteger, HLOptionStage) {
HLOptionStageConnection, //蓝牙连接阶段
HLOptionStageSeekServices, //搜索服务阶段
HLOptionStageSeekCharacteristics, //搜索特性阶段
HLOptionStageSeekdescriptors, //搜索描述信息阶段
};
#pragma mark ------------------- 通知的定义 --------------------------
/** 蓝牙状态改变的通知 */
#define kCentralManagerStateUpdateNoticiation @"kCentralManagerStateUpdateNoticiation"
#pragma mark ------------------- block的定义 --------------------------
/** 蓝牙状态改变的block */
typedef void(^HLStateUpdateBlock)(CBCentralManager *central);
/** 发现一个蓝牙外设的block */
typedef void(^HLDiscoverPeripheralBlock)(CBCentralManager *central, CBPeripheral *peripheral, NSDictionary *advertisementData, NSNumber *RSSI);
/** 连接完成的block,失败error就不为nil */
typedef void(^HLConnectCompletionBlock)(CBPeripheral *peripheral, NSError *error);
/** 搜索到连接上的蓝牙外设的服务block */
typedef void(^HLDiscoveredServicesBlock)(CBPeripheral *peripheral, NSArray *services, NSError *error);
/** 搜索某个服务的子服务 的回调 */
typedef void(^HLDiscoveredIncludedServicesBlock)(CBPeripheral *peripheral,CBService *service, NSArray *includedServices, NSError *error);
/** 搜索到某个服务中的特性的block */
typedef void(^HLDiscoverCharacteristicsBlock)(CBPeripheral *peripheral, CBService *service, NSArray *characteristics, NSError *error);
/** 收到某个特性值更新的回调 */
typedef void(^HLNotifyCharacteristicBlock)(CBPeripheral *peripheral,CBCharacteristic *characteristic,NSError *error);
/** 查找到某个特性的描述 block */
typedef void(^HLDiscoverDescriptorsBlock)(CBPeripheral *peripheral,CBCharacteristic *characteristic,NSArray *descriptors, NSError *error);
/** 统一返回使用的block */
typedef void(^HLBLECompletionBlock)(HLOptionStage stage, CBPeripheral *peripheral,CBService *service, CBCharacteristic *character, NSError *error);
/** 获取特性中的值 */
typedef void(^HLValueForCharacteristicBlock)(CBCharacteristic *characteristic, NSData *value, NSError *error);
/** 获取描述中的值 */
typedef void(^HLValueForDescriptorBlock)(CBDescriptor *descriptor,NSData *data,NSError *error);
/** 往特性中写入数据的回调 */
typedef void(^HLWriteToCharacteristicBlock)(CBCharacteristic *characteristic, NSError *error);
/** 往描述中写入数据的回调 */
typedef void(^HLWriteToDescriptorBlock)(CBDescriptor *descriptor, NSError *error);
/** 获取蓝牙外设信号的回调 */
typedef void(^HLGetRSSIBlock)(CBPeripheral *peripheral,NSNumber *RSSI, NSError *error);
#endif /* HLBLEConst_h */
//
// HLBLEManager.h
// HLBluetoothDemo
//
// Created by Harvey on 16/4/27.
// Copyright © 2016年 Halley. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <CoreBluetooth/CoreBluetooth.h>
#import "HLBLEConst.h"
#import "SEBLEConst.h"
@interface HLBLEManager : NSObject
#pragma mark - properties
/** 蓝牙模块状态改变的回调 */
@property (copy, nonatomic) HLStateUpdateBlock stateUpdateBlock;
/** 发现一个蓝牙外设的回调 */
@property (copy, nonatomic) HLDiscoverPeripheralBlock discoverPeripheralBlcok;
/** 连接外设完成的回调 */
@property (copy, nonatomic) HLConnectCompletionBlock connectCompleteBlock;
/** 发现服务的回调 */
@property (copy, nonatomic) HLDiscoveredServicesBlock discoverServicesBlock;
/** 发现服务中的特性的回调 */
@property (copy, nonatomic) HLDiscoverCharacteristicsBlock discoverCharacteristicsBlock;
/** 特性值改变的回调 */
@property (copy, nonatomic) HLNotifyCharacteristicBlock notifyCharacteristicBlock;
/** 发现服务中的子服务的回调 */
@property (copy, nonatomic) HLDiscoveredIncludedServicesBlock discoverdIncludedServicesBlock;
/** 发现特性的描述的回调 */
@property (copy, nonatomic) HLDiscoverDescriptorsBlock discoverDescriptorsBlock;
/** 操作完成的统一回调 */
@property (copy, nonatomic) HLBLECompletionBlock completionBlock;
/** 获取特性值回调 */
@property (copy, nonatomic) HLValueForCharacteristicBlock valueForCharacteristicBlock;
/** 获取描述值的回调 */
@property (copy, nonatomic) HLValueForDescriptorBlock valueForDescriptorBlock;
/** 将数据写入特性中的回调 */
@property (copy, nonatomic) HLWriteToCharacteristicBlock writeToCharacteristicBlock;
/** 将数据写入描述中的回调*/
@property (copy, nonatomic) HLWriteToDescriptorBlock writeToDescriptorBlock;
/** 获取蓝牙外设信号强度的回调 */
@property (copy, nonatomic) HLGetRSSIBlock getRSSIBlock;
@property (strong, nonatomic, readonly) CBPeripheral *connectedPerpheral; /**< 当前连接的外设 */
/**
* 每次发送的最大数据长度,因为部分型号的蓝牙打印机一次写入数据过长,会导致打印乱码。
* iOS 9之后,会调用系统的API来获取特性能写入的最大数据长度。
* 但是iOS 9之前需要自己测试然后设置一个合适的值。默认值是146,我使用佳博58MB-III的限度。
* 所以,如果你打印乱码,你考虑将该值设置小一点再试试。
*/
@property (assign, nonatomic) NSInteger limitLength;
#pragma mark - method
+ (instancetype)sharedInstance;
/**
* 上次连接的蓝牙外设的UUIDString
*
* @return UUIDString,没有时返回nil
*/
+ (NSString *)UUIDStringForLastPeripheral;
/**
* 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息
*
* @param uuids 服务的CBUUID
* @param option 其他可选参数
*/
- (void)scanForPeripheralsWithServiceUUIDs:(NSArray<CBUUID *> *)uuids options:(NSDictionary<NSString *, id> *)options;
/**
* 开始搜索蓝牙外设,每次在block中返回一个蓝牙外设信息
* 返回的block参数可参考CBCentralManager 的 centralManager:didDiscoverPeripheral:advertisementData:RSSI:
*
* @param uuids 服务的CBUUID
* @param option 其他可选参数
* @param discoverBlock 搜索到蓝牙外设后的回调
*/
- (void)scanForPeripheralsWithServiceUUIDs:(NSArray<CBUUID *> *)uuids options:(NSDictionary<NSString *, id> *)options didDiscoverPeripheral:(HLDiscoverPeripheralBlock)discoverBlock;
/**
* 连接某个蓝牙外设,并查询服务,特性,特性描述
*
* @param peripheral 要连接的蓝牙外设
* @param connectOptions 连接的配置参数
* @param stop 连接成功后是否停止搜索蓝牙外设
* @param serviceUUIDs 要搜索的服务UUID
* @param characteristicUUIDs 要搜索的特性UUID
* @param completionBlock 操作执行完的回调
*/
- (void)connectPeripheral:(CBPeripheral *)peripheral
connectOptions:(NSDictionary<NSString *,id> *)connectOptions
stopScanAfterConnected:(BOOL)stop
servicesOptions:(NSArray<CBUUID *> *)serviceUUIDs
characteristicsOptions:(NSArray<CBUUID *> *)characteristicUUIDs
completeBlock:(HLBLECompletionBlock)completionBlock;
/**
* 查找某个服务的子服务
*
* @param includedServiceUUIDs 要查找的子服务的UUIDs
* @param service 父服务
*/
- (void)discoverIncludedServices:(NSArray<CBUUID *> *)includedServiceUUIDs forService:(CBService *)service;
/**
* 读取某个特性的值
*
* @param characteristic 要读取的特性
*/
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic;
/**
* 读取某个特性的值
*
* @param characteristic 要读取的特性
* @param completionBlock 读取完后的回调
*/
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic completionBlock:(HLValueForCharacteristicBlock)completionBlock;
/**
* 往某个特性中写入数据
*
* @param data 写入的数据
* @param characteristic 特性对象
* @param type 写入类型
*/
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type;
/**
* 往某个特性中写入数据
*
* @param data 写入的数据
* @param characteristic 特性对象
* @param type 写入类型
* @param completionBlock 写入完成后的回调,只有type为CBCharacteristicWriteWithResponse时,才会回调
*/
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type completionBlock:(HLWriteToCharacteristicBlock)completionBlock;
/**
* 读取某特性的描述信息
*
* @param descriptor 描述对象
*/
- (void)readValueForDescriptor:(CBDescriptor *)descriptor;
/**
* 读取某特性的描述信息
*
* @param descriptor 描述对象
* @param completionBlock 读取结果返回时的回调
*/
- (void)readValueForDescriptor:(CBDescriptor *)descriptor completionBlock:(HLValueForDescriptorBlock)completionBlock;
/**
* 将数据写入特性的描述中
*
* @param data 数据
* @param descriptor 描述对象
*/
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor;
/**
* 将数据写入特性的描述中
*
* @param data 数据
* @param descriptor 描述对象
* @param completionBlock 数据写入完成后的回调
*/
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor completionBlock:(HLWriteToDescriptorBlock)completionBlock;
/**
* 获取某外设的信号
*
* @param completionBlock 获取信号完成后的回调
*/
- (void)readRSSICompletionBlock:(HLGetRSSIBlock)getRSSIBlock;
/**
* 停止扫描
*/
- (void)stopScan;
/**
* 断开蓝牙连接
*/
- (void)cancelPeripheralConnection;
#pragma mark - 打印相关的方法
#pragma mark - print method
/**
* 打印自己组装的数据
*
* @param data
* @param result 结果
*/
- (void)sendPrintData:(NSData *)data completion:(SEPrintResult)result;
@end
//
// HLBLEManager.m
// HLBluetoothDemo
//
// Created by Harvey on 16/4/27.
// Copyright © 2016年 Halley. All rights reserved.
//
#import "HLBLEManager.h"
#define kSECharacter @"character"
#define kSEType @"type"
// 发送数据时,需要分段的长度,部分打印机一次发送数据过长就会乱码,需要分段发送。这个长度值不同的打印机可能不一样,你需要调试设置一个合适的值(最好是偶数)
#define kLimitLength 146
@interface HLBLEManager ()<CBCentralManagerDelegate,CBPeripheralDelegate>
@property (strong, nonatomic) CBCentralManager *centralManager; /**< 中心管理器 */
@property (strong, nonatomic) CBPeripheral *connectedPerpheral; /**< 当前连接的外设 */
@property (strong, nonatomic) NSArray<CBUUID *> *serviceUUIDs; /**< 要查找服务的UUIDs */
@property (strong, nonatomic) NSArray<CBUUID *> *characteristicUUIDs; /**< 要查找特性的UUIDs */
@property (assign, nonatomic) BOOL stopScanAfterConnected; /**< 是否连接成功后停止扫描蓝牙设备 */
@property (assign, nonatomic) NSInteger writeCount; /**< 写入次数 */
@property (assign, nonatomic) NSInteger responseCount; /**< 返回次数 */
@property (strong, nonatomic) NSMutableArray *writeChatacters; /**< 可写入数据的特性 */
@property (strong, nonatomic) SEPrintResult printResult; /**< 打印结果的回调 */
@end
static HLBLEManager *instance = nil;
@implementation HLBLEManager
+ (instancetype)sharedInstance
{
return [[self alloc] init];
}
+ (NSString *)UUIDStringForLastPeripheral
{
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSString *UUIDString = [userDefaults objectForKey:@"peripheral"];
return UUIDString;
}
- (instancetype)init {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super init];
//蓝牙没打开时alert提示框
NSDictionary *options = @{CBCentralManagerOptionShowPowerAlertKey:@(YES)};
_centralManager = [[CBCentralManager alloc] initWithDelegate:self queue:dispatch_get_main_queue() options:options];
_limitLength = kLimitLength;
instance.writeChatacters = [[NSMutableArray alloc] init];
});
return instance;
}
+ (instancetype)allocWithZone:(struct _NSZone *)zone
{
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
instance = [super allocWithZone:zone];
});
return instance;
}
- (void)scanForPeripheralsWithServiceUUIDs:(NSArray<CBUUID *> *)uuids options:(NSDictionary<NSString *, id> *)options
{
[_centralManager scanForPeripheralsWithServices:uuids options:options];
}
- (void)scanForPeripheralsWithServiceUUIDs:(NSArray<CBUUID *> *)uuids options:(NSDictionary<NSString *, id> *)options didDiscoverPeripheral:(HLDiscoverPeripheralBlock)discoverBlock
{
_discoverPeripheralBlcok = discoverBlock;
[_centralManager scanForPeripheralsWithServices:uuids options:options];
}
- (void)connectPeripheral:(CBPeripheral *)peripheral
connectOptions:(NSDictionary<NSString *,id> *)connectOptions
stopScanAfterConnected:(BOOL)stop
servicesOptions:(NSArray<CBUUID *> *)serviceUUIDs
characteristicsOptions:(NSArray<CBUUID *> *)characteristicUUIDs
completeBlock:(HLBLECompletionBlock)completionBlock;
{
//1.保存回调的block以及参数
_completionBlock = completionBlock;
_serviceUUIDs = serviceUUIDs;
_characteristicUUIDs = characteristicUUIDs;
_stopScanAfterConnected = stop;
//2.先取消之前连接的蓝牙外设
if (_connectedPerpheral) {
[_centralManager cancelPeripheralConnection:_connectedPerpheral];
}
//3.开始连接新的蓝牙外设
[_centralManager connectPeripheral:peripheral options:connectOptions];
//4.设置代理
peripheral.delegate = self;
}
- (void)discoverIncludedServices:(NSArray<CBUUID *> *)includedServiceUUIDs forService:(CBService *)service
{
[_connectedPerpheral discoverIncludedServices:includedServiceUUIDs forService:service];
}
- (void)stopScan
{
[_centralManager stopScan];
_discoverPeripheralBlcok = nil;
}
- (void)cancelPeripheralConnection
{
if (_connectedPerpheral) {
[_centralManager cancelPeripheralConnection:_connectedPerpheral];
[_writeChatacters removeAllObjects];
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults removeObjectForKey:@"peripheral"];
[userDefaults synchronize];
}
}
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic
{
[_connectedPerpheral readValueForCharacteristic:characteristic];
}
- (void)readValueForCharacteristic:(CBCharacteristic *)characteristic completionBlock:(HLValueForCharacteristicBlock)completionBlock
{
_valueForCharacteristicBlock = completionBlock;
[self readValueForCharacteristic:characteristic];
}
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type
{
_writeCount = 0;
_responseCount = 0;
// iOS 9 以后,系统添加了这个API来获取特性能写入的最大长度
if ([_connectedPerpheral respondsToSelector:@selector(maximumWriteValueLengthForType:)]) {
_limitLength = [_connectedPerpheral maximumWriteValueLengthForType:type];
}
// 如果_limitLength 小于等于0,则表示不用分段发送
if (_limitLength <= 0) {
[_connectedPerpheral writeValue:data forCharacteristic:characteristic type:type];
_writeCount ++;
return;
}
if (data.length <= _limitLength) {
[_connectedPerpheral writeValue:data forCharacteristic:characteristic type:type];
_writeCount ++;
} else {
NSInteger index = 0;
for (index = 0; index < data.length - _limitLength; index += _limitLength) {
NSData *subData = [data subdataWithRange:NSMakeRange(index, _limitLength)];
[_connectedPerpheral writeValue:subData forCharacteristic:characteristic type:type];
_writeCount++;
}
NSData *leftData = [data subdataWithRange:NSMakeRange(index, data.length - index)];
if (leftData) {
[_connectedPerpheral writeValue:leftData forCharacteristic:characteristic type:type];
_writeCount++;
}
}
}
- (void)writeValue:(NSData *)data forCharacteristic:(CBCharacteristic *)characteristic type:(CBCharacteristicWriteType)type completionBlock:(HLWriteToCharacteristicBlock)completionBlock
{
_writeToCharacteristicBlock = completionBlock;
[self writeValue:data forCharacteristic:characteristic type:type];
}
- (void)readValueForDescriptor:(CBDescriptor *)descriptor
{
[_connectedPerpheral readValueForDescriptor:descriptor];
}
- (void)readValueForDescriptor:(CBDescriptor *)descriptor completionBlock:(HLValueForDescriptorBlock)completionBlock
{
_valueForDescriptorBlock = completionBlock;
[self readValueForDescriptor:descriptor];
}
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor
{
[_connectedPerpheral writeValue:data forDescriptor:descriptor];
}
- (void)writeValue:(NSData *)data forDescriptor:(CBDescriptor *)descriptor completionBlock:(HLWriteToDescriptorBlock)completionBlock
{
_writeToDescriptorBlock = completionBlock;
[self writeValue:data forDescriptor:descriptor];
}
- (void)readRSSICompletionBlock:(HLGetRSSIBlock)getRSSIBlock
{
_getRSSIBlock = getRSSIBlock;
[_connectedPerpheral readRSSI];
}
#pragma mark - CBCentralManagerDelegate
- (void)centralManagerDidUpdateState:(CBCentralManager *)central
{
[[NSNotificationCenter defaultCenter] postNotificationName:kCentralManagerStateUpdateNoticiation object:@{@"central":central}];
if (_stateUpdateBlock) {
_stateUpdateBlock(central);
}
}
- (void)centralManager:(CBCentralManager *)central didDiscoverPeripheral:(CBPeripheral *)peripheral advertisementData:(NSDictionary<NSString *, id> *)advertisementData RSSI:(NSNumber *)RSSI
{
if (_discoverPeripheralBlcok) {
_discoverPeripheralBlcok(central, peripheral, advertisementData, RSSI);
}
}
#pragma mark ---------------- 连接外设成功和失败的代理 ---------------
- (void)centralManager:(CBCentralManager *)central didConnectPeripheral:(CBPeripheral *)peripheral
{
_connectedPerpheral = peripheral;
NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
[userDefaults setObject:peripheral.identifier.UUIDString forKey:@"peripheral"];
[userDefaults synchronize];
if (_stopScanAfterConnected) {
[_centralManager stopScan];
}
if (_discoverServicesBlock) {
_discoverServicesBlock(peripheral, peripheral.services,nil);
}
if (_completionBlock) {
_completionBlock(HLOptionStageConnection,peripheral,nil,nil,nil);
}
[peripheral discoverServices:_serviceUUIDs];
}
- (void)centralManager:(CBCentralManager *)central didFailToConnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
if (_discoverServicesBlock) {
_discoverServicesBlock(peripheral, peripheral.services,error);
}
if (_completionBlock) {
_completionBlock(HLOptionStageConnection,peripheral,nil,nil,error);
}
}
- (void)centralManager:(CBCentralManager *)central didDisconnectPeripheral:(CBPeripheral *)peripheral error:(nullable NSError *)error
{
_connectedPerpheral = nil;
[_writeChatacters removeAllObjects];
NSLog(@"断开连接了,断开连接了 %@",error);
if (_completionBlock) {
_completionBlock(HLOptionStageConnection,peripheral,nil,nil,error);
}
}
#pragma mark ---------------- 发现服务的代理 -----------------
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverServices:(nullable NSError *)error
{
if (error) {
if (_completionBlock) {
_completionBlock(HLOptionStageSeekServices,peripheral,nil,nil,error);
}
return;
}
if (_completionBlock) {
_completionBlock(HLOptionStageSeekServices,peripheral,nil,nil,nil);
}
for (CBService *service in peripheral.services) {
[peripheral discoverCharacteristics:_characteristicUUIDs forService:service];
}
}
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverIncludedServicesForService:(CBService *)service error:(nullable NSError *)error
{
if (error) {
if (_discoverdIncludedServicesBlock) {
_discoverdIncludedServicesBlock(peripheral,service,nil,error);
}
return;
}
if (_discoverdIncludedServicesBlock) {
_discoverdIncludedServicesBlock(peripheral,service,service.includedServices,error);
}
}
#pragma mark ---------------- 服务特性的代理 --------------------
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(nullable NSError *)error
{
if (error) {
if (_completionBlock) {
_completionBlock(HLOptionStageSeekCharacteristics,peripheral,service,nil,error);
}
return;
}
if (_discoverCharacteristicsBlock) {
_discoverCharacteristicsBlock(peripheral,service,service.characteristics,nil);
}
if (_completionBlock) {
_completionBlock(HLOptionStageSeekCharacteristics,peripheral,service,nil,nil);
}
for (CBCharacteristic *character in service.characteristics) {
[peripheral discoverDescriptorsForCharacteristic:character];
[peripheral readValueForCharacteristic:character];
CBCharacteristicProperties properties = character.properties;
if (properties & CBCharacteristicPropertyWrite) {
NSDictionary *dict = @{kSECharacter:character,kSEType:@(CBCharacteristicWriteWithResponse)};
[_writeChatacters addObject:dict];
}
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateNotificationStateForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
if (_notifyCharacteristicBlock) {
_notifyCharacteristicBlock(peripheral,characteristic,error);
}
return;
}
if (_notifyCharacteristicBlock) {
_notifyCharacteristicBlock(peripheral,characteristic,nil);
}
}
// 读取特性中的值
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error
{
if (error) {
if (_valueForCharacteristicBlock) {
_valueForCharacteristicBlock(characteristic,nil,error);
}
return;
}
//
NSData *data = characteristic.value;
// if (data.length > 0) {
// const unsigned char *hexBytesLight = [data bytes];
//
// NSString * battery = [NSString stringWithFormat:@"%02x", hexBytesLight[0]];
//
// NSLog(@"batteryInfo:%@",battery);
// }
if (_valueForCharacteristicBlock) {
_valueForCharacteristicBlock(characteristic,data,nil);
}
}
#pragma mark ---------------- 发现服务特性描述的代理 ------------------
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverDescriptorsForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (error) {
if (_completionBlock) {
_completionBlock(HLOptionStageSeekdescriptors,peripheral,nil,characteristic,error);
}
return;
}
if (_completionBlock) {
_completionBlock(HLOptionStageSeekdescriptors,peripheral,nil,characteristic,nil);
}
}
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
{
if (error) {
if (_valueForDescriptorBlock) {
_valueForDescriptorBlock(descriptor,nil,error);
}
return;
}
NSData *data = descriptor.value;
if (_valueForDescriptorBlock) {
_valueForDescriptorBlock(descriptor,data,nil);
}
}
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForDescriptor:(CBDescriptor *)descriptor error:(nullable NSError *)error
{
if (error) {
if (_writeToDescriptorBlock) {
_writeToDescriptorBlock(descriptor, error);
}
return;
}
if (_writeToDescriptorBlock) {
_writeToDescriptorBlock(descriptor, nil);
}
}
#pragma mark ---------------- 写入数据的回调 --------------------
- (void)peripheral:(CBPeripheral *)peripheral didWriteValueForCharacteristic:(CBCharacteristic *)characteristic error:(nullable NSError *)error
{
if (!_writeToCharacteristicBlock) {
return;
}
if (!_printResult) {
return;
}
_responseCount ++;
if (_writeCount != _responseCount) {
return;
}
_writeToCharacteristicBlock(characteristic,error);
if (error) {
_printResult(_connectedPerpheral,NO,@"发送失败");
} else {
_printResult(_connectedPerpheral,YES,@"已成功发送至蓝牙设备");
}
}
#pragma mark ---------------- 获取信号之后的回调 ------------------
# if __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_8_0
- (void)peripheralDidUpdateRSSI:(CBPeripheral *)peripheral error:(nullable NSError *)error {
if (_getRSSIBlock) {
_getRSSIBlock(peripheral,peripheral.RSSI,error);
}
}
#else
- (void)peripheral:(CBPeripheral *)peripheral didReadRSSI:(NSNumber *)RSSI error:(NSError *)error {
if (_getRSSIBlock) {
_getRSSIBlock(peripheral,RSSI,error);
}
}
#endif
#pragma mark - 打印相关的方法
#pragma mark - print method
- (void)sendPrintData:(NSData *)data completion:(SEPrintResult)result {
if (!self.connectedPerpheral) {
if (result) {
result(_connectedPerpheral,NO,@"未连接蓝牙设备");
}
return;
}
if (self.writeChatacters.count == 0) {
if (result) {
result(_connectedPerpheral,NO,@"该蓝牙设备不能写入数据");
}
return;
}
NSDictionary *dict = [_writeChatacters lastObject];
_writeCount = 0;
_responseCount = 0;
// 如果kLimitLength 小于等于0,则表示不用分段发送
if (kLimitLength <= 0) {
_printResult = result;
[_connectedPerpheral writeValue:data forCharacteristic:dict[kSECharacter] type:[dict[kSEType] integerValue]];
_writeCount ++;
return;
}
if (data.length <= kLimitLength) {
_printResult = result;
[_connectedPerpheral writeValue:data forCharacteristic:dict[kSECharacter] type:[dict[kSEType] integerValue]];
_writeCount ++;
} else {
NSInteger index = 0;
for (index = 0; index < data.length - kLimitLength; index += kLimitLength) {
NSData *subData = [data subdataWithRange:NSMakeRange(index, kLimitLength)];
[_connectedPerpheral writeValue:subData forCharacteristic:dict[kSECharacter] type:[dict[kSEType] integerValue]];
_writeCount++;
}
_printResult = result;
NSData *leftData = [data subdataWithRange:NSMakeRange(index, data.length - index)];
if (leftData) {
[_connectedPerpheral writeValue:leftData forCharacteristic:dict[kSECharacter] type:[dict[kSEType] integerValue]];
_writeCount++;
}
}
}
@end
//
// HLPrinter.h
// HLBluetoothDemo
//
// Created by Harvey on 16/5/3.
// Copyright © 2016年 Halley. All rights reserved.
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
#import "UIImage+Bitmap.h"
typedef NS_ENUM(NSInteger, HLPrinterStyle) {
HLPrinterStyleDefault,
HLPrinterStyleCustom
};
/** 文字对齐方式 */
typedef NS_ENUM(NSInteger, HLTextAlignment) {
HLTextAlignmentLeft = 0x00,
HLTextAlignmentCenter = 0x01,
HLTextAlignmentRight = 0x02
};
/** 字号 */
typedef NS_ENUM(NSInteger, HLFontSize) {
HLFontSizeTitleSmalle = 0x00, // 1、16个汉字(32个字符) 2、25 offset 一个汉字2个字符
HLFontSizeTitleMiddle = 0x11, // 1、8个汉字(16个字符) 2、50 offset 一个汉字2个字符
HLFontSizeTitleBig = 0x22
};
@interface HLPrinter : NSObject
/**
* 添加单行标题,默认字号是小号字体
*
* @param title 标题名称
* @param alignment 标题对齐方式
*/
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment;
/**
* 添加单行标题
*
* @param title 标题名称
* @param alignment 标题对齐方式
* @param fontSize 标题字号
*/
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment fontSize:(HLFontSize)fontSize;
/**
* 添加单行信息,左边名称(左对齐),右边实际值(右对齐),默认字号是小号。
* @param title 名称
* @param value 实际值
* 警告:因字号和字体与iOS中字体不一致,计算出来有误差,可以用[-appendTitle:value:valueOffset:]或[-appendTitle:value:valueOffset:fontSize:]
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value;
/**
* 添加单行信息,左边名称(左对齐),右边实际值(右对齐)。
* @param title 名称
* @param value 实际值
* @param fontSize 字号大小
* 警告:因字号和字体与iOS中字体不一致,计算出来有误差,所以建议用在价格方面
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value fontSize:(HLFontSize)fontSize;
/**
* 设置单行信息,左标题,右实际值
* @提醒 该方法的预览效果与实际效果误差较大,请以实际打印小票为准
*
* @param title 标题
* @param value 实际值
* @param offset 实际值偏移量
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset;
/**
* 设置单行信息,左标题,右实际值
* @提醒 该方法的预览效果与实际效果误差较大,请以实际打印小票为准
*
* @param title 标题
* @param value 实际值
* @param offset 实际值偏移量
* @param fontSize 字号
*/
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset fontSize:(HLFontSize)fontSize;
/**
* 添加选购商品信息标题,一般是三列,名称、数量、单价
*
* @param LeftText 左标题
* @param middleText 中间标题
* @param rightText 右标题
*/
- (void)appendLeftText:(NSString *)left middleText:(NSString *)middle rightText:(NSString *)right isTitle:(BOOL)isTitle;
/**
* 添加图片,一般是添加二维码或者条形码
* ⚠️提醒:这种打印图片的方式,是自己生成图片,然后用位图打印
*
* @param image 图片
* @param alignment 图片对齐方式
* @param maxWidth 图片的最大宽度,如果图片过大,会等比缩放
*/
- (void)appendImage:(UIImage *)image alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth;
/**
* 添加条形码图片
* ⚠️提醒:这种打印条形码的方式,是自己生成条形码图片,然后用位图打印图片
*
* @param info 条形码中包含的信息,默认居中显示,最大宽度为300。如果大于300,会等比缩放。
* @param alignment 图片对齐方式
* @param offset 设置偏移量
*/
- (void)getBarCodeWithInfo:(NSString *)info alignment:(HLTextAlignment)alignment offset:(NSInteger)offset width:(NSInteger)width height:(NSInteger)height;
/**
* 添加条形码图片
* ⚠️提醒:这种打印条形码的方式,是自己生成条形码图片,然后用位图打印图片
*
* @param info 条形码中包含的信息,默认居中显示,最大宽度为300。如果大于300,会等比缩放。
*/
- (void)appendBarCodeWithInfo:(NSString *)info;
/**
* 添加条形码图片
* ⚠️提醒:这种打印条形码的方式,是自己生成条形码图片,然后用位图打印图片
*
* @param info 条形码中的信息
* @param alignment 图片对齐方式
* @param maxWidth 图片最大宽度
*/
- (void)appendBarCodeWithInfo:(NSString *)info alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth;
/**
* 添加二维码
* ✅推荐:这种方式使用的是打印机的指令生成二维码并打印机,所以比较推荐这种方式
*
* @param info 二维码中的信息
* @param size 二维码的大小 取值范围1 <= size <= 16
*/
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size;
/**
* 添加二维码
* ✅推荐:这种方式使用的是打印机的指令生成二维码并打印机,所以比较推荐这种方式
*
* @param info 二维码中的信息
* @param size 二维码大小,取值范围 1 <= size <= 16
* @param alignment 设置图片对齐方式
*/
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size alignment:(HLTextAlignment)alignment;
/**
* 添加二维码
* ✅推荐:这种方式使用的是打印机的指令生成二维码并打印机,所以比较推荐这种方式
*
* @param info 二维码中的信息
* @param size 二维码大小,取值范围 1 <= size <= 16
* @param alignment 设置图片对齐方式
* @param offset 设置偏移量
*/
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size alignment:(HLTextAlignment)alignment offset:(NSInteger)offset;
/**
* 添加二维码图片
* ⚠️提醒:这种打印条二维码的方式,是自己生成二维码图片,然后用位图打印图片
*
* @param info 二维码中的信息
*/
- (void)appendQRCodeWithInfo:(NSString *)info;
/**
* 添加二维码图片
* ⚠️提醒:这种打印条二维码的方式,是自己生成二维码图片,然后用位图打印图片
*
* @param info 二维码中的信息
* @param centerImage 二维码中间的图片
* @param alignment 对齐方式
* @param maxWidth 二维码的最大宽度
*/
- (void)appendQRCodeWithInfo:(NSString *)info centerImage:(UIImage *)centerImage alignment:(HLTextAlignment)alignment maxWidth:(CGFloat )maxWidth;
/**
* 添加一条分割线,like this:---------------------------
*/
- (void)appendSeperatorLine;
/**
* 添加底部信息
*
* @param footerInfo 不填默认为 谢谢惠顾,欢迎下次光临!
*/
- (void)appendFooter:(NSString *)footerInfo;
/**
添加自定义的data
@param data 自定义的data
*/
- (void)appendCustomData:(NSData *)data;
/**
* 获取最终的data
*
* @return 最终的data
*/
- (NSData *)getFinalData;
@end
//
// HLPrinter.m
// HLBluetoothDemo
//
// Created by Harvey on 16/5/3.
// Copyright © 2016年 Halley. All rights reserved.
//
#import "HLPrinter.h"
#define kHLMargin 20
#define kHLPadding 2
#define kHLPreviewWidth 320
@interface HLPrinter ()
/** 将要打印的排版后的数据 */
@property (strong, nonatomic) NSMutableData *printerData;
@end
@implementation HLPrinter
- (instancetype)init
{
self = [super init];
if (self) {
[self defaultSetting];
}
return self;
}
- (void)defaultSetting
{
_printerData = [[NSMutableData alloc] init];
// 1.初始化打印机
Byte initBytes[] = {0x1B,0x40};
[_printerData appendBytes:initBytes length:sizeof(initBytes)];
// 2.设置行间距为1/6英寸,约34个点
// 另一种设置行间距的方法看这个 @link{-setLineSpace:}
Byte lineSpace[] = {0x1B,0x32};
[_printerData appendBytes:lineSpace length:sizeof(lineSpace)];
// 3.设置字体:标准0x00,压缩0x01;
Byte fontBytes[] = {0x1B,0x4D,0x00};
[_printerData appendBytes:fontBytes length:sizeof(fontBytes)];
}
#pragma mark - -------------基本操作----------------
/**
* 换行
*/
- (void)appendNewLine
{
Byte nextRowBytes[] = {0x0A};
[_printerData appendBytes:nextRowBytes length:sizeof(nextRowBytes)];
}
/**
* 回车
*/
- (void)appendReturn
{
Byte returnBytes[] = {0x0D};
[_printerData appendBytes:returnBytes length:sizeof(returnBytes)];
}
/**
* 设置对齐方式
*
* @param alignment 对齐方式:居左、居中、居右
*/
- (void)setAlignment:(HLTextAlignment)alignment
{
Byte alignBytes[] = {0x1B,0x61,alignment};
[_printerData appendBytes:alignBytes length:sizeof(alignBytes)];
}
/**
* 设置字体大小
*
* @param fontSize 字号
*/
- (void)setFontSize:(HLFontSize)fontSize
{
Byte fontSizeBytes[] = {0x1D,0x21,fontSize};
[_printerData appendBytes:fontSizeBytes length:sizeof(fontSizeBytes)];
}
/**
* 添加文字,不换行
*
* @param text 文字内容
*/
- (void)setText:(NSString *)text
{
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [text dataUsingEncoding:enc];
[_printerData appendData:data];
}
/**
* 添加文字,不换行
*
* @param text 文字内容
* @param maxChar 最多可以允许多少个字节,后面加...
*/
- (void)setText:(NSString *)text maxChar:(int)maxChar
{
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [text dataUsingEncoding:enc];
if (data.length > maxChar) {
data = [data subdataWithRange:NSMakeRange(0, maxChar)];
text = [[NSString alloc] initWithData:data encoding:enc];
if (!text) {
data = [data subdataWithRange:NSMakeRange(0, maxChar - 1)];
text = [[NSString alloc] initWithData:data encoding:enc];
}
text = [text stringByAppendingString:@"..."];
}
[self setText:text];
}
/**
* 设置偏移文字
*
* @param text 文字
*/
- (void)setOffsetText:(NSString *)text
{
// 1.计算偏移量,因字体和字号不同,所以计算出来的宽度与实际宽度有误差(小字体与22字体计算值接近)
NSDictionary *dict = @{NSFontAttributeName:[UIFont systemFontOfSize:22.0]};
NSAttributedString *valueAttr = [[NSAttributedString alloc] initWithString:text attributes:dict];
int valueWidth = valueAttr.size.width;
// 2.设置偏移量
[self setOffset:368 - valueWidth];
// 3.设置文字
[self setText:text];
}
/**
* 设置偏移量
*
* @param offset 偏移量
*/
- (void)setOffset:(NSInteger)offset
{
NSInteger remainder = offset % 256;
NSInteger consult = offset / 256;
Byte spaceBytes2[] = {0x1B, 0x24, remainder, consult};
[_printerData appendBytes:spaceBytes2 length:sizeof(spaceBytes2)];
}
/**
* 设置左边距
*
* @param offset 偏移量
*/
- (void)setLeftMargin:(NSInteger)left {
NSInteger nL = left % 256;
NSInteger nH = left / 256;
Byte spaceBytes2[] = {0x1D,0x4C,nL,nH};
[_printerData appendBytes:spaceBytes2 length:sizeof(spaceBytes2)];
}
/**
* 设置行间距
*
* @param points 多少个点
*/
- (void)setLineSpace:(NSInteger)points
{
//最后一位,可选 0~255
Byte lineSpace[] = {0x1B,0x33,points};
[_printerData appendBytes:lineSpace length:sizeof(lineSpace)];
}
/**
* 设置二维码模块大小
*
* @param size 1<= size <= 16,二维码的宽高相等
*/
- (void)setQRCodeSize:(NSInteger)size
{
Byte QRSize [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x43,size};
// Byte QRSize [] = {29,40,107,3,0,49,67,size};
[_printerData appendBytes:QRSize length:sizeof(QRSize)];
}
/**
* 设置二维码的纠错等级
*
* @param level 48 <= level <= 51
*/
- (void)setQRCodeErrorCorrection:(NSInteger)level
{
Byte levelBytes [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x45,level};
// Byte levelBytes [] = {29,40,107,3,0,49,69,level};
[_printerData appendBytes:levelBytes length:sizeof(levelBytes)];
}
/**
* 将二维码数据存储到符号存储区
* [范围]: 4≤(pL+pH×256)≤7092 (0≤pL≤255,0≤pH≤27)
* cn=49
* fn=80
* m=48
* k=(pL+pH×256)-3, k就是数据的长度
*
* @param info 二维码数据
*/
- (void)setQRCodeInfo:(NSString *)info
{
NSInteger kLength = info.length + 3;
NSInteger pL = kLength % 256;
NSInteger pH = kLength / 256;
Byte dataBytes [] = {0x1D,0x28,0x6B,pL,pH,0x31,0x50,48};
// Byte dataBytes [] = {29,40,107,pL,pH,49,80,48};
[_printerData appendBytes:dataBytes length:sizeof(dataBytes)];
NSData *infoData = [info dataUsingEncoding:NSUTF8StringEncoding];
[_printerData appendData:infoData];
// [self setText:info];
}
/**
* 打印之前存储的二维码信息
*/
- (void)printStoredQRData
{
Byte printBytes [] = {0x1D,0x28,0x6B,0x03,0x00,0x31,0x51,48};
// Byte printBytes [] = {29,40,107,3,0,49,81,48};
[_printerData appendBytes:printBytes length:sizeof(printBytes)];
}
#pragma mark - ------------function method ----------------
#pragma mark 文字
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment
{
[self appendText:text alignment:alignment fontSize:HLFontSizeTitleSmalle];
}
- (void)appendText:(NSString *)text alignment:(HLTextAlignment)alignment fontSize:(HLFontSize)fontSize
{
// 1.文字对齐方式
[self setAlignment:alignment];
// 2.设置字号
[self setFontSize:fontSize];
// 3.设置标题内容
[self setText:text];
// 4.换行
[self appendNewLine];
if (fontSize != HLFontSizeTitleSmalle) {
[self appendNewLine];
}
}
- (void)appendTitle:(NSString *)title value:(NSString *)value
{
[self appendTitle:title value:value fontSize:HLFontSizeTitleSmalle];
}
- (void)appendTitle:(NSString *)title value:(NSString *)value fontSize:(HLFontSize)fontSize
{
// 1.设置对齐方式
[self setAlignment:HLTextAlignmentLeft];
// 2.设置字号
[self setFontSize:fontSize];
// 3.设置标题内容
[self setText:title];
// 4.设置实际值
[self setOffsetText:value];
// 5.换行
[self appendNewLine];
if (fontSize != HLFontSizeTitleSmalle) {
[self appendNewLine];
}
}
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset
{
[self appendTitle:title value:value valueOffset:offset fontSize:HLFontSizeTitleSmalle];
}
- (void)appendTitle:(NSString *)title value:(NSString *)value valueOffset:(NSInteger)offset fontSize:(HLFontSize)fontSize
{
// 1.设置对齐方式
[self setAlignment:HLTextAlignmentLeft];
// 2.设置字号
[self setFontSize:fontSize];
// 3.设置标题内容
[self setText:title];
// 4.设置内容偏移量
[self setOffset:offset];
// 5.设置实际值
[self setText:value];
// 6.换行
[self appendNewLine];
if (fontSize != HLFontSizeTitleSmalle) {
[self appendNewLine];
}
}
- (void)appendLeftText:(NSString *)left middleText:(NSString *)middle rightText:(NSString *)right isTitle:(BOOL)isTitle
{
[self setAlignment:HLTextAlignmentLeft];
[self setFontSize:HLFontSizeTitleSmalle];
NSInteger offset = 0;
if (!isTitle) {
offset = 10;
}
if (left) {
[self setText:left maxChar:10];
}
if (middle) {
[self setOffset:150 + offset];
[self setText:middle];
}
if (right) {
[self setOffset:300 + offset];
[self setText:right];
}
[self appendNewLine];
}
#pragma mark 图片
- (void)appendImage:(UIImage *)image alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth
{
if (!image) {
return;
}
// 1.设置图片对齐方式
[self setAlignment:alignment];
[self setLeftMargin:40];
// 2.设置图片
UIImage *newImage = [image imageWithscaleMaxWidth:maxWidth];
// newImage = [newImage blackAndWhiteImage];
NSData *imageData = [newImage bitmapData];
[_printerData appendData:imageData];
// 3.换行
[self appendNewLine];
// 4.打印图片后,恢复文字的行间距
Byte lineSpace[] = {0x1B,0x32};
[_printerData appendBytes:lineSpace length:sizeof(lineSpace)];
}
- (void)getBarCodeWithInfo:(NSString *)info alignment:(HLTextAlignment)alignment offset:(NSInteger)offset width:(NSInteger)width height:(NSInteger)height
{
UIImage *image = [UIImage barCodeImageWithInfo:info];
if (!image) { return; }
// alignment:HLTextAlignmentLeft
// offset:40
[self setAlignment:alignment];
[self setLeftMargin:offset];
// width:300 height:150
UIImage *newImage = [image imageWithfixedWidth:width
height:height];
NSData *imageData = [newImage bitmapData];
[_printerData appendData:imageData];
// 3.换行
[self appendNewLine];
// 4.打印图片后,恢复文字的行间距
Byte lineSpace[] = {0x1B,0x32};
[_printerData appendBytes:lineSpace length:sizeof(lineSpace)];
}
- (void)appendBarCodeWithInfo:(NSString *)info
{
[self appendBarCodeWithInfo:info alignment:HLTextAlignmentCenter maxWidth:300];
}
- (void)appendBarCodeWithInfo:(NSString *)info alignment:(HLTextAlignment)alignment maxWidth:(CGFloat)maxWidth
{
UIImage *barImage = [UIImage barCodeImageWithInfo:info];
[self appendImage:barImage alignment:alignment maxWidth:maxWidth];
}
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size
{
[self appendQRCodeWithInfo:info size:size alignment:HLTextAlignmentCenter];
}
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size alignment:(HLTextAlignment)alignment
{
[self appendQRCodeWithInfo:info size:size alignment:alignment offset:0];
}
- (void)appendQRCodeWithInfo:(NSString *)info size:(NSInteger)size alignment:(HLTextAlignment)alignment offset:(NSInteger)offset
{
[self setAlignment:alignment];
[self setOffset:offset];
[self setQRCodeSize:size];
[self setQRCodeErrorCorrection:48];
[self setQRCodeInfo:info];
[self printStoredQRData];
[self appendNewLine];
[self appendNewLine];
}
- (void)appendQRCodeWithInfo:(NSString *)info
{
[self appendQRCodeWithInfo:info centerImage:nil alignment:HLTextAlignmentCenter maxWidth:250];
}
- (void)appendQRCodeWithInfo:(NSString *)info centerImage:(UIImage *)centerImage alignment:(HLTextAlignment)alignment maxWidth:(CGFloat )maxWidth
{
UIImage *QRImage = [UIImage qrCodeImageWithInfo:info centerImage:centerImage width:maxWidth];
[self appendImage:QRImage alignment:alignment maxWidth:maxWidth];
}
/**
添加自定义的data
@param data 自定义的data
*/
- (void)appendCustomData:(NSData *)data
{
if (data.length <= 0) {
return;
}
[_printerData appendData:data];
}
#pragma mark 其他
- (void)appendSeperatorLine
{
// 1.设置分割线居中
[self setAlignment:HLTextAlignmentLeft];
[self setLeftMargin:5];
// 2.设置字号
[self setFontSize:HLFontSizeTitleSmalle];
// 3.添加分割线
NSString *line = @"- - - - - - - - - - - - - - - -";
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [line dataUsingEncoding:enc];
[_printerData appendData:data];
// 4.换行
[self appendNewLine];
}
- (void)appendFooter:(NSString *)footerInfo
{
[self appendSeperatorLine];
if (!footerInfo) {
footerInfo = @"马上来货运";
}
NSUInteger lengthT = [self stringEncode:footerInfo];
if (lengthT <= 32) {
NSInteger offset = (32 - lengthT)/2 * (25/2);
[self appendTitle:@"" value:footerInfo valueOffset:offset fontSize:HLFontSizeTitleSmalle];
} else {
[self appendTitle:@"" value:footerInfo valueOffset:0 fontSize:HLFontSizeTitleSmalle];
}
[self appendNewLine];
[self appendNewLine];
[self appendNewLine];
}
- (NSData *)getFinalData
{
return _printerData;
}
- (NSUInteger)stringEncode:(NSString *)s {
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSData *data = [s dataUsingEncoding:enc];
NSUInteger lengthT = [data length];
return lengthT;
}
@end
//
// SEBLEConst.h
// blue_thermal_printer
//
// Created by 袁静春 on 2022/7/19.
//
#ifndef SEBLEConst_h
#define SEBLEConst_h
#import <CoreBluetooth/CoreBluetooth.h>
/**
* 打印回调
*
* @param completion 是否完成
* @param error 错误信息
*/
typedef void(^SEPrintResult)(CBPeripheral *connectPerpheral, BOOL completion, NSString *error);
#endif /* SEBLEConst_h */
//
// UIImage+Bitmap.h
// HLBluetoothDemo
//
// Created by Harvey on 16/5/3.
// Copyright © 2016年 Halley. All rights reserved.
//
#import <UIKit/UIKit.h>
typedef NS_ENUM(NSInteger,BitPixels) {
BPAlpha = 0,
BPBlue = 1,
BPGreen = 2,
BPRed = 3
};
@interface UIImage (Bitmap)
/**
* 将图片转换为点阵图数据
*
* @return 转化后的点阵图数据
*/
- (NSData *)bitmapData;
/**
* 将图片绘制到绘图上下文中,并返回上下文
*
* @return
*/
//+ (CGContextRef)bitmapRGBA8ContextFromImage:(CGImageRef)image;
- (CGContextRef)bitmapRGBA8Context;
/**
* 缩放图片,会按照给定的最大宽度,等比缩放
*
* @param maxWidth 缩放后的最大宽度
*
* @return 返回缩放后的图片
*/
- (UIImage *)imageWithscaleMaxWidth:(CGFloat)maxWidth;
/**
* 将图片转换为黑白图片
*
* @return 黑白图片
*/
- (UIImage *)blackAndWhiteImage;
/**
* 缩放图片,会按照给定的宽、高缩放
*
* @return 返回缩放后的图片
*/
- (UIImage *)imageWithfixedWidth:(CGFloat)width height:(CGFloat)height;
@end
#pragma mark - -----------制作二维码 条形码------------
@interface UIImage (QRCode)
/**
* 创建条形码
*
* @param info 字符串信息
*
* @return 条形码图片
*/
+ (UIImage *)barCodeImageWithInfo:(NSString *)info;
/**
* 创建二维码
*
* @param info 二维码内的信息
* @param image 二维码中心的logo图片
* @param width 二维码的宽度
*
* @return 二维码图片
*/
+ (UIImage *)qrCodeImageWithInfo:(NSString *)info centerImage:(UIImage *)image width:(CGFloat)width;
/**
* 将CIImage 放大显示,并转换为UIImage。
*
* @param image 原始CIImage
* @param size 最终尺寸的宽度
*
* @return UIImage
*/
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat)size;
/**
* 将原图转变为背景色透明,图片为设置的颜色
*
* @param image 要改变的图片
* @param red red
* @param green green
* @param blue blue
*
* @return 返回修改后的图片
*/
+ (UIImage*)imageBgColorToTransparentWith:(UIImage*)image withRed:(CGFloat)red andGreen:(CGFloat)green andBlue:(CGFloat)blue;
@end
//
// UIImage+Bitmap.m
// HLBluetoothDemo
//
// Created by Harvey on 16/5/3.
// Copyright © 2016年 Halley. All rights reserved.
//
#import "UIImage+Bitmap.h"
#import <AVFoundation/AVFoundation.h>
@implementation UIImage (Bitmap)
//
- (NSData *)bitmapData
{
CGImageRef imageRef = self.CGImage;
// Create a bitmap context to draw the uiimage into
CGContextRef context = [self bitmapRGBA8Context];
if(!context) {
return nil;
}
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
CGRect rect = CGRectMake(0, 0, width, height);
// Draw image into the context to get the raw image data
CGContextDrawImage(context, rect, imageRef);
// Get a pointer to the data
uint32_t *bitmapData = (uint32_t *)CGBitmapContextGetData(context);
if(bitmapData) {
uint8_t *m_imageData = (uint8_t *) malloc(width * height/8 + 8*height/8);
memset(m_imageData, 0, width * height/8 + 8*height/8);
int result_index = 0;
for(int y = 0; (y + 24) < height; ) {
m_imageData[result_index++] = 27;
m_imageData[result_index++] = 51;
m_imageData[result_index++] = 0;
m_imageData[result_index++] = 27;
m_imageData[result_index++] = 42;
m_imageData[result_index++] = 33;
m_imageData[result_index++] = width%256;
m_imageData[result_index++] = width/256;
for(int x = 0; x < width; x++) {
int value = 0;
for (int temp_y = 0 ; temp_y < 8; ++temp_y)
{
uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];
uint32_t gray = 0.3 * rgbaPixel[BPRed] + 0.59 * rgbaPixel[BPGreen] + 0.11 * rgbaPixel[BPBlue];
if (gray < 127)
{
value += 1<<(7-temp_y)&255;
}
}
m_imageData[result_index++] = value;
value = 0;
for (int temp_y = 8 ; temp_y < 16; ++temp_y)
{
uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];
uint32_t gray = 0.3 * rgbaPixel[BPRed] + 0.59 * rgbaPixel[BPGreen] + 0.11 * rgbaPixel[BPBlue];
if (gray < 127)
{
value += 1<<(7-temp_y%8)&255;
}
}
m_imageData[result_index++] = value;
value = 0;
for (int temp_y = 16 ; temp_y < 24; ++temp_y)
{
uint8_t *rgbaPixel = (uint8_t *) &bitmapData[(y+temp_y) * width + x];
uint32_t gray = 0.3 * rgbaPixel[BPRed] + 0.59 * rgbaPixel[BPGreen] + 0.11 * rgbaPixel[BPBlue];
if (gray < 127)
{
value += 1<<(7-temp_y%8)&255;
}
}
m_imageData[result_index++] = value;
}
m_imageData[result_index++] = 13;
m_imageData[result_index++] = 10;
y += 24;
}
NSMutableData *data = [[NSMutableData alloc] initWithCapacity:0];
[data appendBytes:m_imageData length:result_index];
free(bitmapData);
return data;
}
NSLog(@"Error getting bitmap pixel data\n");
CGContextRelease(context);
return nil ;
}
- (CGContextRef)bitmapRGBA8Context
{
CGImageRef imageRef = self.CGImage;
if (!imageRef) {
return NULL;
}
CGContextRef context = NULL;
CGColorSpaceRef colorSpace;
uint32_t *bitmapData;
size_t bitsPerPixel = 32;
size_t bitsPerComponent = 8;
size_t bytesPerPixel = bitsPerPixel / bitsPerComponent;
size_t width = CGImageGetWidth(imageRef);
size_t height = CGImageGetHeight(imageRef);
size_t bytesPerRow = width * bytesPerPixel;
size_t bufferLength = bytesPerRow * height;
colorSpace = CGColorSpaceCreateDeviceRGB();
if(!colorSpace) {
NSLog(@"Error allocating color space RGB\n");
return NULL;
}
// Allocate memory for image data
bitmapData = (uint32_t *)malloc(bufferLength);
if(!bitmapData) {
NSLog(@"Error allocating memory for bitmap\n");
CGColorSpaceRelease(colorSpace);
return NULL;
}
//Create bitmap context
context = CGBitmapContextCreate(bitmapData,
width,
height,
bitsPerComponent,
bytesPerRow,
colorSpace,
kCGBitmapByteOrder32Little | kCGImageAlphaPremultipliedLast); // RGBA
if(!context) {
free(bitmapData);
NSLog(@"Bitmap context not created");
}
CGColorSpaceRelease(colorSpace);
return context;
}
- (UIImage *)imageWithscaleMaxWidth:(CGFloat)maxWidth
{
CGFloat width = self.size.width;
if (width > maxWidth)
{
CGFloat height = self.size.height;
CGFloat maxHeight = maxWidth * height / width;
CGSize size = CGSizeMake(maxWidth, maxHeight);
UIGraphicsBeginImageContext(size);
[self drawInRect:CGRectMake(0, 0, maxWidth, maxHeight)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
return self;
}
- (UIImage *)imageWithfixedWidth:(CGFloat)width height:(CGFloat)height
{
CGSize size = CGSizeMake(width, height);
UIGraphicsBeginImageContext(size);
[self drawInRect:CGRectMake(0, 0, width, height)];
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
- (UIImage *)blackAndWhiteImage
{
// CGSize size = self.size;
CIImage *beginImage = [CIImage imageWithCGImage:self.CGImage];
CIFilter *filter = [CIFilter filterWithName:@"CIColorMonochrome"
keysAndValues:kCIInputImageKey,beginImage,kCIInputColorKey,[CIColor colorWithCGColor:[UIColor blackColor].CGColor],nil];
CIImage *outputImage = [filter outputImage];
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef imageRef = [context createCGImage:outputImage fromRect:outputImage.extent];
UIImage *newImage = [UIImage imageWithCGImage:imageRef];
// UIImage *newImage = [UIImage createNonInterpolatedUIImageFormCIImage:outputImage withSize:size.width];
return newImage;
}
@end
@implementation UIImage (QRCode)
+ (UIImage *)barCodeImageWithInfo:(NSString *)info
{
// 创建条形码
CIFilter *filter = [CIFilter filterWithName:@"CICode128BarcodeGenerator"];
// 恢复滤镜的默认属性
[filter setDefaults];
// 将字符串转换成NSData
NSData *data = [info dataUsingEncoding:NSUTF8StringEncoding];
// 通过KVO设置滤镜inputMessage数据
[filter setValue:data forKey:@"inputMessage"];
// 获得滤镜输出的图像
CIImage *outputImage = [filter outputImage];
// 将CIImage转换成UIImage,并放大显示
UIImage *image =[self createNonInterpolatedUIImageFormCIImage:outputImage withSize:300];
NSLog(@"将CIImage转换成UIImage: width=%f, height=%f", image.size.width, image.size.height);
return image;
}
+ (UIImage *)qrCodeImageWithInfo:(NSString *)info centerImage:(UIImage *)centerImage width:(CGFloat)width
{
if (!info) {
return nil;
}
NSData *strData = [info dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:NO];
//创建二维码滤镜
CIFilter *qrFilter = [CIFilter filterWithName:@"CIQRCodeGenerator"];
[qrFilter setValue:strData forKey:@"inputMessage"];
[qrFilter setValue:@"H" forKey:@"inputCorrectionLevel"];
CIImage *qrImage = qrFilter.outputImage;
UIImage *codeImage = [UIImage createNonInterpolatedUIImageFormCIImage:qrImage withSize:width];
//二维码rect
CGRect rect = CGRectMake(0, 0, codeImage.size.width, codeImage.size.height);
UIGraphicsBeginImageContext(rect.size);
[codeImage drawInRect:rect];
//定制logo
if (centerImage) {
//icon尺寸,UIBezierPath
CGSize logoSize = CGSizeMake(rect.size.width*0.2, rect.size.height*0.2);
CGFloat x = CGRectGetMidX(rect) - logoSize.width*0.5;
CGFloat y = CGRectGetMidY(rect) - logoSize.height*0.5;
CGRect logoFrame = CGRectMake(x, y, logoSize.width, logoSize.height);
[[UIBezierPath bezierPathWithRoundedRect:logoFrame cornerRadius:10] addClip];
[centerImage drawInRect:logoFrame];
}
UIImage *resultImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
return resultImage;
}
+ (UIImage *)createNonInterpolatedUIImageFormCIImage:(CIImage *)image withSize:(CGFloat)size
{
CGRect extent = CGRectIntegral(image.extent);
CGFloat scale = MIN(size/CGRectGetWidth(extent), size/CGRectGetHeight(extent));
// 创建bitmap;
size_t width = CGRectGetWidth(extent) * scale;
size_t height = CGRectGetHeight(extent) * scale;
CGColorSpaceRef cs = CGColorSpaceCreateDeviceGray();
CGContextRef bitmapRef = CGBitmapContextCreate(nil, width, height, 8, 0, cs, (CGBitmapInfo)kCGImageAlphaNone);
CIContext *context = [CIContext contextWithOptions:nil];
CGImageRef bitmapImage = [context createCGImage:image fromRect:extent];
CGContextSetInterpolationQuality(bitmapRef, kCGInterpolationNone);
CGContextScaleCTM(bitmapRef, scale, scale);
CGContextDrawImage(bitmapRef, extent, bitmapImage);
// 保存bitmap到图片
CGImageRef scaledImage = CGBitmapContextCreateImage(bitmapRef);
CGContextRelease(bitmapRef);
CGImageRelease(bitmapImage);
return [UIImage imageWithCGImage:scaledImage];
}
void ProviderReleaseData (void *info, const void *data, size_t size){
free((void*)data);
}
+ (UIImage*)imageBgColorToTransparentWith:(UIImage*)image withRed:(CGFloat)red andGreen:(CGFloat)green andBlue:(CGFloat)blue{
const int imageWidth = image.size.width;
const int imageHeight = image.size.height;
size_t bytesPerRow = imageWidth * 4;
uint32_t* rgbImageBuf = (uint32_t*)malloc(bytesPerRow * imageHeight);
// create context
CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB();
CGContextRef context = CGBitmapContextCreate(rgbImageBuf, imageWidth, imageHeight, 8, bytesPerRow, colorSpace,
kCGBitmapByteOrder32Little | kCGImageAlphaNoneSkipLast);
CGContextDrawImage(context, CGRectMake(0, 0, imageWidth, imageHeight), image.CGImage);
// traverse pixe
int pixelNum = imageWidth * imageHeight;
uint32_t* pCurPtr = rgbImageBuf;
for (int i = 0; i < pixelNum; i++, pCurPtr++){
if ((*pCurPtr & 0xFFFFFF00) < 0x99999900){
// change color
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[3] = red; //0~255
ptr[2] = green;
ptr[1] = blue;
}else{
uint8_t* ptr = (uint8_t*)pCurPtr;
ptr[0] = 0;
}
}
// context to image
CGDataProviderRef dataProvider = CGDataProviderCreateWithData(NULL, rgbImageBuf, bytesPerRow * imageHeight, ProviderReleaseData);
CGImageRef imageRef = CGImageCreate(imageWidth, imageHeight, 8, 32, bytesPerRow, colorSpace,
kCGImageAlphaLast | kCGBitmapByteOrder32Little, dataProvider,
NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease(dataProvider);
UIImage* resultUIImage = [UIImage imageWithCGImage:imageRef];
// release
CGImageRelease(imageRef);
CGContextRelease(context);
CGColorSpaceRelease(colorSpace);
return resultUIImage;
}
@end
import Flutter
import UIKit
public class SwiftBlueThermalPrinterPlugin: NSObject, FlutterPlugin {
public static func register(with registrar: FlutterPluginRegistrar) {
let channel = FlutterMethodChannel(name: "blue_thermal_printer", binaryMessenger: registrar.messenger())
let instance = SwiftBlueThermalPrinterPlugin()
registrar.addMethodCallDelegate(instance, channel: channel)
}
public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
result("iOS " + UIDevice.current.systemVersion)
}
}
//
// TscCommand.h
// Gprinter
//
#import <Foundation/Foundation.h>
#import <UIKit/UIKit.h>
typedef NS_ENUM (NSUInteger,Response){
OFF = 0,//关闭自动返回状态功能
ON = 1,//打开自动返回状态功能,每打印完一张返回一次
BATCH = 2,//打开自动返回状态功能,打印完毕后返回一次
};
@interface TscCommand : NSObject
/**
* 方法说明:设置标签尺寸的宽和高
* @param width 标签宽度
* @param height 标签高度
*/
-(void) addSize:(int) width :(int) height;
/**
* 方法说明:设置标签间隙尺寸 单位mm
* @param m 间隙长度
* @param n 间隙偏移
*/
-(void) addGapWithM:(int) m withN:(int) n;
/**
* 方法说明:设置标签原点坐标
* @param x 横坐标
* @param y 纵坐标
*/
-(void) addReference:(int) x :(int)y;
/**
* 方法说明:设置打印速度
* @param speed 打印速度
*/
-(void) addSpeed:(int) speed;
/**
* 方法说明:设置打印浓度
* @param density 浓度
*/
-(void) addDensity:(int) density;
/**
* 方法说明:设置打印方向
* @param direction 方向
*/
-(void) addDirection:(int) direction;
/**
* 方法说明:清除打印缓冲区
*/
-(void) addCls;
/**
* 方法说明:在标签上绘制文字
* @param x 横坐标
* @param y 纵坐标
* @param font 字体类型
* @param rotation 旋转角度
* @param xScal 横向放大
* @param yScal 纵向放大
* @param text 文字字符串
*/
-(void) addTextwithX:(int)x withY:(int)y withFont:(NSString*)font withRotation:(int)rotation withXscal:(int)xScal withYscal:(int)yScal withText:(NSString*) text;
/*
BITMAP X, Y, width, height, mode, bitmap data
参 数 说 明
x 点阵影像的水平启始位置
y 点阵影像的垂直启始位置
width 影像的宽度,以 byte 表示
height 影像的高度,以点(dot)表示
mode 影像绘制模式
0 OVERWRITE
1 OR
2 XOR
bitmap data 影像数据
*/
-(void) addBitmapwithX:(int)x withY:(int) y withWidth:(int) width withHeight:(int) height withMode:(int) mode withData:(NSData*) data;
-(void)addBitmapwithX:(int)x withY:(int)y withMode:(int)mode withWidth:(int)width withImage:(UIImage *)image;
-(void)addBitmapwithX:(int)x withY:(int)y withMode:(int)mode withImage:(UIImage *)image;
/**
* 方法说明:在标签上绘制一维条码
* @param x 横坐标
* @param y 纵坐标
* @param barcodeType 条码类型
* @param height 条码高度,默认为40
* @param readable 是否可识别,0: 人眼不可识,1: 人眼可识
* @param rotation 旋转角度,条形码旋转角度,顺时钟方向,0不旋转,90顺时钟方向旋转90度,180顺时钟方向旋转180度,270顺时钟方向旋转270度
* @param narrow 默认值2,窄 bar 宽度,以点(dot)表示
* @param wide 默认值4,宽 bar 宽度,以点(dot)表示
* @param content 条码内容
BARCODE X,Y,"code type",height,human readable,rotation,narrow,wide,"code"
BARCODE 100,100,"39",40,1,0,2,4,"1000"
BARCODE 10,10,"128",40,1,0,2,2,"124096ABCDEFZ$%+-./*"
"code type":
EAN13("EAN13"),
EAN8("EAN8"),
UPCA("UPCA"),
ITF14("ITF14"),
CODE39("39"),
CODE128("128"),
*/
-(void) add1DBarcode:(int)x :(int)y :(NSString*)barcodeType :(int)height :(int)readable :(int)rotation :(int)narrow :(int)wide :(NSString*)content;
/**
* 方法说明:在标签上绘制QRCode二维码
* @param x 横坐标
* @param y 纵坐标
* @param ecclever 选择QRCODE纠错等级,L为7%,M为15%,Q为25%,H为30%
* @param cellwidth 二维码宽度1~10,默认为4
* @param mode 默认为A,A为Auto,M为Manual
* @param rotation 旋转角度,QRCode二维旋转角度,顺时钟方向,0不旋转,90顺时钟方向旋转90度,180顺时钟方向旋转180度,270顺时钟方向旋转270度
* @param content 条码内容
* QRCODE X,Y ,ECC LEVER ,cell width,mode,rotation, "data string"
* QRCODE 20,24,L,4,A,0,"佳博集团网站www.Gprinter.com.cn"
*/
-(void) addQRCode:(int)x :(int)y :(NSString*)ecclever :(int)cellwidth :(NSString*)mode :(int)rotation :(NSString*)content;
/**
* 方法说明:执行打印
* @param m
* @param n
*/
-(void) addPrint:(int) m :(int) n;
/**
* 方法说明:获得打印命令
*/
-(NSData*) getCommand;
/**
* 方法说明:将字符串转成十六进制码
* @param str 命令字符串
*/
-(void) addStrToCommand:(NSString *) str;
-(void) addNSDataToCommand:(NSData*) data;
/**
* 方法说明:发送一些TSC的固定命令,在cls命令之前发送
*/
-(void) addComonCommand;
/**
* 方法说明:打印自检页,打印测试页
*/
-(void) addSelfTest;
/**
* 方法说明 :查询打印机型号
*/
-(void) queryPrinterType;
/**
* 方法说明:设置打印机剥离模式
* @param peel ON/OFF 是否开启
*/
-(void) addPeel:(NSString *) peel;
/**
* 方法说明:设置打印机撕离模式
* @param tear ON/OFF 是否开启
*/
-(void) addTear:(NSString *) tear;
/**
* 方法说明:设置切刀是否有效
* @param cutter 是否开启 OFF/pieces (0<=pieces<=127)设定几张标签切一次
*/
-(void) addCutter:(NSString *) cutter;
/**
* 方法说明:设置切刀半切是否有效
* @param cutter 是否开启
*/
-(void) addPartialCutter:(NSString *) cutter;
/**
* 方法说明:设置蜂鸣器
* @param level 频率
* @param interval 时间ms
*/
-(void) addSound:(int) level :(int) interval;
/**
* 方法说明:打开钱箱命令,CASHDRAWER m,t1,t2
* @param m 钱箱号 m 0,48 钱箱插座的引脚2 1,49 钱箱插座的引脚5
* @param t1 高电平时间0 ≤ t1 ≤ 255输出由t1和t2设定的钱箱开启脉冲到由m指定的引脚
* @param t2 低电平时间0 ≤ t2 ≤ 255输出由t1和t2设定的钱箱开启脉冲到由m指定的引脚
*/
-(void) addCashdrawer:(int) m :(int) t1 :(int) t2;
/**
* 方法说明:在标签上绘制黑块,画线
* @param x 起始横坐标
* @param y 起始纵坐标
* @param width 线宽,以点(dot)表示
* @param height 线高,以点(dot)表示
*/
-(void) addBar:(int) x :(int) y :(int) width :(int) height;
/**
* 方法说明:在标签上绘制矩形
* @param xStart 起始横坐标
* @param yStart 起始纵坐标
* @param xEnd 终点横坐标
* @param yEnd 终点纵坐标
* @param lineThickness 矩形框线厚度或宽度,以点(dot)表示
*/
-(void) addBox:(int) xStart :(int) yStart :(int) xEnd :(int) yEnd :(int) lineThickness;
/**
* 方法说明:查询打印机状态<ESC>!?
*询问打印机状态指令为立即响应型指令,该指令控制字符是以<ESC> (ASCII 27=0x1B, escape字符)为控制字符.!(ASCII 33=0x21),?(ASCII 63=0x3F)
*即使打印机在错误状态中仍能透过 RS-232 回传一个 byte 资料来表示打印机状态,若回传值为 0 则表示打印
*机处于正常的状态
*/
-(void) queryPrinterStatus;
/**
* 方法说明:将指定的区域反向打印(黑色变成白色,白色变成黑色)
* @param xStart 起始横坐标
* @param yStart 起始横坐标
* @param xWidth X坐标方向宽度,dot为单位
* @param yHeight Y坐标方向高度,dot为单位
*/
-(void) addReverse:(int) xStart :(int) yStart :(int) xWidth :(int) yHeight;
/**
* 方法说明: 打印机打印完成时,自动返回状态。可用于实现连续打印功能
* @param response 自动返回状态 <a>@see Response</a>
* OFF 关闭自动返回状态功能
* ON 开启自动返回状态功能
* BATCH 全部打印完成后返回状态
*/
-(void)addQueryPrinterStatus:(Response)response;
/**
* 方法说明: 查询打印机电量。
* <p>返回值: 31(低电量);32 (中电量);33 (高电量);35 (正在充电)</p>
*
*/
-(void)queryElectricity;
@end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html
#
Pod::Spec.new do |s|
s.name = 'blue_thermal_printer'
s.version = '0.0.1'
s.summary = 'A new Flutter plugin for connecting to thermal printer vie bluetooth'
s.description = <<-DESC
A new Flutter plugin for connecting to thermal printer vie bluetooth
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.public_header_files = 'Classes/**/*.h'
s.vendored_libraries = 'Classes/*.a'
s.dependency 'Flutter'
s.ios.deployment_target = '8.0'
end
#
# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.
# Run `pod lib lint bluethermalprinter.podspec` to validate before publishing.
#
Pod::Spec.new do |s|
s.name = 'bluethermalprinter'
s.version = '0.0.1'
s.summary = 'A new flutter plugin project.'
s.description = <<-DESC
A new flutter plugin project.
DESC
s.homepage = 'http://example.com'
s.license = { :file => '../LICENSE' }
s.author = { 'Your Company' => 'email@example.com' }
s.source = { :path => '.' }
s.source_files = 'Classes/**/*'
s.vendored_libraries = 'Classes/*.a'
s.dependency 'Flutter'
s.platform = :ios, '9.0'
# Flutter.framework does not contain a i386 slice.
s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' }
s.swift_version = '5.0'
end
/// 蓝牙热敏打印
library blue_thermal_printer;
export 'dart:async';
export 'dart:convert';
export 'dart:math';
export 'dart:typed_data';
export 'package:fast_gbk/fast_gbk.dart';
export 'package:flutter/services.dart';
export 'blue_thermal_printer_plugin.dart';
export 'types/bluetooth_device.dart';
export 'types/bluetooth_device.dart';
export 'utils/logutil.dart';
export 'utils/printer_commands_utils.dart';
export 'utils/printer_data_utils.dart';
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
class BlueThermalPrinter {
static const int STATE_OFF = 10;
static const int STATE_TURNING_ON = 11;
static const int STATE_ON = 12;
static const int STATE_TURNING_OFF = 13;
static const int STATE_BLE_TURNING_ON = 14;
static const int STATE_BLE_ON = 15;
static const int STATE_BLE_TURNING_OFF = 16;
static const int ERROR = -1;
static const int CONNECTED = 1;
static const int DISCONNECTED = 0;
static const String namespace = 'blue_thermal_printer';
static const MethodChannel _channel =
const MethodChannel('$namespace/methods');
static const EventChannel _readChannel =
const EventChannel('$namespace/read');
static const EventChannel _stateChannel =
const EventChannel('$namespace/state');
final StreamController<MethodCall> _methodStreamController =
new StreamController.broadcast();
static BlueThermalPrinter _instance = new BlueThermalPrinter._();
static BlueThermalPrinter get instance => _instance;
BlueThermalPrinter._() {
_channel.setMethodCallHandler((MethodCall call) async {
_methodStreamController.add(call);
});
}
///onStateChanged()
Stream<int?> onStateChanged() =>
_stateChannel.receiveBroadcastStream().map((buffer) => buffer);
///onRead()
Stream<String> onRead() =>
_readChannel.receiveBroadcastStream().map((buffer) => buffer.toString());
Future<bool?> get isAvailable async =>
await _channel.invokeMethod('isAvailable');
Future<bool?> get isOn async => await _channel.invokeMethod('isOn');
Future<bool?> get isConnected async =>
await _channel.invokeMethod('isConnected');
Future<bool?> get openSettings async =>
await _channel.invokeMethod('openSettings');
///getBondedDevices()
Future<List<BluetoothDevice>> getBondedDevices() async {
final List list = await (_channel.invokeMethod('getBondedDevices'));
return list.map((map) => BluetoothDevice.fromMap(map)).toList();
}
Future<String> getLastDevice() async {
final String getLastDeviceName =
await (_channel.invokeMethod('getLastDevice'));
return getLastDeviceName;
}
///printQRcode(String textToQR, int width, int height, int align)
Future<List<int>> getQRcode(String textToQR, int width, int height, int align,
{int? light = 0, int? weight = 0}) async {
final List<int> QRcode = await (_channel.invokeMethod('getQRcode', {
'textToQR': textToQR,
'width': width,
'height': height,
'align': align,
'light': light,
'weight': weight
}));
return QRcode;
}
Future<List<int>> getBarCode(
String textToBar, int width, int height, int align,
{int? light = 0, int? weight = 0}) async {
final List<int> barCode = await (_channel.invokeMethod('getBarCode', {
'textToBar': textToBar,
'width': width,
'height': height,
'align': align,
'light': light,
'weight': weight
}));
return barCode;
}
///connect(BluetoothDevice device)
Future<dynamic> connect(BluetoothDevice device) =>
_channel.invokeMethod('connect', device.toMap());
///disconnect()
Future<dynamic> disconnect() => _channel.invokeMethod('disconnect');
///write(String message)
Future<dynamic> write(String message) =>
_channel.invokeMethod('write', {'message': message});
///writeBytes(Uint8List message)
Future<dynamic> writeBytes(Uint8List message) =>
_channel.invokeMethod('writeBytes', {'message': message});
Future<dynamic> printTSCImage(Map<String, dynamic> config, List<Map<String, dynamic>> data) {
Map<String, Object> args = Map();
args['config'] = config;
args['data'] = data;
print("---- $args");
_channel.invokeMethod('printTSCImage', args);
return Future.value(true);
}
///iOS平台使用
///printSampleCode(String qrcode, String barcode,
/// String title, String subTitle, String smallSubTitle, String bottomContent)
Future<dynamic> printSampleCode(String qrcode, String barcode, String title,
String subTitle, String smallSubTitle, String bottomContent) =>
_channel.invokeMethod('printSampleCode', {
'qrcode': qrcode,
'barcode': barcode,
'title': title,
'subTitle': subTitle,
'smallSubTitle': smallSubTitle,
'bottomContent': bottomContent,
});
///repeatScan()
Future<dynamic> repeatScan() => _channel.invokeMethod('repeatScan');
///printCustom(String message, int size, int align,{String? charset})
Future<dynamic> printText(String message, int size, int align,
{String? charset,
String? format,
int? light = 0,
int? weight = 0,
bool? bold = false}) =>
_channel.invokeMethod('printText', {
'message': message,
'size': size,
'bold': bold,
'align': align,
'charset': charset,
'format': format,
'light': light,
'weight': weight
});
///printNewLine()
Future<dynamic> printNewLine() => _channel.invokeMethod('printNewLine');
///paperCut()
Future<dynamic> paperCut() => _channel.invokeMethod('paperCut');
///initPrinter()
Future<dynamic> initPrinter() => _channel.invokeMethod('initPrinter');
///initMarginLeft(int left)
Future<dynamic> printMarginLeft(int left) =>
_channel.invokeMethod('printMarginLeft', {'left': left});
///printImage(String pathImage)
Future<dynamic> printImage(String pathImage) =>
_channel.invokeMethod('printImage', {'pathImage': pathImage});
///printImageBytes(Uint8List bytes)
Future<dynamic> printImageBytes(Uint8List bytes) =>
_channel.invokeMethod('printImageBytes', {'bytes': bytes});
///printQRcode(String textToQR, int width, int height, int align)
Future<dynamic> printQRcode(String textToQR, int width, int height, int align,
{int? light = 0, int? weight = 0}) =>
_channel.invokeMethod('printQRcode', {
'textToQR': textToQR,
'width': width,
'height': height,
'align': align,
'light': light,
'weight': weight
});
///printQRcode(String textToQR, int width, int height, int align)
Future<dynamic> printBarCode(
String textToBar, int width, int height, int align,
{int? light = 0, int? weight = 0}) =>
_channel.invokeMethod('printBarCode', {
'textToBar': textToBar,
'width': width,
'height': height,
'align': align,
'light': light,
'weight': weight
});
}
class BluetoothDevice {
final String? name;
final String? address;
final int type = 0;
bool connected = false;
BluetoothDevice(this.name, this.address);
BluetoothDevice.fromMap(Map map)
: name = map['name'],
address = map['address'];
Map<String, dynamic> toMap() => {
'name': this.name,
'address': this.address,
'type': this.type,
'connected': this.connected,
};
operator ==(Object other) {
return other is BluetoothDevice && other.address == this.address;
}
@override
int get hashCode => address.hashCode;
}
class LogUtil {
static var _separator = "=";
static var _split =
"$_separator$_separator$_separator$_separator$_separator$_separator$_separator$_separator$_separator";
static var _title = "Yl-Log";
static var _isDebug = true;
static int _limitLength = 800;
static String _startLine = "$_split$_title$_split";
static String _endLine = "$_split$_separator$_separator$_separator$_split";
static void init({String title = "", required bool isDebug, int? limitLength}) {
_title = title;
_isDebug = isDebug;
_limitLength = limitLength ??= _limitLength;
_startLine = "$_split$_title$_split";
var endLineStr = StringBuffer();
var cnCharReg = RegExp("[\u4e00-\u9fa5]");
for (int i = 0; i < _startLine.length; i++) {
if (cnCharReg.stringMatch(_startLine[i]) != null) {
endLineStr.write(_separator);
}
endLineStr.write(_separator);
}
_endLine = endLineStr.toString();
}
//仅Debug模式可见
static void d(dynamic obj) {
if (_isDebug) {
_log(obj.toString());
}
}
static void v(dynamic obj) {
_log(obj.toString());
}
static void _log(String msg) {
print("$_startLine");
_logEmptyLine();
if (msg.length < _limitLength) {
print(msg);
} else {
segmentationLog(msg);
}
_logEmptyLine();
print("$_endLine");
}
static void segmentationLog(String msg) {
var outStr = StringBuffer();
for (var index = 0; index < msg.length; index++) {
outStr.write(msg[index]);
if (index % _limitLength == 0 && index != 0) {
print(outStr);
outStr.clear();
var lastIndex = index + 1;
if (msg.length - lastIndex < _limitLength) {
var remainderStr = msg.substring(lastIndex, msg.length);
print(remainderStr);
break;
}
}
}
}
static void _logEmptyLine() {
print("");
}
}
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
class PrinterCommands {
static const HT = 0x9;
static const LF = 0x0A;
static const CR = 0x0D;
static const ESC = 0x1B;
static const DLE = 0x10;
static const GS = 0x1D;
static const FS = 0x1C;
static const STX = 0x02;
static const US = 0x1F;
static const CAN = 0x18;
static const CLR = 0x0C;
static const EOT = 0x04;
static const initPrinter = [ESC, 0x40];
static const FEED_LINE = [0x0A];
///设置左边距
/// left 用 nL 和 nH 设定左边空白量为(nL+ Nh*256)*0.125 毫米。
static printMarginLeft({int left = 0}) {
List<int> result = [];
result.add(0x1D);
result.add(0x4C);
result.add(left ~/ 0.125);
result.add(0);
return result;
}
///字体变大为标准的n倍
static fontSizeSetBig({int size = 0}) {
var realSize = 1;
switch (size) {
case 1:
realSize = 0x00;
break;
case 2:
realSize = 0x11;
break;
case 3:
realSize = 0x22;
break;
case 4:
realSize = 0x33;
break;
case 5:
realSize = 0x44;
break;
case 6:
realSize = 0x55;
break;
case 7:
realSize = 0x66;
break;
case 8:
realSize = 0x77;
break;
}
List<int> result = [];
result.add(0x1D);
result.add(0x21);
result.add(realSize);
return result;
}
/// 设置汉字字符左右间距
static setCharHSpace({int charSpace = 0}) {
List<int> result = [];
result.add(0x1C);
result.add(0x53);
result.add(charSpace);
result.add(charSpace);
return result;
}
///是否加粗
static bold({bool bold = false}) {
List<int> result = [];
if (bold) {
result.add(ESC);
result.add(69);
result.add(0xF);
} else {
result.add(ESC);
result.add(69);
result.add(0);
}
return result;
}
/// 打印条形码
/// 73 CODE128
static createBarCode(Uint8List infoByte) {
List<int> result = [];
var width = setBarCodeWidth(2);
var height = setBarCodeHeight(120);
var bar = [0x1D, 0x6B, 73, infoByte.length];
result.addAll(width);
result.addAll(height);
result.addAll(bar);
result.addAll(infoByte);
return result;
}
/// 设置条形码 宽度
/// width 2 3 4
static setBarCodeWidth(width) {
List<int> result = [];
result.add(0x1D);
result.add(0x77);
result.add(width);
return result;
}
/// 设置条形码 高度
/// height 0≤n≤255,默认 n=162
static setBarCodeHeight(height) {
List<int> result = [];
result.add(0x1D);
result.add(0x68);
result.add(height);
return result;
}
}
import 'package:blue_thermal_printer/blue_thermal_printer.dart';
class PrintData {
static double printPageWidth = 50; // 打印纸宽度 50mm
static double printTextWidth = 3; // 打印文字的宽度 3mm
static double printBaseUnitValue = 0.125; // 打印基准值 0.125mm
/// 初始化打印机
static initPrinter() {
return PrinterCommands.initPrinter;
}
/// 设置左边距
static printMarginLeft(int left) {
return PrinterCommands.printMarginLeft(left: left);
}
/// 空行
static printNewLine() {
return PrinterCommands.FEED_LINE;
}
/// 打印文字
/// message 文本
/// size 文字放大倍数
/// bold 是否加粗
/// charset 文本编码
/// space 文本间隙
static printText(String message,
{int size = 1,
bool bold = false,
CHARSET charset = CHARSET.GBK,
int space = 0}) {
List<int> data = [];
var textList = splitText(message, size, space);
for (var value in textList) {
data.addAll(_printLineText(value, size, bold, charset, space));
}
return data;
}
/// 打印一行
static List<int> _printLineText(
String text, int size, bool bold, CHARSET charset, int space) {
List<int> data = [];
// 左边距 = 打印纸宽度 - 文字宽度 ~/ 2
var left = getLeftMargin(text, size, space);
var textLeftMargin = PrinterCommands.printMarginLeft(left: left);
var textBold = PrinterCommands.bold(bold: bold);
var textSize = PrinterCommands.fontSizeSetBig(size: size);
var textSpace = PrinterCommands.setCharHSpace(charSpace: space);
List<int> content = [];
if (charset == CHARSET.GBK) {
content.addAll(gbk.encode(text));
} else {
content.addAll(utf8.encode(text));
}
var feedLine = PrinterCommands.FEED_LINE;
data.addAll(textLeftMargin);
data.addAll(textSize);
data.addAll(textBold);
data.addAll(textSpace);
data.addAll(content);
data.addAll(feedLine);
return data;
}
// 文字内容拆分多行
static List splitText(String text, int size, int space) {
var list = [];
var lineTextCount = getLineTextCount(size, space);
for (var i = 0; i < text.length; i = i + lineTextCount) {
list.add(text.substring(i, min(text.length, i + lineTextCount)));
}
return list;
}
// 一行显示的字数
// 字数 = 打印纸宽度 ~/ (打印字字宽度 * 字体放大倍数)
static int getLineTextCount(sizeMul, space) {
var textHSpace = 2 * space * printBaseUnitValue; // 字符左右边距
return printPageWidth ~/ ((printTextWidth + textHSpace) * sizeMul);
}
// 左边距 = 打印纸宽度 - 文字宽度 ~/ 2
static int getLeftMargin(String text, sizeMul, int space) {
return (printPageWidth - getLineTextWidth(text, sizeMul, space)) ~/ 2 - 1;
}
// 一行字的宽度
// 一行字的总宽度 = 字数 * 打印字字宽度(字符宽度+字间距宽度) * 字体放大倍数
static double getLineTextWidth(String text, sizeMul, int space) {
var textHSpace = 2 * space * printBaseUnitValue; // 字符左右边距
var doubleCharCount = matchDoubleCharCount(text); // 双字节字符数量
var doubleCharWidth = doubleCharCount * (printTextWidth + textHSpace);
var singleCharWidth =
(text.length - doubleCharCount) * (printTextWidth / 2 + textHSpace);
// 字节宽度(单字节+双字节) * 字体放大倍数
return (doubleCharWidth + singleCharWidth) * sizeMul;
}
// 双字节字符个数
static matchDoubleCharCount(String text) {
int count = 0;
RegExp regDoubleChar = RegExp("[^\x00-\xff]");
for (var i = 0; i < text.length; i++) {
var char = text.substring(i, i+1);
if (regDoubleChar.hasMatch(char)) {
count++;
}
}
return count;
}
// 打印条形码
static printBarCode(String info, {CHARSET charset = CHARSET.UTF_8}) {
Uint8List infoByte;
if (charset == CHARSET.GBK) {
infoByte = Uint8List.fromList(gbk.encode(info));
} else {
infoByte = Uint8List.fromList(utf8.encode(info));
}
return PrinterCommands.createBarCode(infoByte);
}
/// 打印二维码
static printQRCode(String info, {CHARSET charset = CHARSET.UTF_8}) {
List<int> data = [];
///设置二维码模块大小
var QRCodeSize = [0x1D,0x28,0x6B,0x03,0x00,0x31,0x43,8];
///将二维码数据存储到符号存储区
//第一步
String str = info.toString();
var kLength = str.length + 3;
var pL = kLength % 256;
var pH = kLength ~/ 256;
var QRCodeInfo = [0x1D,0x28,0x6B,pL,pH,0x31,0x50,48];
//第二步
Uint8List infoByte;
if (charset == CHARSET.GBK) {
infoByte = Uint8List.fromList(gbk.encode(info));
} else {
infoByte = Uint8List.fromList(utf8.encode(info));
}
///打印之前存储的二维码信息
var printStoredQRData = [0x1D,0x28,0x6B,0x03,0x00,0x31,0x51,48];
///组装
data.addAll(QRCodeSize);
data.addAll(QRCodeInfo);
data.addAll(infoByte);
data.addAll(printStoredQRData);
return data;
}
}
/// 字体
enum CHARSET {
UTF_8,
GBK,
}
## This file must *NOT* be checked into Version Control Systems,
# as it contains information specific to your local configuration.
#
# Location of the SDK. This is only used by Gradle.
# For customization when using a Version Control System, please read the
# header note.
#Fri Mar 19 16:23:49 ICT 2021
sdk.dir=E\:\\Users\\ZAKI\\AppData\\Local\\Android\\sdk
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.8.2"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
characters:
dependency: transitive
description:
name: characters
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
charcode:
dependency: transitive
description:
name: charcode
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.1"
clock:
dependency: transitive
description:
name: clock
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
collection:
dependency: transitive
description:
name: collection
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.15.0"
fake_async:
dependency: transitive
description:
name: fake_async
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
fast_gbk:
dependency: "direct main"
description:
name: fast_gbk
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
matcher:
dependency: transitive
description:
name: matcher
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.12.11"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.1.3"
meta:
dependency: transitive
description:
name: meta
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.7.0"
path:
dependency: transitive
description:
name: path
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.0"
platform:
dependency: "direct dev"
description:
name: platform
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.8.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.10.0"
stream_channel:
dependency: transitive
description:
name: stream_channel
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.0"
string_scanner:
dependency: transitive
description:
name: string_scanner
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
term_glyph:
dependency: transitive
description:
name: term_glyph
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.2.0"
test_api:
dependency: transitive
description:
name: test_api
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.4.8"
typed_data:
dependency: transitive
description:
name: typed_data
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.0"
vector_math:
dependency: transitive
description:
name: vector_math
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.1.1"
sdks:
dart: ">=2.14.0 <3.0.0"
flutter: ">=1.10.0"
name: blue_thermal_printer
description: A Flutter plugin for connecting to thermal printer via bluetooth
version: 1.3.0
homepage: https://github.com/kakzaki/blue_thermal_printer
environment:
sdk: '>=2.12.0 <3.0.0'
flutter: ">=1.10.0"
dependencies:
flutter:
sdk: flutter
# 编码转码 https://pub.dev/packages/fast_gbk
fast_gbk: ^1.0.0
dev_dependencies:
flutter_test:
sdk: flutter
# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec
platform: ^3.1.0
# The following section is specific to Flutter.
flutter:
# This section identifies this Flutter project as a plugin project.
# The androidPackage and pluginClass identifiers should not ordinarily
# be modified. They are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: id.kakzaki.blue_thermal_printer
pluginClass: BlueThermalPrinterPlugin
# fileName: blue_thermal_printer.dart
ios:
pluginClass: BlueThermalPrinterPlugin
# fileName: blue_thermal_printer.dart
# To add assets to your plugin package, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg
#
# For details regarding assets in packages, see
# https://flutter.dev/assets-and-images/#from-packages
#
# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.dev/assets-and-images/#resolution-aware.
# To add custom fonts to your plugin package, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts in packages, see
# https://flutter.dev/custom-fonts/#from-packages
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
const MethodChannel channel = MethodChannel('blue_thermal_printer');
setUp(() {
channel.setMockMethodCallHandler((MethodCall methodCall) async {
return '42';
});
});
tearDown(() {
channel.setMockMethodCallHandler(null);
});
test('getPlatformVersion', () async {
//expect(await BlueThermalPrinter.platformVersion, '42');
});
}
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论