How to manage the life cycle in react native - react-native

I know when we need a nativeUI component we need override function
getName()and
createViewInstance(ThemedReactContext context) But when I use a dependence about map. The API need me use the component like this
#Override
protected void onDestroy() {
super.onDestroy();
//在activity执行onDestroy时执行mMapView.onDestroy(),
mMapView.onDestroy();
}
#Override
protected void onResume() {
super.onResume();
//在activity执行onResume时执行mMapView. onResume (),
mMapView.onResume();
}
#Override
protected void onPause() {
super.onPause();
//在activity执行onPause时执行mMapView. onPause (),
mMapView.onPause();
}
}
I override the function getetName()and
createViewInstance(ThemedReactContext context) like this
#Override
public String getName() {
return REACT_CLASS;
}
#Override
public MapView createViewInstance(ThemedReactContext context) {
final ThemedReactContext mapContext = context;
bdMapViewInstance = new MapView(context);
bdMapViewInstance.getMap().setOnMarkerClickListener(new BaiduMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
ShopResponseInfo shopResponseInfo = (ShopResponseInfo) marker.getExtraInfo().getSerializable(INDENTIFY);
if(shopResponseInfo != null){
String id = shopResponseInfo.getShop_id() + "";
String shop_name = shopResponseInfo.getShop_name() + "";
onReceiveNativeEvent(mapContext,bdMapViewInstance,id,shop_name);
}
return true;
}
});
return bdMapViewInstance;
}
Finally There are some performance question in my app. I don't know whether it is affect on my app's performance. And I don't know how to meet the official suggestion. I don't know how to control the life cycle of nativeUI component for android. Very Thanks.

You can listen to activity life cycle like this in your createViewInstance. You probably want to keep track of listeners and remove them appropriately based on you keep track of your instances.
LifecycleEventListener lifecycleEventListener = new LifecycleEventListener() {
#Override
public void onHostResume() {
}
#Override
public void onHostPause() {
}
#Override
public void onHostDestroy() {
}
};
getReactApplicationContext().addLifecycleEventListener(lifecycleEventListener);

An example of TestModule as explained in the docs of RN. PS Don't forget to use implements LifecycleEventListener
package com.testpackage;
import com.facebook.react.bridge.Callback;
import com.facebook.react.bridge.NativeModule;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.LifecycleEventListener;
import java.util.Map;
import java.util.HashMap;
public class TestModule extends ReactContextBaseJavaModule implements LifecycleEventListener {
public TestModule(ReactApplicationContext reactContext) {
super(reactContext);
reactContext.addLifecycleEventListener(this);
}
#Override
public void onHostResume() {
}
#Override
public void onHostPause() {
}
#Override
public void onHostDestroy() {
}
}

Related

Jackson-Serialiser: Ignore Field at Serialisation Time

