This commit is contained in:
2026-03-31 08:51:33 +08:00
commit e9f4844352
63 changed files with 8950 additions and 0 deletions

29
.gitignore vendored Normal file
View File

@@ -0,0 +1,29 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
/pubspec.lock
**/doc/api/
.dart_tool/
build/

30
.metadata Normal file
View File

@@ -0,0 +1,30 @@
# 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: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
channel: "stable"
project_type: plugin
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
- platform: android
create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'

3
CHANGELOG.md Normal file
View File

@@ -0,0 +1,3 @@
## 0.0.1
* TODO: Describe initial release.

1
LICENSE Normal file
View File

@@ -0,0 +1 @@
TODO: Add your license here.

15
README.md Normal file
View File

@@ -0,0 +1,15 @@
# zhiwen
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter
[plug-in package](https://flutter.dev/developing-packages/),
a specialized package that includes platform-specific implementation code for
Android and/or iOS.
For help getting started with Flutter development, view the
[online documentation](https://flutter.dev/docs), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

4
analysis_options.yaml Normal file
View File

@@ -0,0 +1,4 @@
include: package:flutter_lints/flutter.yaml
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options

9
android/.gitignore vendored Normal file
View File

@@ -0,0 +1,9 @@
*.iml
.gradle
/local.properties
/.idea/workspace.xml
/.idea/libraries
.DS_Store
/build
/captures
.cxx

60
android/build.gradle Normal file
View File

@@ -0,0 +1,60 @@
group = "com.xiarui.zhiwen"
version = "1.0"
buildscript {
repositories {
google()
mavenCentral()
}
dependencies {
classpath("com.android.tools.build:gradle:7.3.0")
}
}
rootProject.allprojects {
repositories {
google()
mavenCentral()
}
}
apply plugin: "com.android.library"
android {
if (project.android.hasProperty("namespace")) {
namespace = "com.xiarui.zhiwen"
}
compileSdk = 34
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
minSdk = 21
}
sourceSets {
main {
jniLibs.srcDirs = ['libs']
}
}
dependencies {
testImplementation("junit:junit:4.13.2")
testImplementation("org.mockito:mockito-core:5.0.0")
}
testOptions {
unitTests.all {
testLogging {
events "passed", "skipped", "failed", "standardOut", "standardError"
outputs.upToDateWhen {false}
showStandardStreams = true
}
}
}
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

1
android/settings.gradle Normal file
View File

@@ -0,0 +1 @@
rootProject.name = 'zhiwen'

View File

@@ -0,0 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.xiarui.zhiwen">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
</manifest>

View File

@@ -0,0 +1,21 @@
package android_serialport_api;
import java.text.SimpleDateFormat;
/**
* @author benjaminwan
*/
public class ComBean {
public byte[] bRec=null;
public String sRecTime="";
public String sComPort="";
public int nSize = 0;
public ComBean(String sPort,byte[] buffer,int size){
sComPort=sPort;
bRec=new byte[size];
System.arraycopy(buffer, 0, bRec, 0, size);
nSize = size;
SimpleDateFormat sDateFormat = new SimpleDateFormat("hh:mm:ss");
sRecTime = sDateFormat.format(new java.util.Date());
}
}

View File

@@ -0,0 +1,231 @@
package android_serialport_api;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidParameterException;
import com.xiarui.zhiwen.DevComm;
import android_serialport_api.ComBean;
import android_serialport_api.SerialPort;
public abstract class SerialHelper{
private SerialPort mSerialPort;
private OutputStream mOutputStream;
private InputStream mInputStream;
private ReadThread mReadThread;
private SendThread mSendThread;
private String sPort="/dev/s3c2410_serial0";
private int iBaudRate=9600;
private boolean _isOpen=false;
private byte[] _bLoopData=new byte[]{0x30};
private int iDelay=500;
//----------------------------------------------------
public SerialHelper(String sPort,int iBaudRate){
this.sPort = sPort;
this.iBaudRate=iBaudRate;
}
public SerialHelper(){
this("/dev/s3c2410_serial0",9600);
}
public SerialHelper(String sPort){
this(sPort,9600);
}
public SerialHelper(String sPort,String sBaudRate){
this(sPort,Integer.parseInt(sBaudRate));
}
//----------------------------------------------------
public void open() throws SecurityException, IOException,InvalidParameterException{
mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
mOutputStream = mSerialPort.getOutputStream();
mInputStream = mSerialPort.getInputStream();
mReadThread = new ReadThread();
mReadThread.start();
mSendThread = new SendThread();
mSendThread.setSuspendFlag();
mSendThread.start();
_isOpen=true;
}
//----------------------------------------------------
public void close(){
if (mReadThread != null)
mReadThread.interrupt();
if (mSerialPort != null) {
mSerialPort.close();
mSerialPort = null;
}
_isOpen=false;
}
//----------------------------------------------------
public void send(byte[] bOutArray, int nSize){
try
{
mOutputStream.write(bOutArray, 0, nSize);
} catch (IOException e)
{
e.printStackTrace();
}
}
//----------------------------------------------------
public void sendTxt(String sTxt){
byte[] bOutArray =sTxt.getBytes();
send(bOutArray, sTxt.length());
}
//----------------------------------------------------
private class ReadThread extends Thread {
@Override
public void run() {
super.run();
while(!isInterrupted()) {
try
{
if (mInputStream == null) return;
byte[] buffer=new byte[DevComm.MAX_DATA_LEN];
int size = mInputStream.read(buffer);
if (size > 0){
ComBean ComRecData = new ComBean(sPort,buffer,size);
onDataReceived(ComRecData);
}
try
{
Thread.sleep(50);
} catch (InterruptedException e)
{
e.printStackTrace();
}
} catch (Throwable e)
{
e.printStackTrace();
return;
}
}
}
}
//----------------------------------------------------
private class SendThread extends Thread{
public boolean suspendFlag = true;
@Override
public void run() {
super.run();
while(!isInterrupted()) {
synchronized (this)
{
while (suspendFlag)
{
try
{
wait();
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
send(getbLoopData(), 1);
try
{
Thread.sleep(iDelay);
} catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
public void setSuspendFlag() {
this.suspendFlag = true;
}
public synchronized void setResume() {
this.suspendFlag = false;
notify();
}
}
//----------------------------------------------------
public int getBaudRate()
{
return iBaudRate;
}
public boolean setBaudRate(int iBaud)
{
if (_isOpen)
{
return false;
} else
{
iBaudRate = iBaud;
return true;
}
}
public boolean setBaudRate(String sBaud)
{
int iBaud = Integer.parseInt(sBaud);
return setBaudRate(iBaud);
}
//----------------------------------------------------
public String getPort()
{
return sPort;
}
public boolean setPort(String sPort)
{
if (_isOpen)
{
return false;
} else
{
this.sPort = sPort;
return true;
}
}
//----------------------------------------------------
public boolean isOpen()
{
return _isOpen;
}
//----------------------------------------------------
public byte[] getbLoopData()
{
return _bLoopData;
}
//----------------------------------------------------
public void setbLoopData(byte[] bLoopData)
{
this._bLoopData = bLoopData;
}
//----------------------------------------------------
public void setTxtLoopData(String sTxt){
this._bLoopData = sTxt.getBytes();
}
//----------------------------------------------------
public int getiDelay()
{
return iDelay;
}
//----------------------------------------------------
public void setiDelay(int iDelay)
{
this.iDelay = iDelay;
}
//----------------------------------------------------
public void startSend()
{
if (mSendThread != null)
{
mSendThread.setResume();
}
}
//----------------------------------------------------
public void stopSend()
{
if (mSendThread != null)
{
mSendThread.setSuspendFlag();
}
}
//----------------------------------------------------
protected abstract void onDataReceived(ComBean ComRecData);
}

View File

@@ -0,0 +1,89 @@
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android_serialport_api;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import android.util.Log;
public class SerialPort {
private static final String TAG = "SerialPort";
/*
* Do not remove or rename the field mFd: it is used by native method close();
*/
private final FileDescriptor mFd;
private final FileInputStream mFileInputStream;
private final FileOutputStream mFileOutputStream;
public SerialPort(File device, int baudrate, int flags) throws SecurityException, IOException {
/* Check access permission */
if (!device.canRead() || !device.canWrite()) {
try {
/* Missing read/write permission, trying to chmod the file */
Process su;
su = Runtime.getRuntime().exec("/system/bin/su");
String cmd = "chmod 666 " + device.getAbsolutePath() + "\n"
+ "exit\n";
su.getOutputStream().write(cmd.getBytes());
if ((su.waitFor() != 0) || !device.canRead()
|| !device.canWrite()) {
throw new SecurityException();
}
} catch (Exception e) {
e.printStackTrace();
throw new SecurityException();
}
}
mFd = open(device.getAbsolutePath(), baudrate, flags);
if (mFd == null) {
Log.e(TAG, "native open returns null");
throw new IOException();
}
mFileInputStream = new FileInputStream(mFd);
mFileOutputStream = new FileOutputStream(mFd);
}
// Getters and setters
public InputStream getInputStream() {
return mFileInputStream;
}
public OutputStream getOutputStream() {
return mFileOutputStream;
}
// JNI
private native static FileDescriptor open(String path, int baudrate, int flags);
public native void close();
static {
try {
System.loadLibrary("serial_port");
} catch (UnsatisfiedLinkError err) {
err.printStackTrace();
}
}
}

View File

@@ -0,0 +1,122 @@
/*
* Copyright 2009 Cedric Priscal
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android_serialport_api;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.Iterator;
import java.util.Vector;
import android.util.Log;
public class SerialPortFinder {
public class Driver {
public Driver(String name, String root) {
mDriverName = name;
mDeviceRoot = root;
}
private final String mDriverName;
private final String mDeviceRoot;
Vector<File> mDevices = null;
public Vector<File> getDevices() {
if (mDevices == null) {
mDevices = new Vector<File>();
File dev = new File("/dev");
File[] files = dev.listFiles();
int i;
for (i=0; i<files.length; i++) {
if (files[i].getAbsolutePath().startsWith(mDeviceRoot)) {
Log.d(TAG, "Found new device: " + files[i]);
mDevices.add(files[i]);
}
}
}
return mDevices;
}
public String getName() {
return mDriverName;
}
}
private static final String TAG = "SerialPort";
private Vector<Driver> mDrivers = null;
Vector<Driver> getDrivers() throws IOException {
if (mDrivers == null) {
mDrivers = new Vector<Driver>();
LineNumberReader r = new LineNumberReader(new FileReader("/proc/tty/drivers"));
String l;
while((l = r.readLine()) != null) {
// Issue 3:
// Since driver name may contain spaces, we do not extract driver name with split()
String drivername = l.substring(0, 0x15).trim();
String[] w = l.split(" +");
if ((w.length >= 5) && (w[w.length-1].equals("serial"))) {
Log.d(TAG, "Found new driver " + drivername + " on " + w[w.length-4]);
mDrivers.add(new Driver(drivername, w[w.length-4]));
}
}
r.close();
}
return mDrivers;
}
public String[] getAllDevices() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while(itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while(itdev.hasNext()) {
String device = itdev.next().getName();
String value = String.format("%s (%s)", device, driver.getName());
devices.add(value);
}
}
} catch (IOException e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
public String[] getAllDevicesPath() {
Vector<String> devices = new Vector<String>();
// Parse each driver
Iterator<Driver> itdriv;
try {
itdriv = getDrivers().iterator();
while(itdriv.hasNext()) {
Driver driver = itdriv.next();
Iterator<File> itdev = driver.getDevices().iterator();
while(itdev.hasNext()) {
String device = itdev.next().getAbsolutePath();
devices.add(device);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return devices.toArray(new String[devices.size()]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
package com.idworld.noemhost_and;
/**
* Created by KMS on 2016/8/23.
*/
public interface IUsbConnState {
void onUsbConnected();
void onUsbPermissionDenied();
void onDeviceNotFound();
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,440 @@
package com.idworld.noemhost_and;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
/**
* Created by KMS on 2016/8/23.
*/
public class UsbController {
private final Context mApplicationContext;
private final UsbManager mUsbManager;
private final int VID;
private final int PID;
private int m_nEPInSize, m_nEPOutSize;
private final byte[] m_abyTransferBuf;
private boolean m_bInit = false;
private UsbDeviceConnection m_usbConn = null;
private UsbInterface m_usbIf = null;
private UsbEndpoint m_epIN = null;
private UsbEndpoint m_epOUT = null;
private final IUsbConnState mConnectionHandler;
protected static final String ACTION_USB_PERMISSION = "ch.serverbox.android.USB";
/**
* Activity is needed for onResult
*
* @param parentActivity
*/
public UsbController(Activity parentActivity, IUsbConnState connectionHandler, int vid, int pid){
mConnectionHandler = connectionHandler;
mApplicationContext = parentActivity.getApplicationContext();
mUsbManager = (UsbManager) mApplicationContext.getSystemService(Context.USB_SERVICE);
VID = vid;
PID = pid;
m_abyTransferBuf = new byte[512];
//init();
}
public void init(){
enumerate(new IPermissionListener() {
@Override
public void onPermissionDenied(UsbDevice d) {
UsbManager usbman = (UsbManager) mApplicationContext.getSystemService(Context.USB_SERVICE);
PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext, 0, new Intent(ACTION_USB_PERMISSION), FLAG_IMMUTABLE);
mApplicationContext.registerReceiver(mPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
usbman.requestPermission(d, pi);
}
});
}
public void uninit(){
if (m_usbConn != null)
{
m_usbConn.releaseInterface(m_usbIf);
m_usbConn.close();
m_usbConn = null;
m_bInit = false;
}
//stop();
}
public void stop()
{
try{
mApplicationContext.unregisterReceiver(mPermissionReceiver);
}catch(IllegalArgumentException e){}//bravo
}
public boolean IsInit(){
return m_bInit;
}
private void enumerate(IPermissionListener listener) {
boolean bFound = false;
l("enumerating");
HashMap<String, UsbDevice> devlist = mUsbManager.getDeviceList();
Iterator<UsbDevice> deviter = devlist.values().iterator();
while (deviter.hasNext()) {
UsbDevice d = deviter.next();
l("Found device: " + String.format("%04X:%04X", d.getVendorId(), d.getProductId()));
Toast.makeText(mApplicationContext, "Found device: " + String.format("%04X:%04X", d.getVendorId(), d.getProductId()), Toast.LENGTH_SHORT).show();
if (d.getVendorId() == VID && d.getProductId() == PID) {
bFound = true;
l("Device under: " + d.getDeviceName());
if (!mUsbManager.hasPermission(d))
{
Toast.makeText(mApplicationContext, "enumerate, hasPermission return false" , Toast.LENGTH_SHORT).show();
listener.onPermissionDenied(d);
}
else{
Toast.makeText(mApplicationContext, "enumerate, GetConnInerface start" , Toast.LENGTH_SHORT).show();
//startHandler(d);
GetConnInerface(d);
//TestComm(d);
return;
}
break;
}
}
if (!bFound)
{
Toast.makeText(mApplicationContext, "no more devices found" , Toast.LENGTH_SHORT).show();
mConnectionHandler.onDeviceNotFound();
}
}
private class PermissionReceiver extends BroadcastReceiver {
private final IPermissionListener mPermissionListener;
public PermissionReceiver(IPermissionListener permissionListener) {
mPermissionListener = permissionListener;
}
@Override
public void onReceive(Context context, Intent intent) {
mApplicationContext.unregisterReceiver(this);
if (intent.getAction().equals(ACTION_USB_PERMISSION)) {
if (!intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
mPermissionListener.onPermissionDenied(intent
.getParcelableExtra(UsbManager.EXTRA_DEVICE));
mConnectionHandler.onUsbPermissionDenied();
} else {
l("Permission granted");
UsbDevice dev = intent
.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (dev != null) {
if (dev.getVendorId() == VID
&& dev.getProductId() == PID) {
//startHandler(dev);// has new thread
GetConnInerface(dev);
//TestComm(dev);
}
} else {
mConnectionHandler.onDeviceNotFound();
}
}
}
}
}
private void GetConnInerface(UsbDevice dev){
int n;
m_usbConn = mUsbManager.openDevice(dev);
n = dev.getInterfaceCount();
if (n <= 0)
return;
if (!m_usbConn.claimInterface(dev.getInterface(0), true)) {
return;
}
m_usbIf = dev.getInterface(0);
n = m_usbIf.getEndpointCount();
if (n < 2)
return;
for (int i = 0; i < n; i++) {
if (m_usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (m_usbIf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
m_epIN = m_usbIf.getEndpoint(i);
else
m_epOUT = m_usbIf.getEndpoint(i);
}
}
m_nEPInSize = m_epIN.getMaxPacketSize();
m_nEPOutSize = m_epOUT.getMaxPacketSize();
m_bInit = true;
//m_epOUT.getMaxPacketSize();
//Toast.makeText(mApplicationContext, "GetConnInerface OK, Out Max Size="+m_nEPOutSize+" In Max Size=" + m_nEPInSize, Toast.LENGTH_SHORT).show();
mConnectionHandler.onUsbConnected();
}
public boolean OperationInternal(byte[] pData, int nDataLen, int nTimeOut, boolean bRead)
{
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = (byte)(nDataLen & 0xFF);
w_abyTmp[9] = (byte)((nDataLen >> 8)& 0xFF);
w_abyTmp[10] = (byte)((nDataLen >> 16)& 0xFF);
w_abyTmp[11] = (byte)((nDataLen >> 24)& 0xFF);
if(bRead)
w_abyTmp[12] = (byte)0x80;
else
w_abyTmp[12] = 0x00; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
w_abyTmp[15] = (byte)0xef;
if (bRead)
w_abyTmp[16] = (byte)0xff;
else
w_abyTmp[16] = (byte)0xfe;
// send 31bytes
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
// read or write real data
if (bRead)
w_bRet = UsbBulkReceive(pData, nDataLen, nTimeOut);
else
w_bRet = UsbBulkSend(pData, nDataLen, nTimeOut);
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
public boolean UsbSCSIWrite(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut)
{
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
//Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = 0x00;
w_abyTmp[9] = 0x00;
w_abyTmp[10] = 0x00;
w_abyTmp[11] = 0x00;
w_abyTmp[12] = 0x00; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);
//System.arraycopy(pData, 0, w_abyTmp, 31, nDataLen);
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
w_bRet = UsbBulkSend(pData, nDataLen, nTimeOut);
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
public boolean UsbSCSIRead(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut)
{
long w_nTime;
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
//Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = 0x00;
w_abyTmp[9] = 0x00;
w_abyTmp[10] = 0x00;
w_abyTmp[11] = 0x00;
w_abyTmp[12] = (byte)0x80; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
w_nTime = SystemClock.elapsedRealtime();
w_bRet = UsbBulkReceive(pData, nDataLen, nTimeOut);
w_nTime = SystemClock.elapsedRealtime() - w_nTime;
//Toast.makeText(mApplicationContext, "UsbSCSIRead, UsbBulkReceive Time : " + w_nTime , Toast.LENGTH_SHORT).show();
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
private boolean UsbBulkSend(byte[] pBuf, int nLen, int nTimeOut)
{
int i, n, r, w_nRet;
//byte[] w_abyTmp = new byte[m_nEPOutSize];
n = nLen / m_nEPOutSize;
r = nLen % m_nEPOutSize;
for(i=0; i<n; i++)
{
System.arraycopy(pBuf, i*m_nEPOutSize, m_abyTransferBuf, 0, m_nEPOutSize);
w_nRet = m_usbConn.bulkTransfer(m_epOUT, m_abyTransferBuf, m_nEPOutSize, nTimeOut);
if (w_nRet != m_nEPOutSize)
return false;
}
if (r > 0)
{
System.arraycopy(pBuf, i*m_nEPOutSize, m_abyTransferBuf, 0, r);
w_nRet = m_usbConn.bulkTransfer(m_epOUT, m_abyTransferBuf, r, nTimeOut);
return w_nRet == r;
}
return true;
}
private boolean UsbBulkReceive(byte[] pBuf, int nLen, int nTimeOut)
{
int i, n, r, w_nRet;
//byte[] w_abyTmp = new byte[m_nEPInSize];
//w_nRet = m_usbConn.bulkTransfer(m_epIN, pBuf, nLen, nTimeOut);
//if (w_nRet != nLen)
// return false;
n = nLen / m_nEPInSize;
r = nLen % m_nEPInSize;
//Toast.makeText(mApplicationContext, "UsbBulkReceive, Buf Len = " + pBuf.length, Toast.LENGTH_SHORT).show();
for(i=0; i<n; i++)
{
w_nRet = m_usbConn.bulkTransfer(m_epIN, m_abyTransferBuf, m_nEPInSize, nTimeOut);
if (w_nRet != m_nEPInSize)
return false;
System.arraycopy(m_abyTransferBuf, 0, pBuf, i*m_nEPInSize, m_nEPInSize);
}
if (r > 0)
{
w_nRet = m_usbConn.bulkTransfer(m_epIN, m_abyTransferBuf, r, nTimeOut);
if (w_nRet != r)
return false;
System.arraycopy(m_abyTransferBuf, 0, pBuf, i*m_nEPInSize, r);
}
return true;
}
// END MAIN LOOP
private final BroadcastReceiver mPermissionReceiver = new PermissionReceiver(
new IPermissionListener() {
@Override
public void onPermissionDenied(UsbDevice d) {
l("Permission denied on " + d.getDeviceId());
}
});
private interface IPermissionListener {
void onPermissionDenied(UsbDevice d);
}
public final static String TAG = "USBController";
private void l(Object msg) {
Log.d(TAG, ">==< " + msg.toString() + " >==<");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
package com.xiarui.zhiwen;
/**
* Created by KMS on 2016/8/23.
*/
public interface IUsbConnState {
void onUsbConnected();
void onUsbPermissionDenied();
void onDeviceNotFound();
}

View File

@@ -0,0 +1,440 @@
package com.xiarui.zhiwen;
import static android.app.PendingIntent.FLAG_IMMUTABLE;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbDeviceConnection;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
import android.hardware.usb.UsbManager;
import android.os.SystemClock;
import android.util.Log;
import android.widget.Toast;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
/**
* Created by KMS on 2016/8/23.
*/
public class UsbController {
private final Context mApplicationContext;
private final UsbManager mUsbManager;
private final int VID;
private final int PID;
private int m_nEPInSize, m_nEPOutSize;
private final byte[] m_abyTransferBuf;
private boolean m_bInit = false;
private UsbDeviceConnection m_usbConn = null;
private UsbInterface m_usbIf = null;
private UsbEndpoint m_epIN = null;
private UsbEndpoint m_epOUT = null;
private final IUsbConnState mConnectionHandler;
protected static final String ACTION_USB_PERMISSION = "ch.serverbox.android.USB";
/**
* Activity is needed for onResult
*
* @param parentActivity
*/
public UsbController(Activity parentActivity, IUsbConnState connectionHandler, int vid, int pid){
mConnectionHandler = connectionHandler;
mApplicationContext = parentActivity.getApplicationContext();
mUsbManager = (UsbManager) mApplicationContext.getSystemService(Context.USB_SERVICE);
VID = vid;
PID = pid;
m_abyTransferBuf = new byte[512];
//init();
}
public void init(){
enumerate(new IPermissionListener() {
@Override
public void onPermissionDenied(UsbDevice d) {
UsbManager usbman = (UsbManager) mApplicationContext.getSystemService(Context.USB_SERVICE);
PendingIntent pi = PendingIntent.getBroadcast(mApplicationContext, 0, new Intent(ACTION_USB_PERMISSION), FLAG_IMMUTABLE);
mApplicationContext.registerReceiver(mPermissionReceiver, new IntentFilter(ACTION_USB_PERMISSION));
usbman.requestPermission(d, pi);
}
});
}
public void uninit(){
if (m_usbConn != null)
{
m_usbConn.releaseInterface(m_usbIf);
m_usbConn.close();
m_usbConn = null;
m_bInit = false;
}
//stop();
}
public void stop()
{
try{
mApplicationContext.unregisterReceiver(mPermissionReceiver);
}catch(IllegalArgumentException e){}//bravo
}
public boolean IsInit(){
return m_bInit;
}
private void enumerate(IPermissionListener listener) {
boolean bFound = false;
l("enumerating");
HashMap<String, UsbDevice> devlist = mUsbManager.getDeviceList();
Iterator<UsbDevice> deviter = devlist.values().iterator();
while (deviter.hasNext()) {
UsbDevice d = deviter.next();
l("Found device: " + String.format("%04X:%04X", d.getVendorId(), d.getProductId()));
// Toast.makeText(mApplicationContext, "Found device: " + String.format("%04X:%04X", d.getVendorId(), d.getProductId()), Toast.LENGTH_SHORT).show();
if (d.getVendorId() == VID && d.getProductId() == PID) {
bFound = true;
l("Device under: " + d.getDeviceName());
if (!mUsbManager.hasPermission(d))
{
Toast.makeText(mApplicationContext, "enumerate, hasPermission return false" , Toast.LENGTH_SHORT).show();
listener.onPermissionDenied(d);
}
else{
// Toast.makeText(mApplicationContext, "enumerate, GetConnInerface start" , Toast.LENGTH_SHORT).show();
//startHandler(d);
GetConnInerface(d);
//TestComm(d);
return;
}
break;
}
}
if (!bFound)
{
Toast.makeText(mApplicationContext, "no more devices found" , Toast.LENGTH_SHORT).show();
mConnectionHandler.onDeviceNotFound();
}
}
private class PermissionReceiver extends BroadcastReceiver {
private final IPermissionListener mPermissionListener;
public PermissionReceiver(IPermissionListener permissionListener) {
mPermissionListener = permissionListener;
}
@Override
public void onReceive(Context context, Intent intent) {
mApplicationContext.unregisterReceiver(this);
if (intent.getAction().equals(ACTION_USB_PERMISSION)) {
if (!intent.getBooleanExtra(
UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
mPermissionListener.onPermissionDenied(intent
.getParcelableExtra(UsbManager.EXTRA_DEVICE));
mConnectionHandler.onUsbPermissionDenied();
} else {
l("Permission granted");
UsbDevice dev = intent
.getParcelableExtra(UsbManager.EXTRA_DEVICE);
if (dev != null) {
if (dev.getVendorId() == VID
&& dev.getProductId() == PID) {
//startHandler(dev);// has new thread
GetConnInerface(dev);
//TestComm(dev);
}
} else {
mConnectionHandler.onDeviceNotFound();
}
}
}
}
}
private void GetConnInerface(UsbDevice dev){
int n;
m_usbConn = mUsbManager.openDevice(dev);
n = dev.getInterfaceCount();
if (n <= 0)
return;
if (!m_usbConn.claimInterface(dev.getInterface(0), true)) {
return;
}
m_usbIf = dev.getInterface(0);
n = m_usbIf.getEndpointCount();
if (n < 2)
return;
for (int i = 0; i < n; i++) {
if (m_usbIf.getEndpoint(i).getType() == UsbConstants.USB_ENDPOINT_XFER_BULK) {
if (m_usbIf.getEndpoint(i).getDirection() == UsbConstants.USB_DIR_IN)
m_epIN = m_usbIf.getEndpoint(i);
else
m_epOUT = m_usbIf.getEndpoint(i);
}
}
m_nEPInSize = m_epIN.getMaxPacketSize();
m_nEPOutSize = m_epOUT.getMaxPacketSize();
m_bInit = true;
//m_epOUT.getMaxPacketSize();
//Toast.makeText(mApplicationContext, "GetConnInerface OK, Out Max Size="+m_nEPOutSize+" In Max Size=" + m_nEPInSize, Toast.LENGTH_SHORT).show();
mConnectionHandler.onUsbConnected();
}
public boolean OperationInternal(byte[] pData, int nDataLen, int nTimeOut, boolean bRead)
{
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = (byte)(nDataLen & 0xFF);
w_abyTmp[9] = (byte)((nDataLen >> 8)& 0xFF);
w_abyTmp[10] = (byte)((nDataLen >> 16)& 0xFF);
w_abyTmp[11] = (byte)((nDataLen >> 24)& 0xFF);
if(bRead)
w_abyTmp[12] = (byte)0x80;
else
w_abyTmp[12] = 0x00; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
w_abyTmp[15] = (byte)0xef;
if (bRead)
w_abyTmp[16] = (byte)0xff;
else
w_abyTmp[16] = (byte)0xfe;
// send 31bytes
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
// read or write real data
if (bRead)
w_bRet = UsbBulkReceive(pData, nDataLen, nTimeOut);
else
w_bRet = UsbBulkSend(pData, nDataLen, nTimeOut);
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
public boolean UsbSCSIWrite(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut)
{
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
//Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = 0x00;
w_abyTmp[9] = 0x00;
w_abyTmp[10] = 0x00;
w_abyTmp[11] = 0x00;
w_abyTmp[12] = 0x00; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);
//System.arraycopy(pData, 0, w_abyTmp, 31, nDataLen);
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
w_bRet = UsbBulkSend(pData, nDataLen, nTimeOut);
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
public boolean UsbSCSIRead(byte[] pCDB, int nCDBLen, byte[] pData, int nDataLen, int nTimeOut)
{
long w_nTime;
byte[] w_abyTmp = new byte[31];
byte[] w_abyCSW = new byte[13];
boolean w_bRet;
//Arrays.fill(w_abyTmp, (byte)0);
w_abyTmp[0] = 0x55;
w_abyTmp[1] = 0x53;
w_abyTmp[2] = 0x42;
w_abyTmp[3] = 0x43;
w_abyTmp[4] = 0x28;
w_abyTmp[5] = 0x2b;
w_abyTmp[6] = 0x18;
w_abyTmp[7] = (byte)0x89;
w_abyTmp[8] = 0x00;
w_abyTmp[9] = 0x00;
w_abyTmp[10] = 0x00;
w_abyTmp[11] = 0x00;
w_abyTmp[12] = (byte)0x80; //cCBWFlags
w_abyTmp[13] = 0x00; //cCBWlun
w_abyTmp[14] = 0x0a; //cCBWCBLength
System.arraycopy(pCDB, 0, w_abyTmp, 15, nCDBLen);
w_bRet = UsbBulkSend(w_abyTmp, 31, nTimeOut);
if (!w_bRet)
return false;
w_nTime = SystemClock.elapsedRealtime();
w_bRet = UsbBulkReceive(pData, nDataLen, nTimeOut);
w_nTime = SystemClock.elapsedRealtime() - w_nTime;
//Toast.makeText(mApplicationContext, "UsbSCSIRead, UsbBulkReceive Time : " + w_nTime , Toast.LENGTH_SHORT).show();
if (!w_bRet)
return false;
// receive csw
w_bRet = UsbBulkReceive(w_abyCSW, 13, nTimeOut);
return w_bRet;
}
private boolean UsbBulkSend(byte[] pBuf, int nLen, int nTimeOut)
{
int i, n, r, w_nRet;
//byte[] w_abyTmp = new byte[m_nEPOutSize];
n = nLen / m_nEPOutSize;
r = nLen % m_nEPOutSize;
for(i=0; i<n; i++)
{
System.arraycopy(pBuf, i*m_nEPOutSize, m_abyTransferBuf, 0, m_nEPOutSize);
w_nRet = m_usbConn.bulkTransfer(m_epOUT, m_abyTransferBuf, m_nEPOutSize, nTimeOut);
if (w_nRet != m_nEPOutSize)
return false;
}
if (r > 0)
{
System.arraycopy(pBuf, i*m_nEPOutSize, m_abyTransferBuf, 0, r);
w_nRet = m_usbConn.bulkTransfer(m_epOUT, m_abyTransferBuf, r, nTimeOut);
return w_nRet == r;
}
return true;
}
private boolean UsbBulkReceive(byte[] pBuf, int nLen, int nTimeOut)
{
int i, n, r, w_nRet;
//byte[] w_abyTmp = new byte[m_nEPInSize];
//w_nRet = m_usbConn.bulkTransfer(m_epIN, pBuf, nLen, nTimeOut);
//if (w_nRet != nLen)
// return false;
n = nLen / m_nEPInSize;
r = nLen % m_nEPInSize;
//Toast.makeText(mApplicationContext, "UsbBulkReceive, Buf Len = " + pBuf.length, Toast.LENGTH_SHORT).show();
for(i=0; i<n; i++)
{
w_nRet = m_usbConn.bulkTransfer(m_epIN, m_abyTransferBuf, m_nEPInSize, nTimeOut);
if (w_nRet != m_nEPInSize)
return false;
System.arraycopy(m_abyTransferBuf, 0, pBuf, i*m_nEPInSize, m_nEPInSize);
}
if (r > 0)
{
w_nRet = m_usbConn.bulkTransfer(m_epIN, m_abyTransferBuf, r, nTimeOut);
if (w_nRet != r)
return false;
System.arraycopy(m_abyTransferBuf, 0, pBuf, i*m_nEPInSize, r);
}
return true;
}
// END MAIN LOOP
private final BroadcastReceiver mPermissionReceiver = new PermissionReceiver(
new IPermissionListener() {
@Override
public void onPermissionDenied(UsbDevice d) {
l("Permission denied on " + d.getDeviceId());
}
});
private interface IPermissionListener {
void onPermissionDenied(UsbDevice d);
}
public final static String TAG = "USBController";
private void l(Object msg) {
Log.d(TAG, ">==< " + msg.toString() + " >==<");
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resource>
<usb-device product-id="29987" vendor-id="6790" />
<usb-device product-id="21795" vendor-id="6790" />
<usb-device product-id="30264" vendor-id="8201" />
</resource>

View File

@@ -0,0 +1,29 @@
package com.xiarui.zhiwen;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import io.flutter.plugin.common.MethodCall;
import io.flutter.plugin.common.MethodChannel;
import org.junit.Test;
/**
* This demonstrates a simple unit test of the Java portion of this plugin's implementation.
*
* Once you have built the plugin's example app, you can run these tests from the command
* line by running `./gradlew testDebugUnitTest` in the `example/android/` directory, or
* you can run them directly from IDEs that support JUnit such as Android Studio.
*/
public class ZhiwenPluginTest {
@Test
public void onMethodCall_getPlatformVersion_returnsExpectedValue() {
ZhiwenPlugin plugin = new ZhiwenPlugin();
final MethodCall call = new MethodCall("getPlatformVersion", null);
MethodChannel.Result mockResult = mock(MethodChannel.Result.class);
plugin.onMethodCall(call, mockResult);
verify(mockResult).success("Android " + android.os.Build.VERSION.RELEASE);
}
}

43
example/.gitignore vendored Normal file
View File

@@ -0,0 +1,43 @@
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.buildlog/
.history
.svn/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release

16
example/README.md Normal file
View File

@@ -0,0 +1,16 @@
# zhiwen_example
Demonstrates how to use the zhiwen plugin.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.

View File

@@ -0,0 +1,28 @@
# 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.dev/lints.
#
# 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

13
example/android/.gitignore vendored Normal file
View File

@@ -0,0 +1,13 @@
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

View File

@@ -0,0 +1,58 @@
plugins {
id "com.android.application"
id "kotlin-android"
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
id "dev.flutter.flutter-gradle-plugin"
}
def localProperties = new Properties()
def localPropertiesFile = rootProject.file("local.properties")
if (localPropertiesFile.exists()) {
localPropertiesFile.withReader("UTF-8") { reader ->
localProperties.load(reader)
}
}
def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
if (flutterVersionCode == null) {
flutterVersionCode = "1"
}
def flutterVersionName = localProperties.getProperty("flutter.versionName")
if (flutterVersionName == null) {
flutterVersionName = "1.0"
}
android {
namespace = "com.xiarui.zhiwen_example"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.xiarui.zhiwen_example"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
minSdk = flutter.minSdkVersion
targetSdk = flutter.targetSdkVersion
versionCode = flutterVersionCode.toInteger()
versionName = flutterVersionName
}
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
}
}
}
flutter {
source = "../.."
}

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool 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>

View File

@@ -0,0 +1,57 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:label="zhiwen_example"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues
to determine the Window background behind the Flutter UI. -->
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED" />
</intent-filter>
<meta-data android:name="android.hardware.usb.action.USB_DEVICE_ATTACHED"
android:resource="@xml/device_filter" />
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<!-- Required to query activities that can process text, see:
https://developer.android.com/training/package-visibility and
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
</queries>
</manifest>

View File

@@ -0,0 +1,6 @@
package com.xiarui.zhiwen_example;
import io.flutter.embedding.android.FlutterActivity;
public class MainActivity extends FlutterActivity {
}

View File

@@ -0,0 +1,12 @@
<?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>

View File

@@ -0,0 +1,12 @@
<?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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 442 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 721 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -0,0 +1,18 @@
<?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
the Flutter engine 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>

View File

@@ -0,0 +1,18 @@
<?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 off -->
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
<!-- Show a splash screen on the activity. Automatically removed when
the Flutter engine 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.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<resource>
<usb-device product-id="29987" vendor-id="6790" />
<usb-device product-id="21795" vendor-id="6790" />
<usb-device product-id="30264" vendor-id="8201" />
</resource>

View File

@@ -0,0 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool 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>

View File

@@ -0,0 +1,18 @@
allprojects {
repositories {
google()
mavenCentral()
}
}
rootProject.buildDir = "../build"
subprojects {
project.buildDir = "${rootProject.buildDir}/${project.name}"
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register("clean", Delete) {
delete rootProject.buildDir
}

View File

@@ -0,0 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true

View File

@@ -0,0 +1,5 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.6.3-all.zip

View File

@@ -0,0 +1,25 @@
pluginManagement {
def flutterSdkPath = {
def properties = new Properties()
file("local.properties").withInputStream { properties.load(it) }
def flutterSdkPath = properties.getProperty("flutter.sdk")
assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
return flutterSdkPath
}()
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "7.3.0" apply false
id "org.jetbrains.kotlin.android" version "1.7.10" apply false
}
include ":app"

View File

@@ -0,0 +1,25 @@
// This is a basic Flutter integration test.
//
// Since integration tests run in a full Flutter application, they can interact
// with the host side of a plugin implementation, unlike Dart unit tests.
//
// For more information about Flutter integration tests, please see
// https://docs.flutter.dev/cookbook/testing/integration/introduction
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:zhiwen/zhiwen.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
testWidgets('getPlatformVersion test', (WidgetTester tester) async {
final Zhiwen plugin = Zhiwen();
final String? version = await plugin.getPlatformVersion();
// The version string depends on the host platform running the test, so
// just assert that some non-empty string is returned.
expect(version?.isNotEmpty, true);
});
}

130
example/lib/main.dart Normal file
View File

@@ -0,0 +1,130 @@
import 'dart:math';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:zhiwen/zhiwen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
final _zhiwenPlugin = Zhiwen();
@override
void initState() {
super.initState();
initPlatformState();
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> initPlatformState() async {
String platformVersion;
// Platform messages may fail, so we use a try/catch PlatformException.
// We also handle the message potentially returning null.
try {
platformVersion =
await _zhiwenPlugin.getPlatformVersion() ?? 'Unknown platform version';
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// If the widget was removed from the tree while the asynchronous platform
// message was in flight, we want to discard the reply rather than calling
// setState to update our non-existent appearance.
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
@override
Widget build(BuildContext context) {
return ScaffoldMessenger(
child: MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('Plugin example app'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Running on: $_platformVersion\n'),
ElevatedButton(
onPressed: () async {
bool result = await _zhiwenPlugin.openDevice("USB", 9600);
},
child: const Text('打开设备'),
),
ElevatedButton(
onPressed: () async {
await _zhiwenPlugin.closeDevice();
},
child: const Text('关闭设备'),
),
ElevatedButton(
onPressed: () async {
var random = Random();
int randomNumber = random.nextInt(100); // 生成 0 到 99 的随机整数
Map<String, dynamic> result = await _zhiwenPlugin.enrollFingerprint(randomNumber);
print(result);
},
child: const Text('录入指纹'),
),
ElevatedButton(
onPressed: () async {
int userId = 24;
Map<String, dynamic> result = await _zhiwenPlugin.verifyFingerprint(userId);
print(result);
},
child: const Text('验证指纹'),
),
ElevatedButton(
onPressed: () async {
Map<String, dynamic> result = await _zhiwenPlugin.deleteAllFingerprint();
print(result);
},
child: const Text('删除所有指纹'),
),
ElevatedButton(
onPressed: () async {
int userId = 2;
Map<String, dynamic> result = await _zhiwenPlugin.deleteOneFingerprint(userId);
print(result);
},
child: const Text('删除单个指纹'),
),
ElevatedButton(
onPressed: () async {
Map<String, dynamic> result = await _zhiwenPlugin.identifyFingerprint();
print(result);
},
child: const Text('识别指纹'),
),
ElevatedButton(
onPressed: () async {
Map<String, dynamic> result = await _zhiwenPlugin.getUserCount();
print(result);
},
child: const Text('获取指纹总数'),
),
],
),
),
),
));
}
}

283
example/pubspec.lock Normal file
View File

@@ -0,0 +1,283 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
url: "https://pub.dev"
source: hosted
version: "2.11.0"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
url: "https://pub.dev"
source: hosted
version: "2.1.1"
characters:
dependency: transitive
description:
name: characters
sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
url: "https://pub.dev"
source: hosted
version: "1.3.0"
clock:
dependency: transitive
description:
name: clock
sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
url: "https://pub.dev"
source: hosted
version: "1.1.1"
collection:
dependency: transitive
description:
name: collection
sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
url: "https://pub.dev"
source: hosted
version: "1.18.0"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.dev"
source: hosted
version: "1.0.8"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
file:
dependency: transitive
description:
name: file
sha256: "5fc22d7c25582e38ad9a8515372cd9a93834027aacf1801cf01164dac0ffa08c"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_driver:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
integration_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
url: "https://pub.dev"
source: hosted
version: "10.0.4"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
url: "https://pub.dev"
source: hosted
version: "3.0.1"
lints:
dependency: transitive
description:
name: lints
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
url: "https://pub.dev"
source: hosted
version: "3.0.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
url: "https://pub.dev"
source: hosted
version: "0.12.16+1"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
url: "https://pub.dev"
source: hosted
version: "0.8.0"
meta:
dependency: transitive
description:
name: meta
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
url: "https://pub.dev"
source: hosted
version: "1.12.0"
path:
dependency: transitive
description:
name: path
sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
url: "https://pub.dev"
source: hosted
version: "1.9.0"
platform:
dependency: transitive
description:
name: platform
sha256: "12220bb4b65720483f8fa9450b4332347737cf8213dd2840d8b2c823e47243ec"
url: "https://pub.dev"
source: hosted
version: "3.1.4"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
process:
dependency: transitive
description:
name: process
sha256: "21e54fd2faf1b5bdd5102afd25012184a6793927648ea81eea80552ac9405b32"
url: "https://pub.dev"
source: hosted
version: "5.0.2"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.99"
source_span:
dependency: transitive
description:
name: source_span
sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
url: "https://pub.dev"
source: hosted
version: "1.10.0"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
url: "https://pub.dev"
source: hosted
version: "1.11.1"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
url: "https://pub.dev"
source: hosted
version: "2.1.2"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
url: "https://pub.dev"
source: hosted
version: "1.2.0"
sync_http:
dependency: transitive
description:
name: sync_http
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
url: "https://pub.dev"
source: hosted
version: "0.3.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
url: "https://pub.dev"
source: hosted
version: "1.2.1"
test_api:
dependency: transitive
description:
name: test_api
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
url: "https://pub.dev"
source: hosted
version: "0.7.0"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
url: "https://pub.dev"
source: hosted
version: "14.2.1"
webdriver:
dependency: transitive
description:
name: webdriver
sha256: "003d7da9519e1e5f329422b36c4dcdf18d7d2978d1ba099ea4e45ba490ed845e"
url: "https://pub.dev"
source: hosted
version: "3.0.3"
zhiwen:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "0.0.1"
sdks:
dart: ">=3.4.3 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"

85
example/pubspec.yaml Normal file
View File

@@ -0,0 +1,85 @@
name: zhiwen_example
description: "Demonstrates how to use the zhiwen plugin."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
environment:
sdk: '>=3.4.3 <4.0.0'
# Dependencies specify other packages that your package needs in order to work.
# To automatically upgrade your package dependencies to the latest versions
# consider running `flutter pub upgrade --major-versions`. Alternatively,
# dependencies can be manually updated by changing the version numbers below to
# the latest version available on pub.dev. To see which dependencies have newer
# versions available, run `flutter pub outdated`.
dependencies:
flutter:
sdk: flutter
zhiwen:
# When depending on this package from a real application you should use:
# zhiwen: ^x.y.z
# See https://dart.dev/tools/pub/dependencies#version-constraints
# The example app is bundled with the plugin so we use a path dependency on
# the parent directory to use the current plugin's version.
path: ../
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.6
dev_dependencies:
integration_test:
sdk: flutter
flutter_test:
sdk: flutter
# The "flutter_lints" package below contains a set of recommended lints to
# encourage good coding practices. The lint set provided by the package is
# activated in the `analysis_options.yaml` file located at the root of your
# package. See that file for information about deactivating specific lint
# rules and activating additional ones.
flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
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:
# - images/a_dot_burr.jpeg
# - 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

View File

@@ -0,0 +1,27 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility in the flutter_test package. 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:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zhiwen_example/main.dart';
void main() {
testWidgets('Verify Platform version', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(const MyApp());
// Verify that platform version is retrieved.
expect(
find.byWidgetPredicate(
(Widget widget) => widget is Text &&
widget.data!.startsWith('Running on:'),
),
findsOneWidget,
);
});
}

40
lib/zhiwen.dart Normal file
View File

@@ -0,0 +1,40 @@
import 'zhiwen_platform_interface.dart';
class Zhiwen {
Future<String?> getPlatformVersion() {
return ZhiwenPlatform.instance.getPlatformVersion();
}
Future<bool> openDevice(String devicePath, int baudRate) {
return ZhiwenPlatform.instance.openDevice(devicePath, baudRate);
}
Future<void> closeDevice() {
return ZhiwenPlatform.instance.closeDevice();
}
Future<Map<String, dynamic>> enrollFingerprint(int userId) {
return ZhiwenPlatform.instance.enrollFingerprint(userId);
}
Future<Map<String, dynamic>> verifyFingerprint(int userId) {
return ZhiwenPlatform.instance.verifyFingerprint(userId);
}
Future<Map<String, dynamic>> deleteAllFingerprint() {
return ZhiwenPlatform.instance.deleteAllFingerprint();
}
Future<Map<String, dynamic>> deleteOneFingerprint(int userId) {
return ZhiwenPlatform.instance.deleteOneFingerprint(userId);
}
Future<Map<String, dynamic>> identifyFingerprint() {
return ZhiwenPlatform.instance.identifyFingerprint();
}
Future<Map<String, dynamic>> getUserCount() {
return ZhiwenPlatform.instance.getUserCount();
}
}

View File

@@ -0,0 +1,67 @@
import 'package:flutter/foundation.dart';
import 'package:flutter/services.dart';
import 'zhiwen_platform_interface.dart';
/// An implementation of [ZhiwenPlatform] that uses method channels.
class MethodChannelZhiwen extends ZhiwenPlatform {
/// The method channel used to interact with the native platform.
@visibleForTesting
final methodChannel = const MethodChannel('zhiwen');
@override
Future<String?> getPlatformVersion() async {
final version = await methodChannel.invokeMethod<String>('getPlatformVersion');
return version;
}
@override
Future<bool> openDevice(String devicePath, int baudRate) async {
final result = await methodChannel.invokeMethod<bool>(
'openDevice',
{'devicePath': devicePath, 'baudRate': baudRate},
);
return result ?? false;
}
@override
Future<void> closeDevice() async {
await methodChannel.invokeMethod('closeDevice');
}
@override
Future<Map<String, dynamic>> enrollFingerprint(int userId) async {
final result = await methodChannel.invokeMethod('enrollFingerprint', {'userId': userId});
return Map<String, dynamic>.from(result as Map);
}
@override
Future<Map<String, dynamic>> verifyFingerprint(int userId) async {
final result = await methodChannel.invokeMethod('verifyFingerprint', {'userId': userId});
return Map<String, dynamic>.from(result as Map);
}
@override
Future<Map<String, dynamic>> deleteAllFingerprint() async {
final result = await methodChannel.invokeMethod('deleteAllFingerprint');
return Map<String, dynamic>.from(result as Map);
}
@override
Future<Map<String, dynamic>> deleteOneFingerprint(int userId) async {
final result = await methodChannel.invokeMethod('deleteOneFingerprint', {'userId': userId});
return Map<String, dynamic>.from(result as Map);
}
@override
Future<Map<String, dynamic>> identifyFingerprint() async {
final result = await methodChannel.invokeMethod('identifyFingerprint');
return Map<String, dynamic>.from(result as Map);
}
@override
Future<Map<String, dynamic>> getUserCount() async {
final result = await methodChannel.invokeMethod('getUserCount');
return Map<String, dynamic>.from(result as Map);
}
}

View File

@@ -0,0 +1,38 @@
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
import 'zhiwen_method_channel.dart';
abstract class ZhiwenPlatform extends PlatformInterface {
/// Constructs a ZhiwenPlatform.
ZhiwenPlatform() : super(token: _token);
static final Object _token = Object();
static ZhiwenPlatform _instance = MethodChannelZhiwen();
/// The default instance of [ZhiwenPlatform] to use.
///
/// Defaults to [MethodChannelZhiwen].
static ZhiwenPlatform get instance => _instance;
Future<String?> getPlatformVersion();
Future<bool> openDevice(String devicePath, int baudRate);
Future<void> closeDevice();
Future<Map<String, dynamic>> enrollFingerprint(int userId);
Future<Map<String, dynamic>> verifyFingerprint(int userId);
Future<Map<String, dynamic>> deleteAllFingerprint();
Future<Map<String, dynamic>> deleteOneFingerprint(int userId);
Future<Map<String, dynamic>> identifyFingerprint();
Future<Map<String, dynamic>> getUserCount();
/// Platform-specific implementations should set this with their own
/// platform-specific class that extends [ZhiwenPlatform] when
/// they register themselves.
static set instance(ZhiwenPlatform instance) {
PlatformInterface.verifyToken(instance, _token);
_instance = instance;
}
// Future<String?> getPlatformVersion() {
// throw UnimplementedError('platformVersion() has not been implemented.');
// }
}

70
pubspec.yaml Normal file
View File

@@ -0,0 +1,70 @@
name: zhiwen
description: "A new Flutter project."
version: 0.0.1
homepage:
environment:
sdk: '>=3.4.3 <4.0.0'
flutter: '>=3.3.0'
dependencies:
flutter:
sdk: flutter
plugin_platform_interface: ^2.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec
# The following section is specific to Flutter packages.
flutter:
# This section identifies this Flutter project as a plugin project.
# The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)
# which should be registered in the plugin registry. This is required for
# using method channels.
# The Android 'package' specifies package in which the registered class is.
# This is required for using method channels on Android.
# The 'ffiPlugin' specifies that native code should be built and bundled.
# This is required for using `dart:ffi`.
# All these are used by the tooling to maintain consistency when
# adding or updating assets for this project.
plugin:
platforms:
android:
package: com.xiarui.zhiwen
pluginClass: ZhiwenPlugin
# 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

View File

@@ -0,0 +1,27 @@
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:zhiwen/zhiwen_method_channel.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
MethodChannelZhiwen platform = MethodChannelZhiwen();
const MethodChannel channel = MethodChannel('zhiwen');
setUp(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
channel,
(MethodCall methodCall) async {
return '42';
},
);
});
tearDown(() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(channel, null);
});
test('getPlatformVersion', () async {
expect(await platform.getPlatformVersion(), '42');
});
}

29
test/zhiwen_test.dart Normal file
View File

@@ -0,0 +1,29 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:zhiwen/zhiwen.dart';
import 'package:zhiwen/zhiwen_platform_interface.dart';
import 'package:zhiwen/zhiwen_method_channel.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
class MockZhiwenPlatform
with MockPlatformInterfaceMixin
implements ZhiwenPlatform {
@override
Future<String?> getPlatformVersion() => Future.value('42');
}
void main() {
final ZhiwenPlatform initialPlatform = ZhiwenPlatform.instance;
test('$MethodChannelZhiwen is the default instance', () {
expect(initialPlatform, isInstanceOf<MethodChannelZhiwen>());
});
test('getPlatformVersion', () async {
Zhiwen zhiwenPlugin = Zhiwen();
MockZhiwenPlatform fakePlatform = MockZhiwenPlatform();
ZhiwenPlatform.instance = fakePlatform;
expect(await zhiwenPlugin.getPlatformVersion(), '42');
});
}