OSDN Git Service

PTPIP勢の機能追加準備を開始。
authorMRSa <mrsa@myad.jp>
Mon, 27 Jan 2020 14:52:06 +0000 (23:52 +0900)
committerMRSa <mrsa@myad.jp>
Mon, 27 Jan 2020 14:52:06 +0000 (23:52 +0900)
37 files changed:
app/build.gradle
app/src/main/java/net/osdn/gokigen/a01d/IInformationReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/ICanonInterfaceProvider.java
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/PtpIpCommandCanonGetPartialObject.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonGetPartialObject.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonInitEventRequest.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRegistrationMessage.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopEnd.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopStart.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraConnectSequence.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraDisconnectSequence.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonConnection.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/INikonInterfaceProvider.java
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/command/messages/specific/NikonRegistrationMessage.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraConnectSequence.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraDisconnectSequence.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonConnection.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusChecker.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusHolder.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommand.java [moved from app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/command/IPtpIpCommand.java with 96% similarity]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommandCallback.java [moved from app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/command/IPtpIpCommandCallback.java with 77% similarity]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommandPublisher.java [moved from app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/command/IPtpIpCommandPublisher.java with 79% similarity]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommunication.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpMessages.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpAsyncResponseReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpCommandPublisher.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandBase.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandGeneric.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpReceiveOnly.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/specific/StatusRequestMessage.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpCameraProperties.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpInitEventRequest.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpRunModeHolder.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusChecker.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusHolder.java [new file with mode: 0644]
app/src/main/res/values-ja/strings.xml
app/src/main/res/values/strings.xml

