OSDN Git Service

NIKONカメラ(WIFIモード?)での画像取得ロジックを搭載。
authorMRSa <mrsa@myad.jp>
Wed, 20 Nov 2019 14:49:04 +0000 (23:49 +0900)
committerMRSa <mrsa@myad.jp>
Thu, 21 Nov 2019 14:43:54 +0000 (23:43 +0900)
14 files changed:
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/NikonInterfaceProvider.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonFullImageReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonImageObjectReceiver.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonPlaybackControl.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonScreennailImageReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonSmallImageReceiver.java [new file with mode: 0644]
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonStorageContentHolder.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/command/IPtpIpMessages.java
app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/ptpip/wrapper/playback/PtpIpFullImageReceiver.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/IPreferencePropertyAccessor.java
app/src/main/java/net/osdn/gokigen/pkremote/preference/nikon/NikonPreferenceFragment.java
app/src/main/res/values-ja/strings.xml
app/src/main/res/values/strings.xml
app/src/main/res/xml/preferences_nikon.xml

index 177ec2e..8782b0f 100644 (file)
@@ -29,7 +29,6 @@ import net.osdn.gokigen.pkremote.camera.interfaces.status.ICameraStatusReceiver;
 import net.osdn.gokigen.pkremote.camera.interfaces.status.ICameraStatusWatcher;
 import net.osdn.gokigen.pkremote.camera.vendor.nikon.INikonInterfaceProvider;
 import net.osdn.gokigen.pkremote.camera.vendor.nikon.wrapper.playback.NikonPlaybackControl;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.IPtpIpInterfaceProvider;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.operation.PtpIpZoomControl;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.PtpIpButtonControl;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.PtpIpHardwareStatus;
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonFullImageReceiver.java b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonFullImageReceiver.java
new file mode 100644 (file)
index 0000000..a878d4a
--- /dev/null
@@ -0,0 +1,205 @@
+package net.osdn.gokigen.pkremote.camera.vendor.nikon.wrapper.playback;
+
+import android.app.Activity;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentCallback;
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IProgressEvent;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.PtpIpCommandGeneric;
+
+import java.io.ByteArrayOutputStream;
+
+import static net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpMessages.GET_PARTIAL_OBJECT;
+
+public class NikonFullImageReceiver implements IPtpIpCommandCallback
+{
+    private static final String TAG = NikonFullImageReceiver.class.getSimpleName();
+
+    private final Activity activity;
+    private final IPtpIpCommandPublisher publisher;
+    private IDownloadContentCallback callback = null;
+
+    private boolean isReceiveMulti = true;
+    private int objectId = 0;
+
+    private int received_total_bytes = 0;
+    private int received_remain_bytes = 0;
+
+    private int target_image_size = 0;
+    private boolean receivedFirstData = false;
+
+    NikonFullImageReceiver(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher publisher)
+    {
+        this.activity = activity;
+        this.publisher = publisher;
+    }
+
+    void issueCommand(int objectId, int imageSize, IDownloadContentCallback callback)
+    {
+        if (this.objectId != 0)
+        {
+            // already issued
+            Log.v(TAG, " COMMAND IS ALREADY ISSUED. : " + objectId);
+            return;
+        }
+        this.callback = callback;
+        this.objectId = objectId;
+        this.target_image_size = imageSize;
+        this.isReceiveMulti = true;
+        this.receivedFirstData = false;
+/*
+        NikonImageContentInfo content = imageObjectReceiver.getImageContent(objectId);
+        if (content != null)
+        {
+            imageSize = content.getOriginalSize();
+        }
+*/
+        Log.v(TAG, " getPartialObject (id : " + objectId + ", size:" + imageSize + ")");
+        publisher.enqueueCommand(new PtpIpCommandGeneric(this, GET_PARTIAL_OBJECT, true, 0, 0x101b, 12, objectId, 0, imageSize));  // GetPartialObject (0x101b)
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] rx_body)
+    {
+        try
+        {
+            if (id == GET_PARTIAL_OBJECT)
+            {
+                Log.v(TAG, " TransferComplete() RECEIVED  : " + id + " (" + objectId + ") size : " + target_image_size);
+
+                // end of receive sequence.
+                callback.onCompleted();
+                isReceiveMulti = false;
+                receivedFirstData = false;
+                received_remain_bytes = 0;
+                received_total_bytes = 0;
+                target_image_size = 0;
+                objectId = 0;
+                callback = null;
+                System.gc();
+            }
+            else
+            {
+                Log.v(TAG, " RECEIVED UNKNOWN ID : " + id);
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            {
+                callback.onErrorOccurred(e);
+            }
+        }
+    }
+
+    @Override
+    public void onReceiveProgress(final int currentBytes, final int totalBytes, byte[] rx_body)
+    {
+        // 受信したデータから、通信のヘッダ部分を削除する
+        byte[] body = cutHeader(rx_body);
+        int length = (body == null) ? 0 : body.length;
+        Log.v(TAG, " onReceiveProgress() " + currentBytes + "/" + totalBytes + " (" + length + " bytes.)");
+        callback.onProgress(body, length, new IProgressEvent() {
+            @Override
+            public float getProgress() {
+                return ((float) currentBytes / (float) target_image_size);
+            }
+
+            @Override
+            public boolean isCancellable() {
+                return (false);
+            }
+
+            @Override
+            public void requestCancellation() { }
+        });
+    }
+
+    private byte[] cutHeader(byte[] rx_body)
+    {
+        if (rx_body == null)
+        {
+            return (null);
+        }
+        int length = rx_body.length;
+        int data_position = 0;
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        if (!receivedFirstData)
+        {
+            // データを最初に読んだとき。ヘッダ部分を読み飛ばす
+            receivedFirstData = true;
+            data_position = (int) rx_body[0] & (0xff);
+            Log.v(TAG, " FIRST DATA POS. : " + data_position);
+            //SimpleLogDumper.dump_bytes(" [sss]", Arrays.copyOfRange(rx_body, 0, (64)));
+        }
+        else if (received_remain_bytes > 0)
+        {
+            // データの読み込みが途中だった場合...
+            if (length < received_remain_bytes)
+            {
+                // 全部コピーする、足りないバイト数は残す
+                received_remain_bytes = received_remain_bytes - length;
+                received_total_bytes = received_total_bytes + rx_body.length;
+                return (rx_body);
+            }
+            else
+            {
+                byteStream.write(rx_body, data_position, received_remain_bytes);
+                data_position = received_remain_bytes;
+                received_remain_bytes = 0;
+            }
+        }
+
+        while (data_position <= (length - 12))
+        {
+            int body_size =  (rx_body[data_position] & 0xff) + ((rx_body[data_position + 1]  & 0xff) << 8) +
+                    ((rx_body[data_position + 2] & 0xff) << 16) + ((rx_body[data_position + 3] & 0xff) << 24);
+            if (body_size <= 12)
+            {
+                Log.v(TAG, " --- BODY SIZE IS SMALL : " + data_position + " (" + body_size + ") [" + received_remain_bytes + "] " + rx_body.length + "  (" + target_image_size + ")");
+                //int startpos = (data_position > 48) ? (data_position - 48) : 0;
+                //SimpleLogDumper.dump_bytes(" [xxx]", Arrays.copyOfRange(rx_body, startpos, (data_position + 48)));
+                break;
+            }
+
+            // 受信データ(のヘッダ部分)をダンプする
+            //Log.v(TAG, " RX DATA : " + data_position + " (" + body_size + ") [" + received_remain_bytes + "] (" + received_total_bytes + ")");
+            //SimpleLogDumper.dump_bytes(" [zzz] " + data_position + ": ", Arrays.copyOfRange(rx_body, data_position, (data_position + 48)));
+
+            if ((data_position + body_size) > length)
+            {
+                // データがすべてバッファ内になかったときは、バッファすべてコピーして残ったサイズを記憶しておく。
+                int copysize = (length - ((data_position + 12)));
+                byteStream.write(rx_body, (data_position + 12), copysize);
+                received_remain_bytes = body_size - copysize - 12;  // マイナス12は、ヘッダ分
+                received_total_bytes = received_total_bytes + copysize;
+                //Log.v(TAG, " ----- copy : " + (data_position + 12) + " " + copysize + " remain : " + received_remain_bytes + "  body size : " + body_size);
+                break;
+            }
+            try
+            {
+                byteStream.write(rx_body, (data_position + 12), (body_size - 12));
+                data_position = data_position + body_size;
+                received_total_bytes = received_total_bytes + 12;
+                //Log.v(TAG, " --- COPY : " + (data_position + 12) + " " + (body_size - 12) + " remain : " + received_remain_bytes);
+
+            }
+            catch (Exception e)
+            {
+                Log.v(TAG, "  pos : " + data_position + "  size : " + body_size + " length : " + length);
+                e.printStackTrace();
+            }
+        }
+        return (byteStream.toByteArray());
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (isReceiveMulti);
+    }
+}
index bc922cb..9496289 100644 (file)
@@ -156,6 +156,23 @@ public class NikonImageObjectReceiver implements IPtpIpCommandCallback, NikonSto
         return (null);
     }
 