My situation asks for a bit more complex serialisation. I have a class Available (this is a very simplified snippet):
public class Available<T> {
private T value;
private boolean available;
...
}
So a POJO
class Tmp {
private Available<Integer> myInt = Available.of(123);
private Available<Integer> otherInt = Available.clean();
...
}
would normally result in
{"myInt":{available:true,value:123},"otherInt":{available:false,value:null}}
However, I want a serialiser to render the same POJO like this:
{"myInt":123}
What I have now:
public class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
if (available != null && available.isAvailable()) {
jsonGenerator.writeObject(available.getValue());
}
// MISSING: nothing at all should be rendered here for the field
}
#Override
public Class<Available<?>> handledType() {
#SuppressWarnings({ "unchecked", "rawtypes" })
Class<Available<?>> clazz = (Class) Available.class;
return clazz;
}
}
A test
#Test
public void testSerialize() throws Exception {
SimpleModule module = new SimpleModule().addSerializer(new AvailableSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
outputs
{"myInt":123,"otherInt"}
Can anyone tell me how to do the "MISSING"-stuff? Or if I'm doing it all wrong, how do I do it then?
The restriction I have is that I don't want the developers to add #Json...-annotations all the time to fields of type Available. So the Tmp-class above is an example of what a typical using class should look like. If that's possible...
Include.NON_DEFAULT
If we assume that your clean method is implemented in this way:
class Available<T> {
public static final Available<Object> EMPTY = clean();
//....
#SuppressWarnings("unchecked")
static <T> Available<T> clean() {
return (Available<T>) EMPTY;
}
}
You can set serialisation inclusion to JsonInclude.Include.NON_DEFAULT value and it should skip values set to EMPTY (default) values. See below example:
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import java.io.IOException;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(new AvailableSerializer());
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_DEFAULT);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
}
class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeObject(value.getValue());
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public Class<Available<?>> handledType() {
return (Class) Available.class;
}
}
Above code prints:
{"myInt":123}
Custom BeanPropertyWriter
If you do not want to use Include.NON_DEFAULT you can write your custom BeanPropertyWriter and skip all values you want. See below example:
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.BeanDescription;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationConfig;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.BeanPropertyWriter;
import com.fasterxml.jackson.databind.ser.BeanSerializerModifier;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
public class JsonApp {
public static void main(String[] args) throws Exception {
SimpleModule module = new SimpleModule();
module.addSerializer(new AvailableSerializer());
module.setSerializerModifier(new BeanSerializerModifier() {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
List<BeanPropertyWriter> writers = new ArrayList<>(beanProperties.size());
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getType().getRawClass() == Available.class) {
writer = new SkipNotAvailableBeanPropertyWriter(writer);
}
writers.add(writer);
}
return writers;
}
});
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(module);
System.out.println(objectMapper.writeValueAsString(new Tmp()));
}
}
class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> value, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException {
jsonGenerator.writeObject(value.getValue());
}
#Override
#SuppressWarnings({"unchecked", "rawtypes"})
public Class<Available<?>> handledType() {
return (Class) Available.class;
}
}
class SkipNotAvailableBeanPropertyWriter extends BeanPropertyWriter {
SkipNotAvailableBeanPropertyWriter(BeanPropertyWriter base) {
super(base);
}
#Override
public void serializeAsField(Object bean, JsonGenerator gen, SerializerProvider prov) throws Exception {
// copier from super.serializeAsField(bean, gen, prov);
final Object value = (_accessorMethod == null) ? _field.get(bean) : _accessorMethod.invoke(bean, (Object[]) null);
if (value == null || value instanceof Available && !((Available) value).isAvailable()) {
return;
}
super.serializeAsField(bean, gen, prov);
}
}
Above code prints:
{"myInt":123}
After Michał Ziober's answer I had to look for something regarding Include.NON_DEFAULT and the default object and ran into this answer explaining Include.NON_EMPTY that Google didn't return in my first research (thanks Google).
So things become easier, it's now:
public class AvailableSerializer extends JsonSerializer<Available<?>> {
#Override
public void serialize(Available<?> available, JsonGenerator jsonGenerator, SerializerProvider provider) throws IOException, JsonProcessingException {
jsonGenerator.writeObject(available.getValue());
}
#Override
public Class<Available<?>> handledType() {
#SuppressWarnings({ "unchecked", "rawtypes" })
Class<Available<?>> clazz = (Class) Available.class;
return clazz;
}
#Override
public boolean isEmpty(SerializerProvider provider, Available<?> value) {
return value == null || !value.isAvailable();
}
}
with the test
#Test
public void testSerialize() throws Exception {
SimpleModule module = new SimpleModule().addSerializer(availableSerializer);
objectMapper.registerModule(module);
objectMapper.configOverride(Available.class).setInclude(
// the call comes from JavaDoc of objectMapper.setSerializationInclusion(...)
JsonInclude.Value.construct(JsonInclude.Include.NON_EMPTY, JsonInclude.Include.ALWAYS));
Tmp tmp = new Tmp();
assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123}");
tmp.otherInt.setValue(123);
assertThat(objectMapper.writeValueAsString(tmp)).isEqualTo("{\"myInt\":123,\"otherInt\":123}");
}
So please, if you upvote my answer please also upvote Michał Ziober's as that's also working with a mildly different approach.

Android volley singleton for JSON and image