index 66633cc..38250c1 100644 (file)
@@ -6,8 +6,8 @@ android {
         applicationId "net.osdn.gokigen.a01d"
         minSdkVersion 14
         targetSdkVersion 29
-        versionCode 10500
-        versionName "1.5.0"
+        versionCode 10600
+        versionName "1.6.0"
     }
     buildTypes {
         release {
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/IInformationReceiver.java b/app/src/main/java/net/osdn/gokigen/a01d/IInformationReceiver.java
new file mode 100644 (file)
index 0000000..2b465d6
--- /dev/null
@@ -0,0 +1,6 @@
+package net.osdn.gokigen.a01d;
+
+public interface IInformationReceiver
+{
+    void updateMessage(final String message, final boolean isBold, final boolean isColor,  final int color);
+}
index d0ba1b9..0177114 100644 (file)
@@ -3,6 +3,7 @@ package net.osdn.gokigen.a01d.camera.canon;
 
 import androidx.annotation.NonNull;
 
+import net.osdn.gokigen.a01d.IInformationReceiver;
 import net.osdn.gokigen.a01d.camera.ICameraConnection;
 import net.osdn.gokigen.a01d.camera.ICameraInformation;
 import net.osdn.gokigen.a01d.camera.ICameraStatus;
@@ -12,7 +13,10 @@ import net.osdn.gokigen.a01d.camera.IDisplayInjector;
 import net.osdn.gokigen.a01d.camera.IFocusingControl;
 import net.osdn.gokigen.a01d.camera.ILiveViewControl;
 import net.osdn.gokigen.a01d.camera.IZoomLensControl;
-import net.osdn.gokigen.a01d.camera.ptpip.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommunication;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.status.IPtpIpRunModeHolder;
 import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
 import net.osdn.gokigen.a01d.liveview.liveviewlistener.ILiveViewListener;
 
@@ -27,15 +31,13 @@ public interface ICanonInterfaceProvider
     ICaptureControl getCaptureControl();
     IDisplayInjector getDisplayInjector();
 
-    /*
-    IFujiXRunModeHolder getRunModeHolder();
-    IFujiXCommandCallback getStatusHolder();
-    IFujiXCommandPublisher getCommandPublisher();
-    IFujiXCommunication getLiveviewCommunication();
-    IFujiXCommunication getAsyncEventCommunication();
-    IFujiXCommunication getCommandCommunication();
-     */
-
+    IPtpIpRunModeHolder getRunModeHolder();
+    IPtpIpCommandCallback getStatusHolder();
+    IPtpIpCommandPublisher getCommandPublisher();
+    IPtpIpCommunication getLiveviewCommunication();
+    IPtpIpCommunication getAsyncEventCommunication();
+    IPtpIpCommunication getCommandCommunication();
+    IInformationReceiver getInformationReceiver();
 
     ICameraStatusWatcher getStatusWatcher();
     ICameraStatusUpdateNotify getStatusListener();
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/PtpIpCommandCanonGetPartialObject.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/PtpIpCommandCanonGetPartialObject.java
new file mode 100644 (file)
index 0000000..5c6894c
--- /dev/null
@@ -0,0 +1,115 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class PtpIpCommandCanonGetPartialObject extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final boolean isDumpLog;
+    private final int id;
+    private final int holdId;
+    private final int estimatedObjectSize;
+
+    private final byte data0;
+    private final byte data1;
+    private final byte data2;
+    private final byte data3;
+
+    private final byte data4;
+    private final byte data5;
+    private final byte data6;
+    private final byte data7;
+
+    private final byte data8;
+    private final byte data9;
+    private final byte dataA;
+    private final byte dataB;
+
+    public PtpIpCommandCanonGetPartialObject(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int value, int value2, int value3, int estimatedObjectSize)
+    {
+        this.callback = callback;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = estimatedObjectSize;
+
+        this.id = id;
+        this.holdId = holdId;
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = ((byte) (0x000000ff & value2));
+        data5 = ((byte)((0x0000ff00 & value2) >> 8));
+        data6 = ((byte)((0x00ff0000 & value2) >> 16));
+        data7 = ((byte)((0xff000000 & value2) >> 24));
+
+        data8 = ((byte) (0x000000ff & value3));
+        data9 = ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = ((byte)((0xff000000 & value3) >> 24));
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int estimatedReceiveDataSize()
+    {
+        return (estimatedObjectSize);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (20);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[]{
+                // packet type
+                (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                // data phase info
+                (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // operation code
+                (byte) 0x07,  (byte) 0x91,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data ...
+                data0, data1, data2, data3,
+                data4, data5, data6, data7,
+                data8, data9, dataA, dataB,
+        });
+    }
+
+    @Override
+    public int getHoldId()
+    {
+        return (holdId);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (isDumpLog);
+    }
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonGetPartialObject.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonGetPartialObject.java
new file mode 100644 (file)
index 0000000..e76ed07
--- /dev/null
@@ -0,0 +1,113 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific;
+
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class CanonGetPartialObject extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final boolean isDumpLog;
+    private final int id;
+    private final int holdId;
+
+    private final byte data00;
+    private final byte data01;
+    private final byte data02;
+    private final byte data03;
+
+    private final byte data10;
+    private final byte data11;
+    private final byte data12;
+    private final byte data13;
+
+    public CanonGetPartialObject(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int startPosition, int dataSize)
+    {
+        this.callback = callback;
+        this.isDumpLog = isDumpLog;
+        this.id = id;
+        this.holdId = holdId;
+
+        data00 = ((byte) (0x000000ff & startPosition));
+        data01 = ((byte)((0x0000ff00 & startPosition) >> 8));
+        data02 = ((byte)((0x00ff0000 & startPosition) >> 16));
+        data03 = ((byte)((0xff000000 & startPosition) >> 24));
+
+        data10 = ((byte) (0x000000ff & dataSize));
+        data11 = ((byte)((0x0000ff00 & dataSize) >> 8));
+        data12 = ((byte)((0x00ff0000 & dataSize) >> 16));
+        data13 = ((byte)((0xff000000 & dataSize) >> 24));
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (isDumpLog);
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (35);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[]{
+                // packet type
+                (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data phase info
+                (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // operation code
+                (byte) 0x07, (byte) 0x91,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // ??? (0x01)
+                (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data size
+                data00, data01, data02, data03,
+
+                // data size
+                data10, data11, data12, data13,
+        });
+    }
+
+    @Override
+    public int getHoldId()
+    {
+        return (holdId);
+    }
+
+    @Override
+    public boolean isHold()
+    {
+        return (true);
+    }
+
+    @Override
+    public boolean isRelease()
+    {
+        return (false);
+    }
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonInitEventRequest.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonInitEventRequest.java
new file mode 100644 (file)
index 0000000..2954498
--- /dev/null
@@ -0,0 +1,68 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class CanonInitEventRequest extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final int connectionNumber;
+
+    public CanonInitEventRequest(@NonNull IPtpIpCommandCallback callback, int connectionNumber)
+    {
+        this.callback = callback;
+        this.connectionNumber = connectionNumber;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (SEQ_EVENT_INITIALIZE);
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (100);
+    }
+
+    @Override
+    public boolean receiveAgainShortLengthMessage()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean useSequenceNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isIncrementSeqNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        byte data0 = ((byte) (0x000000ff & connectionNumber));
+        byte data1 = ((byte)((0x0000ff00 & connectionNumber) >> 8));
+        byte data2 = ((byte)((0x00ff0000 & connectionNumber) >> 16));
+        byte data3 = ((byte)((0xff000000 & connectionNumber) >> 24));
+
+        return (new byte[] {
+                // packet type
+                (byte)0x03, (byte)0x00, (byte)0x00, (byte)0x00, data0, data1, data2, data3,
+        });
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRegistrationMessage.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRegistrationMessage.java
new file mode 100644 (file)
index 0000000..cd84215
--- /dev/null
@@ -0,0 +1,84 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific;
+
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+public class CanonRegistrationMessage extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+
+    public CanonRegistrationMessage(@NonNull IPtpIpCommandCallback callback)
+    {
+        this.callback = callback;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (SEQ_REGISTRATION);
+    }
+
+    @Override
+    public boolean receiveAgainShortLengthMessage()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean useSequenceNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isIncrementSeqNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        int uuid = UUID.randomUUID().hashCode();
+
+        byte[] typeArray = {
+                (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00,
+        };
+
+        byte[] uuidArray = {
+                (byte)0xad, (byte)0xa5, (byte)0x48, (byte)0x5d, (byte)0x87, (byte)0xb2, (byte)0x7f, (byte)0x0b,
+                (byte)0xd3, (byte)0xd5, (byte)0xde, (byte)0xd0, (byte)0x12, (byte)0x44, (byte)0x99, (byte)0x32,
+        };
+
+        byte[] deviceNameArray = {
+                // device_name 'GOKIGEN_a01'
+                (byte)0x47, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x4b, (byte)0x00, (byte)0x49, (byte)0x00,
+                (byte)0x47, (byte)0x00, (byte)0x45, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x5f, (byte)0x00,
+                (byte)0x61, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x00, (byte)0x00,
+        };
+        byte[] versionArray = {
+                //
+                (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00,
+        };
+
+        ByteBuffer byteBuffer = ByteBuffer.allocate(4 + 16 + 24 + 4);
+        byteBuffer.put(typeArray);
+        byteBuffer.put(uuidArray);
+        byteBuffer.put(deviceNameArray);
+        byteBuffer.put(versionArray);
+
+        return (byteBuffer.array());
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopEnd.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopEnd.java
new file mode 100644 (file)
index 0000000..5c29c2e
--- /dev/null
@@ -0,0 +1,112 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class CanonRequestInnerDevelopEnd  extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final boolean isDumpLog;
+    private final int id;
+    private final int holdId;
+
+    public CanonRequestInnerDevelopEnd(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId)
+    {
+        this.callback = callback;
+        this.isDumpLog = isDumpLog;
+        this.id = id;
+        this.holdId = holdId;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (isDumpLog);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[]{
+                // packet type
+                (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data phase info
+                (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // operation code
+                (byte) 0x43, (byte) 0x91,
+
+                 // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // ???
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+
+    @Override
+    public byte[] commandBody2()
+    {
+        return (new byte[]{
+
+                // packet type
+                (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // データサイズ
+                (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+
+    @Override
+    public byte[] commandBody3()
+    {
+        return (new byte[]{
+
+                // packet type
+                (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // ????
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+    @Override
+    public int getHoldId()
+    {
+        return (holdId);
+    }
+
+    @Override
+    public boolean isHold()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isRelease()
+    {
+        return (true);
+    }
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopStart.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/command/messages/specific/CanonRequestInnerDevelopStart.java
new file mode 100644 (file)
index 0000000..ce1e093
--- /dev/null
@@ -0,0 +1,129 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific;
+
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class CanonRequestInnerDevelopStart  extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final boolean isDumpLog;
+    private final int id;
+    private final int holdId;
+
+    private final byte data0;
+    private final byte data1;
+    private final byte data2;
+    private final byte data3;
+
+    public CanonRequestInnerDevelopStart(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int objectId)
+    {
+        this.callback = callback;
+        this.isDumpLog = isDumpLog;
+        this.id = id;
+        this.holdId = holdId;
+
+        data0 = ((byte) (0x000000ff & objectId));
+        data1 = ((byte)((0x0000ff00 & objectId) >> 8));
+        data2 = ((byte)((0x00ff0000 & objectId) >> 16));
+        data3 = ((byte)((0xff000000 & objectId) >> 24));
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (isDumpLog);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[]{
+                // packet type
+                (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data phase info
+                (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // operation code
+                (byte) 0x41, (byte) 0x91,
+
+                 // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // object id
+                data0, data1, data2, data3,
+
+                // ???
+                (byte) 0x04, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+
+    @Override
+    public byte[] commandBody2()
+    {
+        return (new byte[]{
+
+                // packet type
+                (byte) 0x09, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // データサイズ
+                (byte) 0x08, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+
+    @Override
+    public byte[] commandBody3()
+    {
+        return (new byte[]{
+
+                // packet type
+                (byte) 0x0c, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // sequence number
+                (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                // data
+                (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+                (byte) 0x02, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+        });
+    }
+
+    @Override
+    public int getHoldId()
+    {
+        return (holdId);
+    }
+
+    @Override
+    public boolean isHold()
+    {
+        return (true);
+    }
+
+    @Override
+    public boolean isRelease()
+    {
+        return (false);
+    }
+
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraConnectSequence.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraConnectSequence.java
new file mode 100644 (file)
index 0000000..13606f7
--- /dev/null
@@ -0,0 +1,240 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.connection;
+
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.R;
+import net.osdn.gokigen.a01d.camera.ICameraConnection;
+import net.osdn.gokigen.a01d.camera.ICameraStatusReceiver;
+import net.osdn.gokigen.a01d.camera.canon.ICanonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.canon.wrapper.command.messages.specific.CanonRegistrationMessage;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpMessages;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandGeneric;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.status.PtpIpStatusChecker;
+
+public class CanonCameraConnectSequence implements Runnable, IPtpIpCommandCallback, IPtpIpMessages
+{
+    private final String TAG = this.toString();
+
+    private final Activity context;
+    private final ICameraConnection cameraConnection;
+    private final ICameraStatusReceiver cameraStatusReceiver;
+    private final ICanonInterfaceProvider interfaceProvider;
+    private final IPtpIpCommandPublisher commandIssuer;
+    private final PtpIpStatusChecker statusChecker;
+    private boolean isDumpLog = false;
+
+    CanonCameraConnectSequence(@NonNull Activity context, @NonNull ICameraStatusReceiver statusReceiver, @NonNull final ICameraConnection cameraConnection, @NonNull ICanonInterfaceProvider interfaceProvider, @NonNull PtpIpStatusChecker statusChecker)
+    {
+        Log.v(TAG, " CanonCameraConnectSequenceForPlayback");
+        this.context = context;
+        this.cameraConnection = cameraConnection;
+        this.cameraStatusReceiver = statusReceiver;
+        this.interfaceProvider = interfaceProvider;
+        this.commandIssuer = interfaceProvider.getCommandPublisher();
+        this.statusChecker = statusChecker;
+    }
+
+    @Override
+    public void run()
+    {
+        try
+        {
+            // カメラとTCP接続
+            IPtpIpCommandPublisher issuer = interfaceProvider.getCommandPublisher();
+            if (!issuer.isConnected())
+            {
+                if (!interfaceProvider.getCommandCommunication().connect())
+                {
+                    // 接続失敗...
+                    interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.dialog_title_connect_failed_canon), false, true, Color.RED);
+                    onConnectError(context.getString(R.string.dialog_title_connect_failed_canon));
+                    return;
+                }
+            }
+            else
+            {
+                Log.v(TAG, "SOCKET IS ALREADY CONNECTED...");
+            }
+            // コマンドタスクの実行開始
+            issuer.start();
+
+            // 接続シーケンスの開始
+            sendRegistrationMessage();
+
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.dialog_title_connect_failed_canon), false, true, Color.RED);
+            onConnectError(e.getLocalizedMessage());
+        }
+    }
+
+    private void onConnectError(String reason)
+    {
+        cameraConnection.alertConnectingFailed(reason);
+    }
+
+    @Override
+    public void onReceiveProgress(int currentBytes, int totalBytes, byte[] body)
+    {
+        Log.v(TAG, " " + currentBytes + "/" + totalBytes);
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (false);
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] rx_body)
+    {
+        switch (id)
+        {
+            case SEQ_REGISTRATION:
+                if (checkRegistrationMessage(rx_body))
+                {
+                    sendInitEventRequest(rx_body);
+                }
+                else
+                {
+                    onConnectError(context.getString(R.string.connect_error_message));
+                }
+                break;
+
+            case SEQ_EVENT_INITIALIZE:
+                if (checkEventInitialize(rx_body))
+                {
+                    interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting1), false, false, 0);
+                    commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_OPEN_SESSION, isDumpLog, 0, 0x1002, 4, 0x41));
+                }
+                else
+                {
+                    onConnectError(context.getString(R.string.connect_error_message));
+                }
+                break;
+
+            case SEQ_OPEN_SESSION:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting2), false, false, 0);
+                commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_INIT_SESSION, isDumpLog, 0, 0x902f));
+                break;
+
+            case SEQ_INIT_SESSION:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting3), false, false, 0);
+                commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_CHANGE_REMOTE, isDumpLog, 0, 0x9114, 4, 0x15));
+                break;
+
+            case SEQ_CHANGE_REMOTE:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting4), false, false, 0);
+                commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_SET_EVENT_MODE, isDumpLog, 0, 0x902f, 4, 0x02));
+                break;
+
+            case SEQ_SET_EVENT_MODE:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting5), false, false, 0);
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connect_finished), false, false, 0);
+                connectFinished();
+                Log.v(TAG, "CHANGED PLAYBACK MODE : DONE.");
+                break;
+
+            default:
+                Log.v(TAG, "RECEIVED UNKNOWN ID : " + id);
+                onConnectError(context.getString(R.string.connect_receive_unknown_message));
+                break;
+        }
+    }
+
+    private void sendRegistrationMessage()
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_start), false, false, 0);
+        cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_start));
+        commandIssuer.enqueueCommand(new CanonRegistrationMessage(this));
+    }
+
+    private void sendInitEventRequest(byte[] receiveData)
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_start_2), false, false, 0);
+        cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_start_2));
+        try
+        {
+            int eventConnectionNumber = (receiveData[8] & 0xff);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[9]  & 0xff) << 8);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[10] & 0xff) << 16);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[11] & 0xff) << 24);
+            statusChecker.setEventConnectionNumber(eventConnectionNumber);
+            interfaceProvider.getCameraStatusWatcher().startStatusWatch(null);
+
+            commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_OPEN_SESSION, isDumpLog, 0, 0x1002, 4, 0x41));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private boolean checkRegistrationMessage(byte[] receiveData)
+    {
+        // データ(Connection Number)がないときにはエラーと判断する
+        return (!((receiveData == null)||(receiveData.length < 12)));
+    }
+
+    private boolean checkEventInitialize(byte[] receiveData)
+    {
+        Log.v(TAG, "checkEventInitialize() ");
+        return (!(receiveData == null));
+    }
+
+    private void connectFinished()
+    {
+        try
+        {
+            // 接続成功のメッセージを出す
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connected), false, false, 0);
+
+            // ちょっと待つ
+            Thread.sleep(1000);
+
+            //interfaceProvider.getAsyncEventCommunication().connect();
+            //interfaceProvider.getCameraStatusWatcher().startStatusWatch(interfaceProvider.getStatusListener());  ステータスの定期確認は実施しない
+
+            // 接続成功!のメッセージを出す
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connected), false, false, 0);
+
+            onConnectNotify();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void onConnectNotify()
+    {
+        try
+        {
+            final Thread thread = new Thread(new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    // カメラとの接続確立を通知する
+                    cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_connected));
+                    cameraStatusReceiver.onCameraConnected();
+                    Log.v(TAG, " onConnectNotify()");
+                }
+            });
+            thread.start();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraDisconnectSequence.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonCameraDisconnectSequence.java
new file mode 100644 (file)
index 0000000..d1f8883
--- /dev/null
@@ -0,0 +1,40 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.connection;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.canon.ICanonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommunication;
+
+class CanonCameraDisconnectSequence implements Runnable
+{
+    private final String TAG = this.toString();
+    private final Activity activity;
+    private final IPtpIpCommunication command;
+    private final IPtpIpCommunication async;
+    private final IPtpIpCommunication liveview;
+
+    CanonCameraDisconnectSequence(Activity activity, @NonNull ICanonInterfaceProvider interfaceProvider)
+    {
+        this.activity = activity;
+        this.command = interfaceProvider.getCommandCommunication();
+        this.async = interfaceProvider.getAsyncEventCommunication();
+        this.liveview = interfaceProvider.getLiveviewCommunication();
+    }
+
+    @Override
+    public void run()
+    {
+        try
+        {
+            liveview.disconnect();
+            async.disconnect();
+            command.disconnect();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonConnection.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/canon/wrapper/connection/CanonConnection.java
new file mode 100644 (file)
index 0000000..e09b6e8
--- /dev/null
@@ -0,0 +1,235 @@
+package net.osdn.gokigen.a01d.camera.canon.wrapper.connection;
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import net.osdn.gokigen.a01d.R;
+import net.osdn.gokigen.a01d.camera.ICameraConnection;
+import net.osdn.gokigen.a01d.camera.ICameraStatusReceiver;
+import net.osdn.gokigen.a01d.camera.canon.ICanonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.status.PtpIpStatusChecker;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class CanonConnection implements ICameraConnection
+{
+    private final String TAG = toString();
+    private final Activity context;
+    private final ICameraStatusReceiver statusReceiver;
+    private final ICanonInterfaceProvider interfaceProvider;
+    private final BroadcastReceiver connectionReceiver;
+    private final Executor cameraExecutor = Executors.newFixedThreadPool(1);
+    private final PtpIpStatusChecker statusChecker;
+    private CameraConnectionStatus connectionStatus = CameraConnectionStatus.UNKNOWN;
+
+    public CanonConnection(@NonNull final Activity context, @NonNull final ICameraStatusReceiver statusReceiver, @NonNull ICanonInterfaceProvider interfaceProvider, @NonNull PtpIpStatusChecker statusChecker)
+    {
+        Log.v(TAG, "CanonConnection()");
+        this.context = context;
+        this.statusReceiver = statusReceiver;
+        this.interfaceProvider = interfaceProvider;
+        this.statusChecker = statusChecker;
+        connectionReceiver = new BroadcastReceiver()
+        {
+            @Override
+            public void onReceive(Context context, Intent intent)
+            {
+                onReceiveBroadcastOfConnection(context, intent);
+            }
+        };
+    }
+
+    /**
+     *
+     *
+     */
+    private void onReceiveBroadcastOfConnection(Context context, Intent intent)
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_check_wifi), false, false, 0);
+        statusReceiver.onStatusNotify(context.getString(R.string.connect_check_wifi));
+
+        Log.v(TAG, context.getString(R.string.connect_check_wifi));
+
+        String action = intent.getAction();
+        if (action == null)
+        {
+            Log.v(TAG, "intent.getAction() : null");
+            return;
+        }
+
+        try
+        {
+            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
+            {
+                Log.v(TAG, "onReceiveBroadcastOfConnection() : CONNECTIVITY_ACTION");
+
+                WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+                if (wifiManager != null) {
+                    WifiInfo info = wifiManager.getConnectionInfo();
+                    if (wifiManager.isWifiEnabled() && info != null) {
+                        if (info.getNetworkId() != -1) {
+                            Log.v(TAG, "Network ID is -1, there is no currently connected network.");
+                        }
+                        // 自動接続が指示されていた場合は、カメラとの接続処理を行う
+                        connectToCamera();
+                    } else {
+                        if (info == null) {
+                            Log.v(TAG, "NETWORK INFO IS NULL.");
+                        } else {
+                            Log.v(TAG, "isWifiEnabled : " + wifiManager.isWifiEnabled() + " NetworkId : " + info.getNetworkId());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "onReceiveBroadcastOfConnection() EXCEPTION" + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void startWatchWifiStatus(Context context)
+    {
+        Log.v(TAG, "startWatchWifiStatus()");
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_prepare), false, false, 0);
+        statusReceiver.onStatusNotify("prepare");
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        context.registerReceiver(connectionReceiver, filter);
+    }
+
+    @Override
+    public void stopWatchWifiStatus(Context context)
+    {
+        Log.v(TAG, "stopWatchWifiStatus()");
+        context.unregisterReceiver(connectionReceiver);
+        disconnect(false);
+    }
+
+    @Override
+    public void disconnect(boolean powerOff)
+    {
+        Log.v(TAG, "disconnect()");
+        disconnectFromCamera(powerOff);
+        connectionStatus = CameraConnectionStatus.DISCONNECTED;
+        statusReceiver.onCameraDisconnected();
+    }
+
+    @Override
+    public void connect()
+    {
+        Log.v(TAG, "connect()");
+        connectToCamera();
+    }
+
+    @Override
+    public void alertConnectingFailed(String message)
+    {
+        Log.v(TAG, "alertConnectingFailed() : " + message);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+                .setTitle(context.getString(R.string.dialog_title_connect_failed_canon))
+                .setMessage(message)
+                .setPositiveButton(context.getString(R.string.dialog_title_button_retry), new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which)
+                    {
+                        disconnect(false);
+                        connect();
+                    }
+                })
+                .setNeutralButton(R.string.dialog_title_button_network_settings, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which)
+                    {
+                        try
+                        {
+                            // Wifi 設定画面を表示する
+                            context.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
+                        }
+                        catch (android.content.ActivityNotFoundException ex)
+                        {
+                            // Activity が存在しなかった...設定画面が起動できなかった
+                            Log.v(TAG, "android.content.ActivityNotFoundException...");
+
+                            // この場合は、再試行と等価な動きとする
+                            connect();
+                        }
+                        catch (Exception e)
+                        {
+                            e.printStackTrace();
+                        }
+                    }
+                });
+        context.runOnUiThread(new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                builder.show();
+            }
+        });
+    }
+
+    @Override
+    public CameraConnectionStatus getConnectionStatus()
+    {
+        Log.v(TAG, " getConnectionStatus()");
+        return (connectionStatus);
+    }
+
+    @Override
+    public void forceUpdateConnectionStatus(CameraConnectionStatus status)
+    {
+        Log.v(TAG, " forceUpdateConnectionStatus()");
+        connectionStatus = status;
+    }
+
+    /**
+     * カメラとの切断処理
+     */
+    private void disconnectFromCamera(final boolean powerOff)
+    {
+        Log.v(TAG, " disconnectFromCamera()");
+        try
+        {
+            cameraExecutor.execute(new CanonCameraDisconnectSequence(context, interfaceProvider));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * カメラとの接続処理
+     */
+    private void connectToCamera()
+    {
+        Log.v(TAG, " connectToCamera()");
+        connectionStatus = CameraConnectionStatus.CONNECTING;
+        try
+        {
+            cameraExecutor.execute(new CanonCameraConnectSequence(context, statusReceiver, this, interfaceProvider, statusChecker));
+        }
+        catch (Exception e)
+        {
+            Log.v(TAG, " connectToCamera() EXCEPTION : " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
index 85bc295..2f86b54 100644 (file)
@@ -1,8 +1,8 @@
 package net.osdn.gokigen.a01d.camera.nikon;
 
-
 import androidx.annotation.NonNull;
 
+import net.osdn.gokigen.a01d.IInformationReceiver;
 import net.osdn.gokigen.a01d.camera.ICameraConnection;
 import net.osdn.gokigen.a01d.camera.ICameraInformation;
 import net.osdn.gokigen.a01d.camera.ICameraStatus;
@@ -12,7 +12,10 @@ import net.osdn.gokigen.a01d.camera.IDisplayInjector;
 import net.osdn.gokigen.a01d.camera.IFocusingControl;
 import net.osdn.gokigen.a01d.camera.ILiveViewControl;
 import net.osdn.gokigen.a01d.camera.IZoomLensControl;
-import net.osdn.gokigen.a01d.camera.ptpip.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommunication;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.status.IPtpIpRunModeHolder;
 import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
 import net.osdn.gokigen.a01d.liveview.liveviewlistener.ILiveViewListener;
 
@@ -27,14 +30,13 @@ public interface INikonInterfaceProvider
     ICaptureControl getCaptureControl();
     IDisplayInjector getDisplayInjector();
 
-    /*
-    IFujiXRunModeHolder getRunModeHolder();
-    IFujiXCommandCallback getStatusHolder();
-    IFujiXCommandPublisher getCommandPublisher();
-    IFujiXCommunication getLiveviewCommunication();
-    IFujiXCommunication getAsyncEventCommunication();
-    IFujiXCommunication getCommandCommunication();
-     */
+    IPtpIpRunModeHolder getRunModeHolder();
+    IPtpIpCommandCallback getStatusHolder();
+    IPtpIpCommandPublisher getCommandPublisher();
+    IPtpIpCommunication getLiveviewCommunication();
+    IPtpIpCommunication getAsyncEventCommunication();
+    IPtpIpCommunication getCommandCommunication();
+    IInformationReceiver getInformationReceiver();
 
 
     ICameraStatusWatcher getStatusWatcher();
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/command/messages/specific/NikonRegistrationMessage.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/command/messages/specific/NikonRegistrationMessage.java
new file mode 100644 (file)
index 0000000..12f7ae0
--- /dev/null
@@ -0,0 +1,89 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.command.messages.specific;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+import java.nio.ByteBuffer;
+import java.util.UUID;
+
+public class NikonRegistrationMessage extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+
+    public NikonRegistrationMessage(@NonNull IPtpIpCommandCallback callback)
+    {
+        this.callback = callback;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (SEQ_REGISTRATION);
+    }
+
+    @Override
+    public boolean receiveAgainShortLengthMessage()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean useSequenceNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isIncrementSeqNumber()
+    {
+        return (false);
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (80);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        int uuid = UUID.randomUUID().hashCode();
+
+        byte[] typeArray = {
+                (byte)0x01, (byte)0x00, (byte)0x00, (byte)0x00,
+        };
+
+        byte[] uuidArray = {
+                (byte)0xad, (byte)0xa5, (byte)0x48, (byte)0x5d, (byte)0x87, (byte)0xb2, (byte)0x7f, (byte)0x0b,
+                (byte)0xd3, (byte)0xd5, (byte)0xde, (byte)0xd0, (byte)0x12, (byte)0x44, (byte)0x99, (byte)0x32,
+        };
+
+        byte[] deviceNameArray = {
+                // device_name 'GOKIGEN_a01'
+                (byte)0x47, (byte)0x00, (byte)0x4f, (byte)0x00, (byte)0x4b, (byte)0x00, (byte)0x49, (byte)0x00,
+                (byte)0x47, (byte)0x00, (byte)0x45, (byte)0x00, (byte)0x4e, (byte)0x00, (byte)0x5f, (byte)0x00,
+                (byte)0x61, (byte)0x00, (byte)0x30, (byte)0x00, (byte)0x31, (byte)0x00, (byte)0x00, (byte)0x00,
+        };
+        byte[] versionArray = {
+                //
+                (byte)0x00, (byte)0x00, (byte)0x01, (byte)0x00,
+        };
+
+        ByteBuffer byteBuffer = ByteBuffer.allocate(4 + 16 + 24 + 4);
+        byteBuffer.put(typeArray);
+        byteBuffer.put(uuidArray);
+        byteBuffer.put(deviceNameArray);
+        byteBuffer.put(versionArray);
+
+        return (byteBuffer.array());
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraConnectSequence.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraConnectSequence.java
new file mode 100644 (file)
index 0000000..28b21bb
--- /dev/null
@@ -0,0 +1,233 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.connection;
+
+import android.app.Activity;
+import android.graphics.Color;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.R;
+import net.osdn.gokigen.a01d.camera.ICameraConnection;
+import net.osdn.gokigen.a01d.camera.ICameraStatusReceiver;
+import net.osdn.gokigen.a01d.camera.nikon.INikonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.nikon.wrapper.command.messages.specific.NikonRegistrationMessage;
+import net.osdn.gokigen.a01d.camera.nikon.wrapper.status.NikonStatusChecker;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpMessages;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandGeneric;
+
+public class NikonCameraConnectSequence implements Runnable, IPtpIpCommandCallback, IPtpIpMessages
+{
+    private final String TAG = this.toString();
+
+    private final Activity context;
+    private final ICameraConnection cameraConnection;
+    private final ICameraStatusReceiver cameraStatusReceiver;
+    private final INikonInterfaceProvider interfaceProvider;
+    private final IPtpIpCommandPublisher commandIssuer;
+    private final NikonStatusChecker statusChecker;
+    private boolean isDumpLog = false;
+
+    NikonCameraConnectSequence(@NonNull Activity context, @NonNull ICameraStatusReceiver statusReceiver, @NonNull final ICameraConnection cameraConnection, @NonNull INikonInterfaceProvider interfaceProvider, @NonNull NikonStatusChecker statusChecker)
+    {
+        Log.v(TAG, " NikonCameraConnectSequenceForPlayback");
+        this.context = context;
+        this.cameraConnection = cameraConnection;
+        this.cameraStatusReceiver = statusReceiver;
+        this.interfaceProvider = interfaceProvider;
+        this.commandIssuer = interfaceProvider.getCommandPublisher();
+        this.statusChecker = statusChecker;
+    }
+
+    @Override
+    public void run()
+    {
+        try
+        {
+            // カメラとTCP接続
+            IPtpIpCommandPublisher issuer = interfaceProvider.getCommandPublisher();
+            if (!issuer.isConnected())
+            {
+                if (!interfaceProvider.getCommandCommunication().connect())
+                {
+                    // 接続失敗...
+                    interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.dialog_title_connect_failed_nikon), false, true, Color.RED);
+                    onConnectError(context.getString(R.string.dialog_title_connect_failed_nikon));
+                    return;
+                }
+            }
+            else
+            {
+                Log.v(TAG, "SOCKET IS ALREADY CONNECTED...");
+            }
+            // コマンドタスクの実行開始
+            issuer.start();
+
+            // 接続シーケンスの開始
+            sendRegistrationMessage();
+
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.dialog_title_connect_failed_nikon), false, true, Color.RED);
+            onConnectError(e.getLocalizedMessage());
+        }
+    }
+
+    private void onConnectError(String reason)
+    {
+        cameraConnection.alertConnectingFailed(reason);
+    }
+
+    @Override
+    public void onReceiveProgress(int currentBytes, int totalBytes, byte[] body)
+    {
+        Log.v(TAG, " " + currentBytes + "/" + totalBytes);
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (false);
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] rx_body)
+    {
+        switch (id)
+        {
+            case SEQ_REGISTRATION:
+                if (checkRegistrationMessage(rx_body))
+                {
+                    sendInitEventRequest(rx_body);
+                }
+                else
+                {
+                    onConnectError(context.getString(R.string.connect_error_message));
+                }
+                break;
+
+            case SEQ_EVENT_INITIALIZE:
+                if (checkEventInitialize(rx_body))
+                {
+                    interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting1), false, false, 0);
+                    commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_OPEN_SESSION, 50, isDumpLog, 0, 0x1002, 4, 0x41, 0, 0, 0));  // OpenSession
+                }
+                else
+                {
+                    onConnectError(context.getString(R.string.connect_error_message));
+                }
+                break;
+
+            case SEQ_OPEN_SESSION:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting2), false, false, 0);
+                commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_INIT_SESSION, 50, isDumpLog, 0, 0x1001, 0, 0, 0, 0, 0));  // GetDeviceInfo
+                break;
+
+            case SEQ_INIT_SESSION:
+            case SEQ_CHANGE_REMOTE:
+            case SEQ_SET_EVENT_MODE:
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting3), false, false, 0);
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting4), false, false, 0);
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.canon_connect_connecting5), false, false, 0);
+                interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connect_finished), false, false, 0);
+                connectFinished();
+                Log.v(TAG, "CHANGED PLAYBACK MODE : DONE.");
+                break;
+
+            default:
+                Log.v(TAG, "RECEIVED UNKNOWN ID : " + id);
+                onConnectError(context.getString(R.string.connect_receive_unknown_message));
+                break;
+        }
+    }
+
+    private void sendRegistrationMessage()
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_start), false, false, 0);
+        cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_start));
+        commandIssuer.enqueueCommand(new NikonRegistrationMessage(this));
+    }
+
+    private void sendInitEventRequest(byte[] receiveData)
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_start_2), false, false, 0);
+        cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_start_2));
+        try
+        {
+            int eventConnectionNumber = (receiveData[8] & 0xff);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[9]  & 0xff) << 8);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[10] & 0xff) << 16);
+            eventConnectionNumber = eventConnectionNumber + ((receiveData[11] & 0xff) << 24);
+            statusChecker.setEventConnectionNumber(eventConnectionNumber);
+            interfaceProvider.getCameraStatusWatcher().startStatusWatch(null);
+
+            commandIssuer.enqueueCommand(new PtpIpCommandGeneric(this, SEQ_OPEN_SESSION, 50, isDumpLog, 0, 0x1002, 4, 0x41, 0, 0, 0));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private boolean checkRegistrationMessage(byte[] receiveData)
+    {
+        // データ(Connection Number)がないときにはエラーと判断する
+        return (!((receiveData == null)||(receiveData.length < 12)));
+    }
+
+    private boolean checkEventInitialize(byte[] receiveData)
+    {
+        Log.v(TAG, "checkEventInitialize() ");
+        return (!(receiveData == null));
+    }
+
+    private void connectFinished()
+    {
+        try
+        {
+            // 接続成功のメッセージを出す
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connected), false, false, 0);
+
+            // ちょっと待つ
+            Thread.sleep(1000);
+
+            //interfaceProvider.getAsyncEventCommunication().connect();
+            //interfaceProvider.getCameraStatusWatcher().startStatusWatch(interfaceProvider.getStatusListener());  ステータスの定期確認は実施しない
+
+            // 接続成功!のメッセージを出す
+            interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_connected), false, false, 0);
+
+            onConnectNotify();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void onConnectNotify()
+    {
+        try
+        {
+            final Thread thread = new Thread(new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    // カメラとの接続確立を通知する
+                    cameraStatusReceiver.onStatusNotify(context.getString(R.string.connect_connected));
+                    cameraStatusReceiver.onCameraConnected();
+                    Log.v(TAG, " onConnectNotify()");
+                }
+            });
+            thread.start();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraDisconnectSequence.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonCameraDisconnectSequence.java
new file mode 100644 (file)
index 0000000..55fd950
--- /dev/null
@@ -0,0 +1,44 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.connection;
+
+import android.app.Activity;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.nikon.INikonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.nikon.wrapper.status.NikonStatusChecker;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommunication;
+
+class NikonCameraDisconnectSequence implements Runnable
+{
+    private final String TAG = this.toString();
+    private final Activity activity;
+    private final IPtpIpCommunication command;
+    private final IPtpIpCommunication async;
+    private final IPtpIpCommunication liveview;
+    private final NikonStatusChecker statusChecker;
+
+    NikonCameraDisconnectSequence(Activity activity, @NonNull INikonInterfaceProvider interfaceProvider, @NonNull NikonStatusChecker statusChecker)
+    {
+        this.activity = activity;
+        this.command = interfaceProvider.getCommandCommunication();
+        this.async = interfaceProvider.getAsyncEventCommunication();
+        this.liveview = interfaceProvider.getLiveviewCommunication();
+        this.statusChecker = statusChecker;
+    }
+
+    @Override
+    public void run()
+    {
+        try
+        {
+            statusChecker.stopStatusWatch();
+            liveview.disconnect();
+            async.disconnect();
+            command.disconnect();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonConnection.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/connection/NikonConnection.java
new file mode 100644 (file)
index 0000000..3b1c74b
--- /dev/null
@@ -0,0 +1,240 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.connection;
+
+
+import android.app.Activity;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiInfo;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+import androidx.appcompat.app.AlertDialog;
+
+import net.osdn.gokigen.a01d.R;
+import net.osdn.gokigen.a01d.camera.ICameraConnection;
+import net.osdn.gokigen.a01d.camera.ICameraStatusReceiver;
+import net.osdn.gokigen.a01d.camera.nikon.INikonInterfaceProvider;
+import net.osdn.gokigen.a01d.camera.nikon.wrapper.status.NikonStatusChecker;
+
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+
+public class NikonConnection implements ICameraConnection
+{
+    private final String TAG = toString();
+    private final Activity context;
+    private final ICameraStatusReceiver statusReceiver;
+    private final INikonInterfaceProvider interfaceProvider;
+    private final BroadcastReceiver connectionReceiver;
+    private final Executor cameraExecutor = Executors.newFixedThreadPool(1);
+
+    private ICameraConnection.CameraConnectionStatus connectionStatus = CameraConnectionStatus.UNKNOWN;
+
+    private final NikonCameraConnectSequence connectSequence;
+    private final NikonCameraDisconnectSequence disconnectSequence;
+
+    public NikonConnection(@NonNull final Activity context, @NonNull final ICameraStatusReceiver statusReceiver, @NonNull INikonInterfaceProvider interfaceProvider, @NonNull NikonStatusChecker statusChecker)
+    {
+        Log.v(TAG, "NikonConnection()");
+        this.context = context;
+        this.statusReceiver = statusReceiver;
+        this.interfaceProvider = interfaceProvider;
+        connectionReceiver = new BroadcastReceiver()
+        {
+            @Override
+            public void onReceive(Context context, Intent intent)
+            {
+                onReceiveBroadcastOfConnection(context, intent);
+            }
+        };
+        connectSequence = new NikonCameraConnectSequence(context, statusReceiver, this, interfaceProvider, statusChecker);
+        disconnectSequence = new NikonCameraDisconnectSequence(context, interfaceProvider, statusChecker);
+    }
+
+    /**
+     *
+     *
+     */
+    private void onReceiveBroadcastOfConnection(Context context, Intent intent)
+    {
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_check_wifi), false, false, 0);
+        statusReceiver.onStatusNotify(context.getString(R.string.connect_check_wifi));
+
+        Log.v(TAG, context.getString(R.string.connect_check_wifi));
+
+        String action = intent.getAction();
+        if (action == null)
+        {
+            Log.v(TAG, "intent.getAction() : null");
+            return;
+        }
+
+        try
+        {
+            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION))
+            {
+                Log.v(TAG, "onReceiveBroadcastOfConnection() : CONNECTIVITY_ACTION");
+
+                WifiManager wifiManager = (WifiManager) context.getApplicationContext().getSystemService(Context.WIFI_SERVICE);
+                if (wifiManager != null) {
+                    WifiInfo info = wifiManager.getConnectionInfo();
+                    if (wifiManager.isWifiEnabled() && info != null) {
+                        if (info.getNetworkId() != -1) {
+                            Log.v(TAG, "Network ID is -1, there is no currently connected network.");
+                        }
+                        // 自動接続が指示されていた場合は、カメラとの接続処理を行う
+                        connectToCamera();
+                    } else {
+                        if (info == null) {
+                            Log.v(TAG, "NETWORK INFO IS NULL.");
+                        } else {
+                            Log.v(TAG, "isWifiEnabled : " + wifiManager.isWifiEnabled() + " NetworkId : " + info.getNetworkId());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            Log.w(TAG, "onReceiveBroadcastOfConnection() EXCEPTION" + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void startWatchWifiStatus(Context context)
+    {
+        Log.v(TAG, "startWatchWifiStatus()");
+        interfaceProvider.getInformationReceiver().updateMessage(context.getString(R.string.connect_prepare), false, false, 0);
+        statusReceiver.onStatusNotify("prepare");
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(WifiManager.NETWORK_STATE_CHANGED_ACTION);
+        filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+        context.registerReceiver(connectionReceiver, filter);
+    }
+
+    @Override
+    public void stopWatchWifiStatus(Context context)
+    {
+        Log.v(TAG, "stopWatchWifiStatus()");
+        context.unregisterReceiver(connectionReceiver);
+        disconnect(false);
+    }
+
+    @Override
+    public void disconnect(boolean powerOff)
+    {
+        Log.v(TAG, "disconnect()");
+        disconnectFromCamera(powerOff);
+        connectionStatus = CameraConnectionStatus.DISCONNECTED;
+        statusReceiver.onCameraDisconnected();
+    }
+
+    @Override
+    public void connect()
+    {
+        Log.v(TAG, "connect()");
+        connectToCamera();
+    }
+
+    @Override
+    public void alertConnectingFailed(String message)
+    {
+        Log.v(TAG, "alertConnectingFailed() : " + message);
+        final AlertDialog.Builder builder = new AlertDialog.Builder(context)
+                .setTitle(context.getString(R.string.dialog_title_connect_failed_nikon))
+                .setMessage(message)
+                .setPositiveButton(context.getString(R.string.dialog_title_button_retry), new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which)
+                    {
+                        disconnect(false);
+                        connect();
+                    }
+                })
+                .setNeutralButton(R.string.dialog_title_button_network_settings, new DialogInterface.OnClickListener() {
+                    @Override
+                    public void onClick(DialogInterface dialog, int which)
+                    {
+                        try
+                        {
+                            // Wifi 設定画面を表示する
+                            context.startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
+                        }
+                        catch (android.content.ActivityNotFoundException ex)
+                        {
+                            // Activity が存在しなかった...設定画面が起動できなかった
+                            Log.v(TAG, "android.content.ActivityNotFoundException...");
+
+                            // この場合は、再試行と等価な動きとする
+                            connect();
+                        }
+                        catch (Exception e)
+                        {
+                            e.printStackTrace();
+                        }
+                    }
+                });
+        context.runOnUiThread(new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                builder.show();
+            }
+        });
+    }
+
+    @Override
+    public CameraConnectionStatus getConnectionStatus()
+    {
+        Log.v(TAG, " getConnectionStatus()");
+        return (connectionStatus);
+    }
+
+    @Override
+    public void forceUpdateConnectionStatus(CameraConnectionStatus status)
+    {
+        Log.v(TAG, " forceUpdateConnectionStatus()");
+        connectionStatus = status;
+    }
+
+    /**
+     * カメラとの切断処理
+     */
+    private void disconnectFromCamera(final boolean powerOff)
+    {
+        Log.v(TAG, " disconnectFromCamera()");
+        try
+        {
+            cameraExecutor.execute(disconnectSequence);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     * カメラとの接続処理
+     */
+    private void connectToCamera()
+    {
+        Log.v(TAG, " connectToCamera()");
+        connectionStatus = CameraConnectionStatus.CONNECTING;
+        try
+        {
+            cameraExecutor.execute(connectSequence);
+        }
+        catch (Exception e)
+        {
+            Log.v(TAG, " connectToCamera() EXCEPTION : " + e.getMessage());
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusChecker.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusChecker.java
new file mode 100644 (file)
index 0000000..0ac6265
--- /dev/null
@@ -0,0 +1,440 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.status;
+
+import android.app.Activity;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ICameraStatus;
+import net.osdn.gokigen.a01d.camera.ICameraStatusWatcher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommand;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpMessages;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.status.IPtpIpInitEventRequest;
+import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.osdn.gokigen.a01d.camera.utils.SimpleLogDumper.dump_bytes;
+
+public class NikonStatusChecker implements IPtpIpCommandCallback, ICameraStatusWatcher, ICameraStatus
+{
+    private final String TAG = toString();
+
+    private static final int BUFFER_SIZE = 1024 * 1024 + 8;
+    private static final int STATUS_MESSAGE_HEADER_SIZE = 14;
+    private int sleepMs;
+    private final IPtpIpCommandPublisher issuer;
+    private ICameraStatusUpdateNotify notifier = null;
+    private NikonStatusHolder statusHolder;
+    private boolean whileFetching = false;
+    private boolean logcat = false;
+    private final String ipAddress;
+    private final int portNumber;
+    private final IPtpIpInitEventRequest eventRequest;
+
+    private Socket socket = null;
+    private DataOutputStream dos = null;
+    private BufferedReader bufferedReader = null;
+    private int eventConnectionNumber = 0;
+
+    public NikonStatusChecker(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher issuer, @NonNull String ip, int portNumber, @NonNull IPtpIpInitEventRequest eventRequest)
+    {
+        this.issuer = issuer;
+        this.statusHolder = new NikonStatusHolder();
+        this.ipAddress = ip;
+        this.portNumber = portNumber;
+        this.eventRequest = eventRequest;
+        Log.v(TAG, "POLLING WAIT : " + sleepMs);
+    }
+
+    @Override
+    public void onReceiveProgress(int currentBytes, int totalBytes, byte[] body)
+    {
+        Log.v(TAG, " " + currentBytes + "/" + totalBytes);
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (false);
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] data)
+    {
+        try
+        {
+            logcat("receivedMessage : " + id + ", length: " + data.length);
+            if (id == IPtpIpMessages.SEQ_EVENT_INITIALIZE)
+            {
+                // 終わる
+                Log.v(TAG, " ----- PTP-IP Connection is ESTABLISHED. -----");
+                return;
+            }
+            if (data.length < STATUS_MESSAGE_HEADER_SIZE)
+            {
+                Log.v(TAG, "received status length is short. (" + data.length + " bytes.)");
+                return;
+            }
+
+            int nofStatus = (data[13] * 256) + data[12];
+            int statusCount = 0;
+            int index = STATUS_MESSAGE_HEADER_SIZE;
+            while ((statusCount < nofStatus)&&(index < data.length))
+            {
+                int dataId = ((((int)data[index + 1]) & 0xff) * 256) + (((int) data[index]) & 0xff);
+                statusHolder.updateValue(notifier, dataId, data[index + 2], data[index + 3], data[index +4], data[index + 5]);
+                index = index + 6;
+                statusCount++;
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getStatusList(String key)
+    {
+        try
+        {
+            if (statusHolder == null)
+            {
+                return (new ArrayList<>());
+            }
+            return (statusHolder.getAvailableItemList(key));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (new ArrayList<>());
+    }
+
+    @Override
+    public String getStatus(String key)
+    {
+        try
+        {
+            if (statusHolder == null)
+            {
+                return ("");
+            }
+            return (statusHolder.getItemStatus(key));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return ("");
+    }
+
+    @Override
+    public void setStatus(String key, String value)
+    {
+        try
+        {
+            if (logcat)
+            {
+                Log.v(TAG, "setStatus(" + key + ", " + value + ")");
+            }
+
+            // ここで設定を行う。
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void startStatusWatch(@NonNull ICameraStatusUpdateNotify notifier)
+    {
+        if (whileFetching)
+        {
+            Log.v(TAG, "startStatusWatch() already starting.");
+            return;
+        }
+        try
+        {
+            final IPtpIpCommandCallback callback = this;
+            this.notifier = notifier;
+            whileFetching = true;
+
+            // セッションをオープンする
+            boolean isConnect = connect();
+            if (!isConnect)
+            {
+                Log.v(TAG, "  CONNECT FAIL...(EVENT) : " + ipAddress + "  " + portNumber);
+            }
+            issueCommand(eventRequest.getInitEventRequest(this, eventConnectionNumber));
+
+/*
+            Thread thread = new Thread(new Runnable()
+            {
+                @Override
+                public void run()
+                {
+                    logcat("Start status watch. : " + sleepMs + "ms");
+                    while (whileFetching)
+                    {
+                        try
+                        {
+                            issuer.enqueueCommand(new StatusRequestMessage(callback));
+                            Thread.sleep(sleepMs);
+                        }
+                        catch (Exception e)
+                        {
+                            e.printStackTrace();
+                        }
+                    }
+                    logcat("STATUS WATCH STOPPED.");
+                }
+            });
+            thread.start();
+
+            // 切断する
+            disconnect();
+*/
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void stopStatusWatch()
+    {
+        Log.v(TAG, "stoptStatusWatch()");
+        whileFetching = false;
+        this.notifier = null;
+    }
+
+    private void logcat(String message)
+    {
+        if (logcat)
+        {
+            Log.v(TAG, message);
+        }
+    }
+
+    private boolean connect()
+    {
+        try
+        {
+            socket = new Socket(ipAddress, portNumber);
+            return (true);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            socket = null;
+        }
+        return (false);
+    }
+
+    private void disconnect()
+    {
+        // ストリームを全部閉じる
+        try
+        {
+            if (dos != null)
+            {
+                dos.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        dos = null;
+
+        try
+        {
+            if (bufferedReader != null)
+            {
+                bufferedReader.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        bufferedReader = null;
+
+        try
+        {
+            if (socket != null)
+            {
+                socket.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        socket = null;
+        System.gc();
+    }
+
+
+    public void setEventConnectionNumber(int connectionNumber)
+    {
+        eventConnectionNumber = connectionNumber;
+    }
+
+    private void issueCommand(@NonNull IPtpIpCommand command)
+    {
+        try
+        {
+            //Log.v(TAG, "issueCommand : " + command.getId());
+            byte[] commandBody = command.commandBody();
+            if (commandBody != null)
+            {
+                // コマンドボディが入っていた場合には、コマンド送信(入っていない場合は受信待ち)
+                send_to_camera(command.dumpLog(), commandBody);
+            }
+            receive_from_camera(command.dumpLog(), command.getId(), command.responseCallback(), command.receiveAgainShortLengthMessage(), command.receiveDelayMs());
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *    カメラにコマンドを送信する(メイン部分)
+     *
+     */
+    private void send_to_camera(boolean isDumpReceiveLog, byte[] byte_array)
+    {
+        try
+        {
+            dos = new DataOutputStream(socket.getOutputStream());  // ここにいたらいけない?
+
+            // メッセージボディを加工: 最初に4バイトのレングス長をつける
+            byte[] sendData = new byte[byte_array.length + 4];
+
+            sendData[0] = (byte) (byte_array.length + 4);
+            sendData[1] = 0x00;
+            sendData[2] = 0x00;
+            sendData[3] = 0x00;
+            System.arraycopy(byte_array,0,sendData,4, byte_array.length);
+
+            if (isDumpReceiveLog)
+            {
+                // ログに送信メッセージを出力する
+                dump_bytes("SEND[" + sendData.length + "] ", sendData);
+            }
+
+            // (データを)送信
+            dos.write(sendData);
+            dos.flush();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     *    カメラからにコマンドの結果を受信する(メイン部分)
+     *
+     */
+    private void receive_from_camera(boolean isDumpReceiveLog, int id, IPtpIpCommandCallback callback, boolean receiveAgain, int delayMs) {
+        try {
+            sleep(delayMs);
+
+            boolean isFirstTime = true;
+            int totalReadBytes;
+            int receive_message_buffer_size = BUFFER_SIZE;
+            byte[] byte_array = new byte[receive_message_buffer_size];
+            InputStream is = socket.getInputStream();
+            if (is != null) {
+                int read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
+                byte[] receive_body;
+                if (read_bytes > 4) {
+                    if (receiveAgain) {
+                        int length = ((((int) byte_array[3]) & 0xff) << 24) + ((((int) byte_array[2]) & 0xff) << 16) + ((((int) byte_array[1]) & 0xff) << 8) + (((int) byte_array[0]) & 0xff);
+                        if (length > receive_message_buffer_size) {
+                            Log.v(TAG, "+++++ TOTAL RECEIVE MESSAGE SIZE IS " + length + " +++++");
+                        }
+                        totalReadBytes = read_bytes;
+                        while ((length > totalReadBytes) || ((length == read_bytes) && ((int) byte_array[4] == 0x02))) {
+                            // データについて、もう一回受信が必要な場合...
+                            if (isDumpReceiveLog) {
+                                Log.v(TAG, "--- RECEIVE AGAIN --- [" + length + "(" + read_bytes + ") " + byte_array[4] + "] ");
+                            }
+                            sleep(delayMs);
+                            int read_bytes2 = is.read(byte_array, read_bytes, receive_message_buffer_size - read_bytes);
+                            if (read_bytes2 > 0) {
+                                read_bytes = read_bytes + read_bytes2;
+                                totalReadBytes = totalReadBytes + read_bytes2;
+                            } else {
+                                // よみだし終了。
+                                Log.v(TAG, "FINISHED RECEIVE... ");
+                                break;
+                            }
+                            if (callback != null) {
+                                if (callback.isReceiveMulti()) {
+                                    int offset = 0;
+                                    if (isFirstTime) {
+                                        // 先頭のヘッダ部分をカットして送る
+                                        offset = 12;
+                                        isFirstTime = false;
+                                        //Log.v(TAG, " FIRST TIME : " + read_bytes + " " + offset);
+                                    }
+                                    callback.onReceiveProgress(read_bytes - offset, length, Arrays.copyOfRange(byte_array, offset, read_bytes));
+                                    read_bytes = 0;
+                                } else {
+                                    callback.onReceiveProgress(read_bytes, length, null);
+                                }
+                            }
+                        }
+                    }
+                    receive_body = Arrays.copyOfRange(byte_array, 0, read_bytes);
+                } else {
+                    receive_body = new byte[1];
+                }
+                if (isDumpReceiveLog) {
+                    // ログに受信メッセージを出力する
+                    Log.v(TAG, " receive_from_camera() : " + read_bytes + " bytes.");
+                    dump_bytes("RECV[" + receive_body.length + "] ", receive_body);
+                }
+                if (callback != null) {
+                    if (callback.isReceiveMulti()) {
+                        callback.receivedMessage(id, null);
+                    } else {
+                        callback.receivedMessage(id, receive_body);
+                        //callback.receivedMessage(id, Arrays.copyOfRange(receive_body, 0, receive_body.length));
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void sleep(int delayMs)
+    {
+        try
+        {
+            Thread.sleep(delayMs);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusHolder.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/nikon/wrapper/status/NikonStatusHolder.java
new file mode 100644 (file)
index 0000000..98a0fbe
--- /dev/null
@@ -0,0 +1,207 @@
+package net.osdn.gokigen.a01d.camera.nikon.wrapper.status;
+
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import androidx.annotation.NonNull;
+import androidx.collection.SparseArrayCompat;
+
+import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+//class NikonStatusHolder implements IPtpIpCameraProperties
+class NikonStatusHolder
+{
+    private final String TAG = toString();
+    private SparseIntArray statusHolder;
+    private SparseArrayCompat<String> statusNameArray;
+
+    NikonStatusHolder()
+    {
+        statusHolder = new SparseIntArray();
+        statusHolder.clear();
+
+        statusNameArray = new SparseArrayCompat<>();
+        prepareStatusNameArray();
+    }
+
+    private void prepareStatusNameArray()
+    {
+/*
+        statusNameArray.clear();
+        statusNameArray.append(BATTERY_LEVEL, BATTERY_LEVEL_STR);
+        statusNameArray.append(WHITE_BALANCE, WHITE_BALANCE_STR);
+        statusNameArray.append(APERTURE, APERTURE_STR);
+        statusNameArray.append(FOCUS_MODE, FOCUS_MODE_STR);
+        statusNameArray.append(SHOOTING_MODE, SHOOTING_MODE_STR);
+        statusNameArray.append(FLASH, FLASH_STR);
+        statusNameArray.append(EXPOSURE_COMPENSATION, EXPOSURE_COMPENSATION_STR);
+        statusNameArray.append(SELF_TIMER, SELF_TIMER_STR);
+        statusNameArray.append(FILM_SIMULATION, FILM_SIMULATION_STR);
+        statusNameArray.append(IMAGE_FORMAT, IMAGE_FORMAT_STR);
+        statusNameArray.append(RECMODE_ENABLE, RECMODE_ENABLE_STR);
+        statusNameArray.append(F_SS_CONTROL, F_SS_CONTROL_STR);
+        statusNameArray.append(ISO, ISO_STR);
+        statusNameArray.append(MOVIE_ISO, MOVIE_ISO_STR);
+        statusNameArray.append(FOCUS_POINT, FOCUS_POINT_STR);
+        statusNameArray.append(DEVICE_ERROR, DEVICE_ERROR_STR);
+        statusNameArray.append(IMAGE_FILE_COUNT, IMAGE_FILE_COUNT_STR);
+        statusNameArray.append(SDCARD_REMAIN_SIZE, SDCARD_REMAIN_SIZE_STR);
+        statusNameArray.append(FOCUS_LOCK, FOCUS_LOCK_STR);
+        statusNameArray.append(MOVIE_REMAINING_TIME, MOVIE_REMAINING_TIME_STR);
+        statusNameArray.append(SHUTTER_SPEED, SHUTTER_SPEED_STR);
+        statusNameArray.append(IMAGE_ASPECT,IMAGE_ASPECT_STR);
+        statusNameArray.append(BATTERY_LEVEL_2, BATTERY_LEVEL_2_STR);
+
+        statusNameArray.append(UNKNOWN_DF00, UNKNOWN_DF00_STR);
+        statusNameArray.append(PICTURE_JPEG_COUNT, PICTURE_JPEG_COUNT_STR);
+        statusNameArray.append(UNKNOWN_D400, UNKNOWN_D400_STR);
+        statusNameArray.append(UNKNOWN_D401, UNKNOWN_D401_STR);
+        statusNameArray.append(UNKNOWN_D52F, UNKNOWN_D52F_STR);
+*/
+    }
+
+
+    void updateValue(ICameraStatusUpdateNotify notifier, int id, byte data0, byte data1, byte data2, byte data3)
+    {
+        try
+        {
+            int value = ((((int) data3) & 0xff) << 24) + ((((int) data2) & 0xff) << 16) + ((((int) data1) & 0xff) << 8) + (((int) data0) & 0xff);
+            int currentValue = statusHolder.get(id, -1);
+            Log.v(TAG, "STATUS  ID: " + id + "  value : " + value + " (" + currentValue + ")");
+            statusHolder.put(id, value);
+            if (currentValue != value)
+            {
+                //Log.v(TAG, "STATUS  ID: " + id + " value : " + currentValue + " -> " + value);
+                if (notifier != null)
+                {
+                    updateDetected(notifier, id, currentValue, value);
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void updateDetected(@NonNull ICameraStatusUpdateNotify notifier, int id, int previous, int current)
+    {
+        try
+        {
+            String idName = statusNameArray.get(id, "Unknown");
+            Log.v(TAG, String.format(Locale.US,"<< UPDATE STATUS >> id: 0x%04x[%s] 0x%08x(%d) -> 0x%08x(%d)", id, idName, previous, previous, current, current));
+            //Log.v(TAG, "updateDetected(ID: " + id + " [" + idName + "] " + previous + " -> " + current + " )");
+/*
+            if (id == FOCUS_LOCK)
+            {
+                if (current == 1)
+                {
+                    // focus Lock
+                    notifier.updateFocusedStatus(true, true);
+                }
+                else
+                {
+                    // focus unlock
+                    notifier.updateFocusedStatus(false, false);
+                }
+            }
+*/
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *   認識したカメラのステータス名称のリストを応答する
+     *
+     */
+    private List<String> getAvailableStatusNameList()
+    {
+        ArrayList<String> selection = new ArrayList<>();
+        try
+        {
+            for (int index = 0; index < statusHolder.size(); index++)
+            {
+                int key = statusHolder.keyAt(index);
+                selection.add(statusNameArray.get(key, String.format(Locale.US, "0x%04x", key)));
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (selection);
+
+    }
+
+    List<String> getAvailableItemList(String listKey)
+    {
+        if (listKey == null)
+        {
+            // アイテム名の一覧を応答する
+            return (getAvailableStatusNameList());
+        }
+
+        /////  選択可能なステータスの一覧を取得する : でも以下はアイテム名の一覧... /////
+        ArrayList<String> selection = new ArrayList<>();
+        try
+        {
+            for (int index = 0; index < statusHolder.size(); index++)
+            {
+                int key = statusHolder.keyAt(index);
+                selection.add(statusNameArray.get(key));
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (selection);
+    }
+
+    String getItemStatus(String key)
+    {
+        try
+        {
+            int strIndex = key.indexOf("x");
+            Log.v(TAG, "getItemStatus() : " + key + " [" + strIndex + "]");
+            if (strIndex >= 1)
+            {
+                key = key.substring(strIndex + 1);
+                try
+                {
+                    int id = Integer.parseInt(key, 16);
+                    int value = statusHolder.get(id);
+                    Log.v(TAG, "getItemStatus() value : " + value);
+                    return (value + "");
+                }
+                catch (Exception e)
+                {
+                    e.printStackTrace();
+                }
+            }
+
+            for (int index = 0; index < statusNameArray.size(); index++)
+            {
+                int id = statusNameArray.keyAt(index);
+                String strKey = statusNameArray.valueAt(index);
+                if (key.contentEquals(strKey))
+                {
+                    int value = statusHolder.get(id);
+                    return (value + "");
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return ("? [" + key + "]");
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommunication.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpCommunication.java
new file mode 100644 (file)
index 0000000..19cb066
--- /dev/null
@@ -0,0 +1,7 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command;
+
+public interface IPtpIpCommunication
+{
+    boolean connect();
+    void disconnect();
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpMessages.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/IPtpIpMessages.java
new file mode 100644 (file)
index 0000000..e1120bb
--- /dev/null
@@ -0,0 +1,60 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command;
+
+public interface IPtpIpMessages
+{
+    int SEQ_DUMMY = 0;
+    int SEQ_REGISTRATION = 1;
+    int SEQ_EVENT_INITIALIZE = 2;
+    int SEQ_OPEN_SESSION = 3;
+    int SEQ_INIT_SESSION = 4;
+    int SEQ_CHANGE_REMOTE = 5;
+    int SEQ_SET_EVENT_MODE = 6;
+
+
+    int SEQ_STATUS_REQUEST = 9;
+
+    int GET_STORAGE_ID = 101;
+    int GET_STORAGE_INFO = 102;
+    int GET_OBJECT_INFO_EX = 103;
+    int GET_OBJECT_INFO_EX_2 = 104;
+    int GET_OBJECT_INFO_EX_3 = 105;
+
+    int GET_STORAGE_HANDLE1 = 110;
+    int GET_STORAGE_HANDLE2 = 111;
+    int GET_STORAGE_HANDLE3 = 112;
+    int GET_PARTIAL_OBJECT= 113;
+
+/*
+    int SEQ_REGISTRATION = 1;
+    int SEQ_START = 2;
+    int SEQ_START_2ND = 3;
+    int SEQ_START_2ND_READ = 10;
+    int SEQ_START_2ND_RECEIVE = 4;
+    int SEQ_START_3RD = 5;
+    int SEQ_START_4TH = 6;
+    int SEQ_CAMERA_REMOTE = 7;
+    int SEQ_START_5TH = 8;
+    int SEQ_STATUS_REQUEST = 9;
+    int SEQ_QUERY_CAMERA_CAPABILITIES = 11;
+
+    int SEQ_CHANGE_TO_PLAYBACK_1ST = 12;
+    int SEQ_CHANGE_TO_PLAYBACK_2ND = 13;
+    int SEQ_CHANGE_TO_PLAYBACK_3RD = 14;
+    int SEQ_CHANGE_TO_PLAYBACK_4TH = 15;
+
+    int SEQ_CHANGE_TO_LIVEVIEW_1ST = 16;
+    int SEQ_CHANGE_TO_LIVEVIEW_2ND = 17;
+    int SEQ_CHANGE_TO_LIVEVIEW_3RD = 18;
+    int SEQ_CHANGE_TO_LIVEVIEW_4TH = 19;
+    int SEQ_CHANGE_TO_LIVEVIEW_5TH = 20;
+
+    int SEQ_SET_PROPERTY_VALUE = 21;
+    int SEQ_FOCUS_LOCK = 22;
+    int SEQ_FOCUS_UNLOCK = 23;
+    int SEQ_CAPTURE = 24;
+
+    int SEQ_IMAGE_INFO = 25;
+    int SEQ_THUMBNAIL = 26;
+    int SEQ_FULL_IMAGE = 27;
+*/
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpAsyncResponseReceiver.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpAsyncResponseReceiver.java
new file mode 100644 (file)
index 0000000..2daaa5d
--- /dev/null
@@ -0,0 +1,145 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.InputStream;
+import java.net.Socket;
+
+public class PtpIpAsyncResponseReceiver implements IPtpIpCommunication
+{
+    private final String TAG = toString();
+    private final String ipAddress;
+    private final int portNumber;
+    private static final int BUFFER_SIZE = 1280 + 8;
+    private static final int WAIT_MS = 250;   // 250ms
+    private static final int ERROR_LIMIT = 30;
+    private IPtpIpCommandCallback receiver = null;
+    private boolean isStart = false;
+
+    public PtpIpAsyncResponseReceiver(@NonNull String ip, int portNumber)
+    {
+        this.ipAddress = ip;
+        this.portNumber = portNumber;
+    }
+
+    public void setEventSubscriber(@NonNull IPtpIpCommandCallback receiver)
+    {
+        this.receiver = receiver;
+    }
+
+    @Override
+    public boolean connect()
+    {
+        start();
+        return (true);
+    }
+
+    @Override
+    public void disconnect()
+    {
+        isStart = false;
+    }
+
+    public void start()
+    {
+        if (isStart)
+        {
+            // すでに受信スレッド動作中なので抜ける
+            return;
+        }
+        isStart = true;
+        Thread thread = new Thread(new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    Socket socket = new Socket(ipAddress, portNumber);
+                    startReceive(socket);
+                }
+                catch (Exception e)
+                {
+                    Log.v(TAG, " IP : " + ipAddress + " port : " + portNumber);
+                    e.printStackTrace();
+                }
+            }
+        });
+        try
+        {
+            thread.start();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    public void stop()
+    {
+        isStart = false;
+    }
+
+    private void startReceive(Socket socket)
+    {
+        int errorCount = 0;
+        InputStream isr;
+        byte[] byte_array;
+        try
+        {
+            isr = socket.getInputStream();
+            byte_array = new byte[BUFFER_SIZE];
+
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            Log.v(TAG, "===== startReceive() aborted.");
+            return;
+        }
+        Log.v(TAG, "startReceive() start.");
+        while (isStart)
+        {
+            try
+            {
+                int read_bytes = isr.read(byte_array, 0, BUFFER_SIZE);
+                Log.v(TAG, "RECEIVE ASYNC  : " + read_bytes + " bytes.");
+                if (receiver != null)
+                {
+                    try
+                    {
+                        receiver.receivedMessage(0, byte_array);
+                    }
+                    catch (Exception ee)
+                    {
+                        ee.printStackTrace();
+                    }
+                }
+                Thread.sleep(WAIT_MS);
+                errorCount = 0;
+            }
+            catch (Exception e)
+            {
+                e.printStackTrace();
+                errorCount++;
+            }
+            if (errorCount > ERROR_LIMIT)
+            {
+                // エラーが連続でたくさん出たらループをストップさせる
+                isStart = false;
+            }
+        }
+        try
+        {
+            isr.close();
+            socket.close();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        Log.v(TAG, "startReceive() end.");
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpCommandPublisher.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/PtpIpCommandPublisher.java
new file mode 100644 (file)
index 0000000..dbeef88
--- /dev/null
@@ -0,0 +1,623 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command;
+
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.ArrayDeque;
+import java.util.Arrays;
+import java.util.Queue;
+
+import static net.osdn.gokigen.a01d.camera.utils.SimpleLogDumper.dump_bytes;
+
+public class PtpIpCommandPublisher implements IPtpIpCommandPublisher, IPtpIpCommunication
+{
+    private static final String TAG = PtpIpCommandPublisher.class.getSimpleName();
+
+    private static final int SEQUENCE_START_NUMBER = 1;
+    private static final int BUFFER_SIZE = 1024 * 1024 + 16;  // 受信バッファは 256kB
+    private static final int COMMAND_SEND_RECEIVE_DURATION_MS = 5;
+    private static final int COMMAND_SEND_RECEIVE_DURATION_MAX = 1000;
+    private static final int COMMAND_POLL_QUEUE_MS = 5;
+
+    private final String ipAddress;
+    private final int portNumber;
+
+    private boolean isStart = false;
+    private boolean isHold = false;
+    private int holdId = 0;
+    private Socket socket = null;
+    private DataOutputStream dos = null;
+    private BufferedReader bufferedReader = null;
+    private int sequenceNumber = SEQUENCE_START_NUMBER;
+    private Queue<IPtpIpCommand> commandQueue;
+    private Queue<IPtpIpCommand> holdCommandQueue;
+
+    public PtpIpCommandPublisher(@NonNull String ip, int portNumber)
+    {
+        this.ipAddress = ip;
+        this.portNumber = portNumber;
+        this.commandQueue = new ArrayDeque<>();
+        this.holdCommandQueue = new ArrayDeque<>();
+        commandQueue.clear();
+        holdCommandQueue.clear();
+    }
+
+    @Override
+    public boolean isConnected()
+    {
+        return (socket != null);
+    }
+
+    @Override
+    public boolean connect()
+    {
+        try
+        {
+            socket = new Socket(ipAddress, portNumber);
+            return (true);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            socket = null;
+        }
+        return (false);
+    }
+
+    @Override
+    public void disconnect()
+    {
+        // ストリームを全部閉じる
+        try
+        {
+            if (dos != null)
+            {
+                dos.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        dos = null;
+
+        try
+        {
+            if (bufferedReader != null)
+            {
+                bufferedReader.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        bufferedReader = null;
+
+        try
+        {
+            if (socket != null)
+            {
+                socket.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        socket = null;
+        sequenceNumber = SEQUENCE_START_NUMBER;
+        isStart = false;
+        commandQueue.clear();
+        System.gc();
+    }
+
+    @Override
+    public void start()
+    {
+        if (isStart)
+        {
+            // すでにコマンドのスレッド動作中なので抜ける
+            return;
+        }
+        isStart = true;
+
+        Thread thread = new Thread(new Runnable()
+        {
+            @Override
+            public void run()
+            {
+                try
+                {
+                    dos = new DataOutputStream(socket.getOutputStream());
+                    while (isStart)
+                    {
+                        try
+                        {
+                            IPtpIpCommand command = commandQueue.poll();
+                            if (command != null)
+                            {
+                                issueCommand(command);
+                            }
+                            Thread.sleep(COMMAND_POLL_QUEUE_MS);
+                        }
+                        catch (Exception e)
+                        {
+                            e.printStackTrace();
+                        }
+                    }
+                }
+                catch (Exception e)
+                {
+                    Log.v(TAG, "<<<<< IP : " + ipAddress + " port : " + portNumber + " >>>>>");
+                    e.printStackTrace();
+                }
+            }
+        });
+        try
+        {
+            thread.start();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void stop()
+    {
+        isStart = false;
+        commandQueue.clear();
+    }
+
+    @Override
+    public boolean enqueueCommand(@NonNull IPtpIpCommand command)
+    {
+        try
+        {
+            if (isHold) {
+                if (holdId == command.getHoldId()) {
+                    if (command.isRelease()) {
+                        // コマンドをキューに積んだ後、リリースする
+                        boolean ret = commandQueue.offer(command);
+                        isHold = false;
+
+                        //  溜まっているキューを積みなおす
+                        while (holdCommandQueue.size() != 0) {
+                            IPtpIpCommand queuedCommand = holdCommandQueue.poll();
+                            commandQueue.offer(queuedCommand);
+                            if ((queuedCommand != null)&&(queuedCommand.isHold()))
+                            {
+                                // 特定シーケンスに入った場合は、そこで積みなおすのをやめる
+                                isHold = true;
+                                holdId = queuedCommand.getHoldId();
+                                break;
+                            }
+                        }
+                        return (ret);
+                    }
+                    return (commandQueue.offer(command));
+                } else {
+                    // 特定シーケンスではなかったので HOLD
+                    return (holdCommandQueue.offer(command));
+                }
+            }
+            if (command.isHold())
+            {
+                isHold = true;
+                holdId = command.getHoldId();
+            }
+            //Log.v(TAG, "Enqueue : "  + command.getId());
+            return (commandQueue.offer(command));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (false);
+    }
+
+    @Override
+    public boolean flushHoldQueue()
+    {
+        Log.v(TAG, "  flushHoldQueue()");
+        holdCommandQueue.clear();
+        System.gc();
+        return (true);
+    }
+
+    private void issueCommand(@NonNull IPtpIpCommand command)
+    {
+        try
+        {
+            boolean retry_over = true;
+            while (retry_over)
+            {
+                //Log.v(TAG, "issueCommand : " + command.getId());
+                byte[] commandBody = command.commandBody();
+                if (commandBody != null)
+                {
+                    // コマンドボディが入っていた場合には、コマンド送信(入っていない場合は受信待ち)
+                    send_to_camera(command.dumpLog(), commandBody, command.useSequenceNumber(), command.embeddedSequenceNumberIndex());
+                    byte[] commandBody2 = command.commandBody2();
+                    if (commandBody2 != null)
+                    {
+                        // コマンドボディの2つめが入っていた場合には、コマンドを連続送信する
+                        send_to_camera(command.dumpLog(), commandBody2, command.useSequenceNumber(), command.embeddedSequenceNumberIndex2());
+                    }
+                    byte[] commandBody3 = command.commandBody3();
+                    if (commandBody3 != null)
+                    {
+                        // コマンドボディの3つめが入っていた場合には、コマンドを連続送信する
+                        send_to_camera(command.dumpLog(), commandBody3, command.useSequenceNumber(), command.embeddedSequenceNumberIndex3());
+                    }
+                    if (command.isIncrementSeqNumber())
+                    {
+                        // シーケンス番号を更新する
+                        sequenceNumber++;
+                    }
+                }
+                retry_over = receive_from_camera(command);
+                if ((retry_over)&&(commandBody != null))
+                {
+                    // 再送信...のために、シーケンス番号を戻す...
+                    sequenceNumber--;
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *    カメラにコマンドを送信する(メイン部分)
+     *
+     */
+    private void send_to_camera(boolean isDumpReceiveLog, byte[] byte_array, boolean useSequenceNumber, int embeddedSequenceIndex)
+    {
+        try
+        {
+            if (dos == null)
+            {
+                Log.v(TAG, " DataOutputStream is null.");
+                return;
+            }
+
+            //dos = new DataOutputStream(socket.getOutputStream());  // ここにいたらいけない?
+
+            // メッセージボディを加工: 最初に4バイトのレングス長をつける
+            byte[] sendData = new byte[byte_array.length + 4];
+
+            sendData[0] = (byte) (byte_array.length + 4);
+            sendData[1] = 0x00;
+            sendData[2] = 0x00;
+            sendData[3] = 0x00;
+            System.arraycopy(byte_array,0,sendData,4, byte_array.length);
+
+            if (useSequenceNumber)
+            {
+                // Sequence Number を反映させる
+                sendData[embeddedSequenceIndex] = (byte) ((0x000000ff & sequenceNumber));
+                sendData[embeddedSequenceIndex + 1] = (byte) (((0x0000ff00 & sequenceNumber) >>> 8) & 0x000000ff);
+                sendData[embeddedSequenceIndex + 2] = (byte) (((0x00ff0000 & sequenceNumber) >>> 16) & 0x000000ff);
+                sendData[embeddedSequenceIndex + 3] = (byte) (((0xff000000 & sequenceNumber) >>> 24) & 0x000000ff);
+                if (isDumpReceiveLog)
+                {
+                    Log.v(TAG, "----- SEQ No. : " + sequenceNumber + " -----");
+                }
+            }
+
+            if (isDumpReceiveLog)
+            {
+                // ログに送信メッセージを出力する
+                dump_bytes("SEND[" + sendData.length + "] ", sendData);
+            }
+
+            // (データを)送信
+            dos.write(sendData);
+            dos.flush();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void sleep(int delayMs)
+    {
+        try
+        {
+            Thread.sleep(delayMs);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     *    カメラからにコマンドの結果を受信する(メイン部分)
+     *
+     */
+    private boolean receive_from_camera(@NonNull IPtpIpCommand command)
+    {
+        IPtpIpCommandCallback callback = command.responseCallback();
+        int delayMs = command.receiveDelayMs();
+        if ((delayMs < 0)||(delayMs > COMMAND_SEND_RECEIVE_DURATION_MAX))
+        {
+            delayMs = COMMAND_SEND_RECEIVE_DURATION_MS;
+        }
+        if ((callback != null)&&(callback.isReceiveMulti()))
+        {
+            // 受信したら逐次「受信したよ」と応答するパターン
+            return (receive_multi(command, delayMs));
+        }
+        //  受信した後、すべてをまとめて「受信したよ」と応答するパターン
+        return (receive_single(command, delayMs));
+    }
+
+    private boolean receive_single(@NonNull IPtpIpCommand command, int delayMs)
+    {
+        boolean isDumpReceiveLog = command.dumpLog();
+        int id = command.getId();
+        IPtpIpCommandCallback callback = command.responseCallback();
+        try
+        {
+            int receive_message_buffer_size = BUFFER_SIZE;
+            byte[] byte_array = new byte[receive_message_buffer_size];
+            InputStream is = socket.getInputStream();
+            if (is == null)
+            {
+                Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.");
+                return (false);
+            }
+
+            // 初回データが受信バッファにデータが溜まるまで待つ...
+            int read_bytes = waitForReceive(is, delayMs);
+            if (read_bytes < 0)
+            {
+                // リトライオーバー...
+                Log.v(TAG, " RECEIVE : RETRY OVER...");
+                return (true);
+            }
+
+            // 受信したデータをバッファに突っ込む
+            ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+            while (read_bytes > 0)
+            {
+                read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
+                if (read_bytes <= 0)
+                {
+                    Log.v(TAG, " RECEIVED MESSAGE FINISHED (" + read_bytes + ")");
+                    break;
+                }
+                byteStream.write(byte_array, 0, read_bytes);
+                sleep(delayMs);
+                read_bytes = is.available();
+            }
+            ByteArrayOutputStream outputStream = cutHeader(byteStream);
+            receivedAllMessage(isDumpReceiveLog, id, outputStream.toByteArray(), callback);
+            System.gc();
+        }
+        catch (Throwable e)
+        {
+            e.printStackTrace();
+            System.gc();
+        }
+        return (false);
+    }
+
+    private void receivedAllMessage(boolean isDumpReceiveLog, int id, byte[] body, IPtpIpCommandCallback callback)
+    {
+        Log.v(TAG, "receivedAllMessage() : " + ((body == null) ? 0 : body.length) + " bytes.");
+        if ((isDumpReceiveLog)&&(body != null))
+        {
+            // ログに受信メッセージを出力する
+            dump_bytes("RECV[" + body.length + "] ", body);
+        }
+        if (callback != null)
+        {
+            callback.receivedMessage(id, body);
+        }
+    }
+
+    private boolean receive_multi(@NonNull IPtpIpCommand command, int delayMs)
+    {
+        int estimatedSize = command.estimatedReceiveDataSize();
+        int id = command.getId();
+        IPtpIpCommandCallback callback = command.responseCallback();
+
+        try
+        {
+            Log.v(TAG, " ===== receive_multi() =====");
+            int receive_message_buffer_size = BUFFER_SIZE;
+            byte[] byte_array = new byte[receive_message_buffer_size];
+            InputStream is = socket.getInputStream();
+            if (is == null)
+            {
+                Log.v(TAG, " InputStream is NULL... RECEIVE ABORTED.");
+                return (false);
+            }
+
+            // 初回データが受信バッファにデータが溜まるまで待つ...
+            int read_bytes = waitForReceive(is, delayMs);
+            if (read_bytes < 0)
+            {
+                // リトライオーバー...
+                Log.v(TAG, " RECEIVE : RETRY OVER......");
+                return (true);
+            }
+
+            // 初回データの読み込み
+            read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
+            int target_length = parseDataLength(byte_array, read_bytes);
+            int received_length = read_bytes;
+
+            //  一時的な処理
+            if (callback != null)
+            {
+                Log.v(TAG, "  --- 1st CALL : read_bytes : "+ read_bytes + "(" + received_length + ") : total_length : " + target_length + "  buffer SIZE : " + byte_array.length);
+                callback.onReceiveProgress(received_length, target_length, Arrays.copyOfRange(byte_array, 0, received_length));
+            }
+
+            do
+            {
+                sleep(delayMs);
+                read_bytes = is.available();
+                if (read_bytes == 0)
+                {
+                    Log.v(TAG, " WAIT is.available() ... " + received_length + " < " + estimatedSize);
+                }
+            } while ((read_bytes == 0)&&(estimatedSize > 0)&&(received_length < estimatedSize));
+            while (read_bytes > 0)
+            {
+                read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
+                if (read_bytes <= 0)
+                {
+                    Log.v(TAG, "  RECEIVED MESSAGE FINISHED (" + read_bytes + ")");
+                    break;
+                }
+                received_length = received_length + read_bytes;
+
+                //  一時的な処理
+                if (callback != null)
+                {
+                    //Log.v(TAG, "  --- CALL : read_bytes : "+ read_bytes + " total_read : " + received_length + " : total_length : " + target_length + "  buffer SIZE : " + byte_array.length);
+                    callback.onReceiveProgress(received_length, target_length, Arrays.copyOfRange(byte_array, 0, read_bytes));
+                }
+                //byteStream.write(byte_array, 0, read_bytes);
+
+                do
+                {
+                    sleep(delayMs);
+                    read_bytes = is.available();
+                    //Log.v(TAG, "  is.available() read_bytes : " + read_bytes + " " + received_length + " < " + estimatedSize);
+                } while ((read_bytes == 0)&&(estimatedSize > 0)&&(received_length < estimatedSize));
+            }
+            //ByteArrayOutputStream outputStream = cutHeader(byteStream);
+            //receivedMessage(isDumpReceiveLog, id, outputStream.toByteArray(), callback);
+
+            //  終了報告...一時的?
+            if (callback != null)
+            {
+                Log.v(TAG, "  --- receive_multi : receivedMessage() : " + id + "  (" + read_bytes + ") [" + estimatedSize + "] " + receive_message_buffer_size + " (" + received_length + ")");
+                callback.receivedMessage(id, null);
+            }
+            System.gc();
+        }
+        catch (Throwable e)
+        {
+            e.printStackTrace();
+            System.gc();
+        }
+        return (false);
+    }
+
+    private int parseDataLength(byte[] byte_array, int read_bytes)
+    {
+        int lenlen = 0;
+        int packetType = 0;
+        try
+        {
+            if ((read_bytes > 20)&&((int) byte_array[4] == 0x09))
+            {
+                lenlen = ((((int) byte_array[15]) & 0xff) << 24) + ((((int) byte_array[14]) & 0xff) << 16) + ((((int) byte_array[13]) & 0xff) << 8) + (((int) byte_array[12]) & 0xff);
+                packetType = (((int)byte_array[16]) & 0xff);
+            }
+            Log.v(TAG, " --- parseDataLength() length: " + lenlen + " TYPE: " + packetType + " read_bytes: " + read_bytes);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (lenlen);
+    }
+
+    private ByteArrayOutputStream cutHeader(ByteArrayOutputStream receivedBuffer)
+    {
+        try
+        {
+            byte[] byte_array = receivedBuffer.toByteArray();
+            int limit = byte_array.length;
+            int lenlen = 0;
+            int len = ((((int) byte_array[3]) & 0xff) << 24) + ((((int) byte_array[2]) & 0xff) << 16) + ((((int) byte_array[1]) & 0xff) << 8) + (((int) byte_array[0]) & 0xff);
+            int packetType = (((int) byte_array[4]) & 0xff);
+            if ((limit == len)||(limit < 16384))
+            {
+                // 応答は1つしか入っていない。もしくは受信データサイズが16kBの場合は、そのまま返す。
+                return (receivedBuffer);
+            }
+            if (packetType == 0x09)
+            {
+                lenlen = ((((int) byte_array[15]) & 0xff) << 24) + ((((int) byte_array[14]) & 0xff) << 16) + ((((int) byte_array[13]) & 0xff) << 8) + (((int) byte_array[12]) & 0xff);
+                packetType = (((int) byte_array[16]) & 0xff);
+            }
+            Log.v(TAG, " ---  RECEIVED MESSAGE : " + len + " bytes (BUFFER: " + byte_array.length + " bytes)" + " length : " + lenlen + " TYPE : " + packetType + " --- ");
+            if (lenlen == 0)
+            {
+                // データとしては変なので、なにもしない
+                return (receivedBuffer);
+            }
+            ByteArrayOutputStream outputStream =  new ByteArrayOutputStream();
+            //outputStream.write(byte_array, 0, 20);  //
+            int position = 20;  // ヘッダ込の先頭
+            while (position < limit)
+            {
+                lenlen = ((((int) byte_array[position + 3]) & 0xff) << 24) + ((((int) byte_array[position + 2]) & 0xff) << 16) + ((((int) byte_array[position + 1]) & 0xff) << 8) + (((int) byte_array[position]) & 0xff);
+                packetType = (((int) byte_array[position + 4]) & 0xff);
+                if (packetType != 0x0a)
+                {
+                    Log.v(TAG, " <><><> PACKET TYPE : " + packetType + " LENGTH : " + lenlen);
+                }
+                int copyByte = ((lenlen - 12) > (limit - (position + 12))) ? (limit - (position + 12)) : (lenlen - 12);
+                outputStream.write(byte_array, (position + 12), copyByte);
+                position = position + lenlen;
+            }
+            return (outputStream);
+        }
+        catch (Throwable e)
+        {
+            e.printStackTrace();
+            System.gc();
+        }
+        return (receivedBuffer);
+    }
+
+    private int waitForReceive(InputStream is, int delayMs)
+    {
+        int retry_count = 50;
+        int read_bytes = 0;
+        try
+        {
+            while (read_bytes <= 0)
+            {
+                sleep(delayMs);
+                read_bytes = is.available();
+                if (read_bytes == 0)
+                {
+                    Log.v(TAG, " is.available() WAIT... ");
+                    retry_count--;
+                    if (retry_count < 0)
+                    {
+                        return (-1);
+                    }
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (read_bytes);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandBase.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandBase.java
new file mode 100644 (file)
index 0000000..a00ef84
--- /dev/null
@@ -0,0 +1,110 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommand;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpMessages;
+
+public class PtpIpCommandBase implements IPtpIpCommand, IPtpIpMessages
+{
+    @Override
+    public int getId()
+    {
+        return (SEQ_DUMMY);
+    }
+
+    @Override
+    public boolean receiveAgainShortLengthMessage()
+    {
+        return (true);
+    }
+
+    @Override
+    public boolean useSequenceNumber()
+    {
+        return (true);
+    }
+
+    @Override
+    public boolean isIncrementSeqNumber()
+    {
+        return (true);
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (15);
+    }
+
+    @Override
+    public int embeddedSequenceNumberIndex()
+    {
+        return (14);
+    }
+
+    @Override
+    public int embeddedSequenceNumberIndex2()
+    {
+        return (8);
+    }
+
+    @Override
+    public int embeddedSequenceNumberIndex3()
+    {
+        return (8);
+    }
+
+    @Override
+    public int estimatedReceiveDataSize()
+    {
+        return (-1);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[12]);
+    }
+
+    @Override
+    public byte[] commandBody2()
+    {
+        return (null);
+    }
+
+    @Override
+    public byte[] commandBody3()
+    {
+        return (null);
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (null);
+    }
+
+    @Override
+    public int getHoldId()
+    {
+        return (0);
+    }
+
+    @Override
+    public boolean isHold()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean isRelease()
+    {
+        return (false);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (true);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandGeneric.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpCommandGeneric.java
new file mode 100644 (file)
index 0000000..962c06d
--- /dev/null
@@ -0,0 +1,409 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+
+public class PtpIpCommandGeneric extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final boolean isDumpLog;
+    private final int bodySize;
+    private final int id;
+    private final int holdId;
+    private final int estimatedObjectSize;
+
+    private final int delayMs;
+
+    private final byte opCode0;
+    private final byte opCode1;
+
+    private final byte data0;
+    private final byte data1;
+    private final byte data2;
+    private final byte data3;
+
+    private final byte data4;
+    private final byte data5;
+    private final byte data6;
+    private final byte data7;
+
+    private final byte data8;
+    private final byte data9;
+    private final byte dataA;
+    private final byte dataB;
+
+    private final byte dataC;
+    private final byte dataD;
+    private final byte dataE;
+    private final byte dataF;
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int opcode)
+    {
+        this.callback = callback;
+        this.bodySize = 0;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = 15;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = 0;
+        data1 = 0;
+        data2 = 0;
+        data3 = 0;
+
+        data4 = 0;
+        data5 = 0;
+        data6 = 0;
+        data7 = 0;
+
+        data8 = 0; // ((byte) (0x000000ff & value3));
+        data9 = 0; // ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = 0; // ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = 0; // ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = 0; // ((byte) (0x000000ff & value4));
+        dataD = 0; // ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = 0; // ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = 0; // ((byte)((0xff000000 & value4) >> 24));
+    }
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int opcode, int bodySize, int value)
+    {
+        this.callback = callback;
+        this.bodySize = bodySize;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = 15;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = 0; // ((byte) (0x000000ff & value));
+        data5 = 0; // ((byte)((0x0000ff00 & value) >> 8));
+        data6 = 0; // ((byte)((0x00ff0000 & value) >> 16));
+        data7 = 0; // ((byte)((0xff000000 & value) >> 24));
+
+        data8 = 0; // ((byte) (0x000000ff & value3));
+        data9 = 0; // ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = 0; // ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = 0; // ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = 0; // ((byte) (0x000000ff & value4));
+        dataD = 0; // ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = 0; // ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = 0; // ((byte)((0xff000000 & value4) >> 24));
+    }
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int opcode, int bodySize, int value, int value2)
+    {
+        this.callback = callback;
+        this.bodySize = bodySize;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = 15;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = ((byte) (0x000000ff & value2));
+        data5 = ((byte)((0x0000ff00 & value2) >> 8));
+        data6 = ((byte)((0x00ff0000 & value2) >> 16));
+        data7 = ((byte)((0xff000000 & value2) >> 24));
+
+        data8 = 0; // ((byte) (0x000000ff & value3));
+        data9 = 0; // ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = 0; // ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = 0; // ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = 0; // ((byte) (0x000000ff & value4));
+        dataD = 0; // ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = 0; // ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = 0; // ((byte)((0xff000000 & value4) >> 24));
+    }
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int opcode, int bodySize, int value, int value2, int value3)
+    {
+        this.callback = callback;
+        this.bodySize = bodySize;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = 15;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = ((byte) (0x000000ff & value2));
+        data5 = ((byte)((0x0000ff00 & value2) >> 8));
+        data6 = ((byte)((0x00ff0000 & value2) >> 16));
+        data7 = ((byte)((0xff000000 & value2) >> 24));
+
+        data8 = ((byte) (0x000000ff & value3));
+        data9 = ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = 0; // ((byte) (0x000000ff & value4));
+        dataD = 0; // ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = 0; // ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = 0; // ((byte)((0xff000000 & value4) >> 24));
+
+    }
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, boolean isDumpLog, int holdId, int opcode, int bodySize, int value, int value2, int value3, int value4)
+    {
+        this.callback = callback;
+        this.bodySize = bodySize;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = 15;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = ((byte) (0x000000ff & value2));
+        data5 = ((byte)((0x0000ff00 & value2) >> 8));
+        data6 = ((byte)((0x00ff0000 & value2) >> 16));
+        data7 = ((byte)((0xff000000 & value2) >> 24));
+
+        data8 = ((byte) (0x000000ff & value3));
+        data9 = ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = ((byte) (0x000000ff & value4));
+        dataD = ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = ((byte)((0xff000000 & value4) >> 24));
+    }
+
+    public PtpIpCommandGeneric(@NonNull IPtpIpCommandCallback callback, int id, int delayMs, boolean isDumpLog, int holdId, int opcode, int bodySize, int value, int value2, int value3, int value4)
+    {
+        this.callback = callback;
+        this.bodySize = bodySize;
+        this.isDumpLog = isDumpLog;
+        this.estimatedObjectSize = -1;
+        this.delayMs = delayMs;
+
+        this.id = id;
+        this.holdId = holdId;
+        opCode0 = ((byte) (0x000000ff & opcode));
+        opCode1 = ((byte)((0x0000ff00 & opcode) >> 8));
+
+        data0 = ((byte) (0x000000ff & value));
+        data1 = ((byte)((0x0000ff00 & value) >> 8));
+        data2 = ((byte)((0x00ff0000 & value) >> 16));
+        data3 = ((byte)((0xff000000 & value) >> 24));
+
+        data4 = ((byte) (0x000000ff & value2));
+        data5 = ((byte)((0x0000ff00 & value2) >> 8));
+        data6 = ((byte)((0x00ff0000 & value2) >> 16));
+        data7 = ((byte)((0xff000000 & value2) >> 24));
+
+        data8 = ((byte) (0x000000ff & value3));
+        data9 = ((byte)((0x0000ff00 & value3) >> 8));
+        dataA = ((byte)((0x00ff0000 & value3) >> 16));
+        dataB = ((byte)((0xff000000 & value3) >> 24));
+
+        dataC = ((byte) (0x000000ff & value4));
+        dataD = ((byte)((0x0000ff00 & value4) >> 8));
+        dataE = ((byte)((0x00ff0000 & value4) >> 16));
+        dataF = ((byte)((0xff000000 & value4) >> 24));
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public int estimatedReceiveDataSize()
+    {
+        return (estimatedObjectSize);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        if (bodySize == 2)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data ...
+                    data0, data1,
+            });
+        }
+        else if (bodySize == 4)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data ...
+                    data0, data1, data2, data3,
+            });
+        }
+        else if (bodySize == 8)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data ...
+                    data0, data1, data2, data3,
+                    data4, data5, data6, data7,
+            });
+        }
+        else if (bodySize == 12)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data ...
+                    data0, data1, data2, data3,
+                    data4, data5, data6, data7,
+                    data8, data9, dataA, dataB,
+            });
+        }
+        else if (bodySize == 16)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // data ...
+                    data0, data1, data2, data3,
+                    data4, data5, data6, data7,
+                    data8, data9, dataA, dataB,
+                    dataC, dataD, dataE, dataF,
+            });
+        }
+        else //  ボディ長が 2, 4, 8, 12 以外の場合... (ボディなし)
+        {
+            return (new byte[]{
+
+                    // packet type
+                    (byte) 0x06, (byte) 0x00,  (byte) 0x00, (byte) 0x00,
+
+                    // data phase info
+                    (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+
+                    // operation code
+                    opCode0, opCode1,
+
+                    // sequence number
+                    (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00,
+            });
+        }
+    }
+
+    @Override
+    public int receiveDelayMs()
+    {
+        return (delayMs);
+    }
+
+    @Override
+    public int getHoldId()
+    {
+        return (holdId);
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (isDumpLog);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpReceiveOnly.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/PtpIpReceiveOnly.java
new file mode 100644 (file)
index 0000000..7bdd17c
--- /dev/null
@@ -0,0 +1,35 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+
+public class PtpIpReceiveOnly extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+    private final int id;
+
+    public PtpIpReceiveOnly(int id, @NonNull IPtpIpCommandCallback callback)
+    {
+        this.callback = callback;
+        this.id = id;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (id);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (null);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/specific/StatusRequestMessage.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/command/messages/specific/StatusRequestMessage.java
new file mode 100644 (file)
index 0000000..01e2976
--- /dev/null
@@ -0,0 +1,53 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.specific;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.messages.PtpIpCommandBase;
+
+public class StatusRequestMessage extends PtpIpCommandBase
+{
+    private final IPtpIpCommandCallback callback;
+
+    public StatusRequestMessage(@NonNull IPtpIpCommandCallback callback)
+    {
+        this.callback = callback;
+    }
+
+    @Override
+    public IPtpIpCommandCallback responseCallback()
+    {
+        return (callback);
+    }
+
+    @Override
+    public int getId()
+    {
+        return (SEQ_STATUS_REQUEST);
+    }
+
+    @Override
+    public byte[] commandBody()
+    {
+        return (new byte[] {
+
+                // message_header.index : uint16 (0: terminate, 2: two_part_message, 1: other)
+                (byte)0x01, (byte)0x00,
+
+                // message_header.type : single_part (0x1015) : 0xd212 (status_request)
+                (byte)0x15, (byte)0x10,
+
+                // sequence number
+                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
+
+                // data ...
+                (byte)0x12, (byte)0xd2, (byte)0x00, (byte)0x00,
+        });
+    }
+
+    @Override
+    public boolean dumpLog()
+    {
+        return (false);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpCameraProperties.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpCameraProperties.java
new file mode 100644 (file)
index 0000000..35f91c9
--- /dev/null
@@ -0,0 +1,97 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.status;
+
+public interface IPtpIpCameraProperties
+{
+    int BATTERY_LEVEL         = 0x5001;
+    int WHITE_BALANCE         = 0x5005;
+    int APERTURE               = 0x5007;
+    int FOCUS_MODE            = 0x500a;
+    int SHOOTING_MODE         = 0x500e;
+    int FLASH                 = 0x500c;
+    int EXPOSURE_COMPENSATION = 0x5010;
+    int SELF_TIMER            = 0x5012;
+    int FILM_SIMULATION       = 0xd001;
+    int IMAGE_FORMAT          = 0xd018;
+    int RECMODE_ENABLE        = 0xd019;
+    int F_SS_CONTROL          = 0xd028;
+    int ISO                   = 0xd02a;
+    int MOVIE_ISO             = 0xd02b;
+    int FOCUS_POINT           = 0xd17c;
+    int FOCUS_LOCK            = 0xd209;
+    int DEVICE_ERROR          = 0xd21b;
+    int IMAGE_FILE_COUNT = 0xd222;
+    int SDCARD_REMAIN_SIZE    = 0xd229;
+    int MOVIE_REMAINING_TIME  = 0xd22a;
+    int SHUTTER_SPEED         = 0xd240;
+    int IMAGE_ASPECT          = 0xd241;
+    int BATTERY_LEVEL_2       = 0xd242;
+    int UNKNOWN_DF00             = 0xdf00;
+    int PICTURE_JPEG_COUNT = 0xd220;
+    int UNKNOWN_D400             = 0xd400;
+    int UNKNOWN_D401             = 0xd401;
+    int UNKNOWN_D52F             = 0xd52f;
+
+
+
+    String BATTERY_LEVEL_STR         = "Battery";
+    String WHITE_BALANCE_STR         = "WhiteBalance";
+    String APERTURE_STR               = "Aperture";
+    String FOCUS_MODE_STR            = "FocusMode";
+    String SHOOTING_MODE_STR         = "ShootingMode";
+    String FLASH_STR                 = "FlashMode";
+    String EXPOSURE_COMPENSATION_STR = "ExposureCompensation";
+    String SELF_TIMER_STR            = "SelfTimer";
+    String FILM_SIMULATION_STR       = "FilmSimulation";
+    String IMAGE_FORMAT_STR          = "ImageFormat";
+    String RECMODE_ENABLE_STR        = "RecModeEnable";
+    String F_SS_CONTROL_STR          = "F_SS_Control";
+    String ISO_STR                   = "Iso";
+    String MOVIE_ISO_STR             = "MovieIso";
+    String FOCUS_POINT_STR           = "FocusPoint";
+    String FOCUS_LOCK_STR            = "FocusLock";
+    String DEVICE_ERROR_STR          = "DeviceError";
+    String IMAGE_FILE_COUNT_STR = "ImageFileCount";
+    String SDCARD_REMAIN_SIZE_STR    = "ImageRemainCount";
+    String MOVIE_REMAINING_TIME_STR  = "MovieRemainTime";
+    String SHUTTER_SPEED_STR         = "ShutterSpeed";
+    String IMAGE_ASPECT_STR          = "ImageAspect";
+    String BATTERY_LEVEL_2_STR       = "BatteryLevel";
+
+    String UNKNOWN_DF00_STR             = "0xdf00";
+    String PICTURE_JPEG_COUNT_STR = "PictureCount";
+    String UNKNOWN_D400_STR             = "0xd400";
+    String UNKNOWN_D401_STR             = "0xd401";
+    String UNKNOWN_D52F_STR             = "0xd52f";
+
+
+    String BATTERY_LEVEL_STR_ID         = "0x5001";
+    String WHITE_BALANCE_STR_ID         = "0x5005";
+    String APERTURE_STR_ID               = "0x5007";
+    String FOCUS_MODE_STR_ID            = "0x500a";
+    String SHOOTING_MODE_STR_ID         = "0x500e";
+    String FLASH_STR_ID                 = "0x500c";
+    String EXPOSURE_COMPENSATION_STR_ID = "0x5010";
+    String SELF_TIMER_STR_ID            = "0x5012";
+    String FILM_SIMULATION_STR_ID       = "0xd001";
+    String IMAGE_FORMAT_STR_ID          = "0xd018";
+    String RECMODE_ENABLE_STR_ID        = "0xd019";
+    String F_SS_CONTROL_STR_ID          = "0xd028";
+    String ISO_STR_ID                   = "0xd02a";
+    String MOVIE_ISO_STR_ID             = "0xd02b";
+    String FOCUS_POINT_STR_ID           = "0xd17c";
+    String FOCUS_LOCK_STR_ID            = "0xd209";
+    String DEVICE_ERROR_STR_ID          = "0xd21b";
+    String IMAGE_FILE_COUNT_STR_ID = "0xd222";
+    String SDCARD_REMAIN_SIZE_STR_ID    = "0xd229";
+    String MOVIE_REMAINING_TIME_STR_ID  = "0xd22a";
+    String SHUTTER_SPEED_STR_ID         = "0xd240";
+    String IMAGE_ASPECT_STR_ID          = "0xd241";
+    String BATTERY_LEVEL_2_STR_ID       = "0xd242";
+
+    String UNKNOWN_DF00_STR_ID             = "0xdf00";
+    String PICTURE_JPEG_COUNT_STR_ID = "0xd220";
+    String UNKNOWN_D400_STR_ID             = "0xd400";
+    String UNKNOWN_D401_STR_ID             = "0xd401";
+    String UNKNOWN_D52F_STR_ID             = "0xd52f";
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpInitEventRequest.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpInitEventRequest.java
new file mode 100644 (file)
index 0000000..4a7e773
--- /dev/null
@@ -0,0 +1,9 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.status;
+
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommand;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+
+public interface IPtpIpInitEventRequest
+{
+    IPtpIpCommand getInitEventRequest(IPtpIpCommandCallback callback,  int connectionNumber);
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpRunModeHolder.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/IPtpIpRunModeHolder.java
new file mode 100644 (file)
index 0000000..7d3a9ae
--- /dev/null
@@ -0,0 +1,7 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.status;
+
+public interface IPtpIpRunModeHolder
+{
+    void transitToRecordingMode(boolean isFinished);
+    void transitToPlaybackMode(boolean isFinished);
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusChecker.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusChecker.java
new file mode 100644 (file)
index 0000000..1835d7b
--- /dev/null
@@ -0,0 +1,410 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.status;
+
+import android.app.Activity;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.a01d.camera.ICameraStatus;
+import net.osdn.gokigen.a01d.camera.ICameraStatusWatcher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommand;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.a01d.camera.ptpip.wrapper.command.IPtpIpMessages;
+import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
+
+import java.io.BufferedReader;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.net.Socket;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import static net.osdn.gokigen.a01d.camera.utils.SimpleLogDumper.dump_bytes;
+
+public class PtpIpStatusChecker implements IPtpIpCommandCallback, ICameraStatusWatcher, ICameraStatus
+{
+    private final String TAG = toString();
+
+    private static final int BUFFER_SIZE = 1024 * 1024 + 8;
+    private static final int STATUS_MESSAGE_HEADER_SIZE = 14;
+    private int sleepMs;
+    private final IPtpIpCommandPublisher issuer;
+    private ICameraStatusUpdateNotify notifier = null;
+    private PtpIpStatusHolder statusHolder;
+    private boolean whileFetching = false;
+    private boolean logcat = false;
+    private final String ipAddress;
+    private final int portNumber;
+    private final IPtpIpInitEventRequest eventRequest;
+
+    private Socket socket = null;
+    private DataOutputStream dos = null;
+    private BufferedReader bufferedReader = null;
+    private int eventConnectionNumber = 0;
+
+    public PtpIpStatusChecker(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher issuer, @NonNull String ip, int portNumber, @NonNull IPtpIpInitEventRequest eventRequest)
+    {
+        this.issuer = issuer;
+        this.statusHolder = new PtpIpStatusHolder();
+        this.ipAddress = ip;
+        this.portNumber = portNumber;
+        this.eventRequest = eventRequest;
+        Log.v(TAG, "POLLING WAIT : " + sleepMs);
+    }
+
+    @Override
+    public void onReceiveProgress(int currentBytes, int totalBytes, byte[] body)
+    {
+        Log.v(TAG, " " + currentBytes + "/" + totalBytes);
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (false);
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] data)
+    {
+        try
+        {
+            logcat("receivedMessage : " + id + ", length: " + data.length);
+            if (id == IPtpIpMessages.SEQ_EVENT_INITIALIZE)
+            {
+                // 終わる
+                Log.v(TAG, " ----- PTP-IP Connection is ESTABLISHED. -----");
+                return;
+            }
+            if (data.length < STATUS_MESSAGE_HEADER_SIZE)
+            {
+                Log.v(TAG, "received status length is short. (" + data.length + " bytes.)");
+                return;
+            }
+
+            int nofStatus = (data[13] * 256) + data[12];
+            int statusCount = 0;
+            int index = STATUS_MESSAGE_HEADER_SIZE;
+            while ((statusCount < nofStatus)&&(index < data.length))
+            {
+                int dataId = ((((int)data[index + 1]) & 0xff) * 256) + (((int) data[index]) & 0xff);
+                statusHolder.updateValue(notifier, dataId, data[index + 2], data[index + 3], data[index +4], data[index + 5]);
+                index = index + 6;
+                statusCount++;
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public List<String> getStatusList(String key)
+    {
+        try
+        {
+            if (statusHolder == null)
+            {
+                return (new ArrayList<>());
+            }
+            return (statusHolder.getAvailableItemList(key));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (new ArrayList<>());
+    }
+
+    @Override
+    public String getStatus(String key)
+    {
+        try
+        {
+            if (statusHolder == null)
+            {
+                return ("");
+            }
+            return (statusHolder.getItemStatus(key));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return ("");
+    }
+
+    @Override
+    public void setStatus(String key, String value)
+    {
+        try
+        {
+            if (logcat)
+            {
+                Log.v(TAG, "setStatus(" + key + ", " + value + ")");
+            }
+
+            // ここで設定を行う。
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void startStatusWatch(@NonNull ICameraStatusUpdateNotify notifier)
+    {
+        if (whileFetching)
+        {
+            Log.v(TAG, "startStatusWatch() already starting.");
+            return;
+        }
+        try
+        {
+            this.notifier = notifier;
+            whileFetching = true;
+
+            // セッションをオープンする
+            boolean isConnect = connect();
+            if (!isConnect)
+            {
+                Log.v(TAG, "  CONNECT FAIL...(EVENT) : " + ipAddress + "  " + portNumber);
+            }
+            issueCommand(eventRequest.getInitEventRequest(this, eventConnectionNumber));
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    @Override
+    public void stopStatusWatch()
+    {
+        Log.v(TAG, "stoptStatusWatch()");
+        whileFetching = false;
+        this.notifier = null;
+    }
+
+    private void logcat(String message)
+    {
+        if (logcat)
+        {
+            Log.v(TAG, message);
+        }
+    }
+
+    private boolean connect()
+    {
+        try
+        {
+            socket = new Socket(ipAddress, portNumber);
+            return (true);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            socket = null;
+        }
+        return (false);
+    }
+
+    private void disconnect()
+    {
+        // ストリームを全部閉じる
+        try
+        {
+            if (dos != null)
+            {
+                dos.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        dos = null;
+
+        try
+        {
+            if (bufferedReader != null)
+            {
+                bufferedReader.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        bufferedReader = null;
+
+        try
+        {
+            if (socket != null)
+            {
+                socket.close();
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        socket = null;
+        System.gc();
+    }
+
+    public void setEventConnectionNumber(int connectionNumber)
+    {
+        eventConnectionNumber = connectionNumber;
+    }
+
+    private void issueCommand(@NonNull IPtpIpCommand command)
+    {
+        try
+        {
+            //Log.v(TAG, "issueCommand : " + command.getId());
+            byte[] commandBody = command.commandBody();
+            if (commandBody != null)
+            {
+                // コマンドボディが入っていた場合には、コマンド送信(入っていない場合は受信待ち)
+                send_to_camera(command.dumpLog(), commandBody);
+            }
+            receive_from_camera(command.dumpLog(), command.getId(), command.responseCallback(), command.receiveAgainShortLengthMessage(), command.receiveDelayMs());
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *    カメラにコマンドを送信する(メイン部分)
+     *
+     */
+    private void send_to_camera(boolean isDumpReceiveLog, byte[] byte_array)
+    {
+        try
+        {
+            dos = new DataOutputStream(socket.getOutputStream());  // ここにいたらいけない?
+
+            // メッセージボディを加工: 最初に4バイトのレングス長をつける
+            byte[] sendData = new byte[byte_array.length + 4];
+
+            sendData[0] = (byte) (byte_array.length + 4);
+            sendData[1] = 0x00;
+            sendData[2] = 0x00;
+            sendData[3] = 0x00;
+            System.arraycopy(byte_array,0,sendData,4, byte_array.length);
+
+            if (isDumpReceiveLog)
+            {
+                // ログに送信メッセージを出力する
+                dump_bytes("SEND[" + sendData.length + "] ", sendData);
+            }
+
+            // (データを)送信
+            dos.write(sendData);
+            dos.flush();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+
+    /**
+     *    カメラからにコマンドの結果を受信する(メイン部分)
+     *
+     */
+    private void receive_from_camera(boolean isDumpReceiveLog, int id, IPtpIpCommandCallback callback, boolean receiveAgain, int delayMs) {
+        try {
+            sleep(delayMs);
+
+            boolean isFirstTime = true;
+            int totalReadBytes;
+            int receive_message_buffer_size = BUFFER_SIZE;
+            byte[] byte_array = new byte[receive_message_buffer_size];
+            InputStream is = socket.getInputStream();
+            if (is != null) {
+                int read_bytes = is.read(byte_array, 0, receive_message_buffer_size);
+                byte[] receive_body;
+                if (read_bytes > 4) {
+                    if (receiveAgain) {
+                        int length = ((((int) byte_array[3]) & 0xff) << 24) + ((((int) byte_array[2]) & 0xff) << 16) + ((((int) byte_array[1]) & 0xff) << 8) + (((int) byte_array[0]) & 0xff);
+                        if (length > receive_message_buffer_size) {
+                            Log.v(TAG, "+++++ TOTAL RECEIVE MESSAGE SIZE IS " + length + " +++++");
+                        }
+                        totalReadBytes = read_bytes;
+                        while ((length > totalReadBytes) || ((length == read_bytes) && ((int) byte_array[4] == 0x02))) {
+                            // データについて、もう一回受信が必要な場合...
+                            if (isDumpReceiveLog) {
+                                Log.v(TAG, "--- RECEIVE AGAIN --- [" + length + "(" + read_bytes + ") " + byte_array[4] + "] ");
+                            }
+                            sleep(delayMs);
+                            int read_bytes2 = is.read(byte_array, read_bytes, receive_message_buffer_size - read_bytes);
+                            if (read_bytes2 > 0) {
+                                read_bytes = read_bytes + read_bytes2;
+                                totalReadBytes = totalReadBytes + read_bytes2;
+                            } else {
+                                // よみだし終了。
+                                Log.v(TAG, "FINISHED RECEIVE... ");
+                                break;
+                            }
+                            if (callback != null) {
+                                if (callback.isReceiveMulti()) {
+                                    int offset = 0;
+                                    if (isFirstTime) {
+                                        // 先頭のヘッダ部分をカットして送る
+                                        offset = 12;
+                                        isFirstTime = false;
+                                        //Log.v(TAG, " FIRST TIME : " + read_bytes + " " + offset);
+                                    }
+                                    callback.onReceiveProgress(read_bytes - offset, length, Arrays.copyOfRange(byte_array, offset, read_bytes));
+                                    read_bytes = 0;
+                                } else {
+                                    callback.onReceiveProgress(read_bytes, length, null);
+                                }
+                            }
+                        }
+                    }
+                    receive_body = Arrays.copyOfRange(byte_array, 0, read_bytes);
+                } else {
+                    receive_body = new byte[1];
+                }
+                if (isDumpReceiveLog) {
+                    // ログに受信メッセージを出力する
+                    Log.v(TAG, " receive_from_camera() : " + read_bytes + " bytes.");
+                    dump_bytes("RECV[" + receive_body.length + "] ", receive_body);
+                }
+                if (callback != null) {
+                    if (callback.isReceiveMulti()) {
+                        callback.receivedMessage(id, null);
+                    } else {
+                        callback.receivedMessage(id, receive_body);
+                        //callback.receivedMessage(id, Arrays.copyOfRange(receive_body, 0, receive_body.length));
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            e.printStackTrace();
+        }
+    }
+
+    private void sleep(int delayMs)
+    {
+        try
+        {
+            Thread.sleep(delayMs);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+}
diff --git a/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusHolder.java b/app/src/main/java/net/osdn/gokigen/a01d/camera/ptpip/wrapper/status/PtpIpStatusHolder.java
new file mode 100644 (file)
index 0000000..6c2e2b8
--- /dev/null
@@ -0,0 +1,204 @@
+package net.osdn.gokigen.a01d.camera.ptpip.wrapper.status;
+
+import android.util.Log;
+import android.util.SparseIntArray;
+
+import androidx.annotation.NonNull;
+import androidx.collection.SparseArrayCompat;
+
+import net.osdn.gokigen.a01d.liveview.ICameraStatusUpdateNotify;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+class PtpIpStatusHolder implements IPtpIpCameraProperties
+{
+    private final String TAG = toString();
+    private SparseIntArray statusHolder;
+    private SparseArrayCompat<String> statusNameArray;
+
+    PtpIpStatusHolder()
+    {
+        statusHolder = new SparseIntArray();
+        statusHolder.clear();
+
+        statusNameArray = new SparseArrayCompat<>();
+        prepareStatusNameArray();
+    }
+
+    private void prepareStatusNameArray()
+    {
+        statusNameArray.clear();
+        statusNameArray.append(BATTERY_LEVEL, BATTERY_LEVEL_STR);
+        statusNameArray.append(WHITE_BALANCE, WHITE_BALANCE_STR);
+        statusNameArray.append(APERTURE, APERTURE_STR);
+        statusNameArray.append(FOCUS_MODE, FOCUS_MODE_STR);
+        statusNameArray.append(SHOOTING_MODE, SHOOTING_MODE_STR);
+        statusNameArray.append(FLASH, FLASH_STR);
+        statusNameArray.append(EXPOSURE_COMPENSATION, EXPOSURE_COMPENSATION_STR);
+        statusNameArray.append(SELF_TIMER, SELF_TIMER_STR);
+        statusNameArray.append(FILM_SIMULATION, FILM_SIMULATION_STR);
+        statusNameArray.append(IMAGE_FORMAT, IMAGE_FORMAT_STR);
+        statusNameArray.append(RECMODE_ENABLE, RECMODE_ENABLE_STR);
+        statusNameArray.append(F_SS_CONTROL, F_SS_CONTROL_STR);
+        statusNameArray.append(ISO, ISO_STR);
+        statusNameArray.append(MOVIE_ISO, MOVIE_ISO_STR);
+        statusNameArray.append(FOCUS_POINT, FOCUS_POINT_STR);
+        statusNameArray.append(DEVICE_ERROR, DEVICE_ERROR_STR);
+        statusNameArray.append(IMAGE_FILE_COUNT, IMAGE_FILE_COUNT_STR);
+        statusNameArray.append(SDCARD_REMAIN_SIZE, SDCARD_REMAIN_SIZE_STR);
+        statusNameArray.append(FOCUS_LOCK, FOCUS_LOCK_STR);
+        statusNameArray.append(MOVIE_REMAINING_TIME, MOVIE_REMAINING_TIME_STR);
+        statusNameArray.append(SHUTTER_SPEED, SHUTTER_SPEED_STR);
+        statusNameArray.append(IMAGE_ASPECT,IMAGE_ASPECT_STR);
+        statusNameArray.append(BATTERY_LEVEL_2, BATTERY_LEVEL_2_STR);
+
+        statusNameArray.append(UNKNOWN_DF00, UNKNOWN_DF00_STR);
+        statusNameArray.append(PICTURE_JPEG_COUNT, PICTURE_JPEG_COUNT_STR);
+        statusNameArray.append(UNKNOWN_D400, UNKNOWN_D400_STR);
+        statusNameArray.append(UNKNOWN_D401, UNKNOWN_D401_STR);
+        statusNameArray.append(UNKNOWN_D52F, UNKNOWN_D52F_STR);
+
+    }
+
+
+    void updateValue(ICameraStatusUpdateNotify notifier, int id, byte data0, byte data1, byte data2, byte data3)
+    {
+        try
+        {
+            int value = ((((int) data3) & 0xff) << 24) + ((((int) data2) & 0xff) << 16) + ((((int) data1) & 0xff) << 8) + (((int) data0) & 0xff);
+            int currentValue = statusHolder.get(id, -1);
+            Log.v(TAG, "STATUS  ID: " + id + "  value : " + value + " (" + currentValue + ")");
+            statusHolder.put(id, value);
+            if (currentValue != value)
+            {
+                //Log.v(TAG, "STATUS  ID: " + id + " value : " + currentValue + " -> " + value);
+                if (notifier != null)
+                {
+                    updateDetected(notifier, id, currentValue, value);
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    private void updateDetected(@NonNull ICameraStatusUpdateNotify notifier, int id, int previous, int current)
+    {
+        try
+        {
+            String idName = statusNameArray.get(id, "Unknown");
+            Log.v(TAG, String.format(Locale.US,"<< UPDATE STATUS >> id: 0x%04x[%s] 0x%08x(%d) -> 0x%08x(%d)", id, idName, previous, previous, current, current));
+            //Log.v(TAG, "updateDetected(ID: " + id + " [" + idName + "] " + previous + " -> " + current + " )");
+
+            if (id == FOCUS_LOCK)
+            {
+                if (current == 1)
+                {
+                    // focus Lock
+                    notifier.updateFocusedStatus(true, true);
+                }
+                else
+                {
+                    // focus unlock
+                    notifier.updateFocusedStatus(false, false);
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+    /**
+     *   認識したカメラのステータス名称のリストを応答する
+     *
+     */
+    private List<String> getAvailableStatusNameList()
+    {
+        ArrayList<String> selection = new ArrayList<>();
+        try
+        {
+            for (int index = 0; index < statusHolder.size(); index++)
+            {
+                int key = statusHolder.keyAt(index);
+                selection.add(statusNameArray.get(key, String.format(Locale.US, "0x%04x", key)));
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (selection);
+
+    }
+
+    List<String> getAvailableItemList(String listKey)
+    {
+        if (listKey == null)
+        {
+            // アイテム名の一覧を応答する
+            return (getAvailableStatusNameList());
+        }
+
+        /////  選択可能なステータスの一覧を取得する : でも以下はアイテム名の一覧... /////
+        ArrayList<String> selection = new ArrayList<>();
+        try
+        {
+            for (int index = 0; index < statusHolder.size(); index++)
+            {
+                int key = statusHolder.keyAt(index);
+                selection.add(statusNameArray.get(key));
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return (selection);
+    }
+
+    String getItemStatus(String key)
+    {
+        try
+        {
+            int strIndex = key.indexOf("x");
+            Log.v(TAG, "getItemStatus() : " + key + " [" + strIndex + "]");
+            if (strIndex >= 1)
+            {
+                key = key.substring(strIndex + 1);
+                try
+                {
+                    int id = Integer.parseInt(key, 16);
+                    int value = statusHolder.get(id);
+                    Log.v(TAG, "getItemStatus() value : " + value);
+                    return (value + "");
+                }
+                catch (Exception e)
+                {
+                    e.printStackTrace();
+                }
+            }
+
+            for (int index = 0; index < statusNameArray.size(); index++)
+            {
+                int id = statusNameArray.keyAt(index);
+                String strKey = statusNameArray.valueAt(index);
+                if (key.contentEquals(strKey))
+                {
+                    int value = statusHolder.get(id);
+                    return (value + "");
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+        return ("? [" + key + "]");
+    }
+}
index 9385e37..b4cf48c 100644 (file)
     <string name="dialog_title_connect_failed_sony">接続失敗(SONY)</string>
     <string name="dialog_title_connect_failed_fuji">接続失敗(FUJI)</string>
     <string name="dialog_title_connect_failed_theta">接続失敗(THETA)</string>
+    <string name="dialog_title_connect_failed_nikon">接続失敗(NIKON)</string>
+    <string name="dialog_title_connect_failed_canon">接続失敗(CANON)</string>
     <string name="fatal_cannot_use_camera">Olympus Camera Kitが動作しないモードのため、カメラのリセットが必要です。電源ボタンを8秒以上長押しして、カメラをリセットしてください。</string>
     <string name="network_connection_timeout">接続タイムアウト</string>
     <string name="restored_my_props">設定を展開しました。 : </string>
     <string name="connect_connecting12">接続中&#8230;(12/12)</string>
     <string name="connect_connect_finished">カメラと接続</string>
 
+    <string name="connect_start_2">準備中&#8230;</string>
+    <string name="canon_connect_connecting1">接続中&#8230;(1/5)</string>
+    <string name="canon_connect_connecting2">接続中&#8230;(2/5)</string>
+    <string name="canon_connect_connecting3">接続中&#8230;(3/5)</string>
+    <string name="canon_connect_connecting4">接続中&#8230;(4/5)</string>
+    <string name="canon_connect_connecting5">接続中&#8230;(5/5)</string>
+
+    <string name="connect_error_message">接続失敗</string>
+
     <string name="connect_error_message_olympus">接続失敗 (OLYMPUS)</string>
     <string name="connect_error_message_fuji">接続失敗 (FUJI)</string>
 
index 01e18fa..7860a99 100644 (file)
@@ -89,6 +89,8 @@
     <string name="dialog_title_connect_failed_sony">Connect failed (SONY)</string>
     <string name="dialog_title_connect_failed_fuji">Connect failed (FUJI)</string>
     <string name="dialog_title_connect_failed_theta">Connect failed (THETA)</string>
+    <string name="dialog_title_connect_failed_nikon">Connect failed (NIKON)</string>
+    <string name="dialog_title_connect_failed_canon">Connect failed (CANON)</string>
     <string name="dialog_title_button_retry">Retry</string>
     <string name="dialog_title_button_network_settings">WiFi Settings</string>
 
     <string name="connect_connecting12">Connecting&#8230;(12/12)</string>
     <string name="connect_connect_finished">Connect Finished.</string>
 
+    <string name="connect_start_2">Preparing&#8230;</string>
+    <string name="canon_connect_connecting1">Connecting&#8230;(1/5)</string>
+    <string name="canon_connect_connecting2">Connecting&#8230;(2/5)</string>
+    <string name="canon_connect_connecting3">Connecting&#8230;(3/5)</string>
+    <string name="canon_connect_connecting4">Connecting&#8230;(4/5)</string>
+    <string name="canon_connect_connecting5">Connecting&#8230;(5/5)</string>
+
+    <string name="connect_error_message">CONNECTION ERROR</string>
+
     <string name="connect_error_message_olympus">CONNECTION ERROR (OLYMPUS)</string>
     <string name="connect_error_message_fuji">CONNECTION ERROR (FUJI)</string>