+    NikonImageContentInfo getImageContent(int objectId)
+    {
+        for(int index = 0; index < storageIdList.size(); index++)
+        {
+            int key = storageIdList.keyAt(index);
+            NikonStorageContentHolder contentHolder = storageIdList.get(key);
+            SparseArray<NikonImageContentInfo> objectArray = contentHolder.getObjectIdList();
+            NikonImageContentInfo content = objectArray.get(objectId);
+            if (content != null)
+            {
+                return (content);
+            }
+        }
+        return (null);
+    }
+
+
     void getCameraContents(ICameraContentListCallback callback)
     {
         this.callback = null;
index 9a8d027..3be5b46 100644 (file)
@@ -17,13 +17,9 @@ import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentList
 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadThumbnailImageCallback;
 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IPlaybackControl;
 import net.osdn.gokigen.pkremote.camera.vendor.nikon.wrapper.NikonInterfaceProvider;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandCallback;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandPublisher;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.PtpIpCommandGeneric;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.specific.CanonRequestInnerDevelopStart;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.PtpIpFullImageReceiver;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.PtpIpImageContentInfo;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.PtpIpScreennailImageReceiver;
-import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.PtpIpSmallImageReceiver;
 import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.playback.PtpIpThumbnailImageReceiver;
 import net.osdn.gokigen.pkremote.preference.IPreferencePropertyAccessor;
 
@@ -36,8 +32,8 @@ public class NikonPlaybackControl implements IPlaybackControl
     private final String TAG = toString();
     private final Activity activity;
     private final NikonInterfaceProvider provider;
-    private final PtpIpFullImageReceiver fullImageReceiver;
-    private final PtpIpSmallImageReceiver smallImageReciever;
+    private final NikonFullImageReceiver fullImageReceiver;
+    private final NikonSmallImageReceiver smallImageReciever;
     private String raw_suffix = "NEF";
     private boolean use_screennail_image = false;
     private NikonImageObjectReceiver nikonImageObjectReceiver;
@@ -46,15 +42,15 @@ public class NikonPlaybackControl implements IPlaybackControl
     {
         this.activity = activity;
         this.provider = provider;
-        this.fullImageReceiver = new PtpIpFullImageReceiver(activity, provider.getCommandPublisher());
-        this.smallImageReciever = new PtpIpSmallImageReceiver(activity, provider.getCommandPublisher());
         nikonImageObjectReceiver = new NikonImageObjectReceiver(provider);
+        this.fullImageReceiver = new NikonFullImageReceiver(activity, provider.getCommandPublisher());
+        this.smallImageReciever = new NikonSmallImageReceiver(activity, provider.getCommandPublisher());
 
         try
         {
             SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
             raw_suffix = preferences.getString(IPreferencePropertyAccessor.CANON_RAW_SUFFIX, IPreferencePropertyAccessor.CANON_RAW_SUFFIX_DEFAULT_VALUE);
-            use_screennail_image = preferences.getBoolean(IPreferencePropertyAccessor.CANON_USE_SCREENNAIL_AS_SMALL, false);
+            use_screennail_image = preferences.getBoolean(IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL, false);
         }
         catch (Exception e)
         {
@@ -78,7 +74,6 @@ public class NikonPlaybackControl implements IPlaybackControl
     public void getContentInfo(String path, String name, IContentInfoCallback callback)
     {
         // showFileInformation
-
     }
 
     @Override
@@ -91,7 +86,6 @@ public class NikonPlaybackControl implements IPlaybackControl
     public void downloadContentScreennail(String path, IDownloadThumbnailImageCallback callback)
     {
         Log.v(TAG, " downloadContentScreennail() " + path);
-
         if (!use_screennail_image)
         {
             // Thumbnail と同じ画像を表示する
@@ -111,7 +105,6 @@ public class NikonPlaybackControl implements IPlaybackControl
             if (content != null)
             {
                 IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
-                //int storageId = content.getStorageId();
                 int objectId = content.getObjectId();
 
                 // 画像表示中...のメッセージを表示する
@@ -123,8 +116,7 @@ public class NikonPlaybackControl implements IPlaybackControl
                 }
 
                 // 画像を取得する
-                PtpIpScreennailImageReceiver receiver = new PtpIpScreennailImageReceiver(activity, objectId, publisher, callback);
-                publisher.enqueueCommand(new PtpIpCommandGeneric(new PtpIpThumbnailImageReceiver(activity, callback), objectId, false, 0, 0x90c4, 4, objectId));
+                publisher.enqueueCommand(new PtpIpCommandGeneric(new NikonScreennailImageReceiver(activity, callback), objectId, false, 0, 0x90c4, 4, objectId));
             }
         }
         catch (Exception e)
@@ -143,17 +135,36 @@ public class NikonPlaybackControl implements IPlaybackControl
             {
                 start = 1;
             }
-            //String indexStr = path.substring(start, path.indexOf("."));
-            final String indexStr = path.substring(start);
-            Log.v(TAG, "downloadContentThumbnail() : [" + path + "] " + indexStr);
 
+            final String indexStr = path.substring(start);
             NikonImageContentInfo content = nikonImageObjectReceiver.getContentObject(indexStr);
             if (content != null)
             {
                 IPtpIpCommandPublisher publisher = provider.getCommandPublisher();
-                int objectId = content.getObjectId();
+                final int objectId = content.getObjectId();
+                if (!content.isDateValid())
+                {
+                    publisher.enqueueCommand(new PtpIpCommandGeneric(new IPtpIpCommandCallback() {
+                        @Override
+                        public void receivedMessage(int id, byte[] rx_body)
+                        {
+                            updateImageContent(objectId, rx_body);
+                        }
+
+                        @Override
+                        public void onReceiveProgress(int currentBytes, int totalBytes, byte[] rx_body) {
+
+                        }
+
+                        @Override
+                        public boolean isReceiveMulti() {
+                            return (false);
+                        }
+                    }, objectId, false, 0, 0x1008, 4, objectId));  // getObjectInfo
+                }
+
                 // Log.v(TAG, "downloadContentThumbnail() " + indexStr + " [" + objectId + "] (" + storageId + ")");
-                publisher.enqueueCommand(new PtpIpCommandGeneric(new PtpIpThumbnailImageReceiver(activity, callback), objectId, false, 0, 0x100a, 4, objectId));
+                publisher.enqueueCommand(new PtpIpCommandGeneric(new PtpIpThumbnailImageReceiver(activity, callback), objectId, false, 0, 0x100a, 4, objectId));  // getThumb
             }
         }
         catch (Exception e)
@@ -162,6 +173,27 @@ public class NikonPlaybackControl implements IPlaybackControl
         }
     }
 