Intially i was using volley mainly for JSONObject. the following was my singleton
package com.simha.yatras;
import android.app.Application;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.toolbox.Volley;
public class MyApplication extends Application {
private RequestQueue mRequestQueue;
private static MyApplication mInstance;
#Override
public void onCreate() {
super.onCreate();
mInstance = this;
}
public static synchronized MyApplication getInstance() {
return mInstance;
}
public RequestQueue getReqQueue() {
if (mRequestQueue == null) {
mRequestQueue = Volley.newRequestQueue(getApplicationContext());
}
return mRequestQueue;
}
public <T> void addToReqQueue(Request<T> req, String tag) {
getReqQueue().add(req);
}
public <T> void addToReqQueue(Request<T> req) {
getReqQueue().add(req);
}
public void cancelPendingReq(Object tag) {
if (mRequestQueue != null) {
mRequestQueue.cancelAll(tag);
}
}
}
Now i want to use volley for bitmap imagerequest. I want the images to be cached so that i need not load them everytime.
So what should be the singleton code be.
You can use Volley provide ImageRequest class:
ImageView mImageView;
String url = "http://i.imgur.com/7spzG.png";
mImageView = (ImageView) findViewById(R.id.myImage);
// Retrieves an image specified by the URL, displays it in the UI.
ImageRequest request = new ImageRequest(url,
new Response.Listener<Bitmap>() {
#Override
public void onResponse(Bitmap bitmap) {
mImageView.setImageBitmap(bitmap);
}
}, 0, 0, null,
new Response.ErrorListener() {
public void onErrorResponse(VolleyError error) {
mImageView.setImageResource(R.drawable.image_load_error);
}
});
// Access the RequestQueue through your singleton class.
MySingleton.getInstance(this).addToRequestQueue(request);## Heading ##

Intellij plugin development, how to prevent an action from occuring, like closing a tab

Is this possible?
I need to subscribe to the event somehow and possibly return false or similar, i am guessing. I have no idea how though.
What event is that?
Where do I register it?
Anyone?
EDIT:
I have tried this:
import com.intellij.openapi.components.ApplicationComponent;
import com.intellij.openapi.editor.impl.EditorComponentImpl;
import org.jetbrains.annotations.NotNull;
import java.awt.*;
import java.awt.event.AWTEventListener;
import java.awt.event.KeyEvent;
public class MyPlugin implements ApplicationComponent {
static {
/*MessageBus bus = ApplicationManager.getApplication().get
MessageBusConnection connection = bus.connect();
connection.subscribe(AppTopics.FILE_DOCUMENT_SYNC,
new FileDocumentManagerAdapter() {
#Override
public void beforeDocumentSaving(Document document) {
// create your custom logic here
}
});*/
}
private final AWTEventListener listener;
public MyPlugin() {
System.out.println("111111111111111111");
listener = new AWTEventListener() {
#Override
public void eventDispatched(AWTEvent event) {
if ( event instanceof KeyEvent ) {
KeyEvent kv = (KeyEvent) event;
Component component = kv.getComponent();
if ( component instanceof EditorComponentImpl) {
EditorComponentImpl cp = (EditorComponentImpl) component;
}
System.out.println("3333333" + component.getClass());
}
System.out.println("aaaaaaa" + event.getClass());
}
};
}
#Override
public void initComponent() {
System.out.println("bbbbbbbbbbbbbbbbbbbbbbbbbbb");
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.KEY_EVENT_MASK);
}
#Override
public void disposeComponent() {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener);
}
#NotNull
#Override
public String getComponentName() {
return "temp";
}
}
But it does not work. I get events but the wrong kind.
Two plugins were developed in the end to accomplish this:
https://plugins.jetbrains.com/space/index?pr=idea&lg=opensource%40momomo.com

custom colorpicker not working in android

