@capgo/capacitor-ricoh360 8.0.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,453 @@
1
+ package java.ee.forgr.capacitor.ricoh.camera;
2
+
3
+ import android.graphics.Bitmap;
4
+ import android.graphics.BitmapFactory;
5
+ import android.graphics.Color;
6
+ import android.util.Base64;
7
+ import android.view.ViewGroup;
8
+ import android.widget.FrameLayout;
9
+ import android.widget.ImageView;
10
+ import com.getcapacitor.JSObject;
11
+ import com.getcapacitor.Plugin;
12
+ import com.getcapacitor.PluginCall;
13
+ import com.getcapacitor.PluginMethod;
14
+ import com.getcapacitor.annotation.CapacitorPlugin;
15
+ import java.io.BufferedReader;
16
+ import java.io.ByteArrayOutputStream;
17
+ import java.io.InputStream;
18
+ import java.io.InputStreamReader;
19
+ import java.io.OutputStream;
20
+ import java.net.HttpURLConnection;
21
+ import java.net.URL;
22
+ import java.util.Arrays;
23
+ import java.util.stream.Collectors;
24
+ import org.json.JSONException;
25
+ import org.json.JSONObject;
26
+
27
+ @CapacitorPlugin(name = "Ricoh360Camera")
28
+ public class Ricoh360CameraPlugin extends Plugin {
29
+
30
+ private final String pluginVersion = "7.2.5";
31
+
32
+ private String cameraUrl = "http://192.168.1.1";
33
+ private ImageView previewView;
34
+ private Thread streamThread;
35
+ private boolean isStreaming = false;
36
+ private final byte[] startMarker = new byte[] { (byte) 0xFF, (byte) 0xD8 };
37
+ private final byte[] endMarker = new byte[] { (byte) 0xFF, (byte) 0xD9 };
38
+ private final int containerViewId = 20;
39
+ private FrameLayout containerView;
40
+
41
+ @PluginMethod
42
+ public void initialize(PluginCall call) {
43
+ String url = call.getString("url", cameraUrl);
44
+ cameraUrl = url;
45
+
46
+ new Thread(() -> {
47
+ try {
48
+ URL infoUrl = new URL(cameraUrl + "/osc/info");
49
+ HttpURLConnection connection = (HttpURLConnection) infoUrl.openConnection();
50
+ connection.setRequestMethod("GET");
51
+ connection.setConnectTimeout(5000);
52
+ connection.connect();
53
+
54
+ int responseCode = connection.getResponseCode();
55
+ if (responseCode == HttpURLConnection.HTTP_OK) {
56
+ InputStream inputStream = connection.getInputStream();
57
+ String result = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
58
+ call.resolve(new JSObject().put("session", "Initialized").put("info", result));
59
+ } else {
60
+ call.reject("Camera is not reachable or info could not be retrieved");
61
+ }
62
+ } catch (Exception e) {
63
+ call.reject("Camera is not reachable or info could not be retrieved", e);
64
+ }
65
+ })
66
+ .start();
67
+ }
68
+
69
+ @PluginMethod
70
+ public void livePreview(PluginCall call) {
71
+ boolean displayInFront = call.getBoolean("displayInFront", true);
72
+ boolean cropPreview = call.getBoolean("cropPreview", false);
73
+
74
+ getActivity().runOnUiThread(() -> {
75
+ containerView = getBridge().getActivity().findViewById(containerViewId);
76
+ if (containerView == null) {
77
+ containerView = new FrameLayout(getActivity().getApplicationContext());
78
+ containerView.setId(containerViewId);
79
+ FrameLayout.LayoutParams containerParams = new FrameLayout.LayoutParams(
80
+ ViewGroup.LayoutParams.MATCH_PARENT,
81
+ ViewGroup.LayoutParams.MATCH_PARENT
82
+ );
83
+ containerView.setLayoutParams(containerParams);
84
+
85
+ if (previewView == null) {
86
+ previewView = new ImageView(getContext());
87
+ FrameLayout.LayoutParams params;
88
+ if (cropPreview) {
89
+ params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
90
+ params.gravity = android.view.Gravity.FILL;
91
+ previewView.setAdjustViewBounds(true);
92
+ } else {
93
+ params = new FrameLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
94
+ params.gravity = android.view.Gravity.CENTER;
95
+ }
96
+ previewView.setLayoutParams(params);
97
+ previewView.setScaleType(cropPreview ? ImageView.ScaleType.CENTER_CROP : ImageView.ScaleType.FIT_CENTER);
98
+ containerView.addView(previewView);
99
+ }
100
+
101
+ // Make webview transparent
102
+ getBridge().getWebView().setBackgroundColor(Color.TRANSPARENT);
103
+ ((ViewGroup) getBridge().getWebView().getParent()).addView(containerView);
104
+
105
+ if (!displayInFront) {
106
+ getBridge().getWebView().getParent().bringChildToFront(getBridge().getWebView());
107
+ }
108
+
109
+ startMJPEGStream();
110
+ call.resolve(new JSObject().put("preview", "Started"));
111
+ } else {
112
+ call.reject("preview already started");
113
+ }
114
+ });
115
+ }
116
+
117
+ @PluginMethod
118
+ public void stopLivePreview(PluginCall call) {
119
+ getActivity().runOnUiThread(() -> {
120
+ isStreaming = false;
121
+ if (streamThread != null) {
122
+ streamThread.interrupt();
123
+ streamThread = null;
124
+ }
125
+ if (containerView != null) {
126
+ ((ViewGroup) getBridge().getWebView().getParent()).removeView(containerView);
127
+ getBridge().getWebView().setBackgroundColor(Color.WHITE);
128
+ containerView = null;
129
+ previewView = null;
130
+ }
131
+ call.resolve(new JSObject().put("preview", "Stopped"));
132
+ });
133
+ }
134
+
135
+ private void startMJPEGStream() {
136
+ isStreaming = true;
137
+ streamThread = new Thread(() -> {
138
+ try {
139
+ URL url = new URL(cameraUrl + "/osc/commands/execute");
140
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
141
+ connection.setRequestMethod("POST");
142
+ connection.setRequestProperty("Content-Type", "application/json; utf-8");
143
+ connection.setRequestProperty("Accept", "application/json");
144
+ connection.setDoOutput(true);
145
+
146
+ String jsonInputString = "{\"name\": \"camera.getLivePreview\"}";
147
+ try (OutputStream os = connection.getOutputStream()) {
148
+ byte[] input = jsonInputString.getBytes("utf-8");
149
+ os.write(input, 0, input.length);
150
+ }
151
+
152
+ InputStream inputStream = connection.getInputStream();
153
+ ByteArrayOutputStream buffer = new ByteArrayOutputStream();
154
+ byte[] data = new byte[8192];
155
+ int bytesRead;
156
+
157
+ while (isStreaming && (bytesRead = inputStream.read(data)) != -1) {
158
+ buffer.write(data, 0, bytesRead);
159
+ byte[] bufferData = buffer.toByteArray();
160
+
161
+ // Find start marker
162
+ int startIndex = -1;
163
+ for (int i = 0; i < bufferData.length - 1; i++) {
164
+ if (bufferData[i] == (byte) 0xFF && bufferData[i + 1] == (byte) 0xD8) {
165
+ startIndex = i;
166
+ break;
167
+ }
168
+ }
169
+
170
+ // Find end marker
171
+ int endIndex = -1;
172
+ if (startIndex != -1) {
173
+ for (int i = startIndex; i < bufferData.length - 1; i++) {
174
+ if (bufferData[i] == (byte) 0xFF && bufferData[i + 1] == (byte) 0xD9) {
175
+ endIndex = i + 1;
176
+ break;
177
+ }
178
+ }
179
+ }
180
+
181
+ // Process frame if we have both markers
182
+ if (startIndex != -1 && endIndex != -1) {
183
+ byte[] imageData = Arrays.copyOfRange(bufferData, startIndex, endIndex + 1);
184
+ processFrame(imageData);
185
+
186
+ // Keep remaining data after end marker
187
+ if (endIndex + 1 < bufferData.length) {
188
+ buffer.reset();
189
+ buffer.write(bufferData, endIndex + 1, bufferData.length - (endIndex + 1));
190
+ } else {
191
+ buffer.reset();
192
+ }
193
+ } else {
194
+ String bufferContent = new String(bufferData);
195
+ }
196
+ }
197
+ } catch (Exception e) {
198
+ e.printStackTrace();
199
+ }
200
+ });
201
+ streamThread.start();
202
+ }
203
+
204
+ private void processFrame(byte[] imageData) {
205
+ try {
206
+ BitmapFactory.Options options = new BitmapFactory.Options();
207
+ options.inPreferredConfig = Bitmap.Config.ARGB_8888;
208
+
209
+ final Bitmap bitmap = BitmapFactory.decodeByteArray(imageData, 0, imageData.length, options);
210
+ if (bitmap != null) {
211
+ getActivity().runOnUiThread(() -> {
212
+ if (previewView != null) {
213
+ previewView.setImageBitmap(bitmap);
214
+ }
215
+ });
216
+ }
217
+ } catch (Exception e) {
218
+ e.printStackTrace();
219
+ }
220
+ }
221
+
222
+ @PluginMethod
223
+ public void getCameraAsset(PluginCall call) {
224
+ try {
225
+ JSObject data = call.getData();
226
+ String assetUrl = data.getString("url");
227
+ boolean saveToFile = call.getBoolean("saveToFile", false);
228
+
229
+ if (assetUrl == null) {
230
+ call.reject("URL is required");
231
+ return;
232
+ }
233
+
234
+ new Thread(() -> {
235
+ try {
236
+ URL url = new URL(assetUrl);
237
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
238
+ connection.setRequestMethod("GET");
239
+ connection.setConnectTimeout(10000);
240
+ connection.setReadTimeout(10000);
241
+
242
+ int responseCode = connection.getResponseCode();
243
+
244
+ InputStream inputStream;
245
+ if (responseCode >= 400) {
246
+ inputStream = connection.getErrorStream();
247
+ } else {
248
+ inputStream = connection.getInputStream();
249
+ }
250
+
251
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
252
+ byte[] buffer = new byte[4096];
253
+ int bytesRead;
254
+ while ((bytesRead = inputStream.read(buffer)) != -1) {
255
+ outputStream.write(buffer, 0, bytesRead);
256
+ }
257
+
258
+ byte[] bytes = outputStream.toByteArray();
259
+ JSObject result = new JSObject();
260
+ result.put("statusCode", responseCode);
261
+
262
+ if (saveToFile) {
263
+ // Create a unique filename based on timestamp
264
+ String timestamp = String.valueOf(System.currentTimeMillis());
265
+ String filename = "ricoh_" + timestamp + ".jpg";
266
+ java.io.File outputDir = getContext().getCacheDir();
267
+ java.io.File outputFile = new java.io.File(outputDir, filename);
268
+
269
+ try (java.io.FileOutputStream fos = new java.io.FileOutputStream(outputFile)) {
270
+ fos.write(bytes);
271
+ result.put("filePath", outputFile.getAbsolutePath());
272
+ }
273
+ } else {
274
+ // downSize here to make it work with our magic watcher lib
275
+ String base64 = android.util.Base64.encodeToString(downSizeImage(bytes), Base64.NO_WRAP);
276
+ result.put("data", base64);
277
+ }
278
+
279
+ call.resolve(result);
280
+ } catch (Exception e) {
281
+ call.reject("Failed to fetch asset: " + e.getMessage(), e);
282
+ }
283
+ })
284
+ .start();
285
+ } catch (Exception e) {
286
+ call.reject("Failed to process request: " + e.getMessage(), e);
287
+ }
288
+ }
289
+
290
+ @PluginMethod
291
+ public void listFiles(PluginCall call) {
292
+ JSObject parameters = new JSObject();
293
+ parameters.put("fileType", call.getString("fileType", "all"));
294
+ parameters.put("startPosition", call.getInt("startPosition", 0));
295
+ parameters.put("entryCount", call.getInt("entryCount", 100));
296
+ parameters.put("maxThumbSize", call.getInt("maxThumbSize", 0));
297
+ parameters.put("_detail", call.getBoolean("_detail", true));
298
+
299
+ JSObject payload = new JSObject();
300
+ payload.put("name", "camera.listFiles");
301
+ payload.put("parameters", parameters);
302
+
303
+ sendCommandRaw(call, "/osc/commands/execute", payload);
304
+ }
305
+
306
+ @PluginMethod
307
+ public void capturePicture(PluginCall call) {
308
+ JSObject payload = new JSObject();
309
+ payload.put("name", "camera.takePicture");
310
+ sendCommandRaw(call, "/osc/commands/execute", payload);
311
+ }
312
+
313
+ @PluginMethod
314
+ public void readSettings(PluginCall call) {
315
+ try {
316
+ JSObject data = call.getData();
317
+ JSObject payload = new JSObject();
318
+ payload.put("name", "camera.getOptions");
319
+ JSObject parameters = new JSObject();
320
+ parameters.put("optionNames", data.getJSONArray("options"));
321
+ payload.put("parameters", parameters);
322
+
323
+ sendCommandRaw(call, "/osc/commands/execute", payload);
324
+ } catch (Exception e) {
325
+ android.util.Log.e("Ricoh360Camera", "Exception: " + e.getMessage(), e);
326
+ call.reject("Failed to read settings: " + e.getMessage(), e);
327
+ }
328
+ }
329
+
330
+ @PluginMethod
331
+ public void setSettings(PluginCall call) {
332
+ try {
333
+ JSObject data = call.getData();
334
+ JSObject payload = new JSObject();
335
+ payload.put("name", "camera.setOptions");
336
+ JSObject parameters = new JSObject();
337
+ parameters.put("options", data.getJSObject("options"));
338
+ payload.put("parameters", parameters);
339
+
340
+ sendCommandRaw(call, "/osc/commands/execute", payload);
341
+ } catch (Exception e) {
342
+ android.util.Log.e("Ricoh360Camera", "Exception: " + e.getMessage(), e);
343
+ call.reject("Failed to set settings: " + e.getMessage(), e);
344
+ }
345
+ }
346
+
347
+ private void sendCommandRaw(PluginCall call, String endpoint, JSONObject payload) {
348
+ try {
349
+ String commandUrl = cameraUrl + endpoint;
350
+ String jsonInputString = payload.toString();
351
+
352
+ android.util.Log.d("Ricoh360Camera", "Request body: " + jsonInputString);
353
+
354
+ HttpURLConnection connection = createConnection(commandUrl, jsonInputString);
355
+ int responseCode = connection.getResponseCode();
356
+ android.util.Log.d("Ricoh360Camera", "Response code: " + responseCode);
357
+
358
+ if (responseCode == HttpURLConnection.HTTP_OK) {
359
+ InputStream inputStream = connection.getInputStream();
360
+ String result = new BufferedReader(new InputStreamReader(inputStream)).lines().collect(Collectors.joining("\n"));
361
+ android.util.Log.d("Ricoh360Camera", "Response body: " + result);
362
+ call.resolve(new JSObject(result));
363
+ } else {
364
+ InputStream errorStream = connection.getErrorStream();
365
+ String error = new BufferedReader(new InputStreamReader(errorStream)).lines().collect(Collectors.joining("\n"));
366
+ android.util.Log.e("Ricoh360Camera", "Error response: " + error);
367
+ call.reject("Command failed: " + error);
368
+ }
369
+ } catch (Exception e) {
370
+ android.util.Log.e("Ricoh360Camera", "Exception: " + e.getMessage(), e);
371
+ call.reject("Command failed: " + e.getMessage(), e);
372
+ }
373
+ }
374
+
375
+ @PluginMethod
376
+ public void sendCommand(PluginCall call) {
377
+ String endpoint = call.getString("endpoint");
378
+ JSObject payload = call.getObject("payload");
379
+ if (endpoint == null || payload == null) {
380
+ call.reject("Endpoint and payload are required");
381
+ return;
382
+ }
383
+
384
+ sendCommandRaw(call, endpoint, payload);
385
+ }
386
+
387
+ private HttpURLConnection createConnection(String urlString, String jsonInputString) throws Exception {
388
+ URL url = new URL(urlString);
389
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
390
+ connection.setRequestMethod("POST");
391
+ connection.setRequestProperty("Content-Type", "application/json; charset=utf-8");
392
+ connection.setRequestProperty("Accept", "application/json");
393
+ connection.setConnectTimeout(10000);
394
+ connection.setReadTimeout(10000);
395
+ connection.setDoInput(true);
396
+ connection.setDoOutput(true);
397
+ connection.setUseCaches(false);
398
+
399
+ OutputStream os = connection.getOutputStream();
400
+ byte[] input = jsonInputString.getBytes("utf-8");
401
+ os.write(input);
402
+ os.flush();
403
+ os.close();
404
+
405
+ return connection;
406
+ }
407
+
408
+ private byte[] downSizeImage(byte[] image) {
409
+ final int MAX_WIDTH = 2048; // Must be equal to or less than 4096
410
+ BitmapFactory.Options options = new BitmapFactory.Options();
411
+ options.inJustDecodeBounds = true;
412
+
413
+ BitmapFactory.decodeByteArray(image, 0, image.length, options);
414
+ android.util.Log.d("Ricoh360Camera", "JPEG width: " + options.outWidth);
415
+
416
+ if (options.outWidth > MAX_WIDTH) {
417
+ float scaleFactor = MAX_WIDTH / (float) options.outWidth;
418
+ android.graphics.Matrix scale = new android.graphics.Matrix();
419
+ scale.postScale(scaleFactor, scaleFactor);
420
+
421
+ Bitmap bitmap = BitmapFactory.decodeByteArray(image, 0, image.length);
422
+ Bitmap resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, options.outWidth, options.outHeight, scale, false);
423
+
424
+ android.util.Log.d("Ricoh360Camera", "Resized width: " + resizedBitmap.getWidth());
425
+ bitmap.recycle();
426
+
427
+ ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
428
+ try {
429
+ resizedBitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
430
+ resizedBitmap.recycle();
431
+ return outputStream.toByteArray();
432
+ } finally {
433
+ try {
434
+ outputStream.close();
435
+ } catch (Exception e) {
436
+ android.util.Log.e("Ricoh360Camera", "Error closing output stream", e);
437
+ }
438
+ }
439
+ }
440
+ return image;
441
+ }
442
+
443
+ @PluginMethod
444
+ public void getPluginVersion(final PluginCall call) {
445
+ try {
446
+ final JSObject ret = new JSObject();
447
+ ret.put("version", this.pluginVersion);
448
+ call.resolve(ret);
449
+ } catch (final Exception e) {
450
+ call.reject("Could not get plugin version", e);
451
+ }
452
+ }
453
+ }
File without changes