+    private void updateImageContent(int objectId, byte[] rx_body)
+    {
+        try
+        {
+            NikonImageContentInfo content = nikonImageObjectReceiver.getImageContent(objectId);
+            // 受信データを解析してオブジェクトに値をまとめて設定する
+            int readPosition = 40;
+            int imageSize =  ((int) rx_body[readPosition] & 0x000000ff) +
+                    (((int) rx_body[readPosition + 1] & 0x000000ff) << 8) +
+                    (((int) rx_body[readPosition + 2] & 0x000000ff) << 16) +
+                    (((int) rx_body[readPosition + 3] & 0x000000ff) << 24);
+            content.setOriginalSize(imageSize);
+            Log.v(TAG, " CONTENT SIZE : " + imageSize);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+        }
+    }
+
+
     @Override
     public void downloadContent(String path, boolean isSmallSize, IDownloadContentCallback callback)
     {
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonScreennailImageReceiver.java b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonScreennailImageReceiver.java
new file mode 100644 (file)
index 0000000..3a346e0
--- /dev/null
@@ -0,0 +1,77 @@
+package net.osdn.gokigen.pkremote.camera.vendor.nikon.wrapper.playback;
+
+import android.app.Activity;
+import android.graphics.BitmapFactory;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.pkremote.R;
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadThumbnailImageCallback;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandCallback;
+
+import java.util.Arrays;
+
+/**
+ *   Canonサムネイル画像の受信
+ *
+ *
+ */
+public class NikonScreennailImageReceiver implements IPtpIpCommandCallback
+{
+    private final String TAG = toString();
+    private final Activity context;
+    private final IDownloadThumbnailImageCallback callback;
+
+    NikonScreennailImageReceiver(@NonNull Activity context, @NonNull IDownloadThumbnailImageCallback callback)
+    {
+        this.context = context;
+        this.callback = callback;
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] rx_body)
+    {
+        try
+        {
+            if (rx_body == null)
+            {
+                Log.v(TAG, " BITMAP IS NONE...");
+                callback.onCompleted(BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_broken_image_black_24dp), null);
+                return;
+            }
+
+            /////// 受信データから、サムネイルの先頭(0xff 0xd8)を検索する  /////
+            int offset = 0;
+            while (offset < rx_body.length)
+            {
+                if ((rx_body[offset] == (byte) 0xff)&&((rx_body[offset + 1] == (byte) 0xd8)))
+                {
+                    break;
+                }
+                offset++;
+            }
+            byte[] thumbnail = Arrays.copyOfRange(rx_body, offset, rx_body.length);
+            callback.onCompleted(BitmapFactory.decodeByteArray(thumbnail, 0, thumbnail.length), null);
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            {
+                callback.onErrorOccurred(e);
+            }
+        }
+    }
+
+    @Override
+    public void onReceiveProgress(int currentBytes, int totalBytes, byte[] body)
+    {
+        Log.v(TAG, " " + currentBytes + "/" + totalBytes);
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (false);
+    }
+}
diff --git a/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonSmallImageReceiver.java b/app/src/main/java/net/osdn/gokigen/pkremote/camera/vendor/nikon/wrapper/playback/NikonSmallImageReceiver.java
new file mode 100644 (file)
index 0000000..2e3d6d1
--- /dev/null
@@ -0,0 +1,198 @@
+package net.osdn.gokigen.pkremote.camera.vendor.nikon.wrapper.playback;
+
+import android.app.Activity;
+import android.util.Log;
+
+import androidx.annotation.NonNull;
+
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentCallback;
+import net.osdn.gokigen.pkremote.camera.interfaces.playback.IProgressEvent;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandCallback;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpCommandPublisher;
+import net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.messages.PtpIpCommandGeneric;
+
+import java.io.ByteArrayOutputStream;
+
+import static net.osdn.gokigen.pkremote.camera.vendor.ptpip.wrapper.command.IPtpIpMessages.GET_PARTIAL_OBJECT;
+
+public class NikonSmallImageReceiver implements IPtpIpCommandCallback
+{
+    private static final String TAG = NikonFullImageReceiver.class.getSimpleName();
+
+    private final Activity activity;
+    private final IPtpIpCommandPublisher publisher;
+    private IDownloadContentCallback callback = null;
+
+    private boolean isReceiveMulti = true;
+    private int objectId = 0;
+
+    private int received_total_bytes = 0;
+    private int received_remain_bytes = 0;
+
+    private int target_image_size = 0;
+    private boolean receivedFirstData = false;
+
+    NikonSmallImageReceiver(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher publisher)
+    {
+        this.activity = activity;
+        this.publisher = publisher;
+    }
+
+    void issueCommand(int objectId, IDownloadContentCallback callback)
+    {
+        if (this.objectId != 0)
+        {
+            // already issued
+            Log.v(TAG, " COMMAND IS ALREADY ISSUED. : " + objectId);
+            return;
+        }
+        this.callback = callback;
+        this.objectId = objectId;
+        this.isReceiveMulti = true;
+        this.receivedFirstData = false;
+        //Log.v(TAG, " getPartialObject (id : " + objectId + ", size:" + imageSize + ")");
+        //publisher.enqueueCommand(new PtpIpCommandGeneric(this, GET_PARTIAL_OBJECT, true, 0, 0x101b, 12, objectId, 0, imageSize));  // GetPartialObject 0x101b
+        publisher.enqueueCommand(new PtpIpCommandGeneric(this, GET_PARTIAL_OBJECT, true, 0, 0x90c4, 4, objectId));  // GetLargeThumb
+    }
+
+    @Override
+    public void receivedMessage(int id, byte[] rx_body)
+    {
+        try
+        {
+            if (id == GET_PARTIAL_OBJECT)
+            {
+                Log.v(TAG, " TransferComplete() RECEIVED  : " + id + " (" + objectId + ") size : " + target_image_size);
+
+                // end of receive sequence.
+                callback.onCompleted();
+                isReceiveMulti = false;
+                receivedFirstData = false;
+                received_remain_bytes = 0;
+                received_total_bytes = 0;
+                target_image_size = 0;
+                objectId = 0;
+                callback = null;
+                System.gc();
+            }
+            else
+            {
+                Log.v(TAG, " RECEIVED UNKNOWN ID : " + id);
+            }
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            {
+                callback.onErrorOccurred(e);
+            }
+        }
+    }
+
+    @Override
+    public void onReceiveProgress(final int currentBytes, final int totalBytes, byte[] rx_body)
+    {
+        // 受信したデータから、通信のヘッダ部分を削除する
+        byte[] body = cutHeader(rx_body);
+        int length = (body == null) ? 0 : body.length;
+        Log.v(TAG, " onReceiveProgress() " + currentBytes + "/" + totalBytes + " (" + length + " bytes.)");
+        callback.onProgress(body, length, new IProgressEvent() {
+            @Override
+            public float getProgress() {
+                return ((float) currentBytes / (float) target_image_size);
+            }
+
+            @Override
+            public boolean isCancellable() {
+                return (false);
+            }
+
+            @Override
+            public void requestCancellation() { }
+        });
+    }
+
+    private byte[] cutHeader(byte[] rx_body)
+    {
+        if (rx_body == null)
+        {
+            return (null);
+        }
+        int length = rx_body.length;
+        int data_position = 0;
+        ByteArrayOutputStream byteStream = new ByteArrayOutputStream();
+        if (!receivedFirstData)
+        {
+            // データを最初に読んだとき。ヘッダ部分を読み飛ばす
+            receivedFirstData = true;
+            data_position = (int) rx_body[0] & (0xff);
+            Log.v(TAG, " FIRST DATA POS. : " + data_position);
+            //SimpleLogDumper.dump_bytes(" [sss]", Arrays.copyOfRange(rx_body, 0, (64)));
+        }
+        else if (received_remain_bytes > 0)
+        {
+            // データの読み込みが途中だった場合...
+            if (length < received_remain_bytes)
+            {
+                // 全部コピーする、足りないバイト数は残す
+                received_remain_bytes = received_remain_bytes - length;
+                received_total_bytes = received_total_bytes + rx_body.length;
+                return (rx_body);
+            }
+            else
+            {
+                byteStream.write(rx_body, data_position, received_remain_bytes);
+                data_position = received_remain_bytes;
+                received_remain_bytes = 0;
+            }
+        }
+
+        while (data_position <= (length - 12))
+        {
+            int body_size =  (rx_body[data_position] & 0xff) + ((rx_body[data_position + 1]  & 0xff) << 8) +
+                    ((rx_body[data_position + 2] & 0xff) << 16) + ((rx_body[data_position + 3] & 0xff) << 24);
+            if (body_size <= 12)
+            {
+                Log.v(TAG, " --- BODY SIZE IS SMALL : " + data_position + " (" + body_size + ") [" + received_remain_bytes + "] " + rx_body.length + "  (" + target_image_size + ")");
+                //int startpos = (data_position > 48) ? (data_position - 48) : 0;
+                //SimpleLogDumper.dump_bytes(" [xxx]", Arrays.copyOfRange(rx_body, startpos, (data_position + 48)));
+                break;
+            }
+
+            // 受信データ(のヘッダ部分)をダンプする
+            //Log.v(TAG, " RX DATA : " + data_position + " (" + body_size + ") [" + received_remain_bytes + "] (" + received_total_bytes + ")");
+            //SimpleLogDumper.dump_bytes(" [zzz] " + data_position + ": ", Arrays.copyOfRange(rx_body, data_position, (data_position + 48)));
+
+            if ((data_position + body_size) > length)
+            {
+                // データがすべてバッファ内になかったときは、バッファすべてコピーして残ったサイズを記憶しておく。
+                int copysize = (length - ((data_position + 12)));
+                byteStream.write(rx_body, (data_position + 12), copysize);
+                received_remain_bytes = body_size - copysize - 12;  // マイナス12は、ヘッダ分
+                received_total_bytes = received_total_bytes + copysize;
+                //Log.v(TAG, " ----- copy : " + (data_position + 12) + " " + copysize + " remain : " + received_remain_bytes + "  body size : " + body_size);
+                break;
+            }
+            try
+            {
+                byteStream.write(rx_body, (data_position + 12), (body_size - 12));
+                data_position = data_position + body_size;
+                received_total_bytes = received_total_bytes + 12;
+                //Log.v(TAG, " --- COPY : " + (data_position + 12) + " " + (body_size - 12) + " remain : " + received_remain_bytes);
+
+            }
+            catch (Exception e)
+            {
+                Log.v(TAG, "  pos : " + data_position + "  size : " + body_size + " length : " + length);
+                e.printStackTrace();
+            }
+        }
+        return (byteStream.toByteArray());
+    }
+
+    @Override
+    public boolean isReceiveMulti()
+    {
+        return (isReceiveMulti);
+    }
+}
index 0420889..1e46030 100644 (file)
@@ -23,7 +23,7 @@ public class NikonStorageContentHolder  implements IPtpIpCommandCallback
     private final int storageId;
     private final NikonInterfaceProvider provider;
     private final ImageObjectReceivedCallback callback;