I have made a Simple drawing application in android for the learning purpose..In that i have taken diffrent colorbuttons just like a colorpicker in horizontalscrollview,Now i need is when one of them is clicked that particular color should be chosen and pencolor od drawing pen should be changed..I have tried as below,but its not working..Please help me for the same,Thanx in advance...!
main.java
public void onClick(View v) {
switch (v.getId()) {
case R.id.black:
myplate.setVisibility(View.GONE);
mDrawView.setColor(SingleTouchView.DrawingColors.Black);
break;
case R.id.blue:
myplate.setVisibility(View.GONE);
mDrawView.setColor(SingleTouchView.DrawingColors.Blue);
break;
...so on...for other colors
MyView.java
package com.example.singletouch;
import java.util.AbstractMap;
import java.util.Map;
import java.util.concurrent.ConcurrentLinkedQueue;
import android.R.color;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.Switch;
import android.widget.Toast;
public class SingleTouchView extends View {
public static int width;
public int height;
public Bitmap mBitmap;
public Canvas mCanvas;
public Path mPath;
public Paint mBitmapPaint;
Context context;
public Paint mPaint;
public Paint circlePaint;
public Path circlePath;
public enum DrawingPens {
PEN_1(6), PEN_2(4), PEN_3(2), PEN_4(1);
public Paint mPaint;
private DrawingPens(final int width) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(width);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
}
Paint getPaint() {
return mPaint;
}
}
public enum DrawingColors{
Black(Color.parseColor("#000000")),Blue(Color.parseColor("#0000FF")),Cofee(Color.parseColor("#D2691E")),Cyan(Color.parseColor("#00FFFF"))
,Fuchiya(Color.parseColor("#FF00FF")),Gray(Color.parseColor("#808080")),Green(Color.parseColor("#00FF00")),Indigo(Color.parseColor("#4B0082")),
Khaki(Color.parseColor("#F0E68C")),Lavendar(Color.parseColor("#E6E6FA")),Magenta(Color.parseColor("#FF00FF")),Mango(Color.parseColor("#FF8C00"))
,Maroon(Color.parseColor("#800000")),Orange(Color.parseColor("#FFA500")),Pink(Color.parseColor("#FFC0CB")),Pista(Color.parseColor("#9ACD32")),
Purple(Color.parseColor("#800080")),Red(Color.parseColor("#FF0000")),Tan(Color.parseColor("#0000A0")),Yellow(Color.parseColor("#FFD801"));
public Paint mPaint;
private DrawingColors(final int color) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStrokeWidth(width);
mPaint.setColor(color);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
}
Paint getPaint() {
return mPaint;
}
}
public SingleTouchView(final Context context) {
super(context);
init(context);
}
public SingleTouchView(final Context context, final AttributeSet attrs) {
super(context, attrs);
init(context);
mBitmap = Bitmap.createBitmap(400, 400, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mPath = new Path();
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setColor(0xFFFF0000);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeJoin(Paint.Join.ROUND);
mPaint.setStrokeCap(Paint.Cap.ROUND);
mPaint.setStrokeWidth(12);
}
public SingleTouchView(final Context context, final AttributeSet attrs,
final int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private ConcurrentLinkedQueue<Map.Entry<Path, DrawingPens>> mPaths = new ConcurrentLinkedQueue<Map.Entry<Path, DrawingPens>>();
private ConcurrentLinkedQueue<Map.Entry<Path, DrawingColors>> mPaths1 = new ConcurrentLinkedQueue<Map.Entry<Path, DrawingColors>>();
private Path mCurrentPath;
private void init(final Context context) {
setPen(DrawingPens.PEN_1);
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (Map.Entry<Path, DrawingPens> entry : mPaths) {
canvas.drawPath(entry.getKey(), entry.getValue().getPaint());
}
}
#Override
public boolean onTouchEvent(MotionEvent me) {
float eventX = me.getX();
float eventY = me.getY();
switch (me.getAction()) {
case MotionEvent.ACTION_DOWN:
mCurrentPath.moveTo(eventX, eventY);
return true;
case MotionEvent.ACTION_MOVE:
mCurrentPath.lineTo(eventX, eventY);
break;
case MotionEvent.ACTION_UP:
break;
}
invalidate();
return true;
}
public void setPen(final DrawingPens pen) {
mCurrentPath = new Path();
mPaths.add(new AbstractMap.SimpleImmutableEntry<Path, DrawingPens>(
mCurrentPath, pen));
}
public void eraser() {
// TODO Auto-generated method stub
mPaint = new Paint();
/* Toast.makeText(getContext(), "eraser", Toast.LENGTH_LONG).show();
mPaint.setXfermode(null);
mPaint.setAlpha(0x00FFFFFF);
mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));*/
// invalidate();
}
public void setColor(final DrawingColors color ) {
mCurrentPath = new Path();
mPaths1.add(new AbstractMap.SimpleImmutableEntry<Path, DrawingColors>(
mCurrentPath, color));
}
}
Please help me friends..please...
still a bit unclear but I will try to give you a direction. What happens if you try the below onDraw method? I have a feeling you have not set the colors. The code is a bit messy and not clear to read. Don't worry for now about the performance, creating a new paint every time, just want to make sure it give's you the wanted result.
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
Paint paint = new Paint();
paint.setColor(Color.BLACK);
for (Map.Entry<Path, DrawingPens> entry : mPaths) {
canvas.drawPath(entry.getKey(), paint);
}
}

USB/Serial communication with Netty

In our organisation, we implemented our own protocol over UDP and TCP to let external devices connected to the Internet exchange messages with a server that we developed using Netty (indeed!).
For testing purpose, we would like to connect those devices directly to our computers through USB/serial interface (we did not choose the serial communication library yet). We would also like to deploy/port the embedded software we developed for our devices on our computer to simulate the devices and to connect directly to our server using a named pipe for example (IPC).
In the Architecture Overview documentation of Netty, you claim that we could use Netty as well for such serial communication:
"Also, you are even able to take advantage of new transports which aren't yet written (such as serial port communication transport), again by replacing just a couple lines of constructor calls. Moreover, you can write your own transport by extending the core API."
Is anyone somewhere already developed such implementation in Netty or does someone else plan to do such implementation? I am also wondering if Netty is really well-suited for that since the Channel interface and many other ones use a SocketAddress to bind/connect to a peer?
Thank you for your suggestions, advices!
I wonder if you may be able to use the new iostream package for that. All you need here is an InputStream and Outputstream. See [1]
[1] https://github.com/netty/netty/tree/master/transport/src/main/java/io/netty/channel/iostream
It is possible to implement such a solutions. I have not meet problems with binding with SocketAddress.
I’m posting my implementation of USB connection with Netty.
Serial communication is quite simillar, I'm not posting it for brevity. However I am happy to add it as well if anyone needs it.
Here is base class for connection. A ChannelHandler shall be implemented according to communication needs.
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelOutboundHandlerAdapter;
import io.netty.channel.ChannelPromise;
import java.net.SocketAddress;
public abstract class ConnectorImpl {
protected ChannelHandler handler;
protected Bootstrap bootstrap;
protected ChannelFuture channelFuture;
public ChannelFuture connect() throws Exception {
if (!isConnected()) {
channelFuture = bootstrap.connect(getSocketAddress()).sync();
}
return channelFuture.channel().closeFuture();
}
public boolean isConnected() {
try {
return channelFuture.channel().isOpen();
} catch (NullPointerException ex) {
return false;
}
}
public void close() {
if (!isConnected()) {
return;
}
try {
channelFuture.channel().close().sync();
} catch (InterruptedException e) {
}
}
protected ChannelOutboundHandlerAdapter createOutgoingErrorHandler() {
return new ChannelOutboundHandlerAdapter() {
#Override
public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) {
final ChannelFutureListener channelFutureListener = future -> {
if (!future.isSuccess()) {
future.channel().close();
}
};
promise.addListener(channelFutureListener);
ctx.write(msg, promise);
}
};
}
public abstract SocketAddress getSocketAddress();
}
An extensions of that connector for needed type of connection together with Channel implementations is needed.
USB connector:
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.handler.timeout.ReadTimeoutHandler;
import javax.usb.UsbDevice;
import java.net.SocketAddress;
import java.util.concurrent.TimeUnit;
public class UsbConnectorImpl extends ConnectorImpl {
private static final int READ_TIMEOUT = 60;
private final UsbDevice usbDevice;
public UsbConnectorImpl(UsbChannelHandler handler, UsbDevice usbDevice) {
this.handler = handler;
this.usbDevice = usbDevice;
this.bootstrap = new Bootstrap()
.channel(getChannelClass())
.group(getLoop())
.handler(getChannelInitializer());
}
public EventLoopGroup getLoop() {
return new NioEventLoopGroup(1);
}
Class<UsbAsyncChannel> getChannelClass() {
return UsbAsyncChannel.class;
}
ChannelInitializer<Channel> getChannelInitializer() {
return new ChannelInitializer<Channel>() {
#Override
public void initChannel(#SuppressWarnings("NullableProblems") Channel ch) {
ch.pipeline()
.addLast("Generic encoder", new RequestEncoder())
.addLast("Decoder", new ResponseDecoder())
.addLast("Read timeout handler", new ReadTimeoutHandler(READ_TIMEOUT, TimeUnit.SECONDS))
.addLast("Outgoing Error Handler", createOutgoingErrorHandler())
.addLast("Card Reader handler", handler);
}
};
}
public SocketAddress getSocketAddress() {
return new UsbDeviceAddress(usbDevice);
}
}
USB Channel:
import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.FileRegion;
import io.netty.channel.nio.AbstractNioByteChannel;
import org.usb4java.LibUsb;
import javax.usb.UsbConfiguration;
import javax.usb.UsbDevice;
import javax.usb.UsbEndpoint;
import javax.usb.UsbInterface;
import javax.usb.UsbPipe;
import javax.usb.event.UsbPipeDataEvent;
import javax.usb.event.UsbPipeErrorEvent;
import javax.usb.event.UsbPipeListener;
import java.io.IOException;
import java.net.SocketAddress;
import java.nio.channels.Pipe;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public abstract class UsbChannel extends AbstractNioByteChannel {
protected static final byte INTERFACE_BULK_PIPES = (byte) 1;
private static final AtomicInteger READ_TASK_COUNTER = new AtomicInteger();
private final UsbChannelConfig config;
protected UsbPipe outPipe = null;
protected UsbPipe inPipe = null;
private UsbDevice usbDevice;
private UsbDeviceAddress deviceAddress;
private UsbInterface usbInterface;
public UsbChannel() throws IOException {
super(null, Pipe.open().source());
config = new UsbChannelConfig(this);
}
#Override
public UsbChannelConfig config() {
return config;
}
#Override
public boolean isActive() {
return usbDevice != null;
}
#Override
protected ChannelFuture shutdownInput() {
try {
doClose();
} catch (Exception e) {
pipeline().fireExceptionCaught(e);
}
return null;
}
protected abstract ReadTask createReadTask();
protected void invokeRead() {
ReadTask task = createReadTask();
task.scheduledFuture = eventLoop().schedule(task, 0, TimeUnit.MILLISECONDS);
}
#Override
protected AbstractNioUnsafe newUnsafe() {
return new UsbUnsafe();
}
#Override
protected long doWriteFileRegion(FileRegion region) throws Exception {
throw new UnsupportedOperationException();
}
#Override
protected int doReadBytes(ByteBuf buf) throws Exception {
return 0;
}
#Override
protected Pipe.SourceChannel javaChannel() {
return (Pipe.SourceChannel) super.javaChannel();
}
#Override
protected boolean doConnect(SocketAddress remoteAddress, SocketAddress localAddress) throws Exception {
UsbDeviceAddress remote = (UsbDeviceAddress) remoteAddress;
usbDevice = remote.value();
UsbConfiguration configuration = usbDevice.getActiveUsbConfiguration();
usbInterface = configuration.getUsbInterface(INTERFACE_BULK_PIPES);
usbInterface = usbInterface.getActiveSetting();
usbInterface.claim();
for (int i = 0; i < usbInterface.getUsbEndpoints().size(); i++) {
UsbEndpoint endpoint = (UsbEndpoint) usbInterface.getUsbEndpoints().get(i);
UsbPipe usbPipe = endpoint.getUsbPipe();
if (endpoint.getDirection() == LibUsb.ENDPOINT_IN) {
inPipe = usbPipe;
inPipe.open();
} else if (endpoint.getDirection() == LibUsb.ENDPOINT_OUT) {
outPipe = usbPipe;
outPipe.open();
}
if (inPipe != null && outPipe != null) {
break;
}
}
outPipe.addUsbPipeListener(new UsbPipeListener() {
#Override
public void errorEventOccurred(UsbPipeErrorEvent event) {
pipeline().fireExceptionCaught(event.getUsbException());
}
#Override
public void dataEventOccurred(UsbPipeDataEvent event) {
invokeRead();
}
});
inPipe.addUsbPipeListener(new UsbPipeListener() {
#Override
public void errorEventOccurred(UsbPipeErrorEvent event) {
pipeline().fireExceptionCaught(event.getUsbException());
}
#Override
public void dataEventOccurred(UsbPipeDataEvent event) {
pipeline().fireChannelRead(Unpooled.wrappedBuffer(event.getData(), 0, event.getData().length));
}
});
deviceAddress = remote;
return true;
}
#Override
protected void doFinishConnect() throws Exception {
}
#Override
public UsbDeviceAddress localAddress() {
return (UsbDeviceAddress) super.localAddress();
}
#Override
public UsbDeviceAddress remoteAddress() {
return (UsbDeviceAddress) super.remoteAddress();
}
#Override
protected UsbDeviceAddress localAddress0() {
return deviceAddress;
}
#Override
protected UsbDeviceAddress remoteAddress0() {
return deviceAddress;
}
#Override
protected void doBind(SocketAddress localAddress) throws Exception {
throw new UnsupportedOperationException();
}
#Override
protected void doDisconnect() throws Exception {
doClose();
}
#Override
protected void doClose() throws Exception {
try {
super.doClose();
javaChannel().close();
} finally {
if (inPipe != null) {
inPipe.close();
inPipe = null;
}
if (outPipe != null) {
outPipe.close();
outPipe = null;
}
if (usbInterface != null) {
usbInterface.release();
usbInterface = null;
}
if (usbDevice != null) {
usbDevice = null;
}
}
}
protected abstract static class ReadTask implements Runnable, ChannelFutureListener {
protected final int id;
protected ScheduledFuture<?> scheduledFuture;
public ReadTask() {
this.id = READ_TASK_COUNTER.incrementAndGet();
}
}
private final class UsbUnsafe extends AbstractNioUnsafe {
#Override
public void read() {
}
}
}