1 package net.osdn.gokigen.pkremote.camera.vendor.sony.wrapper.playback;
3 import android.app.Activity;
4 import android.content.SharedPreferences;
5 import android.graphics.Bitmap;
6 import android.graphics.Color;
7 import android.util.Log;
9 import androidx.annotation.NonNull;
10 import androidx.preference.PreferenceManager;
12 import net.osdn.gokigen.pkremote.IInformationReceiver;
13 import net.osdn.gokigen.pkremote.R;
14 import net.osdn.gokigen.pkremote.camera.interfaces.playback.ICameraContent;
15 import net.osdn.gokigen.pkremote.camera.interfaces.playback.ICameraContentListCallback;
16 import net.osdn.gokigen.pkremote.camera.interfaces.playback.ICameraFileInfo;
17 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IContentInfoCallback;
18 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentCallback;
19 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadContentListCallback;
20 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IDownloadThumbnailImageCallback;
21 import net.osdn.gokigen.pkremote.camera.interfaces.playback.IPlaybackControl;
22 import net.osdn.gokigen.pkremote.camera.playback.ProgressEvent;
23 import net.osdn.gokigen.pkremote.camera.utils.SimpleHttpClient;
24 import net.osdn.gokigen.pkremote.camera.vendor.sony.wrapper.ISonyCameraApi;
25 import net.osdn.gokigen.pkremote.preference.IPreferencePropertyAccessor;
27 import org.json.JSONArray;
28 import org.json.JSONObject;
30 import java.util.ArrayList;
31 import java.util.HashMap;
32 import java.util.List;
35 public class SonyPlaybackControl implements IPlaybackControl
37 private final String TAG = toString();
38 private final Activity activity;
39 private final IInformationReceiver informationReceiver;
40 private ISonyCameraApi cameraApi = null;
41 private HashMap<String, SonyImageContentInfo> contentList;
42 private int timeoutMs = 55000;
43 private boolean contentListIsCreating = false;
45 public SonyPlaybackControl(@NonNull Activity activity, @NonNull IInformationReceiver informationReceiver)
47 Log.v(TAG, "SonyPlaybackControl()");
48 this.activity = activity;
49 this.informationReceiver = informationReceiver;
50 contentList = new HashMap<>();
53 public void setCameraApi(@NonNull ISonyCameraApi sonyCameraApi) {
54 cameraApi = sonyCameraApi;
58 public String getRawFileSuffix() {
63 public void downloadContentList(IDownloadContentListCallback callback)
65 Log.v(TAG, "downloadContentList()");
70 public void getContentInfo(String path, String name, IContentInfoCallback callback)
72 Log.v(TAG, "getContentInfo()");
76 public void updateCameraFileInfo(ICameraFileInfo info) {
77 Log.v(TAG, "updateCameraFileInfo()");
81 public void downloadContentScreennail(String path, IDownloadThumbnailImageCallback callback)
83 //Log.v(TAG, "downloadContentScreennail()" + path);
86 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
89 Log.v(TAG, " CONTENT IS NULL... : " + path);
94 String url = content.getSmallUrl(); // Screennail は VGAサイズ
97 url = content.getThumbnailUrl(); // VGAサイズが取れなかった場合はサムネイルサイズ
101 Bitmap bmp = SimpleHttpClient.httpGetBitmap(url, timeoutMs);
102 HashMap<String, Object> map = new HashMap<>();
103 map.put("Orientation", 0);
104 callback.onCompleted(bmp, map);
110 callback.onErrorOccurred(new NullPointerException());
120 public void downloadContentThumbnail(String path, IDownloadThumbnailImageCallback callback)
122 //Log.v(TAG, "downloadContentThumbnail() : " + path);
125 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
128 Log.v(TAG, " CONTENT IS NULL... : " + path);
133 String url = content.getThumbnailUrl();
134 if (url.length() > 1)
136 Bitmap bmp = SimpleHttpClient.httpGetBitmap(url, timeoutMs);
137 HashMap<String, Object> map = new HashMap<>();
138 map.put("Orientation", 0);
139 callback.onCompleted(bmp, map);
145 callback.onErrorOccurred(new NullPointerException());
155 public void downloadContent(String path, boolean isSmallSize, final IDownloadContentCallback callback)
157 //Log.v(TAG, "downloadContent() : " + path);
160 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
163 Log.v(TAG, " CONTENT IS NULL... : " + path);
168 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
169 boolean isVgaSize = preferences.getBoolean(IPreferencePropertyAccessor.GET_SMALL_PICTURE_AS_VGA, false);
170 String url = (isSmallSize) ? ((isVgaSize) ? content.getSmallUrl() : content.getLargeUrl()) : content.getOriginalUrl();
171 if (url.length() < 1)
173 url = content.getOriginalUrl();
174 if (url.length() < 1)
176 // 全然だめなら、サムネイルサイズ...
177 url = content.getThumbnailUrl();
180 Log.v(TAG, "downloadContent() PATH : " + path + " [SMALL:" + isSmallSize + "][VGA:" + isVgaSize + "]" + " GET URL : " + url);
182 SimpleHttpClient.httpGetBytes(url, timeoutMs, new SimpleHttpClient.IReceivedMessageCallback()
185 public void onCompleted() {
186 callback.onCompleted();
190 public void onErrorOccurred(Exception e) {
191 callback.onErrorOccurred(e);
195 public void onReceive(int readBytes, int length, int size, byte[] data) {
196 float percent = (length == 0) ? 0.0f : ((float) readBytes / (float) length);
197 //Log.v(TAG, " onReceive : " + readBytes + " " + length + " " + size);
198 ProgressEvent event = new ProgressEvent(percent, null);
199 callback.onProgress(data, size, event);
206 callback.onErrorOccurred(new NullPointerException());
216 public void getCameraContentList(ICameraContentListCallback callback)
218 Log.v(TAG, "getCameraContentList()");
221 if (cameraApi == null)
223 Log.v(TAG, "CAMERA API is NULL.");
226 if (contentListIsCreating)
228 // すでにコンテントリストを作り始めているので、処理は継続しない。
229 Log.v(TAG, "ALREADY CREATING CONTENT LIST.");
232 contentListIsCreating = true;
234 // 画像転送に「スマートフォン転送機能」を使う場合...
235 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
236 boolean useSmartphoneTransfer = preferences.getBoolean(IPreferencePropertyAccessor.USE_SMARTPHONE_TRANSFER_MODE, false);
237 if (useSmartphoneTransfer)
239 // DLNAを使用したコンテンツ特定モードを使う
242 getContentDirectorySoapAction();
246 ee.printStackTrace();
248 contentListIsCreating = false;
251 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + contentList.size() + "/" + contentList.size() + " ", false, false, 0);
252 if (callback != null)
255 callback.onCompleted(new ArrayList<ICameraContent>(contentList.values()));
260 informationReceiver.updateMessage(activity.getString(R.string.get_image_list), false, false, 0);
261 boolean ret = changeContentsTransferMode(); // コンテンツトランスファモードに切り替える
264 informationReceiver.updateMessage(activity.getString(R.string.change_transfer_mode_failure), true, true, Color.RED);
265 contentListIsCreating = false;
269 JSONObject storageInformationObj = cameraApi.getStorageInformation();
270 JSONObject schemeListObj = cameraApi.getSchemeList();
271 //JSONArray schemeArray = schemeListObj.getJSONArray("result");
272 JSONObject sourceObj = cameraApi.getSourceList("storage");
273 //JSONArray sourceArray = sourceObj.getJSONArray("result");
274 JSONObject countObject = cameraApi.getContentCountFlatAll("storage:memoryCard1");
275 JSONArray resultArray = countObject.getJSONArray("result");
276 int objectCount = resultArray.getJSONObject(0).getInt("count");
277 Log.v(TAG, " OBJECT COUNT : " + objectCount);
281 informationReceiver.updateMessage(activity.getString(R.string.content_is_nothing), true, false, 0);
282 contentListIsCreating = false;
289 while ((index >= 0) && (index < objectCount))
291 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + index + "/" + objectCount + " ", false, false, 0);
293 int remainCount = objectCount - index;
294 JSONObject paramsObj = new JSONObject();
295 paramsObj.put("uri", "storage:memoryCard1");
296 paramsObj.put("stIdx", index);
297 paramsObj.put("cnt", (remainCount > 100 ? 100 : remainCount)); // 一括取得数...最大100
298 //paramsObj.put("cnt", (remainCount > 50 ? 50 : remainCount)); // 一括取得数
299 paramsObj.put("view", "flat");
300 paramsObj.put("sort", "descending");
303 JSONObject responseObject = cameraApi.getContentList(new JSONArray().put(paramsObj));
304 JSONArray resultsArray = responseObject.getJSONArray("result").getJSONArray(0);
305 int nofContents = resultsArray.length();
306 for (int pos = 0; pos < nofContents; pos++)
309 SonyImageContentInfo contentInfo = new SonyImageContentInfo(resultsArray.getJSONObject(pos), null);
310 String contentName = contentInfo.getContentName();
311 //Date createdTime = contentInfo.getCapturedDate();
312 //String folderNo = contentInfo.getContentPath();
313 if (contentName.length() > 0)
315 contentList.put(contentName, contentInfo);
317 //Log.v(TAG, " [" + pos + "] " + " " + contentName + " " + " " + createdTime + " " + folderNo);
319 index = index + nofContents;
320 //Log.v(TAG, " COUNT : " + index);
328 contentListIsCreating = false;
329 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + index + "/" + objectCount + " ", false, false, 0);
330 if (callback != null)
333 callback.onCompleted(new ArrayList<ICameraContent>(contentList.values()));
340 contentListIsCreating = false;
343 private boolean changeContentsTransferMode()
347 if (cameraApi == null)
352 boolean isAvailable = false;
353 int maxRetryCount = 10; // 最大リトライ回数
354 while ((!isAvailable) && (maxRetryCount > 0))
356 isAvailable = setCameraFunction(false);
359 if (maxRetryCount <= 0)
362 informationReceiver.updateMessage(activity.getString(R.string.change_transfer_mode_retry_over), true, true, Color.RED);
365 QX10actEnableMethods actEnableMethods = new QX10actEnableMethods(cameraApi);
366 boolean ret = actEnableMethods.actEnableMethods();
369 // actEnableMethods がうまく動かなかった場合... ここで処理を止める
370 getContentDirectorySoapAction(); // ← やっても動かないはず
375 informationReceiver.updateMessage(activity.getString(R.string.image_checking), false, false, Color.BLACK);
376 getContentDirectorySoapAction();
387 private boolean setCameraFunction(boolean isRecording)
391 JSONObject reply = cameraApi.setCameraFunction((isRecording) ? "Remote Shooting" : "Contents Transfer");
394 int value = reply.getJSONArray("result").getInt(0);
395 Log.v(TAG, "CHANGE RUN MODE : " + value);
400 ee.printStackTrace();
401 informationReceiver.updateMessage(activity.getString(R.string.change_transfer_mode_retry), false, false, 0);
402 Thread.sleep(500); // 500ms 待つ
413 * スマートフォン転送(DLNAを使用したコンテンツ一覧取得)時の一覧取得処理
416 private void getContentDirectorySoapAction()
420 String accessUrl = cameraApi.getDdUrl();
421 accessUrl = accessUrl.substring(0, accessUrl.lastIndexOf("/"));
423 //String reply = getSortCapabilities(accessUrl);
424 String reply = browseRootDirectory(accessUrl);
425 ContentDirectoryInfo directoryInfo = parseObjectId(parseResult(reply, true));
427 // PhotoRoot Directory
428 int returnedCount = 0;
429 int totalCount = directoryInfo.getCount();
430 List<ContentDirectoryInfo> dateFolderInfoList = new ArrayList<>();
431 while (returnedCount < totalCount)
433 reply = browsePhotoSubRootDirectory(accessUrl, directoryInfo.getObjectId(), returnedCount);
434 List<ContentDirectoryInfo> objectInfoList = parseObjectIds(parseResult(reply, true));
435 returnedCount = objectInfoList.size();
436 dateFolderInfoList.addAll(objectInfoList);
439 ///////////////// Date Directories /////////////////
440 int totalObjectCount = 0;
441 List<ContentDirectoryInfo> folderInfoList = new ArrayList<>();
442 for (ContentDirectoryInfo rootObjectInfo : dateFolderInfoList)
444 int returnedFolderCount = 0;
445 reply = browsePhotoSubRootDirectory(accessUrl, rootObjectInfo.getObjectId(), returnedFolderCount);
446 List<ContentDirectoryInfo> folderList = parseObjectIds(parseResult(reply, true));
447 folderInfoList.addAll(folderList);
448 for (ContentDirectoryInfo folderInfo : folderList)
450 totalObjectCount = totalObjectCount + folderInfo.getCount();
454 ///////////////// GET CONTENTS /////////////////
457 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + contentList.size() + "/" + totalObjectCount + " ", false, false, 0);
458 Log.v(TAG, " TOTAL OBJECT COUNT : " + contentList.size() + "/" + totalObjectCount);
459 for (ContentDirectoryInfo dateFolderInfo : folderInfoList)
461 objectCount = objectCount + getObjects(accessUrl, dateFolderInfo, totalObjectCount);
470 private int getObjects(String accessUrl, ContentDirectoryInfo dateFolderInfo, int totalObjectCount)
472 int currentCount = 0;
473 while (currentCount < dateFolderInfo.getCount())
475 String reply = browsePhotoSubRootDirectory(accessUrl, dateFolderInfo.getObjectId(), currentCount);
476 currentCount = currentCount + parseContentObject(parseResult(reply, false));
478 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + contentList.size() + "/" + totalObjectCount + " ", false, false, 0);
479 Log.v(TAG, " TOTAL OBJECT COUNT : " + contentList.size() + "/" + totalObjectCount);
481 return (currentCount);
484 private String getSortCapabilities(String accessUrl)
486 String url = accessUrl + "/upnp/control/ContentDirectory";
487 String postData = "<?xml version=\"1.0\"?>" +
488 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
490 "<u:GetSortCapabilities xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">" +
491 "</u:GetSortCapabilities>" +
493 "</s:Envelope>\r\n\r\n";
494 Map<String, String> header = new HashMap<>();
496 header.put("SOAPACTION", "\"urn:schemas-upnp-org:service:ContentDirectory:1" + "#GetSortCapabilities\"");
497 return (SimpleHttpClient.httpPostWithHeader(url, postData, header, "text/xml; charset=\"utf-8\"", timeoutMs));
500 private String browseRootDirectory(String accessUrl)
502 String url = accessUrl + "/upnp/control/ContentDirectory";
503 String postData = "<?xml version=\"1.0\"?>" +
504 "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
506 "<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">" +
507 "<ObjectID>0</ObjectID>" +
508 "<BrowseFlag>BrowseDirectChildren</BrowseFlag>" +
509 "<Filter>*</Filter>" +
510 "<StartingIndex>0</StartingIndex>" +
511 "<RequestedCount>8000</RequestedCount>" +
512 "<SortCriteria></SortCriteria>" +
517 Map<String, String> header = new HashMap<>();
519 header.put("SOAPACTION", "\"urn:schemas-upnp-org:service:ContentDirectory:1" + "#Browse\"");
520 return (SimpleHttpClient.httpPostWithHeader(url, postData, header, "text/xml; charset=\"utf-8\"", timeoutMs));
523 private List<ContentDirectoryInfo> parseObjectIds(String targetString)
527 List<ContentDirectoryInfo> objectIds = new ArrayList<>();
531 int maxSize = targetString.length();
532 while (parsedIndex < maxSize)
534 String checkString = targetString.substring(parsedIndex);
535 int startIndex = checkString.toLowerCase().indexOf("<container ");
538 // containerタグが見つからない
541 int endIndex = checkString.indexOf(">", startIndex);
542 if (startIndex > endIndex)
545 //Log.v(TAG, " NOT FOUND END CLAUSE TAG");
548 ContentDirectoryInfo objectInfo = parseObjectId(checkString.substring(startIndex, endIndex + 1));
549 if (objectInfo.getObjectId().length() > 0)
551 objectIds.add(objectInfo);
553 parsedIndex = parsedIndex + endIndex;
561 return (new ArrayList<>());
564 private ContentDirectoryInfo parseObjectId(String targetString)
566 String objectId = "";
567 String childCount = "0";
571 int startIndex = targetString.toLowerCase().indexOf("<container ");
575 return (new ContentDirectoryInfo("", 0));
577 int endIndex = targetString.indexOf(">", startIndex);
578 String containerString = targetString.substring(startIndex + 11, endIndex - 1);
579 String[] attrList = containerString.split(" ");
580 for (String attribute : attrList)
582 if (attribute.indexOf("id=") == 0)
584 objectId = attribute.substring(3).replaceAll("\"","");
586 else if (attribute.toLowerCase().indexOf("childcount=") == 0)
588 childCount = attribute.substring(12).replaceAll("\"","");
591 count = Integer.parseInt(childCount);
597 Log.v(TAG, " OBJECT ID : " + objectId + " COUNT : " + childCount);
598 return (new ContentDirectoryInfo(objectId, count));
601 private String browsePhotoSubRootDirectory(String accessUrl, String objectId, int startIndex)
603 String url = accessUrl + "/upnp/control/ContentDirectory";
604 String postData = "<?xml version=\"1.0\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>" +
605 "<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:1\">" +
606 "<ObjectID>" + objectId + "</ObjectID>" +
607 "<BrowseFlag>BrowseDirectChildren</BrowseFlag>" +
608 "<Filter>*</Filter>" +
609 "<StartingIndex>" + startIndex + "</StartingIndex>" +
610 "<RequestedCount>80000</RequestedCount>" +
611 "<SortCriteria></SortCriteria>" +
612 "</u:Browse></s:Body></s:Envelope>";
614 Map<String, String> header = new HashMap<>();
616 header.put("SOAPACTION", "\"urn:schemas-upnp-org:service:ContentDirectory:1" + "#Browse\"");
617 return (SimpleHttpClient.httpPostWithHeader(url, postData, header, "text/xml; charset=\"utf-8\"", timeoutMs));
620 private int parseContentObject(String receivedData)
624 int endLength = receivedData.length();
627 // <item> ~ </item> を切り出して保管する
628 while (startIndex < endLength)
630 int index = receivedData.indexOf("<item", startIndex);
636 int endIndex = receivedData.indexOf("</item>", index);
639 endIndex = endLength;
641 String itemString = receivedData.substring(index, endIndex + 7);
642 SonyImageContentInfo contentInfo = new SonyImageContentInfo(null, itemString);
643 String contentName = contentInfo.getContentName();
644 if (contentName.length() > 0)
646 contentList.put(contentName, contentInfo);
649 startIndex = endIndex;
659 private String parseResult(String reply, boolean isResultSubstring)
661 String decordReply = reply;
664 int startIndex = reply.indexOf("<Result>");
665 int endIndex = reply.indexOf("</Result>");
666 if ((isResultSubstring)&&(startIndex < endIndex) && (startIndex > 0))
668 decordReply = reply.substring((startIndex + 8), endIndex); // = URLDecoder.decode(reply.substring((startIndex + 8), endIndex), "UTF-8");
670 decordReply = decordReply.replaceAll("<", "<");
671 decordReply = decordReply.replaceAll(">", ">");
672 decordReply = decordReply.replaceAll(""", "\"");
678 return (decordReply);