-    private boolean isDumpLog = true;
+    private boolean isDumpLog = false;
     private int subDirectoryCount = 0;
     private int receivedDirectoryCount = 0;
     private boolean isObjectReceived = false;
index 291373d..1e0e95b 100644 (file)
@@ -22,7 +22,7 @@ public interface IPtpIpMessages
     int GET_STORAGE_HANDLE1 = 110;
     int GET_STORAGE_HANDLE2 = 111;
     int GET_STORAGE_HANDLE3 = 112;
-    int GET_FOLDER_INFO = 113;
+    int GET_PARTIAL_OBJECT = 113;
 
 
 /*
index cc8d5e0..bfa7ca2 100644 (file)
@@ -31,13 +31,13 @@ public class PtpIpFullImageReceiver implements IPtpIpCommandCallback
     private int target_image_size = 0;
     private boolean receivedFirstData = false;
 
-    public PtpIpFullImageReceiver(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher publisher)
+    PtpIpFullImageReceiver(@NonNull Activity activity, @NonNull IPtpIpCommandPublisher publisher)
     {
         this.activity = activity;
         this.publisher = publisher;
     }
 
-    public void issueCommand(int objectId, int imageSize, IDownloadContentCallback callback)
+    void issueCommand(int objectId, int imageSize, IDownloadContentCallback callback)
     {
         if (this.objectId != 0)
         {
index 6b0d33e..64a9e7f 100644 (file)
@@ -104,6 +104,8 @@ public interface IPreferencePropertyAccessor
 
     String OLYMPUS_USE_SCREENNAIL_AS_SMALL = "olympus_get_screennail_as_small_picture";
 
+    String NIKON_USE_SCREENNAIL_AS_SMALL = "nikon_get_screennail_as_small_picture";
+
     String NIKON_CAMERA_IP_ADDRESS = "nikon_host_ip";
     String NIKON_CAMERA_IP_ADDRESS_DEFAULT_VALUE = "192.168.1.1";
 
index 367183b..e00aaaf 100644 (file)
@@ -129,6 +129,9 @@ public class NikonPreferenceFragment  extends PreferenceFragmentCompat implement
             if (!items.containsKey(IPreferencePropertyAccessor.NIKON_CAMERA_IP_ADDRESS)) {
                 editor.putString(IPreferencePropertyAccessor.NIKON_CAMERA_IP_ADDRESS, IPreferencePropertyAccessor.NIKON_CAMERA_IP_ADDRESS_DEFAULT_VALUE);
             }
+            if (!items.containsKey(IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL)) {
+                editor.putBoolean(IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL, false);
+            }
             if (!items.containsKey(IPreferencePropertyAccessor.BLE_WIFI_ON)) {
                 editor.putBoolean(IPreferencePropertyAccessor.BLE_WIFI_ON, false);
             }
@@ -163,6 +166,11 @@ public class NikonPreferenceFragment  extends PreferenceFragmentCompat implement
                     Log.v(TAG, " " + key + " , " + value);
                     break;
 
+                case IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL:
+                    value = preferences.getBoolean(key, false);
+                    Log.v(TAG, " " + key + " , " + value);
+                    break;
+
                 case IPreferencePropertyAccessor.BLE_WIFI_ON:
                     value = preferences.getBoolean(key, false);
                     Log.v(TAG, " " + key + " , " + value);
@@ -321,6 +329,7 @@ public class NikonPreferenceFragment  extends PreferenceFragmentCompat implement
                         // Preferenceの画面に反映させる
                         setBooleanPreference(IPreferencePropertyAccessor.AUTO_CONNECT_TO_CAMERA, IPreferencePropertyAccessor.AUTO_CONNECT_TO_CAMERA, true);
                         setBooleanPreference(IPreferencePropertyAccessor.CAPTURE_BOTH_CAMERA_AND_LIVE_VIEW, IPreferencePropertyAccessor.CAPTURE_BOTH_CAMERA_AND_LIVE_VIEW, true);
+                        setBooleanPreference(IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL, IPreferencePropertyAccessor.NIKON_USE_SCREENNAIL_AS_SMALL, false);
                         setBooleanPreference(IPreferencePropertyAccessor.BLE_WIFI_ON, IPreferencePropertyAccessor.BLE_WIFI_ON, false);
                     }
                     catch (Exception e)
index 50a90a3..39de67d 100644 (file)
     <string name="pref_olympus_get_screennail_as_small_picture">表示画像はスモール画像を使用</string>
     <string name="pref_summary_olympus_get_screennail_as_small_picture">すこし時間がかかりますが、画像表示にスモール画像を使用します。</string>
 
+    <string name="pref_nikon_get_screennail_as_small_picture">表示画像はスモール画像を使用</string>
+    <string name="pref_summary_nikon_get_screennail_as_small_picture">すこし時間がかかりますが、画像表示にスモール画像を使用します。</string>
+
     <string name="pref_canon_get_screennail_as_small_picture">表示画像はスモール画像を使用</string>
     <string name="pref_summary_canon_get_screennail_as_small_picture">すこし時間がかかりますが、画像表示にスモール画像を使用します。</string>
+
     <string name="canon_get_image_screennail">画像取得中&#8230;</string>
     <string name="canon_get_image_screennail_done">完了</string>
 
index 12b03ad..a53072f 100644 (file)
     <string name="pref_olympus_get_screennail_as_small_picture">Use small image as screen nail</string>
     <string name="pref_summary_olympus_get_screennail_as_small_picture">Use small size image as </string>
 
+    <string name="pref_nikon_get_screennail_as_small_picture">Use small image as screen nail</string>
+    <string name="pref_summary_nikon_get_screennail_as_small_picture">Use small size image as </string>
+
     <string name="pref_canon_get_screennail_as_small_picture">Use small image as screen nail</string>
     <string name="pref_summary_canon_get_screennail_as_small_picture">Use small size image as </string>
+
     <string name="canon_get_image_screennail">Get Image&#8230;</string>
     <string name="canon_get_image_screennail_done">Done.</string>
 
index 3551e26..defba82 100644 (file)
     <PreferenceCategory
         android:title="@string/pref_cat_camera">
 
+        <CheckBoxPreference
+            android:key="nikon_get_screennail_as_small_picture"
+            android:title="@string/pref_nikon_get_screennail_as_small_picture"
+            android:summary="@string/pref_summary_nikon_get_screennail_as_small_picture" />
+
         <EditTextPreference
             android:key="nikon_host_ip"
             android:title="@string/pref_nikon_host_ip"