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;
33 public class SonyPlaybackControl implements IPlaybackControl {
34 private final String TAG = toString();
35 private final Activity activity;
36 private final IInformationReceiver informationReceiver;
37 private ISonyCameraApi cameraApi = null;
38 private HashMap<String, SonyImageContentInfo> contentList;
39 private int timeoutMs = 55000;
40 private boolean contentListIsCreating = false;
42 public SonyPlaybackControl(@NonNull Activity activity, @NonNull IInformationReceiver informationReceiver) {
43 Log.v(TAG, "SonyPlaybackControl()");
44 this.activity = activity;
45 this.informationReceiver = informationReceiver;
46 contentList = new HashMap<>();
50 public void setCameraApi(@NonNull ISonyCameraApi sonyCameraApi) {
51 cameraApi = sonyCameraApi;
55 public String getRawFileSuffix() {
60 public void downloadContentList(IDownloadContentListCallback callback) {
61 Log.v(TAG, "downloadContentList()");
66 public void getContentInfo(String path, String name, IContentInfoCallback callback) {
67 Log.v(TAG, "getContentInfo()");
71 public void updateCameraFileInfo(ICameraFileInfo info) {
72 Log.v(TAG, "updateCameraFileInfo()");
76 public void downloadContentScreennail(String path, IDownloadThumbnailImageCallback callback) {
77 //Log.v(TAG, "downloadContentScreennail()" + path);
79 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
80 if (content == null) {
81 Log.v(TAG, " CONTENT IS NULL... : " + path);
85 String url = content.getSmallUrl(); // Screennail は VGAサイズ
86 if (url.length() < 1) {
87 url = content.getThumbnailUrl(); // VGAサイズが取れなかった場合はサムネイルサイズ
89 if (url.length() > 1) {
90 Bitmap bmp = SimpleHttpClient.httpGetBitmap(url, timeoutMs);
91 HashMap<String, Object> map = new HashMap<>();
92 map.put("Orientation", 0);
93 callback.onCompleted(bmp, map);
95 } catch (Throwable e) {
97 callback.onErrorOccurred(new NullPointerException());
99 } catch (Exception e) {
105 public void downloadContentThumbnail(String path, IDownloadThumbnailImageCallback callback) {
106 //Log.v(TAG, "downloadContentThumbnail() : " + path);
108 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
109 if (content == null) {
110 Log.v(TAG, " CONTENT IS NULL... : " + path);
114 String url = content.getThumbnailUrl();
115 if (url.length() > 1) {
116 Bitmap bmp = SimpleHttpClient.httpGetBitmap(url, timeoutMs);
117 HashMap<String, Object> map = new HashMap<>();
118 map.put("Orientation", 0);
119 callback.onCompleted(bmp, map);
121 } catch (Throwable e) {
123 callback.onErrorOccurred(new NullPointerException());
125 } catch (Exception e) {
131 public void downloadContent(String path, boolean isSmallSize, final IDownloadContentCallback callback) {
132 //Log.v(TAG, "downloadContent() : " + path);
134 SonyImageContentInfo content = contentList.get(path.substring(path.indexOf('/') + 1));
135 if (content == null) {
136 Log.v(TAG, " CONTENT IS NULL... : " + path);
140 SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
141 boolean isVgaSize = preferences.getBoolean(IPreferencePropertyAccessor.GET_SMALL_PICTURE_AS_VGA, false);
142 String url = (isSmallSize) ? ((isVgaSize) ? content.getSmallUrl() : content.getLargeUrl()) : content.getOriginalUrl();
143 if (url.length() < 1) {
144 url = content.getOriginalUrl();
145 if (url.length() < 1) {
146 // 全然だめなら、サムネイルサイズ...
147 url = content.getThumbnailUrl();
150 Log.v(TAG, "downloadContent() PATH : " + path + " [SMALL:" + isSmallSize + "][VGA:" + isVgaSize + "]" + " GET URL : " + url);
152 SimpleHttpClient.httpGetBytes(url, timeoutMs, new SimpleHttpClient.IReceivedMessageCallback() {
154 public void onCompleted() {
155 callback.onCompleted();
159 public void onErrorOccurred(Exception e) {
160 callback.onErrorOccurred(e);
164 public void onReceive(int readBytes, int length, int size, byte[] data) {
165 float percent = (length == 0) ? 0.0f : ((float) readBytes / (float) length);
166 //Log.v(TAG, " onReceive : " + readBytes + " " + length + " " + size);
167 ProgressEvent event = new ProgressEvent(percent, null);
168 callback.onProgress(data, size, event);
171 } catch (Throwable e) {
173 callback.onErrorOccurred(new NullPointerException());
175 } catch (Exception e) {
181 public void getCameraContentList(ICameraContentListCallback callback) {
182 Log.v(TAG, "getCameraContentList()");
184 if (cameraApi == null) {
185 Log.v(TAG, "CAMERA API is NULL.");
188 if (contentListIsCreating) {
189 // すでにコンテントリストを作り始めているので、処理は継続しない。
190 Log.v(TAG, "ALREADY CREATING CONTENT LIST.");
193 contentListIsCreating = true;
194 informationReceiver.updateMessage(activity.getString(R.string.get_image_list), false, false, 0);
195 changeContentsTransferMode(); // コンテンツトランスファモードに切り替える
197 JSONObject storageInformationObj = cameraApi.getStorageInformation();
198 JSONObject schemeListObj = cameraApi.getSchemeList();
199 //JSONArray schemeArray = schemeListObj.getJSONArray("result");
200 JSONObject sourceObj = cameraApi.getSourceList("storage");
201 //JSONArray sourceArray = sourceObj.getJSONArray("result");
202 JSONObject countObject = cameraApi.getContentCountFlatAll("storage:memoryCard1");
203 JSONArray resultArray = countObject.getJSONArray("result");
204 int objectCount = resultArray.getJSONObject(0).getInt("count");
205 Log.v(TAG, " OBJECT COUNT : " + objectCount);
206 if (objectCount < 1) {
208 informationReceiver.updateMessage(activity.getString(R.string.content_is_nothing), true, false, 0);
209 contentListIsCreating = false;
216 while ((index >= 0) && (index < objectCount)) {
217 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + index + "/" + objectCount + " ", false, false, 0);
219 int remainCount = objectCount - index;
220 JSONObject paramsObj = new JSONObject();
221 paramsObj.put("uri", "storage:memoryCard1");
222 paramsObj.put("stIdx", index);
223 paramsObj.put("cnt", (remainCount > 100 ? 100 : remainCount)); // 一括取得数...最大100
224 //paramsObj.put("cnt", (remainCount > 50 ? 50 : remainCount)); // 一括取得数
225 paramsObj.put("view", "flat");
226 paramsObj.put("sort", "descending");
228 JSONObject responseObject = cameraApi.getContentList(new JSONArray().put(paramsObj));
229 JSONArray resultsArray = responseObject.getJSONArray("result").getJSONArray(0);
230 int nofContents = resultsArray.length();
231 for (int pos = 0; pos < nofContents; pos++) {
233 SonyImageContentInfo contentInfo = new SonyImageContentInfo(resultsArray.getJSONObject(pos));
234 String contentName = contentInfo.getContentName();
235 //Date createdTime = contentInfo.getCapturedDate();
236 //String folderNo = contentInfo.getContentPath();
237 if (contentName.length() > 0) {
238 contentList.put(contentName, contentInfo);
240 //Log.v(TAG, " [" + pos + "] " + " " + contentName + " " + " " + createdTime + " " + folderNo);
242 index = index + nofContents;
243 //Log.v(TAG, " COUNT : " + index);
244 } catch (Exception e) {
249 contentListIsCreating = false;
250 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + index + "/" + objectCount + " ", false, false, 0);
251 if (callback != null) {
253 callback.onCompleted(new ArrayList<ICameraContent>(contentList.values()));
255 } catch (Exception e) {
258 contentListIsCreating = false;
261 private void changeContentsTransferMode() {
263 if (cameraApi == null) {
266 boolean isAvailable = false;
267 int maxRetryCount = 10; // 最大リトライ回数
268 while ((!isAvailable) && (maxRetryCount > 0)) {
269 isAvailable = setCameraFunction(false);
272 if (maxRetryCount <= 0) {
274 informationReceiver.updateMessage(activity.getString(R.string.change_transfer_mode_retry_over), true, true, Color.RED);
277 getContentDirectorySoapAction();
280 } catch (Exception e) {
285 private boolean setCameraFunction(boolean isRecording) {
287 JSONObject reply = cameraApi.setCameraFunction((isRecording) ? "Remote Shooting" : "Contents Transfer");
289 int value = reply.getInt("result");
290 Log.v(TAG, "CHANGE RUN MODE : " + value);
292 } catch (Exception ee) {
293 ee.printStackTrace();
294 informationReceiver.updateMessage(activity.getString(R.string.change_transfer_mode_retry), false, false, 0);
295 Thread.sleep(500); // 500ms 待つ
297 } catch (Exception e) {
303 private void getContentDirectorySoapAction()
306 //////////// ある程度の数に区切って送られてくる... 何度か繰り返す必要があるようだ ////////////
307 int sequenceNumber = 0;
308 int totalCount = 100000;
309 int returnedCount = 0;
310 while (totalCount > returnedCount)
312 Log.v(TAG, " ===== getContentList() " + sequenceNumber + " =====");
314 String accessUrl = cameraApi.getDdUrl();
315 String url = accessUrl.substring(0, accessUrl.lastIndexOf("/")) + "/upnp/control/ContentDirectory";
317 String postData = "<?xml version=\"1.0\"?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">" +
318 "<s:Body><u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:" + sequenceNumber + "\">" +
319 //"<ObjectID>0</ObjectID>" +
320 "<ObjectID>03_01_0002002552_000002_000000_000000</ObjectID>" +
321 "<BrowseFlag>BrowseDirectChildren</BrowseFlag><Filter>*</Filter>" +
322 "<StartingIndex>" + returnedCount + "</StartingIndex>" +
323 //"<RequestedCount>3500</RequestedCount>" +
324 "<RequestedCount>1</RequestedCount>" +
325 //"<SortCriteria>" + "-dc:flat" + "</SortCriteria>" +
326 "<SortCriteria>" + "-dc:date" + "</SortCriteria>" +
327 "</u:Browse></s:Body></s:Envelope>";
329 String postData = "<?xml version=\"1.0\" encoding=\"utf-8\" ?><s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\"><s:Body>" +
330 "<u:Browse xmlns:u=\"urn:schemas-upnp-org:service:ContentDirectory:" + sequenceNumber + "\" xmlns:pana=\"urn:schemas-panasonic-com:pana\">" +
331 "<ObjectID>0</ObjectID><BrowseFlag>BrowseDirectChildren</BrowseFlag><Filter>*</Filter><StartingIndex>" + returnedCount + "</StartingIndex><RequestedCount>3500</RequestedCount><SortCriteria></SortCriteria>" +
332 "<pana:X_FromCP>LumixLink2.0</pana:X_FromCP></u:Browse></s:Body></s:Envelope>";
334 String reply = SimpleHttpClient.httpPostWithHeader(url, postData, "SOAPACTION", "urn:schemas-upnp-org:service:ContentDirectory:" + sequenceNumber + "#Browse", "text/xml; charset=\"utf-8\"", timeoutMs);
335 if (reply.length() < 10)
337 Log.v(TAG, postData);
338 Log.v(TAG, "ContentDirectory is FAILURE. [" + sequenceNumber + "]");
341 Log.v(TAG, " < REPLY > " + reply);
343 getObjectLists = getObjectLists.append(reply);
344 String matches = reply.substring(reply.indexOf("<TotalMatches>") + 14, reply.indexOf("</TotalMatches>"));
347 totalCount = Integer.parseInt(matches);
355 String returned = reply.substring(reply.indexOf("<NumberReturned>") + 16, reply.indexOf("</NumberReturned>"));
358 returnedCount = returnedCount + Integer.parseInt(returned);
364 Log.v(TAG, " REPLY DATA : (" + matches + "/" + totalCount + ") [" + returned + "/" + returnedCount + "] " + " " + reply.length() + "bytes");
365 informationReceiver.updateMessage(activity.getString(R.string.get_image_list) + " " + returnedCount + "/" + totalCount + " ", false, false, 0);