diff --git a/.github/workflows/dummy.yaml b/.github/workflows/dummy.yaml new file mode 100644 index 0000000000..cf858bd139 --- /dev/null +++ b/.github/workflows/dummy.yaml @@ -0,0 +1,11 @@ +name: Dummy for test + +on: + workflow_dispatch: + +jobs: + dummy: + if: ${{ false }} + runs-on: ubuntu-latest + steps: + - run: "" diff --git a/demos/demo-flutter/lib/capture_video_screen.dart b/demos/demo-flutter/lib/capture_video_screen.dart index f4c97b5d36..a18b61845b 100644 --- a/demos/demo-flutter/lib/capture_video_screen.dart +++ b/demos/demo-flutter/lib/capture_video_screen.dart @@ -184,6 +184,9 @@ class _CaptureVideoScreen extends State with WidgetsBindingO } void startVideoCapture() { + if (videoCapture == null) { + return; + } if (shooting) { debugPrint('already shooting'); return; @@ -195,27 +198,34 @@ class _CaptureVideoScreen extends State with WidgetsBindingO // Stops while shooting is in progress stopLivePreview(); - videoCapturing = videoCapture!.startCapture((fileUrl) { + + videoCapturing = videoCapture?.startCapture((fileUrl) { setState(() { shooting = false; }); debugPrint('capture video: $fileUrl'); if (!mounted) return; - final uri = Uri.parse(fileUrl); - Navigator.of(context).push( - MaterialPageRoute(builder: (_) => VideoScreen( - name: uri.pathSegments.last, - fileUrl: fileUrl, + if (fileUrl != null) { + final uri = Uri.parse(fileUrl); + Navigator.of(context).push( + MaterialPageRoute(builder: (_) => VideoScreen( + name: uri.pathSegments.last, + fileUrl: fileUrl, + ) ) - ) - ).then((value) => startLivePreview()); + ).then((value) => startLivePreview()); + } }, (exception) { setState(() { shooting = false; }); startLivePreview(); debugPrint(exception.toString()); + + }, onStopFailed: (exception) { + debugPrint(exception.toString()); + MessageBox.show(context, 'Error. stopCapture.\n$exception'); }); } @@ -224,6 +234,7 @@ class _CaptureVideoScreen extends State with WidgetsBindingO debugPrint('Not start capture.'); return; } + debugPrint("stopVideoCapture"); videoCapturing!.stopCapture(); } } diff --git a/docs/tutorial-android.ja.md b/docs/tutorial-android.ja.md index b660d850e3..6a067207a8 100644 --- a/docs/tutorial-android.ja.md +++ b/docs/tutorial-android.ja.md @@ -1,42 +1,45 @@ -# RICOH360 THETA clientチュートリアル +# RICOH360 THETA client チュートリアル ## 事前準備 -* モジュールの`build.gradle`の`dependencies`に次を追加します。 +- モジュールの`build.gradle`の`dependencies`に次を追加します。 ``` implementation "com.ricoh360.thetaclient:theta-client:1.0.0" ``` -* 本SDKを使用したアプリケーションが動作するスマートフォンとTHETAを無線LAN接続しておきます。 +- 本 SDK を使用したアプリケーションが動作するスマートフォンと THETA を無線 LAN 接続しておきます。 -## THETA clientのインスタンス作成 +## THETA client のインスタンス作成 インスタンス作成時にカメラの日時設定、言語設定、シャッター音量設定、スリープまでの時間設定、電源オフまでの時間設定を行うこともできます。 -* 言語設定(THETA S, THETA SCでは指定しても無視される) - * “en-US” - * “en-GB” - * “ja” - * “fr” - * “de” - * “zh-TW” - * “zh-CN” - * “it” - * “ko” +- 言語設定(THETA S, THETA SC では指定しても無視される) -* 日時設定 - * YYYY:MM:DD HH:MM:SS+HH:MMの形式 + - “en-US” + - “en-GB” + - “ja” + - “fr” + - “de” + - “zh-TW” + - “zh-CN” + - “it” + - “ko” -* スリープモードに入るまでの時間(分) - * 自動オフしない, 3分, 5分, 7分, 10分 +- 日時設定 -* スリープから自動電源オフまでの時間(分) - * 自動オフしない, 5分, 10分, 15分, 30分 + - YYYY:MM:DD HH:MM:SS+HH:MM の形式 -* シャッター音 - * 0〜100 +- スリープモードに入るまでの時間(分) + - 自動オフしない, 3 分, 5 分, 7 分, 10 分 -``` kotlin +- スリープから自動電源オフまでの時間(分) + + - 自動オフしない, 5 分, 10 分, 15 分, 30 分 + +- シャッター音 + - 0〜100 + +```kotlin import com.ricoh360.pf.theta.ThetaRepository val thetaUrl = "192.168.1.1:80" @@ -53,7 +56,7 @@ thetaRepository = ThetaRepository.newInstance(thetaUrl, thetaConfig) まず`PhotoCapture.Builder`を使って撮影設定を行い、`PhotoCapture`オブジェクトを生成します。 -``` kotlin +```kotlin class TakenCallback : PhotoCapture.TakePictureCallback { override fun onSuccess(fileUrl: String) { // get JPEG file @@ -79,104 +82,110 @@ thetaRepository.getPhotoCaptureBuilder() ### 静止画撮影時に設定できる項目 -* 露出補正 - * -2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0 -* セルフタイマー - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -* 解像度 +- 露出補正 + - -2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0 +- セルフタイマー + - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +- 解像度 + +| 機種 | 解像度指定 | 横(pixel) | 縦(pixel) | +| ------------------- | ---------- | --------- | --------- | +| THETA X | IMAGE_11K | 11,008 | 5,504 | +| THETA X | IMAGE_5_5K | 5,504 | 2,752 | +| THETA Z1 | IMAGE_6_7K | 6,720 | 3,360 | +| THETA S, SC, SC2, V | IMAGE_5K | 5,376 | 2,688 | -| 機種 | 解像度指定 | 横(pixel) | 縦(pixel) | -| ---- | --------- |-- | -- | -| THETA X | IMAGE_11K | 11,008 | 5,504 | -| THETA X | IMAGE_5_5K | 5,504 | 2,752 | -| THETA Z1 | IMAGE_6_7K | 6,720 | 3,360 | -| THETA S, SC, SC2, V | IMAGE_5K | 5,376 | 2,688 | +- 画像処理 -* 画像処理 - * なし - * ノイズ軽減 - * HDR + - なし + - ノイズ軽減 + - HDR -* GPSオン/オフ (THETA X以外は指定しても無視される) - * ON - * OFF +- GPS オン/オフ (THETA X 以外は指定しても無視される) -* ISO上限 (THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される) - * 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 + - ON + - OFF +- ISO 上限 (THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される) + - 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 ## 動画を撮影する まず`VideoCapture.Builder`を使って撮影設定を行い、`VideoCapture`オブジェクトを生成します。 -``` kotlin +```kotlin val videoCapture: VideoCapture = VideoCapture.Builder() .setAutoIsoUpperLimit(IsoEnum.ISO_800) .setFileFormat(VideoFileFormatEnum.VIDEO_5_7K_30F) .build() ``` -上の例ではISO感度の最大値を800に、解像度を5.7Kに設定しています。 +上の例では ISO 感度の最大値を 800 に、解像度を 5.7K に設定しています。 -Theta SとTheta SC以外ではプレビューを表示できます。表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください +Theta S と Theta SC 以外ではプレビューを表示できます。表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください -次に`VideoCapture.startCapture()`を呼んで動画の撮影を開始します。引数にはMP4ファイルをHTTP GETするコールバック関数を渡します。 +次に`VideoCapture.startCapture()`を呼んで動画の撮影を開始します。引数には MP4 ファイルを HTTP GET するコールバック関数を渡します。 -``` kotlin +```kotlin class TakenCallback : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onCaptureCompleted(fileUrl: String?) { // get MP4 file } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { // error processing } + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + // error stopCapture + } } val videoCapturing: VideoCapturing = videoCapture.startCapture(TakenCallback()) ``` -次に`VideoCapturing.stopCapture()`を呼んで動画の撮影を終了します。撮影終了後、MP4ファイルの生成が完了すると、`startCapture()`に渡したコールバック関数が呼ばれます。 +次に`VideoCapturing.stopCapture()`を呼んで動画の撮影を終了します。撮影終了後、MP4 ファイルの生成が完了すると、`startCapture()`に渡したコールバック関数が呼ばれます。 -``` kotlin +```kotlin videoCapturing.stopCapture() ``` ### 動画撮影時に設定できる項目 -* 解像度 +- 解像度 -| 機種 | 解像度指定 | 横(pixel) | 縦(pixel) | -| ---- | --------- |-- | -- | -| THETA X | VIDEO_5_7K_30F | 5,760 | 2,880 | -| THETA X | VIDEO_4K_30F | 3,840 | 1,920 | -| THETA X | VIDEO_2K_30F | 1,920 | 960 | -| THETA Z1 | VIDEO_4K | 3,840 | 1,920 | -| THETA Z1 | VIDEO_2K | 1,920 | 960 | -| THETA SC2, V | VIDEO_4K | 3,840 | 1,920 | -| THETA SC2, V | VIDEO_2K | 1,920 | 960 | -| THETA S, SC | VIDEO_FULL_HD | 1,920 | 1,080 | +| 機種 | 解像度指定 | 横(pixel) | 縦(pixel) | +| ------------ | -------------- | --------- | --------- | +| THETA X | VIDEO_5_7K_30F | 5,760 | 2,880 | +| THETA X | VIDEO_4K_30F | 3,840 | 1,920 | +| THETA X | VIDEO_2K_30F | 1,920 | 960 | +| THETA Z1 | VIDEO_4K | 3,840 | 1,920 | +| THETA Z1 | VIDEO_2K | 1,920 | 960 | +| THETA SC2, V | VIDEO_4K | 3,840 | 1,920 | +| THETA SC2, V | VIDEO_2K | 1,920 | 960 | +| THETA S, SC | VIDEO_FULL_HD | 1,920 | 1,080 | -* 最長撮影時間(分) - * 5 - * 25 +- 最長撮影時間(分) -* ISO上限 (THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される) - * 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 + - 5 + - 25 + +- ISO 上限 (THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される) + - 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 + +| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | +| ----------- | --------- | --------- | ------------------- | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1 以降 | +| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1 以前 | +| THETA SC2 | 1024 | 512 | 30 | | +| THETA S, SC | 640 | 320 | 10 | | -| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | -| ---- | -- | -- | ------------ | ---- | -| THETA X | 1024 | 512 | 30 | | -| THETA Z1 | 1024 | 512 | 30 | | -| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1以降 | -| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1以前 | -| THETA SC2 | 1024 | 512 | 30 || -| THETA S, SC | 640 | 320 | 10 | | +`ThetaRepository.getLivePreview()`を呼ぶと、`Flow`が返るので、そこか各フレームの JPEG を取得します。 -`ThetaRepository.getLivePreview()`を呼ぶと、`Flow`が返るので、そこか各フレームのJPEGを取得します。 ```kotlin import io.ktor.utils.io.core.ByteReadPacket @@ -184,54 +193,55 @@ thetaRepository.getLivePreview() .collect { byteReadPacket -> if (isActive) { byteReadPacket.inputStream().use { - // ストリームをJPEGとして表示 + // Displaying the stream as JPEG } } byteReadPacket.release() } ``` +## THETA 内の静止画・動画を一覧する -## THETA内の静止画・動画を一覧する -THETA内の静止画(JPEGファイル)や動画(MP4ファイル)の一覧は`ThetaRepository.listFiles(fileType, startPosition, entryCount)`を使って取得できます。 +THETA 内の静止画(JPEG ファイル)や動画(MP4 ファイル)の一覧は`ThetaRepository.listFiles(fileType, startPosition, entryCount)`を使って取得できます。 `fileType`は`ThetaRepository.FileTypeEnum`型で内容は以下の通りです。 -`ThetaRepository.listFiles()`の戻り値型は`ThetaRepository.ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` がTHETA内のファイル一覧です。 +`ThetaRepository.listFiles()`の戻り値型は`ThetaRepository.ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` が THETA 内のファイル一覧です。 `fileList`は `ThetaRepository.FileInfo`のリストです。 -JPEGファイル、MP4ファイルは`FileInfo.fileUrl`、サムネイルのJPEGファイルは`FileInfo.thumbnailUrl`の値を参照し、HTTP GETします。 +JPEG ファイル、MP4 ファイルは`FileInfo.fileUrl`、サムネイルの JPEG ファイルは`FileInfo.thumbnailUrl`の値を参照し、HTTP GET します。 -* ThetaRepository.FileTypeEnum +- ThetaRepository.FileTypeEnum - |値|内容| - |---|---| - |IMAGE|静止画(JPEGファイル)を一覧| - |VIDEO|動画(MP4ファイル)を一覧| - |ALL|全てのファイルを一覧| + | 値 | 内容 | + | ----- | ----------------------------- | + | IMAGE | 静止画(JPEG ファイル)を一覧 | + | VIDEO | 動画(MP4 ファイル)を一覧 | + | ALL | 全てのファイルを一覧 | -* ThetaRepository.ThetaFiles +- ThetaRepository.ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|THETA内のファイル一覧| - |totalEntries|Int| THETA内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) + | Property name | Type | Contents | + | ------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | THETA 内のファイル一覧 | + | totalEntries | Int | THETA 内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) | -* ThetaRepository.FileInfo +- ThetaRepository.FileInfo - |プロパティ名|型|内容| - |---|---|---| - |name|String|ファイル名を表します| - |size|Long|ファイルサイズ(バイト数)を表します| - |dateTime|String|撮影日時(YYYY:MM:DD HH:MM:SS)を表します| - |fileUrl|String|ファイルのURLを表します| - |thumbnailUrl|String|サムネールのURLを表します| + | プロパティ名 | 型 | 内容 | + | ------------ | ------ | ----------------------------------------- | + | name | String | ファイル名を表します | + | size | Long | ファイルサイズ(バイト数)を表します | + | dateTime | String | 撮影日時(YYYY:MM:DD HH:MM:SS)を表します | + | fileUrl | String | ファイルの URL を表します | + | thumbnailUrl | String | サムネールの URL を表します | -## THETAの情報を取得する +## THETA の情報を取得する `ThetaRepository.getThetaInfo()`を呼びます。 -## THETAの状態を取得する +## THETA の状態を取得する `ThetaRepository.getThetaState()`を呼びます。 -## THETAをリセットする +## THETA をリセットする + `ThetaRepository.reset()`を呼びます。 diff --git a/docs/tutorial-android.md b/docs/tutorial-android.md index 5404a36785..a77bbf838b 100644 --- a/docs/tutorial-android.md +++ b/docs/tutorial-android.md @@ -2,36 +2,38 @@ ## Advance preparation -* Add following descriptions to the `dependencies` of your module's `build.gradle`. +- Add following descriptions to the `dependencies` of your module's `build.gradle`. + ``` implementation "com.ricoh360.thetaclient:theta-client:1.0.0" ``` -* Connect the wireless LAN between THETA and the smartphone that runs on the application using this SDK. +- Connect the wireless LAN between THETA and the smartphone that runs on the application using this SDK. ## Create an instance of THETA client When creating an instance, you can set the camera date and time, the language, the shutter volume, the time to sleep, and the time to power off. -* Language setting (ignored by THETA S, THETA SC) - * "en-US" - * "en-GB" - * "ja" - * "fr" - * "de" - * "zh-TW" - * "zh-CN" - * "it" - * "ko" -* Date Adjust - * YYYY:MM:DD HH:MM:SS+HH:MM format -* Time to enter sleep mode (min) - * Not Auto Off, 3 min, 5 min, 7 min, 10 min -* Time from sleep to automatic power-off (minutes) - * Not automatically off, 5 minutes, 10 minutes, 15 minutes, 30 minutes -* Shutter sound - * 0~100 - -``` kotlin + +- Language setting (ignored by THETA S, THETA SC) + - "en-US" + - "en-GB" + - "ja" + - "fr" + - "de" + - "zh-TW" + - "zh-CN" + - "it" + - "ko" +- Date Adjust + - YYYY:MM:DD HH:MM:SS+HH:MM format +- Time to enter sleep mode (min) + - Not Auto Off, 3 min, 5 min, 7 min, 10 min +- Time from sleep to automatic power-off (minutes) + - Not automatically off, 5 minutes, 10 minutes, 15 minutes, 30 minutes +- Shutter sound + - 0 ~ 100 + +```kotlin Import com.ricoh360.pf.theta.ThetaRepository Val thetaUrl = "192.168.1.1:80" Val thetaConfig = ThetaRepository.Config() @@ -42,9 +44,12 @@ Val thetaConfig = ThetaRepository.Config() // thetaConfig.offDelay = ThetaRepository.OffDelayEnum.DISABLE ThetaRepository = ThetaRepository.newInstance(thetaUrl, thetaConfig) ``` + ## Shoot still images + First, shooting settings are performed using `PhotoCapture.Builder` to create `PhotoCapture` objects. -``` kotlin + +```kotlin class TakenCallback : PhotoCapture.TakePictureCallback { override fun onSuccess(fileUrl: String) { // get JPEG file @@ -70,96 +75,102 @@ See [Display a preview](#preview) for instructions on how to view preview. ### Properties that can be set for shooting still images -* Exposure compensation - * -2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0 -* Self-timer - * 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 -* Resolution - -| Model |Specify resolution |Width (Pixel) |Height (Pixel) | -| ---- | --------- |-- | -- | -| THETA X | IMAGE_11K |11,008 |5,504 | -| THETA X |IMAGE_5_5K |5,504 |2,752 | -| THETA Z1 |IMAGE_6_7K |6,720 |3,360 | -| THETA S, SC, SC2, V |IMAGE_5K |5,376 |2,688 | - -* Image processing - * None - * Noise reduction - * HDR -* GPS ON/OFF (ignored except for THETA X) - * ON - * OFF -* ISO upper limit (THETA V firmware v2.50.1 or earlier, ignored even if specified in THETA S or THETA SC) - * 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 +- Exposure compensation + - -2.0, -1.7, -1.3, -1.0, -0.7, -0.3, 0.0, 0.3, 0.7, 1.0, 1.3, 1.7, 2.0 +- Self-timer + - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 +- Resolution + +| Model | Specify resolution | Width (Pixel) | Height (Pixel) | +| ------------------- | ------------------ | ------------- | -------------- | +| THETA X | IMAGE_11K | 11,008 | 5,504 | +| THETA X | IMAGE_5_5K | 5,504 | 2,752 | +| THETA Z1 | IMAGE_6_7K | 6,720 | 3,360 | +| THETA S, SC, SC2, V | IMAGE_5K | 5,376 | 2,688 | + +- Image processing + - None + - Noise reduction + - HDR +- GPS ON/OFF (ignored except for THETA X) + - ON + - OFF +- ISO upper limit (THETA V firmware v2.50.1 or earlier, ignored even if specified in THETA S or THETA SC) + - 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 ## Shoot a video First, shooting settings are performed using `VideoCapture.Builder` to create `VideoCapture` objects. -``` kotlin +```kotlin Val videoCapture: VideoCapture = VideoCapture.Builder() .setAutoIsoUpperLimit(IsoEnum.ISO_800) .setFileFormat(VideoFileFormatEnum.VIDEO_5_7K_30F) .build() ``` + In the example above, the maximum ISO sensitivity value is 800 and the resolution is 5.7K. You can display previews other than THETA S and THETA SC. See [Display a preview](#preview) Next, we call `Video Capture.startCapture()` to start recording videos. The argument is passed to the callback function that HTTP GET the MP4 file. -``` kotlin + +```kotlin class TakenCallback : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onCaptureCompleted(fileUrl: String?) { // get MP4 file } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { // error processing } + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + // error stopCapture + } } val videoCapturing: VideoCapturing = videoCapture.startCapture(TakenCallback()) ``` + Next, we call `Video Capturing.stopCapture()` to finish recording the video. When the MP4 file is created after shooting, the callback function passed to `startCapture()` is called. -``` kotlin +```kotlin VideoCapturing.stopCapture() ``` ### Properties that can be set when shooting videos -* Resolution - -| Model |Specify resolution |Width (pixel) | Height (pixel) | -| ---- | --------- |-- | -- | -| THETA X | VIDEO_5_7K_30F |5,760 |2,880 | -| THETA X |VIDEO_4K_30F |3,840 |1,920 | -| THETA X |VIDEO_2K_30F |1,920 |960 | -| THETA Z1 |VIDEO_4K |3,840 |1,920 | -| THETA Z1 |VIDEO_2K |1,920 |960 | -| THETA SC2, V |VIDEO_4K |3,840 |1,920 | -| THETA SC2, V |VIDEO_2K |1,920 |960 | -| THETA S, SC |VIDEO_FULL_HD |1,920 |1,080 | - -* Maximum shooting time (min) - * 5 - * 25 -* ISO upper limit (THETA V firmware v2.50.1 or earlier, ignored even if specified in THETA S or THETA SC) - * 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 +- Resolution + +| Model | Specify resolution | Width (pixel) | Height (pixel) | +| ------------ | ------------------ | ------------- | -------------- | +| THETA X | VIDEO_5_7K_30F | 5,760 | 2,880 | +| THETA X | VIDEO_4K_30F | 3,840 | 1,920 | +| THETA X | VIDEO_2K_30F | 1,920 | 960 | +| THETA Z1 | VIDEO_4K | 3,840 | 1,920 | +| THETA Z1 | VIDEO_2K | 1,920 | 960 | +| THETA SC2, V | VIDEO_4K | 3,840 | 1,920 | +| THETA SC2, V | VIDEO_2K | 1,920 | 960 | +| THETA S, SC | VIDEO_FULL_HD | 1,920 | 1,080 | + +- Maximum shooting time (min) + - 5 + - 25 +- ISO upper limit (THETA V firmware v2.50.1 or earlier, ignored even if specified in THETA S or THETA SC) + - 200, 250, 320, 400, 500, 640, 800, 1000, 1250, 1600, 2000, 2500, 3200 ## Display a preview The preview is an equirectangular form of motion JPEG. -| Model |Pixel |Pixel |Frame rate (fps) |Remarks | -| ---- | -- | -- | ------------ | ---- | -| THETA X | 1024 |512 | 30 || -| THETA Z1 |1024 | 512 |30 | | -| THETA V |1024 | 512 |30 | Firmware v2.21.1 or later | -| THETA V |1024 | 512 |8 | Firmware v2.20.1 or earlier | -| THETA SC2 |1024 | 512 |30 || -| THETA S, SC |640 | 320 |10 | | +| Model | Pixel | Pixel | Frame rate (fps) | Remarks | +| ----------- | ----- | ----- | ---------------- | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | Firmware v2.21.1 or later | +| THETA V | 1024 | 512 | 8 | Firmware v2.20.1 or earlier | +| THETA SC2 | 1024 | 512 | 30 | | +| THETA S, SC | 640 | 320 | 10 | | If you call `ThetaRepository.getLivePreview()` then "Flow<[io.ktor.utils.io.core.ByteReadPacket](https://api.ktor.io/ktor-io/io.ktor.utils.io.core/-byte-read-packet/index.html)>" returns, you get the JPEG for each frame. @@ -186,32 +197,33 @@ The return type of `ThetaRepository.listFiles()` is `ThetaRepository.ThetaFiles` JPEG and MP4 files refer to `FileInfo.fileUrl` and thumbnail JPEG files refer to `FileInfo.thumbnailUrl` and HTTP GET. -* ThetaRepository.FileTypeEnum +- ThetaRepository.FileTypeEnum - |Value|Content| - |---|---| - |IMAGE|List of still images (JPEG files)| - |VIDEO|List of videos (MP4 files)| - |ALL|List all files| + | Value | Content | + | ----- | --------------------------------- | + | IMAGE | List of still images (JPEG files) | + | VIDEO | List of videos (MP4 files) | + | ALL | List all files | -* ThetaFiles +- ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|The list of files in THETA| - |totalEntries|Int| Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) + | Property name | Type | Contents | + | ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | The list of files in THETA | + | totalEntries | Int | Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) | -* ThetaRepository.FileInfo +- ThetaRepository.FileInfo - |Property name|Type|Contents| - |---|---|---| - |name|String|Represents the file name| - |size|Long|Indicates the file size (in bytes)| - |dateTime|String|Shooting date and time (YYYY:MM:DD HH:MM:SS)| - |fileUrl|String|Represents the URL of the file| - |thumbnailUrl|String|Represents a thumbnail URL| + | Property name | Type | Contents | + | ------------- | ------ | -------------------------------------------- | + | name | String | Represents the file name | + | size | Long | Indicates the file size (in bytes) | + | dateTime | String | Shooting date and time (YYYY:MM:DD HH:MM:SS) | + | fileUrl | String | Represents the URL of the file | + | thumbnailUrl | String | Represents a thumbnail URL | ## Get the THETA information + `ThetaRepository.getThetaInfo()` is called. ## Get the state of THETA diff --git a/docs/tutorial-flutter.ja.md b/docs/tutorial-flutter.ja.md index 1013b3bbd7..dcd162222c 100644 --- a/docs/tutorial-flutter.ja.md +++ b/docs/tutorial-flutter.ja.md @@ -1,31 +1,34 @@ -# RICOH360 THETA Clientチュートリアル +# RICOH360 THETA Client チュートリアル ## 使用可能な機種 -* RICOH THETA X -* RICOH THETA Z1 -* RICOH THETA V -* RICOH THETA S (ファームウェアv1.62以降のみ) -* RICOH THETA SC +- RICOH THETA X +- RICOH THETA Z1 +- RICOH THETA V +- RICOH THETA S (ファームウェア v1.62 以降のみ) +- RICOH THETA SC -## Flutterプロジェクトの作成 +## Flutter プロジェクトの作成 -対応しているプラットフォームは、iOSとAndroid、言語はそれぞれ、kotlin、swiftを指定してプロジェクトを作成する。 +対応しているプラットフォームは、iOS と Android、言語はそれぞれ、kotlin、swift を指定してプロジェクトを作成する。 -``` Terminal +```Terminal flutter create --platforms=android,ios -i swift -a kotlin your_app_name ``` ## プロジェクトの設定 -### theta clientのコピー -THETA ClientのFlutter pluginパッケージを作成したプロジェクトにコピーする。 + +### theta client のコピー + +THETA Client の Flutter plugin パッケージを作成したプロジェクトにコピーする。 `demo-flutter`では、プロジェクト直下`demo-flutter/packages/theta_client_flutter`に配置。 -### Flutterのプラグインの設定 +### Flutter のプラグインの設定 + `pubspec.yaml`の`dependencies`にコピーした`theta_client_flutter`を追加。 -``` pubspec.yaml +```pubspec.yaml dependencies: flutter: sdk: flutter @@ -33,23 +36,25 @@ dependencies: path: ./packages/theta_client_flutter ``` -### Androidの設定 -最小SDKバージョンを26以上に設定 +### Android の設定 + +最小 SDK バージョンを 26 以上に設定 ``` build.gradle minSdkVersion 26 ``` -### iOSの設定 -iOS Deployment Target を15以上に設定 +### iOS の設定 + +iOS Deployment Target を 15 以上に設定 ## 事前準備 -本SDKを使用したアプリケーションが動作するスマートフォンとTHETAを無線LAN接続しておきます。 +本 SDK を使用したアプリケーションが動作するスマートフォンと THETA を無線 LAN 接続しておきます。 -## THETA Clientの初期化 +## THETA Client の初期化 -``` Dart +```Dart import 'package:theta_client_flutter/theta_client_flutter.dart'; final _thetaClientFlutter = ThetaClientFlutter(); @@ -73,18 +78,18 @@ _thetaClientFlutter.initialize('http://:<ポート番号>') }); ``` -* THETA IP ADDRESS +- THETA IP ADDRESS - | モード | アドレス | - |-------|---------| - |ダイレクトモード| 192.168.1.1 | - |その他| カメラのIPアドレス| + | モード | アドレス | + | ---------------- | -------------------- | + | ダイレクトモード | 192.168.1.1 | + | その他 | カメラの IP アドレス | -* Thetaから画像や動画をダウンロードする場合は、plainな接続となりますので、接続先のアドレス(デフォルト192.168.1.1)に応じて各プラットフォーム毎の設定が必要になります。 +- Theta から画像や動画をダウンロードする場合は、plain な接続となりますので、接続先のアドレス(デフォルト 192.168.1.1)に応じて各プラットフォーム毎の設定が必要になります。 - * iOS: デフォルトの場合のInfo.plistの例を示します。なお、Xcodeの`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 + - iOS: デフォルトの場合の Info.plist の例を示します。なお、Xcode の`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 - ``` xml + ```xml @@ -110,7 +115,7 @@ _thetaClientFlutter.initialize('http://:<ポート番号>') ``` - * Android: res/xml/network_security_config.xmlに記述するデフォルトの場合の例を示します。 + - Android: res/xml/network_security_config.xml に記述するデフォルトの場合の例を示します。 ```xml @@ -126,7 +131,7 @@ _thetaClientFlutter.initialize('http://:<ポート番号>') まず`getPhotoCaptureBuilder()`を使って撮影設定を行い、`PhotoCapture`オブジェクトを生成します。 -``` Dart +```Dart _thetaClientFlutter.getPhotoCaptureBuilder() .setIsoAutoHighLimit(IsoAutoHighLimitEnum.iso1000) .setFileFormat(PhotoFileFormatEnum.image_5K) @@ -139,15 +144,15 @@ _thetaClientFlutter.getPhotoCaptureBuilder() }); ``` -上の例ではISO感度の最大値を1000に、ファイルフォーマットをIMAGE_5Kに設定しています。 +上の例では ISO 感度の最大値を 1000 に、ファイルフォーマットを IMAGE_5K に設定しています。 プレビューを表示する方法は[プレビューを表示する](#プレビューを表示する)をご覧ください。 次に`PhotoCapture.takePicture()`を呼んで静止画を撮影します。 -``` Dart - photoCapture.takePicture((fileUrl) { - // fileUrl をGETリクエストを送信してJPEGファイルを受け取る処理 +```Dart + photoCapture.takePicture((fileUrl) { + // send HTTP GET request for fileUrl and receive JPEG file }, (exception) { // catch error while take picture }); @@ -155,175 +160,176 @@ _thetaClientFlutter.getPhotoCaptureBuilder() ### 静止画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(ExposureCompensationEnum value) - - | 値 | 補正値 |備考| - |---|---:|---| - |m2_0|-2.0f|| - |m1_7|-1.7f|| - |m1_3|-1.0f|| - |m0_7|-0.7f|| - |m0_3|-0.3f|| - |zero|0.0f|デフォルト| - |p0_3|0.3f|| - |p0_7|0.7f|| - |p1_3|1.0f|| - |p1_7|1.7f|| - |p2_0|2.0f|| - -* 露出遅延設定 - setExposureDelay(ExposureDelayEnum value) - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - setExposureProgram(ExposureProgramEnum program) - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - setFileFormat(PhotoFileFormatEnum fileFormat) - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |image_2K|jpeg|2048|1024|○|○|×|×|×| - |image_5K|jpeg|5376|2688|○|○|○|×|×| - |image_6_7K|jpeg|6720|3360|×|×|×|○|×| - |rawP_6_7K|raw+|6720|3360|×|×|×|○|×| - |image_5_5K|jpeg|5504|2752|×|×|×|×|○| - |image_11K|jpeg|11008|5504|×|×|×|×|○| - -* 画像処理 - setFilter(FilterEnum filter) - - | 値 | 内容 | 備考 - |---|---|---| - |off|なし|| - |noiseReduction|ノイズ軽減|| - |hdr|HDR|デフォルト| - -* GPSオン/オフ - setGpsTagRecording(GpsTagRecordingEnum value) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |on|GPSあり|デフォルト| - |off|GPSなし|| - -* ISO値 - setIso(IsoEnum iso) - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(IsoAutoHighLimitEnum iso) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - setAperture(ApertureEnum aperture) - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture_2_0|2.0f|| - |aperture_2_1|2.1f|| - |aperture_2_4|2.4f|| - |aperture_3_5|3.5f|| - |aperture_5_6|5.6f|| - -* 色温度設定 - setColorTemperature(int kelvin) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(GpsInfo gpsInfo) - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(WhiteBalanceEnum whiteBalance) - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |sade|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| +- 露出補正 - setExposureCompensation(ExposureCompensationEnum value) + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m2_0 | -2.0f | | + | m1_7 | -1.7f | | + | m1_3 | -1.0f | | + | m0_7 | -0.7f | | + | m0_3 | -0.3f | | + | zero | 0.0f | デフォルト | + | p0_3 | 0.3f | | + | p0_7 | 0.7f | | + | p1_3 | 1.0f | | + | p1_7 | 1.7f | | + | p2_0 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(ExposureDelayEnum value) + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - setExposureProgram(ExposureProgramEnum program) + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(PhotoFileFormatEnum fileFormat) + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | ---------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | image_2K | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | image_5K | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | image_6_7K | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | rawP_6_7K | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | image_5_5K | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | image_11K | jpeg | 11008 | 5504 | × | × | × | × | ○ | + +- 画像処理 - setFilter(FilterEnum filter) + + | 値 | 内容 | 備考 | + | -------------- | ---------- | ---------- | + | off | なし | | + | noiseReduction | ノイズ軽減 | | + | hdr | HDR | デフォルト | + +- GPS オン/オフ - setGpsTagRecording(GpsTagRecordingEnum value) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | on | GPS あり | デフォルト | + | off | GPS なし | | + +- ISO 値 - setIso(IsoEnum iso) + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(IsoAutoHighLimitEnum iso) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - setAperture(ApertureEnum aperture) + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture_2_0 | 2.0f | | + | aperture_2_1 | 2.1f | | + | aperture_2_4 | 2.4f | | + | aperture_3_5 | 3.5f | | + | aperture_5_6 | 5.6f | | + +- 色温度設定 - setColorTemperature(int kelvin) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(GpsInfo gpsInfo) + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(WhiteBalanceEnum whiteBalance) + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | sade | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## 動画を撮影する まず`getVideoCaptureBuilder()`を使って撮影設定を行い、`VideoCapture`オブジェクトを生成します。 -``` Dart +```Dart _thetaClientFlutter.getVideoCaptureBuilder() .setIsoAutoHighLimit(IsoAutoHighLimitEnum.iso800) .setFileFormat(VideoFileFormatEnum.videoHD) @@ -336,219 +342,219 @@ _thetaClientFlutter.getVideoCaptureBuilder() }); ``` -上の例ではISO感度の最大値を800に、ファイルフォーマットを`videoHD`に設定しています。 +上の例では ISO 感度の最大値を 800 に、ファイルフォーマットを`videoHD`に設定しています。 表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください 次に`VideoCapture.startCapture()`を呼んで動画の撮影を開始します。 -``` Dart -VideoCapturing videoCapturing = videoCapture.startCapture() - .then(fileUrl => { - // GETリクエストを送信してMP4ファイルを受け取る処理 - }) - .catch(error => { - // handle error +```Dart + VideoCapturing videoCapturing = videoCapture.startCapture((fileUrl) { + // get MP4 file + }, (exception) { + // handle error of startCapture + }, onStopFailed: (exception) { + // handle error of stopCapture }); ``` -次に`VideoCapture.stopCapture()`を呼び出して動画の撮影を終了します。成功すると上記の通り、撮影したファイルのURLを引数にthenが呼び出されます。 +次に`VideoCapture.stopCapture()`を呼び出して動画の撮影を終了します。成功すると上記の通り、撮影したファイルの URL を引数にコールバック関数が呼び出されます。 -``` Dart +```Dart videoCapturing.stopCapture(); ``` ### 動画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(ExposureCompensationEnum value) - - | 値 | 補正値 |備考| - |---|---:|---| - |m2_0|-2.0f|| - |m1_7|-1.7f|| - |m1_3|-1.0f|| - |m0_7|-0.7f|| - |m0_3|-0.3f|| - |zero|0.0f|デフォルト| - |p0_3|0.3f|| - |p0_7|0.7f|| - |p1_3|1.0f|| - |p1_7|1.7f|| - |p2_0|2.0f|| - -* 露出遅延設定 - setExposureDelay(ExposureDelayEnum value) - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - setExposureProgram(ExposureProgramEnum program) - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - setFileFormat(VideoFileFormatEnum fileFormat) - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |videoHD|mp4|1280|570|||○|○|×|×|×| - |videoFullHD|mp4|1920|1080|||○|○|×|×|×| - |video_2K|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |video_4K|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |video_2K_30F|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_2K_60F|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_4K_30F|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_4K_60F|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_2F|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_5F|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_30F|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_2F|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_5F|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_10F|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 最大録画時間設定 - setMaxRecordableTime(MaxRecordableTimeEnum time) - - | 値 | 内容 | 備考 - |---|---|---| - |time_300|300秒|| - |time_1500|1500秒|| - -* GPSオン/オフ - setGpsTagRecording(GpsTagRecordingEnum value) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |on|GPSあり|デフォルト| - |off|GPSなし|| - -* ISO値 - setIso(IsoEnum iso) - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(IsoAutoHighLimitEnum iso) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - setAperture(ApertureEnum aperture) - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture_2_0|2.0f|| - |aperture_2_1|2.1f|| - |aperture_2_4|2.4f|| - |aperture_3_5|3.5f|| - |aperture_5_6|5.6f|| - -* 色温度設定 - setColorTemperature(int kelvin) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(GpsInfo gpsInfo) - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(WhiteBalanceEnum whiteBalance) - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |SHADE|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| - +- 露出補正 - setExposureCompensation(ExposureCompensationEnum value) + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m2_0 | -2.0f | | + | m1_7 | -1.7f | | + | m1_3 | -1.0f | | + | m0_7 | -0.7f | | + | m0_3 | -0.3f | | + | zero | 0.0f | デフォルト | + | p0_3 | 0.3f | | + | p0_7 | 0.7f | | + | p1_3 | 1.0f | | + | p1_7 | 1.7f | | + | p2_0 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(ExposureDelayEnum value) + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - setExposureProgram(ExposureProgramEnum program) + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(VideoFileFormatEnum fileFormat) + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | -------------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | videoHD | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | videoFullHD | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | video_2K | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video_4K | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video_2K_30F | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_2K_60F | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_4K_30F | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_4K_60F | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_2F | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_5F | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_30F | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_2F | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_5F | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_10F | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 最大録画時間設定 - setMaxRecordableTime(MaxRecordableTimeEnum time) + + | 値 | 内容 | 備考 | + | --------- | ------- | ---- | + | time_300 | 300 秒 | | + | time_1500 | 1500 秒 | | + +- GPS オン/オフ - setGpsTagRecording(GpsTagRecordingEnum value) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | on | GPS あり | デフォルト | + | off | GPS なし | | + +- ISO 値 - setIso(IsoEnum iso) + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(IsoAutoHighLimitEnum iso) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - setAperture(ApertureEnum aperture) + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture_2_0 | 2.0f | | + | aperture_2_1 | 2.1f | | + | aperture_2_4 | 2.4f | | + | aperture_3_5 | 3.5f | | + | aperture_5_6 | 5.6f | | + +- 色温度設定 - setColorTemperature(int kelvin) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(GpsInfo gpsInfo) + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(WhiteBalanceEnum whiteBalance) + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | SHADE | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 -| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | -| ---- | --: | --: | -----------: | ---- | -| THETA X | 1024 | 512 | 30 | | -| THETA Z1 | 1024 | 512 | 30 | | -| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1以降 | -| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1以前 | -| THETA S | 640 | 320 | 10 | | -| THETA SC | 640 | 320 | 10 | | +| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | +| -------- | --------: | --------: | ------------------: | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1 以降 | +| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1 以前 | +| THETA S | 640 | 320 | 10 | | +| THETA SC | 640 | 320 | 10 | | `getLivePreview()`を呼ぶと、プレビューの各フレームの受信が完了する度に、コールバック関数が呼ばれます。コールバック関数の引数には、フレームデータ(JPEG)のメモリイメージが渡されます。 プレビューを終了する場合はコールバック関数の戻り値で、`false`を返してください。 -``` Dart +```Dart bool previewing = false; bool frameHandler(Uint8List frameData) { @@ -589,7 +595,7 @@ videoCapturing.stopCapture(); カメラを設定するには、設定したい内容を`Options`に設定して`setOptions()`を呼び出します。 -``` Dart +```Dart final options = Options(); options.aperture = ApertureEnum.apertureAuto; _thetaClientFlutter.setOptions(options) @@ -603,255 +609,259 @@ _thetaClientFlutter.setOptions(options) `Options`に設定できる項目と内容は以下を参照してください。 -* 日付時刻 - dateTimeZone:String +- 日付時刻 - dateTimeZone:String 形式: YYYY:MM:DD hh:mm:ss+(-)hh:mm hh 0-23、+(-)hh:mm は、タイムゾーン 例: 2014:05:18 01:04:29+08:00 -* 露出補正 - exposureCompensation:ExposureCompensationEnum - - | 値 | 補正値 |備考| - |---|---:|---| - |m2_0|-2.0f|| - |m1_7|-1.7f|| - |m1_3|-1.0f|| - |m0_7|-0.7f|| - |m0_3|-0.3f|| - |zero|0.0f|デフォルト| - |p0_3|0.3f|| - |p0_7|0.7f|| - |p1_3|1.0f|| - |p1_7|1.7f|| - |p2_0|2.0f|| - -* 露出遅延設定 - exposureDelay:ExposureDelayEnum - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - exposureProgram:ExposureProgramEnum - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - fileFormat:FileFormatEnum - - * 静止画 - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |image_2K|jpeg|2048|1024|○|○|×|×|×| - |image_5K|jpeg|5376|2688|○|○|○|×|×| - |image_6_7K|jpeg|6720|3360|×|×|×|○|×| - |rawP_6_7K|raw+|6720|3360|×|×|×|○|×| - |image_5_5K|jpeg|5504|2752|×|×|×|×|○| - |image_11K|jpeg|11008|5504|×|×|×|×|○| - - * 動画 - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |videoHD|mp4|1280|570|||○|○|×|×|×| - |videoFullHD|mp4|1920|1080|||○|○|×|×|×| - |video_2K|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |video_4K|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |video_2K_30F|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_2K_60F|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_4K_30F|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_4K_60F|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_2F|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_5F|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_5_7K_30F|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_2F|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_5F|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video_7K_10F|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 画像処理 - filter:FilterEnum - - | 値 | 内容 | 備考 - |---|---|---| - |off|なし|| - |noiseReduction|ノイズ軽減|| - |hdr|HDR|デフォルト| - -* GPSオン/オフ - isGpsOn:bool - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |true|GPSあり|デフォルト| - |false|GPSなし|| - -* ISO値 - iso:IsoEnum - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - isoAutoHighLimit:IsoAutoHighLimitEnum - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - aperture:ApertureEnum - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture_2_0|2.0f|| - |aperture_2_1|2.1f|| - |aperture_2_4|2.4f|| - |aperture_3_5|3.5f|| - |aperture_5_6|5.6f|| - -* 色温度設定 - colorTemperature:int - * 2500 ~ 10000 - -* GPS 情報 - gpsInfo:GpsInfo - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - whiteBalance:WhiteBalanceEnum - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |sade|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| - -* 最大録画時間設定 - maxRecordableTime:MaxRecordableTimeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |time_300|300秒|| - |time_1500|1500秒|| - -* キャプチャモード - captureMode:CaptureModeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |image|静止画撮影モード|| - |video|動画撮影モード|| - -* 言語設定 - language:LanguageEnum -(THETA S, THETA SCでは指定しても無視される) - - |値|内容|備考| - |--|---|---| - |de|ドイツ語|| - |enGB|英国英語|| - |enUS|米国英語|| - |fr|フランス語|| - |it|イタリア語|| - |ja|日本語|| - |ko|韓国語|| - |zhCN|中国語|| - |zhTW|中国語台湾|| - -* スリープモードに入るまでの時間(分) - sleepDelay:SleepDelayEnum - * SleepDelayEnum - - |値|設定値(秒)|備考| - |--|--:|---| - |disable|65535|自動オフしない| - |sleepDelay_3m|180|| - |sleepDelay_5m|300|| - |sleepDelay_7m|420|| - |sleepDelay_10m|600|| - -* スリープから自動電源オフまでの時間(秒) offDelay:OffDelayEnum - * OffDelayEnum - - |値|設定値(秒)|備考| - |--|--:|---| - |DISABLE|65535|自動オフしない| - |offDelay_5m|300|| - |offDelay_10m|600|| - |offDelay_15m|900|| - |offDelay_30m|1800|| - -* シャッター音 - shutterVolume:int - * 0 - 100 +- 露出補正 - exposureCompensation:ExposureCompensationEnum + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m2_0 | -2.0f | | + | m1_7 | -1.7f | | + | m1_3 | -1.0f | | + | m0_7 | -0.7f | | + | m0_3 | -0.3f | | + | zero | 0.0f | デフォルト | + | p0_3 | 0.3f | | + | p0_7 | 0.7f | | + | p1_3 | 1.0f | | + | p1_7 | 1.7f | | + | p2_0 | 2.0f | | + +- 露出遅延設定 - exposureDelay:ExposureDelayEnum + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - exposureProgram:ExposureProgramEnum + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - fileFormat:FileFormatEnum + + - 静止画 + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | ---------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | image_2K | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | image_5K | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | image_6_7K | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | rawP_6_7K | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | image_5_5K | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | image_11K | jpeg | 11008 | 5504 | × | × | × | × | ○ | + + - 動画 + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | -------------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | videoHD | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | videoFullHD | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | video_2K | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video_4K | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video_2K_30F | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_2K_60F | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_4K_30F | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_4K_60F | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_2F | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_5F | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_5_7K_30F | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_2F | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_5F | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video_7K_10F | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 画像処理 - filter:FilterEnum + + | 値 | 内容 | 備考 | + | -------------- | ---------- | ---------- | + | off | なし | | + | noiseReduction | ノイズ軽減 | | + | hdr | HDR | デフォルト | + +- GPS オン/オフ - isGpsOn:bool + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | ----- | -------- | ---------- | + | true | GPS あり | デフォルト | + | false | GPS なし | | + +- ISO 値 - iso:IsoEnum + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - isoAutoHighLimit:IsoAutoHighLimitEnum + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - aperture:ApertureEnum + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture_2_0 | 2.0f | | + | aperture_2_1 | 2.1f | | + | aperture_2_4 | 2.4f | | + | aperture_3_5 | 3.5f | | + | aperture_5_6 | 5.6f | | + +- 色温度設定 - colorTemperature:int + + - 2500 ~ 10000 + +- GPS 情報 - gpsInfo:GpsInfo + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - whiteBalance:WhiteBalanceEnum + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | sade | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | + +- 最大録画時間設定 - maxRecordableTime:MaxRecordableTimeEnum + + | 値 | 内容 | 備考 | + | --------- | ------- | ---- | + | time_300 | 300 秒 | | + | time_1500 | 1500 秒 | | + +- キャプチャモード - captureMode:CaptureModeEnum + + | 値 | 内容 | 備考 | + | ----- | ---------------- | ---- | + | image | 静止画撮影モード | | + | video | 動画撮影モード | | + +- 言語設定 - language:LanguageEnum + (THETA S, THETA SC では指定しても無視される) + + | 値 | 内容 | 備考 | + | ---- | ---------- | ---- | + | de | ドイツ語 | | + | enGB | 英国英語 | | + | enUS | 米国英語 | | + | fr | フランス語 | | + | it | イタリア語 | | + | ja | 日本語 | | + | ko | 韓国語 | | + | zhCN | 中国語 | | + | zhTW | 中国語台湾 | | + +- スリープモードに入るまでの時間(分) - sleepDelay:SleepDelayEnum + + - SleepDelayEnum + + | 値 | 設定値(秒) | 備考 | + | -------------- | ---------: | -------------- | + | disable | 65535 | 自動オフしない | + | sleepDelay_3m | 180 | | + | sleepDelay_5m | 300 | | + | sleepDelay_7m | 420 | | + | sleepDelay_10m | 600 | | + +- スリープから自動電源オフまでの時間(秒) offDelay:OffDelayEnum + + - OffDelayEnum + + | 値 | 設定値(秒) | 備考 | + | ------------ | ---------: | -------------- | + | DISABLE | 65535 | 自動オフしない | + | offDelay_5m | 300 | | + | offDelay_10m | 600 | | + | offDelay_15m | 900 | | + | offDelay_30m | 1800 | | + +- シャッター音 - shutterVolume:int + - 0 - 100 ## カメラの設定を取得する + カメラの設定を取得するには、取得したいオプションの一覧を指定して`getOptions()`を呼び出します。 -``` Dart +```Dart final optionNames = [ OptionNameEnum.aperture, OptionNameEnum.captureMode, @@ -866,65 +876,66 @@ _thetaClientFlutter.getOptions(optionNames) }); ``` -* OptionNameEnum は以下の表を参照してください - - | 値 | 意味 |型| - |----|-----|--| - |aperture|絞り|ApertureEnum| - |captureMode|キャプチャーモード|CaptureModeEnum| - |ColorTemperature|色温度|number| - |dateTimeZone|日時|String| - |exposureCompensation|露出補正|ExposureCompensationEnum| - |exposureDelay|露出遅延時間|ExposureDelayEnum| - |exposureProgram|露出プログラム|ExposureProgram| - |fileFormat|ファイルフォーマット|FileFormatEnum| - |filter|画像フィルタ|FilterEnum| - |gpsInfo|GPS情報|GpsInfo| - |isGpsOn|GPSフラグ|bool| - |iso|ISO値|IsoEnum| - |isoAutoHighLimit|ISO上限|IsoAutoHighLimitEnum| - |language|言語|LanguageEnum| - |maxRecordableTime|最長録画時間|MaxRecordableTimeEnum| - |offDelay|電源オフ時間|OffDelayEnum| - |sleepDelay|スリープ時間|SleepDelayEnum| - |remainingPictures|残り画像数|int| - |remainingVideoSeconds|残り録画秒数|int| - |remainingSpace|残領域|int| - |totalSpace|合計領域|int| - |shutterVolume|シャッター音量|int| - |whiteBalance|ホワイトバランス|WhiteBalanceEnum| - -## THETA内の静止画・動画を一覧する -THETA内の静止画(JPEGファイル)や動画(MP4ファイル)の一覧は`listFiles(FileTypeEnum fileType, int entryCount, [int startPosition])`を使って取得できます。 -`listFiles()`の戻り値型は`ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` がTHETA内のファイル一覧です。 +- OptionNameEnum は以下の表を参照してください + + | 値 | 意味 | 型 | + | --------------------- | -------------------- | ------------------------ | + | aperture | 絞り | ApertureEnum | + | captureMode | キャプチャーモード | CaptureModeEnum | + | ColorTemperature | 色温度 | number | + | dateTimeZone | 日時 | String | + | exposureCompensation | 露出補正 | ExposureCompensationEnum | + | exposureDelay | 露出遅延時間 | ExposureDelayEnum | + | exposureProgram | 露出プログラム | ExposureProgram | + | fileFormat | ファイルフォーマット | FileFormatEnum | + | filter | 画像フィルタ | FilterEnum | + | gpsInfo | GPS 情報 | GpsInfo | + | isGpsOn | GPS フラグ | bool | + | iso | ISO 値 | IsoEnum | + | isoAutoHighLimit | ISO 上限 | IsoAutoHighLimitEnum | + | language | 言語 | LanguageEnum | + | maxRecordableTime | 最長録画時間 | MaxRecordableTimeEnum | + | offDelay | 電源オフ時間 | OffDelayEnum | + | sleepDelay | スリープ時間 | SleepDelayEnum | + | remainingPictures | 残り画像数 | int | + | remainingVideoSeconds | 残り録画秒数 | int | + | remainingSpace | 残領域 | int | + | totalSpace | 合計領域 | int | + | shutterVolume | シャッター音量 | int | + | whiteBalance | ホワイトバランス | WhiteBalanceEnum | + +## THETA 内の静止画・動画を一覧する + +THETA 内の静止画(JPEG ファイル)や動画(MP4 ファイル)の一覧は`listFiles(FileTypeEnum fileType, int entryCount, [int startPosition])`を使って取得できます。 +`listFiles()`の戻り値型は`ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` が THETA 内のファイル一覧です。 `fileList`は `FileInfo`のリストです。 -* FileTypeEnum +- FileTypeEnum - |値|内容| - |---|---| - |all|全てのファイルを一覧| - |image|静止画(JPEGファイル)を一覧| - |video|動画(MP4ファイル)を一覧| + | 値 | 内容 | + | ----- | ----------------------------- | + | all | 全てのファイルを一覧 | + | image | 静止画(JPEG ファイル)を一覧 | + | video | 動画(MP4 ファイル)を一覧 | -* ThetaFiles +- ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|THETA内のファイル一覧| - |totalEntries|int| THETA内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) + | Property name | Type | Contents | + | ------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | THETA 内のファイル一覧 | + | totalEntries | int | THETA 内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) | -* FileInfo +- FileInfo - |プロパティ名|型|内容| - |---|---|---| - |name|String|ファイル名を表します| - |size|int|ファイルサイズ(バイト数)を表します| - |dateTime|String|撮影日時(YYYY:MM:DD HH:MM:SS)を表します| - |fileUrl|String|ファイルのURLを表します| - |thumbnailUrl|String|サムネールのURLを表します| + | プロパティ名 | 型 | 内容 | + | ------------ | ------ | ----------------------------------------- | + | name | String | ファイル名を表します | + | size | int | ファイルサイズ(バイト数)を表します | + | dateTime | String | 撮影日時(YYYY:MM:DD HH:MM:SS)を表します | + | fileUrl | String | ファイルの URL を表します | + | thumbnailUrl | String | サムネールの URL を表します | -``` Dart +```Dart _thetaClientFlutter.listFiles(FileTypeEnum.image, 1000, 0) .then((files) { // handle file list(files.fileList) @@ -935,14 +946,14 @@ _thetaClientFlutter.listFiles(FileTypeEnum.image, 1000, 0) ``` ## 静止画・動画を取得する -撮影した静止画(JPEGファイル)や動画(MP4ファイル)のURLからデータを取得します。httpパッケージを使用して、以下のような関数を使えばダウンロードできます。 -なお、静止画像の場合は、Image Widgetのnetworkコンストラクタを使用することでダウンロード後表示することができます。 -* httpパッケージ -https://pub.dev/packages/http +撮影した静止画(JPEG ファイル)や動画(MP4 ファイル)の URL からデータを取得します。http パッケージを使用して、以下のような関数を使えばダウンロードできます。 +なお、静止画像の場合は、Image Widget の network コンストラクタを使用することでダウンロード後表示することができます。 +- http パッケージ + https://pub.dev/packages/http -``` Dart +```Dart void downloadFile(String fileUrl, String filePath) async { final url = Uri.parse(fileUrl); final response = await get(url); @@ -953,9 +964,10 @@ void downloadFile(String fileUrl, String filePath) async { ``` ## サムネイルを取得する -THETA内のファイルのサムネイルは、`listFiles`を使って取得した `FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 -``` Dart +THETA 内のファイルのサムネイルは、`listFiles`を使って取得した `FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 + +```Dart final url = Uri.parse(fileInfo.thumbnailUrl); final response = await get(url); final file = File(filePath); @@ -965,13 +977,13 @@ THETA内のファイルのサムネイルは、`listFiles`を使って取得し ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 それぞれのフレーム処理を行うコールバック関数を引数として `getLivePreview()`を呼び出します。 プレビュー中のフレームは、イベントのパラメータとして受け取ることができます。 -``` Dart +```Dart bool previewing = false; bool frameHandler(Uint8List frameData) { @@ -1008,10 +1020,11 @@ THETA内のファイルのサムネイルは、`listFiles`を使って取得し ...略 ``` -## THETAをリセットする -接続しているThetaをリセットするには、`reset()`を呼び出します。 +## THETA をリセットする + +接続している Theta をリセットするには、`reset()`を呼び出します。 -``` Dart +```Dart _thetaClientFlutter.reset() .then((value) { // reset done @@ -1021,21 +1034,22 @@ _thetaClientFlutter.reset() }); ``` -## THETAの情報を取得する -接続しているThetaの情報を取得するには、`getThetaInfo()`を呼び出します。呼び出しに成功すると`ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 +## THETA の情報を取得する + +接続している Theta の情報を取得するには、`getThetaInfo()`を呼び出します。呼び出しに成功すると`ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 -* ThetaInfo +- ThetaInfo - |プロパティ名|型|内容| - |---|---|---| - |firmwareVersion|String|ファームウェアバージョンを表します| - |hasGps|bool|GPS機能を持っているかどうかを表します| - |hasGyro|bool|ジャイロを持っているかどうかを表します| - |model|String|Thetaの型番を表します| - |serialNumber|String|Thetaのシリアル番号を表します| - |uptime|int|Thetaの電源を入れてからの秒数を表します| + | プロパティ名 | 型 | 内容 | + | --------------- | ------ | ---------------------------------------- | + | firmwareVersion | String | ファームウェアバージョンを表します | + | hasGps | bool | GPS 機能を持っているかどうかを表します | + | hasGyro | bool | ジャイロを持っているかどうかを表します | + | model | String | Theta の型番を表します | + | serialNumber | String | Theta のシリアル番号を表します | + | uptime | int | Theta の電源を入れてからの秒数を表します | -``` Dart +```Dart _thetaClientFlutter.getThetaInfo() .then((thetaInfo) { // processing thetaInfo @@ -1045,31 +1059,31 @@ _thetaClientFlutter.getThetaInfo() }); ``` -## THETAの状態を取得する -接続しているThetaの状態を取得するには、`getThetaState()`を呼び出します。呼び出しに成功すると`ThetaState`を取得できます。このデータは以下のような情報を含みます。 +## THETA の状態を取得する -* ThetaState +接続している Theta の状態を取得するには、`getThetaState()`を呼び出します。呼び出しに成功すると`ThetaState`を取得できます。このデータは以下のような情報を含みます。 - |プロパティ名|型|内容| - |---|---|---| - |batteryLevel|double|バッテリーレベルを表します| - |chargingState|ChargingStateEnum|充電状態を表します| - |fingerprint|String|現在の状態ごとに一意に決まる識別子を表します| - |isSdCard|bool|SDカードが存在するかどうかを表します| - |latestFileUrl|String|最後の取得したメディアのURLを表します| - |recordableTime|int|録画可能秒数を表します| - |recordedTime|int|録画済み秒数を表します| +- ThetaState -* ChargingStateEnum + | プロパティ名 | 型 | 内容 | + | -------------- | ----------------- | -------------------------------------------- | + | batteryLevel | double | バッテリーレベルを表します | + | chargingState | ChargingStateEnum | 充電状態を表します | + | fingerprint | String | 現在の状態ごとに一意に決まる識別子を表します | + | isSdCard | bool | SD カードが存在するかどうかを表します | + | latestFileUrl | String | 最後の取得したメディアの URL を表します | + | recordableTime | int | 録画可能秒数を表します | + | recordedTime | int | 録画済み秒数を表します | - |プロパティ名|内容| - |---|---| - |charging|充電中を表します| - |completed|充電完了を表します| - |notCharging|ケーブル未接続を表します| +- ChargingStateEnum + | プロパティ名 | 内容 | + | ------------ | ------------------------ | + | charging | 充電中を表します | + | completed | 充電完了を表します | + | notCharging | ケーブル未接続を表します | -``` Dart +```Dart _thetaClientFlutter.getThetaState() .then((thetaState) { // processing thetaState diff --git a/docs/tutorial-flutter.md b/docs/tutorial-flutter.md index 191437d8d1..1e6bbe6ad7 100644 --- a/docs/tutorial-flutter.md +++ b/docs/tutorial-flutter.md @@ -4,7 +4,7 @@ The supported platform is iOS and Android, and the language is kotlin and swift, respectively, to create a project. -``` Terminal +```Terminal flutter create --platforms=android,ios -i swift -a kotlin your_app_name ``` @@ -20,19 +20,22 @@ Copy to the project that created the Flutter plugin package for THETA Client. Added `theta_client_flutter` copied to `dependencies` of `pubspec.yaml`. -``` pubspec.yaml +```pubspec.yaml dependencies: flutter: sdk: flutter theta_client_flutter: path: ./packages/theta_client_flutter ``` + ### Android setting + Set the minimum SDK version to 26 or higher -``` build.gradle +```build.gradle MinSdkVersion 26 ``` + ### iOS setting Set iOS Deployment Target to 15 or higher @@ -43,7 +46,7 @@ Connect the wireless LAN between THETA and the smartphone that runs on the appli ## Initialize THETA Client -``` Dart +```Dart import 'package:theta_client_flutter/theta_client_flutter.dart'; final _thetaClientFlutterPlugin = ThetaClientFlutter(); @@ -66,19 +69,21 @@ _thetaClientFlutterPlugin.initialize('http://:') // handle error }); ``` -* THETA IP ADDRESS -| Mode |Address | -|-------|---------| -|Direct mode|192.168.1.1| -|Other| Camera IP address| +- THETA IP ADDRESS + +| Mode | Address | +| ----------- | ----------------- | +| Direct mode | 192.168.1.1 | +| Other | Camera IP address | -* When downloading images or videos from THETA, the connection is plain, so the settings for each platform are required depending on the destination address (default 192.168.1.1). +- When downloading images or videos from THETA, the connection is plain, so the settings for each platform are required depending on the destination address (default 192.168.1.1). - * iOS: shows an example of Info.plist by default. -Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. + * iOS: shows an example of Info.plist by default. -``` xml + Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. + +```xml @@ -104,7 +109,7 @@ Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be ``` -* Android: Shows an example of the Res/xml/network_security_config.xml. +- Android: Shows an example of the Res/xml/network_security_config.xml. ```xml @@ -120,7 +125,7 @@ Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be First, shooting settings are performed using `getPhotoCaptureBuilder()` to create `PhotoCapture` objects. -``` Dart +```Dart _thetaClientFlutterPlugin.getPhotoCaptureBuilder() .setIsoAutoHighLimit(IsoAutoHighLimitEnum.iso1000) .setFileFormat(PhotoFileFormatEnum.image_5K) @@ -132,26 +137,26 @@ _thetaClientFlutterPlugin.getPhotoCaptureBuilder() // handle error }); ``` + The above example sets the maximum ISO sensitivity to 1000 and the file format to IMAGE_5K. See [DIsplay a preview](#preview) for instructions on how to view preview. Next, we call `PhotoCapture.takePicture()` to shoot still pictures. -``` Dart - photoCapture.takePicture((fileUrl) { +```Dart + photoCapture.takePicture((fileUrl) { // send HTTP GET request for fileUrl and receive JPEG file }, (exception) { // catch error while take picture }); -});}); ``` ## Shoot a video First, you set up shooting using `getVideoCaptureBuilder()` and create `VideoCapture` objects. -``` Dart +```Dart _thetaClientFlutterPlugin.getVideoCaptureBuilder() .setIsoAutoHighLimit(IsoAutoHighLimitEnum.iso800) .setFileFormat(VideoFileFormatEnum.videoHD) @@ -170,21 +175,20 @@ See [Display a preview](#preview) for instructions on how to view preview. Next, we call `VideoCapture.startCapture()` to start recording videos. - -``` Dart -VideoCapturing videoCapturing = videoCapture.startCapture() - .then(fileUrl => { - // GETリクエストを送信してMP4ファイルを受け取る処理 - }) - .catch(error => { - // handle error +```Dart + VideoCapturing videoCapturing = videoCapture.startCapture((fileUrl) { + // get MP4 file + }, (exception) { + // handle error of startCapture + }, onStopFailed: (exception) { + // handle error of stopCapture }); ``` Next, call `VideoCapture.stopCapture()` to finish recording the video. -If successful, then the URL of the shot file is called then as shown above. +If successful, then the URL of the shot file is called callback function as shown above. -``` Dart +```Dart VideoCapturing.stopCapture(); ``` @@ -195,7 +199,7 @@ The preview is an equirectangular form of motion JPEG. The arguments to the callback function are passed to the memory image of the frame data (JPEG). To exit the preview, return `false` as the return value of the callback function. -``` Dart +```Dart bool previewing = false; bool frameHandler(Uint8List frameData) { @@ -236,8 +240,7 @@ To exit the preview, return `false` as the return value of the callback function To configure the camera, set the desired settings to `Options` and call `setOptions()`. - -``` Dart +```Dart final options = Options(); options.aperture = ApertureEnum.apertureAuto; _thetaClientFlutterPlugin.setOptions(options) @@ -253,7 +256,7 @@ _thetaClientFlutterPlugin.setOptions(options) To get the camera settings, specify the list of options you want to obtain and call `getOptions()`. -``` Dart +```Dart final optionNames = [ OptionNameEnum.aperture, OptionNameEnum.captureMode, @@ -266,7 +269,7 @@ _thetaClientFlutterPlugin.getOptions(optionNames) .onError((error, stackTrace) { // handle error }); - ``` +``` ## List still images and videos in THETA @@ -274,32 +277,32 @@ The list of still pictures (JPEG file) and videos (MP4 file) in THETA can be obt The return type of `listFiles()` is `ThetaFiles`, and property `fileList` of `ThetaFiles` is the list of files in THETA. `fileList` is a list of `FileInfo`. -* FileTypeEnum +- FileTypeEnum - |Value|Content| - |---|---| - |IMAGE|List of still images (JPEG files)| - |VIDEO|List of videos (MP4 files)| - |ALL|List all files| + | Value | Content | + | ----- | --------------------------------- | + | IMAGE | List of still images (JPEG files) | + | VIDEO | List of videos (MP4 files) | + | ALL | List all files | -* ThetaFiles +- ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|The list of files in THETA| - |totalEntries|int| Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) + | Property name | Type | Contents | + | ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | The list of files in THETA | + | totalEntries | int | Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) | -* FileInfo +- FileInfo - |Property name|Type|Contents| - |---|---|---| - |name|String|Represents the file name| - |size|int|Indicates the file size (in bytes)| - |dateTime|String|Shooting date and time (YYYY:MM:DD HH:MM:SS)| - |fileUrl|String|Represents the URL of the file| - |thumbnailUrl|String|Represents a thumbnail URL| + | Property name | Type | Contents | + | ------------- | ------ | -------------------------------------------- | + | name | String | Represents the file name | + | size | int | Indicates the file size (in bytes) | + | dateTime | String | Shooting date and time (YYYY:MM:DD HH:MM:SS) | + | fileUrl | String | Represents the URL of the file | + | thumbnailUrl | String | Represents a thumbnail URL | -``` Dart +```Dart _thetaClientFlutterPlugin.listFiles(FileTypeEnum.image, 1000, 0) .then((files) { // handle file list(files.fileList) @@ -315,9 +318,9 @@ You can retrieve data from the URL of still pictures (JPEG file) or videos (MP4 You can download using the http package by using the following functions: For still images, the image can be downloaded and displayed using the Image Widget network constructor. -* Http package: https://pub.dev/packages/http +- Http package: https://pub.dev/packages/http -``` Dart +```Dart void downloadFile(String fileUrl, String filePath) async { final url = Uri.parse(fileUrl); final response = await get(url); @@ -331,7 +334,7 @@ void downloadFile(String fileUrl, String filePath) async { Thumbnails of the files in THETA can be downloaded using the `FileInfo.thumbnailUrl` acquired by `listFiles()` as follows: -``` Dart +```Dart final url = Uri.parse(fileInfo.thumbnailUrl); final response = await get(url); final file = File(filePath); @@ -347,7 +350,7 @@ As an argument to a callback function that performs each frame processing Call `getLivePreview()`. A previewing frame can be received as an event parameter. -``` Dart +```Dart bool previewing = false; bool frameHandler(Uint8List frameData) { @@ -389,7 +392,7 @@ A previewing frame can be received as an event parameter. To obtain the information about the connected THETA, call `getThetaInfo()`. If the call is successful, you can get `ThetaInfo`. -``` Dart +```Dart _thetaClientFlutterPlugin.getThetaInfo() .then((thetaInfo) { // processing thetaInfo @@ -404,7 +407,7 @@ _thetaClientFlutterPlugin.getThetaInfo() To get the state of the connected THETA, call `getThetaState()`. Successful calling allows you to acquire the `ThetaState`. -``` Dart +```Dart _thetaClientFlutterPlugin.getThetaState() .then((thetaState) { // processing thetaState @@ -418,7 +421,7 @@ _thetaClientFlutterPlugin.getThetaState() Call `reset()` to reset the connected THETA. -``` Dart +```Dart _thetaClientFlutterPlugin.reset() .then((value) { // reset done @@ -426,5 +429,4 @@ _thetaClientFlutterPlugin.reset() .onError((error, stackTrace) { // handle error }); - ``` - +``` diff --git a/docs/tutorial-ios.ja.md b/docs/tutorial-ios.ja.md index 923324d119..15a269f21c 100644 --- a/docs/tutorial-ios.ja.md +++ b/docs/tutorial-ios.ja.md @@ -1,20 +1,20 @@ -# RICOH360 THETA SDKチュートリアル +# RICOH360 THETA SDK チュートリアル ## 使用可能な機種 -* RICOH THETA X -* RICOH THETA Z1 -* RICOH THETA V -* RICOH THETA S (ファームウェアv1.62以降のみ) -* RICOH THETA SC +- RICOH THETA X +- RICOH THETA Z1 +- RICOH THETA V +- RICOH THETA S (ファームウェア v1.62 以降のみ) +- RICOH THETA SC ## 事前準備 -本SDKを使用したアプリケーションが動作するスマートフォンとTHETAを無線LAN接続しておきます。 +本 SDK を使用したアプリケーションが動作するスマートフォンと THETA を無線 LAN 接続しておきます。 -## SDKのインスタンス作成 +## SDK のインスタンス作成 -``` swift +```swift import THETAClient // THETAがIPアドレスを指定して作成する @@ -29,16 +29,17 @@ ThetaRepository.Companion.shared.doNewInstance( } } ``` -* THETA IP ADDRESS - | モード | アドレス | - |-------|---------| - |ダイレクトモード| 192.168.1.1 | - |その他| 割り当てたIPアドレス| +- THETA IP ADDRESS + + | モード | アドレス | + | ---------------- | ---------------------- | + | ダイレクトモード | 192.168.1.1 | + | その他 | 割り当てた IP アドレス | -* Thetaから画像や動画をURLSessionを使ってダウンロードする場合は、plainな接続となりますので、接続先のアドレス(デフォルト192.168.1.1)に応じてInfo.plistへの接続許可設定が必要になります。デフォルトの場合のInfo.plistの例を示します。なお、Xcodeの`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 +- Theta から画像や動画を URLSession を使ってダウンロードする場合は、plain な接続となりますので、接続先のアドレス(デフォルト 192.168.1.1)に応じて Info.plist への接続許可設定が必要になります。デフォルトの場合の Info.plist の例を示します。なお、Xcode の`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 -``` xml +```xml @@ -68,7 +69,7 @@ ThetaRepository.Companion.shared.doNewInstance( まず`ThetaRepository.getPhotoCaptureBuilder()`を使って撮影設定を行い、`PhotoCapture`オブジェクトを生成します。 -``` swift +```swift Task { do { let photoCapture: PhotoCapture = try await withCheckedThrowingContinuation {continuation in @@ -90,13 +91,13 @@ Task { } ``` -上の例ではISO感度の最大値を1000に、ファイルフォーマットをimage5kに設定しています。 +上の例では ISO 感度の最大値を 1000 に、ファイルフォーマットを image5k に設定しています。 プレビューを表示する方法は[プレビューを表示する](#プレビューを表示する)をご覧ください。 次に`PhotoCapture.takePicture(callback:)`を呼んで静止画を撮影します。以下のように`PhotoCaptureTakePictureCallback`を実装したコールバック用クラスを作成して呼び出します。 -``` swift +```swift do { class Callback: PhotoCaptureTakePictureCallback { let callback: (_ fileUrl: String?, _ error: Error?) -> Void @@ -122,7 +123,7 @@ do { } ) } - // fileUrl をGETリクエストを送信してJPEGファイルを受け取る処理 + // send GET request for fileUrl and receive a JPEG file } catch { // catch error while take picture } @@ -130,176 +131,177 @@ do { ### 静止画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum) - - | 値 | 補正値 |備考| - |---|---:|---| - |m20|-2.0f|| - |m17|-1.7f|| - |m13|-1.0f|| - |m07|-0.7f|| - |m03|-0.3f|| - |zero|0.0f|デフォルト| - |p03|0.3f|| - |p07|0.7f|| - |p13|1.0f|| - |p17|1.7f|| - |p20|2.0f|| - -* 露出遅延設定 - setExposureDelay(delay: ThetaRepository.ExposureDelayEnum) - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - setExposureProgram(program:ThetaRepository.ExposureProgramEnum) - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - setFileFormat(fileFormat:ThetaRepository.PhotoFileFormatEnum) - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |image2k|jpeg|2048|1024|○|○|×|×|×| - |image5k|jpeg|5376|2688|○|○|○|×|×| - |image67k|jpeg|6720|3360|×|×|×|○|×| - |rawP67k|raw+|6720|3360|×|×|×|○|×| - |image55k|jpeg|5504|2752|×|×|×|×|○| - |image11k|jpeg|11008|5504|×|×|×|×|○| - -* 画像処理 - setFilter(filter:ThetaRepository.Filter) - - | 値 | 内容 | 備考 - |---|---|---| - |off|なし|| - |noiseReduction|ノイズ軽減|| - |hdr|HDR|デフォルト| - -* GPSオン/オフ - setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |on|GPSあり|デフォルト| - |off|GPSなし|| - -* ISO値 - setIso(iso:ThetaRepository.IsoEnum) - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - setAperture(aperture:ThetaRepository.ApertureEnum) - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture20|2.0f|| - |aperture21|2.1f|| - |aperture24|2.4f|| - |aperture35|3.5f|| - |aperture56|5.6f|| - -* 色温度設定 - setColorTemperature(kelvin:Int) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(gpsInfo:ThetaRepository.GpsInfo) - GpsInfoは以下の内容で作成する。 +- 露出補正 - setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum) + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m20 | -2.0f | | + | m17 | -1.7f | | + | m13 | -1.0f | | + | m07 | -0.7f | | + | m03 | -0.3f | | + | zero | 0.0f | デフォルト | + | p03 | 0.3f | | + | p07 | 0.7f | | + | p13 | 1.0f | | + | p17 | 1.7f | | + | p20 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(delay: ThetaRepository.ExposureDelayEnum) + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - setExposureProgram(program:ThetaRepository.ExposureProgramEnum) + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(fileFormat:ThetaRepository.PhotoFileFormatEnum) + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | -------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | image2k | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | image5k | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | image67k | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | rawP67k | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | image55k | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | image11k | jpeg | 11008 | 5504 | × | × | × | × | ○ | + +- 画像処理 - setFilter(filter:ThetaRepository.Filter) + + | 値 | 内容 | 備考 | + | -------------- | ---------- | ---------- | + | off | なし | | + | noiseReduction | ノイズ軽減 | | + | hdr | HDR | デフォルト | + +- GPS オン/オフ - setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | on | GPS あり | デフォルト | + | off | GPS なし | | + +- ISO 値 - setIso(iso:ThetaRepository.IsoEnum) + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - setAperture(aperture:ThetaRepository.ApertureEnum) + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture20 | 2.0f | | + | aperture21 | 2.1f | | + | aperture24 | 2.4f | | + | aperture35 | 3.5f | | + | aperture56 | 5.6f | | + +- 色温度設定 - setColorTemperature(kelvin:Int) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(gpsInfo:ThetaRepository.GpsInfo) + GpsInfo は以下の内容で作成する。 ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:) - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum) - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |shade|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum) + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | shade | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## 動画を撮影する まず`ThetaRepository.getVideoCaptureBuilder()`を使って撮影設定を行い、`VideoCapture`オブジェクトを生成します。 -``` swift +```swift Task { do { let videoCapture: VideoCapture = try await withCheckedThrowingContinuation {continuation in @@ -321,29 +323,32 @@ Task { } ``` -上の例ではISO感度の最大値を800に、ファイルフォーマットを`videoHd`に設定しています。 +上の例では ISO 感度の最大値を 800 に、ファイルフォーマットを`videoHd`に設定しています。 -Theta SとTheta SC以外ではプレビューを表示できます。表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください +Theta S と Theta SC 以外ではプレビューを表示できます。表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください 次に`VideoCapture.startCapture(callback:)`を呼んで動画の撮影を開始します。以下のように`VideoCaptureStartCaptureCallback`を実装したコールバック用クラスを作成して呼び出します。 -``` swift +```swift class Callback: VideoCaptureStartCaptureCallback { let callback: (_ fileUrl: String?, _ error: Error?) -> Void init(_ callback: @escaping (_ fileUrl: String?, _ error: Error?) -> Void) { self.callback = callback } - func onSuccess(fileUrl: String) { + func onCaptureCompleted(fileUrl: String?) { callback(fileUrl, nil) } - func onError(exception: ThetaRepository.ThetaRepositoryException) { + func onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { callback(nil, exception as? Error) } + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + // handle error of stopCapture + } } let videoCapturing = videoCapture.startCapture( callback: Callback {fileUrl, error in if let videoUrl = fileUrl { - // GETリクエストを送信してMP4ファイルを受け取る処理 + // send GET request and receive MP4 file } if let thetaError = error { // handle error @@ -352,204 +357,206 @@ let videoCapturing = videoCapture.startCapture( ) ``` -次に`VideoCapturing.stopCapture()`を呼んで動画の撮影を終了します。撮影終了後、MP4ファイルの生成が完了すると、`startCapture(callback:)`に渡したコールバック関数が呼ばれます。 +次に`VideoCapturing.stopCapture()`を呼んで動画の撮影を終了します。撮影終了後、MP4 ファイルの生成が完了すると、`startCapture(callback:)`に渡したコールバック関数が呼ばれます。 -``` swift +```swift videoCapturing.stopCapture() ``` ### 動画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum) - - | 値 | 補正値 |備考| - |---|---:|---| - |m20|-2.0f|| - |m17|-1.7f|| - |m13|-1.0f|| - |m07|-0.7f|| - |m03|-0.3f|| - |zero|0.0f|デフォルト| - |p03|0.3f|| - |p07|0.7f|| - |p13|1.0f|| - |p17|1.7f|| - |p20|2.0f|| - -* 露出遅延設定 - setExposureDelay(delay: ThetaRepository.ExposureDelayEnum) - startCaptureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - setExposureProgram(program:ThetaRepository.ExposureProgramEnum) - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - setFileFormat(fileFormat:ThetaRepository.VideoFileFormatEnum) - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |videoHd|mp4|1280|570|||○|○|×|×|×| - |videoFullHd|mp4|1920|1080|||○|○|×|×|×| - |video2k|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |video4k|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |video2k30f|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video2k60f|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video4k30f|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video4k60f|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k2f|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k5f|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k30f|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k2f|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k5f|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k10f|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 最大録画時間設定 - setMaxRecordableTime(time:ThetaRepository.MaxRecordableTimeEnum) - - | 値 | 内容 | 備考 - |---|---|---| - |recordableTime300|300秒|| - |recordableTime1500|1500秒|| - -* GPSオン/オフ - setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |on|GPSあり|デフォルト| - |off|GPSなし|| - -* ISO値 - setIso(iso:ThetaRepository.IsoEnum) - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - setAperture(aperture:ThetaRepository.ApertureEnum) - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture20|2.0f|| - |aperture21|2.1f|| - |aperture24|2.4f|| - |aperture35|3.5f|| - |aperture56|5.6f|| - -* 色温度設定 - setColorTemperature(kelvin:Int) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(gpsInfo:ThetaRepository.GpsInfo) - GpsInfoは以下の内容で作成する。 +- 露出補正 - setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum) + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m20 | -2.0f | | + | m17 | -1.7f | | + | m13 | -1.0f | | + | m07 | -0.7f | | + | m03 | -0.3f | | + | zero | 0.0f | デフォルト | + | p03 | 0.3f | | + | p07 | 0.7f | | + | p13 | 1.0f | | + | p17 | 1.7f | | + | p20 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(delay: ThetaRepository.ExposureDelayEnum) + startCapture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - setExposureProgram(program:ThetaRepository.ExposureProgramEnum) + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(fileFormat:ThetaRepository.VideoFileFormatEnum) + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | ----------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | videoHd | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | videoFullHd | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | video2k | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video4k | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video2k30f | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video2k60f | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video4k30f | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video4k60f | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k2f | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k5f | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k30f | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k2f | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k5f | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k10f | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 最大録画時間設定 - setMaxRecordableTime(time:ThetaRepository.MaxRecordableTimeEnum) + + | 値 | 内容 | 備考 | + | ------------------ | ------- | ---- | + | recordableTime300 | 300 秒 | | + | recordableTime1500 | 1500 秒 | | + +- GPS オン/オフ - setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | on | GPS あり | デフォルト | + | off | GPS なし | | + +- ISO 値 - setIso(iso:ThetaRepository.IsoEnum) + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - setAperture(aperture:ThetaRepository.ApertureEnum) + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture20 | 2.0f | | + | aperture21 | 2.1f | | + | aperture24 | 2.4f | | + | aperture35 | 3.5f | | + | aperture56 | 5.6f | | + +- 色温度設定 - setColorTemperature(kelvin:Int) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(gpsInfo:ThetaRepository.GpsInfo) + GpsInfo は以下の内容で作成する。 ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:) - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum) - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |shade|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum) + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | shade | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 -| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | -| ---- | --: | --: | -----------: | ---- | -| THETA X | 1024 | 512 | 30 | | -| THETA Z1 | 1024 | 512 | 30 | | -| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1以降 | -| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1以前 | -| THETA S | 640 | 320 | 10 | | -| THETA SC | 640 | 320 | 10 | | +| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | +| -------- | --------: | --------: | ------------------: | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1 以降 | +| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1 以前 | +| THETA S | 640 | 320 | 10 | | +| THETA SC | 640 | 320 | 10 | | -`getLivePreview(frameHandler:completionHandler:)`を呼ぶと、プレビューの各フレームの受信が完了する度に、コールバック関数が呼ばれます。コールバック関数の引数には[`Ktor_ByteReadPacket`](https://api.ktor.io/ktor-io/io.ktor.utils.io.core/-byte-read-packet/index.html)オブジェクトと処理結果を返すコールバック関数が渡されます。`Ktor_ioByteReadPacket`オブジェクトからフレームデータ(JPEG画像)を抽出するには、`PlatformKt.frameFrom(byteReadPacket:)`を利用します。この関数の戻り値は`Data`オブジェクト(jpegデータ)で`UIImage(data:)`などを使ってデコードして表示することができます。 -プレビューを継続する場合はframeHandlerコールバック関数内で、処理結果に`true`を設定し、プレビューを終了する場合は、`false`を設定します。 -なお、CPUの利用比率が高く長時間プレビュー表示を続けますとシステムが負荷を検出しアプリケーションを終了させてしまう場合があります。そのような場合は、フレームレートを調整(概ね10fps程度)してCPUに負荷を掛けないように`Data`を抽出する前にフレームを捨てるようにします。 +`getLivePreview(frameHandler:completionHandler:)`を呼ぶと、プレビューの各フレームの受信が完了する度に、コールバック関数が呼ばれます。コールバック関数の引数には[`Ktor_ByteReadPacket`](https://api.ktor.io/ktor-io/io.ktor.utils.io.core/-byte-read-packet/index.html)オブジェクトと処理結果を返すコールバック関数が渡されます。`Ktor_ioByteReadPacket`オブジェクトからフレームデータ(JPEG 画像)を抽出するには、`PlatformKt.frameFrom(byteReadPacket:)`を利用します。この関数の戻り値は`Data`オブジェクト(jpeg データ)で`UIImage(data:)`などを使ってデコードして表示することができます。 +プレビューを継続する場合は frameHandler コールバック関数内で、処理結果に`true`を設定し、プレビューを終了する場合は、`false`を設定します。 +なお、CPU の利用比率が高く長時間プレビュー表示を続けますとシステムが負荷を検出しアプリケーションを終了させてしまう場合があります。そのような場合は、フレームレートを調整(概ね 10fps 程度)して CPU に負荷を掛けないように`Data`を抽出する前にフレームを捨てるようにします。 フレームを受け取る例は以下の通りです。 -``` swift + +```swift class FrameHandler: KotlinSuspendFunction1 { static let FrameInterval = CFTimeInterval(1.0/10.0) var last: CFTimeInterval = 0 @@ -590,6 +597,7 @@ theta.getLivePreview( } ) ``` + ## カメラを設定する カメラを設定するには、設定したい内容を`ThetaRepository.Options`に設定して`ThetaRepository.SetOptions(options:)`を呼び出します。 @@ -615,256 +623,259 @@ Task { `Options`に設定できる項目と内容は以下を参照してください。 -* 日付時刻 - dateTimeZone:String +- 日付時刻 - dateTimeZone:String 形式: YYYY:MM:DD hh:mm:ss+(-)hh:mm hh 0-23、+(-)hh:mm は、タイムゾーン 例: 2014:05:18 01:04:29+08:00 -* 露出補正 - exposureCompensation:ThetaRepository.ExposureCompensationEnum - - | 値 | 補正値 |備考| - |---|---:|---| - |m20|-2.0f|| - |m17|-1.7f|| - |m13|-1.0f|| - |m07|-0.7f|| - |m03|-0.3f|| - |zero|0.0f|デフォルト| - |p03|0.3f|| - |p07|0.7f|| - |p13|1.0f|| - |p17|1.7f|| - |p20|2.0f|| - -* 露出遅延設定 - exposureDelay: ThetaRepository.ExposureDelayEnum - takePicture/startCaptureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |delayOff|0|デフォルト| - |delay1|1|| - |delay2|2|| - |delay3|3|| - |delay4|4|| - |delay5|5|| - |delay6|6|| - |delay7|7|| - |delay8|8|| - |delay9|9|| - |delay10|10|| - -* 露出プログラム - exposureProgram:ThetaRepository.ExposureProgramEnum - - | 値 | 内容 |備考| - |---|---|---| - |manual|手動|| - |normalProgram|通常のプログラム|| - |aperturePriority|絞り優先|| - |shutterPriority|シャッター優先|| - |isoPriority|ISO優先|| - -* ファイルフォーマット - fileFormat:ThetaRepository.FileFormatEnum - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |image2k|jpeg|2048|1024|○|○|×|×|×| - |image5k|jpeg|5376|2688|○|○|○|×|×| - |image67k|jpeg|6720|3360|×|×|×|○|×| - |rawP67k|raw+|6720|3360|×|×|×|○|×| - |image55k|jpeg|5504|2752|×|×|×|×|○| - |image11k|jpeg|11008|5504|×|×|×|×|○| - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |videoHd|mp4|1280|570|||○|○|×|×|×| - |videoFullHd|mp4|1920|1080|||○|○|×|×|×| - |video2k|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |video4k|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |video2k30f|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video2k60f|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video4k30f|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video4k60f|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k2f|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k5f|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video57k30f|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k2f|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k5f|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |video7k10f|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 画像処理 - filter:ThetaRepository.Filter - - | 値 | 内容 | 備考 - |---|---|---| - |off|なし|| - |noiseReduction|ノイズ軽減|| - |hdr|HDR|デフォルト| - -* GPSオン/オフ - isGpsOn:Bool - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |true|GPSあり|デフォルト| - |false|GPSなし|| - -* ISO値 - iso:ThetaRepository.IsoEnum - - | 値 | ISO値 |備考| - |---|---:|---| - |isoAuto|0|| - |iso50|50|| - |iso64|64|| - |iso80|80|| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* ISO上限 - isoAutoHighLimit:ThetaRepository.IsoAutoHighLimitEnum - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |iso100|100|| - |iso125|125|| - |iso160|160|| - |iso200|200|| - |iso250|250|| - |iso320|320|| - |iso400|400|| - |iso500|150|| - |iso640|640|| - |iso800|800|| - |iso1000|1000|| - |iso1250|1250|| - |iso1600|1600|| - |iso2000|2000|| - |iso2500|2500|| - |iso3200|3200|| - |iso4000|4000|| - |iso5000|5000|| - |iso6400|6400|| - -* 絞り設定 - aperture:ThetaRepository.ApertureEnum - - | 値 | 設定値 |備考| - |---|---:|---| - |apertureAuto|自動|| - |aperture20|2.0f|| - |aperture21|2.1f|| - |aperture24|2.4f|| - |aperture35|3.5f|| - |aperture56|5.6f|| - -* 色温度設定 - colorTemperature:Int - * 2500 ~ 10000 - -* GPS 情報 - gpsInfo:ThetaRepository.GpsInfo - GpsInfoは以下の内容で作成する。 +- 露出補正 - exposureCompensation:ThetaRepository.ExposureCompensationEnum + + | 値 | 補正値 | 備考 | + | ---- | -----: | ---------- | + | m20 | -2.0f | | + | m17 | -1.7f | | + | m13 | -1.0f | | + | m07 | -0.7f | | + | m03 | -0.3f | | + | zero | 0.0f | デフォルト | + | p03 | 0.3f | | + | p07 | 0.7f | | + | p13 | 1.0f | | + | p17 | 1.7f | | + | p20 | 2.0f | | + +- 露出遅延設定 - exposureDelay: ThetaRepository.ExposureDelayEnum + takePicture/startCapture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | -------- | -----------: | ---------- | + | delayOff | 0 | デフォルト | + | delay1 | 1 | | + | delay2 | 2 | | + | delay3 | 3 | | + | delay4 | 4 | | + | delay5 | 5 | | + | delay6 | 6 | | + | delay7 | 7 | | + | delay8 | 8 | | + | delay9 | 9 | | + | delay10 | 10 | | + +- 露出プログラム - exposureProgram:ThetaRepository.ExposureProgramEnum + + | 値 | 内容 | 備考 | + | ---------------- | ---------------- | ---- | + | manual | 手動 | | + | normalProgram | 通常のプログラム | | + | aperturePriority | 絞り優先 | | + | shutterPriority | シャッター優先 | | + | isoPriority | ISO 優先 | | + +- ファイルフォーマット - fileFormat:ThetaRepository.FileFormatEnum + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | -------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | image2k | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | image5k | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | image67k | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | rawP67k | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | image55k | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | image11k | jpeg | 11008 | 5504 | × | × | × | × | ○ | + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | ----------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | videoHd | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | videoFullHd | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | video2k | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video4k | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | video2k30f | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video2k60f | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video4k30f | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video4k60f | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k2f | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k5f | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video57k30f | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k2f | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k5f | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | video7k10f | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 画像処理 - filter:ThetaRepository.Filter + + | 値 | 内容 | 備考 | + | -------------- | ---------- | ---------- | + | off | なし | | + | noiseReduction | ノイズ軽減 | | + | hdr | HDR | デフォルト | + +- GPS オン/オフ - isGpsOn:Bool + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | ----- | -------- | ---------- | + | true | GPS あり | デフォルト | + | false | GPS なし | | + +- ISO 値 - iso:ThetaRepository.IsoEnum + + | 値 | ISO 値 | 備考 | + | ------- | -----: | ---- | + | isoAuto | 0 | | + | iso50 | 50 | | + | iso64 | 64 | | + | iso80 | 80 | | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- ISO 上限 - isoAutoHighLimit:ThetaRepository.IsoAutoHighLimitEnum + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | ------- | ---------: | ---- | + | iso100 | 100 | | + | iso125 | 125 | | + | iso160 | 160 | | + | iso200 | 200 | | + | iso250 | 250 | | + | iso320 | 320 | | + | iso400 | 400 | | + | iso500 | 150 | | + | iso640 | 640 | | + | iso800 | 800 | | + | iso1000 | 1000 | | + | iso1250 | 1250 | | + | iso1600 | 1600 | | + | iso2000 | 2000 | | + | iso2500 | 2500 | | + | iso3200 | 3200 | | + | iso4000 | 4000 | | + | iso5000 | 5000 | | + | iso6400 | 6400 | | + +- 絞り設定 - aperture:ThetaRepository.ApertureEnum + + | 値 | 設定値 | 備考 | + | ------------ | -----: | ---- | + | apertureAuto | 自動 | | + | aperture20 | 2.0f | | + | aperture21 | 2.1f | | + | aperture24 | 2.4f | | + | aperture35 | 3.5f | | + | aperture56 | 5.6f | | + +- 色温度設定 - colorTemperature:Int + + - 2500 ~ 10000 + +- GPS 情報 - gpsInfo:ThetaRepository.GpsInfo + GpsInfo は以下の内容で作成する。 ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:) - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - whiteBalance:ThetaRepository.WhiteBalanceEnum - - | 値 | 設定値 |備考| - |---|---|---| - |auto|自動|| - |daylight|Outdoor|約5,200,000| - |shade|Shade|約7,000,000| - |cloudyDaylight|Cloudy|約6,000,000| - |incandescent|Incandescent light 1|約3,200,000| - |warmWhiteFluorescent|Incandescent light 2|| - |daylightFluorescent|Fluorescent light 1(daylight)|| - |daywhiteFluorescent|Fluorescent light 2(natural white)|| - |fluorescent|Fluorescent light 3 (white)|約4,000,000| - |bulbFluorescent|Fluorescent light 4 (light bulb color)|| - |colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| - -* 最大録画時間設定 - maxRecordableTime:ThetaRepository.MaxRecordableTimeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |recordableTime300|300秒|| - |recordableTime1500|1500秒|| - -* キャプチャモード - captureMode:ThetaRepository.CaptureModeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |image|静止画撮影モード|| - |video|動画撮影モード|| - -* 言語設定 - language:ThetaRepository.LanguageEnum -(THETA S, THETA SCでは指定しても無視される) - - |値|内容|備考| - |--|---|---| - |enUs|米国英語|| - |enGb|英国英語|| - |ja|日本語|| - |fr|フランス語|| - |de|ドイツ語|| - |zhTw|中国語台湾|| - |zhCn|中国語|| - |it|イタリア語|| - |ko|韓国語|| - -* スリープモードに入るまでの時間(分) - sleepDelay:ThetaRepository.SleepDelayEnum/SleepDelaySec - * ThetaRepository.SleepDelayEnum - - |値|設定値(分)|備考| - |--|--:|---| - |disable|0|自動オフしない| - |sleepDelay3m|3|| - |sleepDelay5m|5|| - |sleepDelay7m|7|| - |sleepDelay10m|10|| - - * ThetaRepository.SleepDelaySec(sec:Int) - * 0〜1800 (秒) - -* スリープから自動電源オフまでの時間(分) offDelay:ThetaRepository.OffDelayEnum/OffDelaySec - - * ThetaRepository.OffDelayEnum - - |値|設定値(分)|備考| - |--|--:|---| - |disable|0|自動オフしない| - |offDelay5m|5|| - |offDelay10m|10|| - |offDelay15m|15|| - |offDelay30m|30|| - - * ThetaRepository.OffDelaySec(sec:Int) - * 0〜1800 (秒) デフォルトは3分(180秒) - -* シャッター音 - shutterVolume:Int - * 0 - 100 + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - whiteBalance:ThetaRepository.WhiteBalanceEnum + + | 値 | 設定値 | 備考 | + | -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | auto | 自動 | | + | daylight | Outdoor | 約 5,200,000 | + | shade | Shade | 約 7,000,000 | + | cloudyDaylight | Cloudy | 約 6,000,000 | + | incandescent | Incandescent light 1 | 約 3,200,000 | + | warmWhiteFluorescent | Incandescent light 2 | | + | daylightFluorescent | Fluorescent light 1(daylight) | | + | daywhiteFluorescent | Fluorescent light 2(natural white) | | + | fluorescent | Fluorescent light 3 (white) | 約 4,000,000 | + | bulbFluorescent | Fluorescent light 4 (light bulb color) | | + | colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | + +- 最大録画時間設定 - maxRecordableTime:ThetaRepository.MaxRecordableTimeEnum + + | 値 | 内容 | 備考 | + | ------------------ | ------- | ---- | + | recordableTime300 | 300 秒 | | + | recordableTime1500 | 1500 秒 | | + +- キャプチャモード - captureMode:ThetaRepository.CaptureModeEnum + + | 値 | 内容 | 備考 | + | ----- | ---------------- | ---- | + | image | 静止画撮影モード | | + | video | 動画撮影モード | | + +- 言語設定 - language:ThetaRepository.LanguageEnum + (THETA S, THETA SC では指定しても無視される) + + | 値 | 内容 | 備考 | + | ---- | ---------- | ---- | + | enUs | 米国英語 | | + | enGb | 英国英語 | | + | ja | 日本語 | | + | fr | フランス語 | | + | de | ドイツ語 | | + | zhTw | 中国語台湾 | | + | zhCn | 中国語 | | + | it | イタリア語 | | + | ko | 韓国語 | | + +- スリープモードに入るまでの時間(分) - sleepDelay:ThetaRepository.SleepDelayEnum/SleepDelaySec + + - ThetaRepository.SleepDelayEnum + + | 値 | 設定値(分) | 備考 | + | ------------- | ---------: | -------------- | + | disable | 0 | 自動オフしない | + | sleepDelay3m | 3 | | + | sleepDelay5m | 5 | | + | sleepDelay7m | 7 | | + | sleepDelay10m | 10 | | + + - ThetaRepository.SleepDelaySec(sec:Int) + - 0〜1800 (秒) + +- スリープから自動電源オフまでの時間(分) offDelay:ThetaRepository.OffDelayEnum/OffDelaySec + + - ThetaRepository.OffDelayEnum + + | 値 | 設定値(分) | 備考 | + | ----------- | ---------: | -------------- | + | disable | 0 | 自動オフしない | + | offDelay5m | 5 | | + | offDelay10m | 10 | | + | offDelay15m | 15 | | + | offDelay30m | 30 | | + + - ThetaRepository.OffDelaySec(sec:Int) + - 0〜1800 (秒) デフォルトは 3 分(180 秒) + +- シャッター音 - shutterVolume:Int + - 0 - 100 ## カメラの設定を取得する + カメラの設定を取得するには、取得したいオプションの一覧を指定して`ThetaRepository.getOptions(optionNames:)`を呼び出します。 ```swift @@ -890,66 +901,67 @@ Task { } ``` -* ThetaRepository.OptionNameEnum は以下の表を参照してください - - | 値 | 意味 |型| - |----|-----|--| - |aperture|絞り|ThetaRepository.ApertureEnum| - |capturemode|キャプチャーモード|ThetaRepository.CaptureModeEnum| - |colortemperature|色温度|Int| - |datetimezone|日時|String| - |exposurecompensation|露出補正|ThetaRepository.ExposureCompensationEnum| - |exposuredelay|露出遅延時間|ThetaRepository.ExposureDelayEnum| - |exposureprogram|露出プログラム|ThetaRepository.ExposureProgram| - |fileformat|ファイルフォーマット|ThetaRepository.FileFormatEnum| - |filter|画像フィルタ|ThetaRepository.FilterEnum| - |gpsinfo|GPS情報|ThetaRepository.GpsInfo| - |isgpson|GPSフラグ|Bool| - |iso|ISO値|ThetaRepository.IsoEnum| - |isoautohighlimit|ISO上限|ThetaRepository.IsoAutoHighLimitEnum| - |language|言語|ThetaRepository.LanguageEnum| - |maxrecordabletime|最長録画時間|ThetaRepository.MaxRecordableTimeEnum| - |offdelay|電源オフ時間|ThetaRepository.OffDelayEnum,OffDelaySec| - |sleepdelay|スリープ時間|ThetaRepository.SleepDelayEnum,SleepDelaySec| - |remainingpictures|残り画像数|Int| - |remainingvideoseconds|残り録画秒数|Int| - |remainingspace|残領域|Long| - |totalspace|合計領域|Long| - |shuttervolume|シャッター音量|Int| - |whitebalance|ホワイトバランス|ThetaRepository.WhiteBalanceEnum| - -## THETA内の静止画・動画を一覧する -THETA内の静止画(JPEGファイル)や動画(MP4ファイル)の一覧は`ThetaRepository.listFiles(fileType:startPosition:entryCount:)`を使って取得できます。 +- ThetaRepository.OptionNameEnum は以下の表を参照してください + + | 値 | 意味 | 型 | + | --------------------- | -------------------- | -------------------------------------------- | + | aperture | 絞り | ThetaRepository.ApertureEnum | + | capturemode | キャプチャーモード | ThetaRepository.CaptureModeEnum | + | colortemperature | 色温度 | Int | + | datetimezone | 日時 | String | + | exposurecompensation | 露出補正 | ThetaRepository.ExposureCompensationEnum | + | exposuredelay | 露出遅延時間 | ThetaRepository.ExposureDelayEnum | + | exposureprogram | 露出プログラム | ThetaRepository.ExposureProgram | + | fileformat | ファイルフォーマット | ThetaRepository.FileFormatEnum | + | filter | 画像フィルタ | ThetaRepository.FilterEnum | + | gpsinfo | GPS 情報 | ThetaRepository.GpsInfo | + | isgpson | GPS フラグ | Bool | + | iso | ISO 値 | ThetaRepository.IsoEnum | + | isoautohighlimit | ISO 上限 | ThetaRepository.IsoAutoHighLimitEnum | + | language | 言語 | ThetaRepository.LanguageEnum | + | maxrecordabletime | 最長録画時間 | ThetaRepository.MaxRecordableTimeEnum | + | offdelay | 電源オフ時間 | ThetaRepository.OffDelayEnum,OffDelaySec | + | sleepdelay | スリープ時間 | ThetaRepository.SleepDelayEnum,SleepDelaySec | + | remainingpictures | 残り画像数 | Int | + | remainingvideoseconds | 残り録画秒数 | Int | + | remainingspace | 残領域 | Long | + | totalspace | 合計領域 | Long | + | shuttervolume | シャッター音量 | Int | + | whitebalance | ホワイトバランス | ThetaRepository.WhiteBalanceEnum | + +## THETA 内の静止画・動画を一覧する + +THETA 内の静止画(JPEG ファイル)や動画(MP4 ファイル)の一覧は`ThetaRepository.listFiles(fileType:startPosition:entryCount:)`を使って取得できます。 `fileType`は`ThetaRepository.FileTypeEnum`型で内容は以下の通りです。 -`ThetaRepository.listFiles()`の戻り値型は`ThetaRepository.ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` がTHETA内のファイル一覧です。 +`ThetaRepository.listFiles()`の戻り値型は`ThetaRepository.ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` が THETA 内のファイル一覧です。 `fileList`は `ThetaRepository.FileInfo`のリストです。 -* ThetaRepository.FileTypeEnum +- ThetaRepository.FileTypeEnum - |値|内容| - |---|---| - |image|静止画(JPEGファイル)を一覧| - |video|動画(MP4ファイル)を一覧| - |all|全てのファイルを一覧| + | 値 | 内容 | + | ----- | ----------------------------- | + | image | 静止画(JPEG ファイル)を一覧 | + | video | 動画(MP4 ファイル)を一覧 | + | all | 全てのファイルを一覧 | -* ThetaRepository.ThetaFiles +- ThetaRepository.ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|[ThetaRepository.FileInfo]|THETA内のファイル一覧| - |totalEntries|Int32| THETA内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) + | Property name | Type | Contents | + | ------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | [ThetaRepository.FileInfo] | THETA 内のファイル一覧 | + | totalEntries | Int32 | THETA 内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) | -* ThetaRepository.FileInfo +- ThetaRepository.FileInfo - |プロパティ名|型|内容| - |---|---|---| - |name|String|ファイル名を表します| - |size|Long|ファイルサイズ(バイト数)を表します| - |dateTime|String|撮影日時(YYYY:MM:DD HH:MM:SS)を表します| - |fileUrl|String|ファイルのURLを表します| - |thumbnailUrl|String|サムネールのURLを表します| + | プロパティ名 | 型 | 内容 | + | ------------ | ------ | ----------------------------------------- | + | name | String | ファイル名を表します | + | size | Long | ファイルサイズ(バイト数)を表します | + | dateTime | String | 撮影日時(YYYY:MM:DD HH:MM:SS)を表します | + | fileUrl | String | ファイルの URL を表します | + | thumbnailUrl | String | サムネールの URL を表します | -``` swift +```swift Task { do { let listFiles: [ThetaRepository.FileInfo] = @@ -973,9 +985,10 @@ Task { ``` ## 静止画・動画を取得する -撮影した静止画(JPEGファイル)や動画(MP4ファイル)のURLからURLSessionを使ってデータを取得します。以下のような関数を使えばDataとしてダウンロードできます。 -``` swift +撮影した静止画(JPEG ファイル)や動画(MP4 ファイル)の URL から URLSession を使ってデータを取得します。以下のような関数を使えば Data としてダウンロードできます。 + +```swift func download(url: String) async throws -> Data { return try await withCheckedThrowingContinuation {continuation in URLSession.shared.dataTask(with: URL(string: url)!) {data, _, error in @@ -1000,7 +1013,8 @@ Task { ``` ## サムネイルを取得する -THETA内のファイルのサムネイルは、`ThetaRepository.listFiles`を使って取得した `ThetaRepository.FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 + +THETA 内のファイルのサムネイルは、`ThetaRepository.listFiles`を使って取得した `ThetaRepository.FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 ``` Task { @@ -1015,13 +1029,12 @@ Task { ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 - +プレビューは equirectangular 形式の motion JPEG です。 それぞれのフレーム処理を行うコールバック関数を引数として -`ThetaRepository.getPreview(frameHandler:completionHandler:)`を呼び出します。プレビューの取得を継続する場合はコールバック関数の返り値を`true`、終了する場合は`false`とします。コールバック関数は、前述のFrameHandlerを利用します。 +`ThetaRepository.getPreview(frameHandler:completionHandler:)`を呼び出します。プレビューの取得を継続する場合はコールバック関数の返り値を`true`、終了する場合は`false`とします。コールバック関数は、前述の FrameHandler を利用します。 -``` swift +```swift theta.getPreview( frameHandler: FrameHandler {data in if (isActive) { @@ -1037,10 +1050,11 @@ theta.getPreview( ) ``` -## THETAをリセットする -接続しているThetaをリセットするには、`ThetaRepository.reset(completionHandler:)`を呼び出します。 +## THETA をリセットする -``` swift +接続している Theta をリセットするには、`ThetaRepository.reset(completionHandler:)`を呼び出します。 + +```swift Task { do { let _:Bool = try await withCheckedThrowingContinuation {continuation in @@ -1058,21 +1072,22 @@ Task { } ``` -## THETAの情報を取得する -接続しているThetaの情報を取得するには、`ThetaRepository.getThetaInfo(completionHandler:)`を呼び出します。呼び出しに成功すると`ThetaRepository.ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 +## THETA の情報を取得する + +接続している Theta の情報を取得するには、`ThetaRepository.getThetaInfo(completionHandler:)`を呼び出します。呼び出しに成功すると`ThetaRepository.ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 -* ThetaRepository.ThetaInfo +- ThetaRepository.ThetaInfo - |プロパティ名|型|内容| - |---|---|---| - |firmwareVersion|String|ファームウェアバージョンを表します| - |hasGps|Bool|GPS機能を持っているかどうかを表します| - |hasGyro|Bool|ジャイロを持っているかどうかを表します| - |model|String|Thetaの型番を表します| - |serialNumber|String|Thetaのシリアル番号を表します| - |uptime|Integer|Thetaの電源を入れてからの秒数を表します| + | プロパティ名 | 型 | 内容 | + | --------------- | ------- | ---------------------------------------- | + | firmwareVersion | String | ファームウェアバージョンを表します | + | hasGps | Bool | GPS 機能を持っているかどうかを表します | + | hasGyro | Bool | ジャイロを持っているかどうかを表します | + | model | String | Theta の型番を表します | + | serialNumber | String | Theta のシリアル番号を表します | + | uptime | Integer | Theta の電源を入れてからの秒数を表します | -``` swift +```swift Task { do { let thetaInfo: ThetaRepository.ThetaInfo = try await withCheckedThrowingContinuation {continuation in @@ -1093,31 +1108,31 @@ Task { ``` -## THETAの状態を取得する -接続しているThetaの状態を取得するには、`ThetaRepository.getThetaState(completionHandler:)`を呼び出します。呼び出しに成功すると`ThetaRepository.ThetaState`を取得できます。このデータは以下のような情報を含みます。 +## THETA の状態を取得する -* ThetaRepository.ThetaState +接続している Theta の状態を取得するには、`ThetaRepository.getThetaState(completionHandler:)`を呼び出します。呼び出しに成功すると`ThetaRepository.ThetaState`を取得できます。このデータは以下のような情報を含みます。 - |プロパティ名|型|内容| - |---|---|---| - |batteryLevel|float|バッテリーレベルを表します| - |chargingState|ThetaRepository.ChargingStateEnum|充電状態を表します| - |fingerprint|String|現在の状態ごとに一意に決まる識別子を表します| - |isSdCard|Bool|SDカードが存在するかどうかを表します| - |latestFileUrl|String|最後の取得したメディアのURLを表します| - |recordableTime|int32_t|録画可能秒数を表します| - |recordedTime|int32_t|録画済み秒数を表します| +- ThetaRepository.ThetaState -* ThetaRepository.ChargingStateEnum + | プロパティ名 | 型 | 内容 | + | -------------- | --------------------------------- | -------------------------------------------- | + | batteryLevel | float | バッテリーレベルを表します | + | chargingState | ThetaRepository.ChargingStateEnum | 充電状態を表します | + | fingerprint | String | 現在の状態ごとに一意に決まる識別子を表します | + | isSdCard | Bool | SD カードが存在するかどうかを表します | + | latestFileUrl | String | 最後の取得したメディアの URL を表します | + | recordableTime | int32_t | 録画可能秒数を表します | + | recordedTime | int32_t | 録画済み秒数を表します | - |プロパティ名|内容| - |---|---| - |charging|充電中を表します| - |charged|充電完了を表します| - |notCharging|ケーブル未接続を表します| +- ThetaRepository.ChargingStateEnum + | プロパティ名 | 内容 | + | ------------ | ------------------------ | + | charging | 充電中を表します | + | charged | 充電完了を表します | + | notCharging | ケーブル未接続を表します | -``` swift +```swift Task { do { let thetaState: ThetaRepository.ThetaState = try await withCheckedThrowingContinuation {continuation in diff --git a/docs/tutorial-ios.md b/docs/tutorial-ios.md index 8edeca1ea9..aaff8ea2d0 100644 --- a/docs/tutorial-ios.md +++ b/docs/tutorial-ios.md @@ -1,12 +1,13 @@ # THETA CLient Tutorial for iOS ## Available models -* RICOH THETA X -* RICOH THETA Z1 -* RICOH THETA V -* RICOH THETA SC2 -* RICOH THETA S (firmware v1.62 or later only) -* RICOH THETA SC + +- RICOH THETA X +- RICOH THETA Z1 +- RICOH THETA V +- RICOH THETA SC2 +- RICOH THETA S (firmware v1.62 or later only) +- RICOH THETA SC ## Advance preparation @@ -14,7 +15,7 @@ Connect the wireless LAN between THETA and the smartphone that runs on the appli ## To instantiate SDK -``` swift +```swift import THETAClient // Create ThetaRepository object by specifying an IP address ThetaRepository.Companion.shared.doNewInstance( @@ -28,17 +29,19 @@ ThetaRepository.Companion.shared.doNewInstance( } } ``` -* THETA IP ADDRESS -| Mode |Address | -|-------|---------| -|Direct mode|192.168.1.1| -|Other| Assigned IP address| +- THETA IP ADDRESS + +| Mode | Address | +| ----------- | ------------------- | +| Direct mode | 192.168.1.1 | +| Other | Assigned IP address | + +- When downloading images or videos from THETA using URLSession, the connection is plain. Therefore, the connection permission setting to Info.plist is required depending on the destination address (default 192.168.1.1). + The following is an example of Info.plist by default. + Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. -* When downloading images or videos from THETA using URLSession, the connection is plain. Therefore, the connection permission setting to Info.plist is required depending on the destination address (default 192.168.1.1). -The following is an example of Info.plist by default. -Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. -``` xml +```xml @@ -68,7 +71,7 @@ Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be First, use `ThetaRepository.getPhotoCaptureBuilder()` to set the shooting and create the `PhotoCapture` object. -``` swift +```swift Task { do { let photoCapture: PhotoCapture = try await withCheckedThrowingContinuation {continuation in @@ -89,6 +92,7 @@ Task { } } ``` + The above example sets the maximum ISO sensitivity to 1000 and the file format to image5k. See [View preview](#preview) for instructions on how to view preview. @@ -96,7 +100,7 @@ See [View preview](#preview) for instructions on how to view preview. Then we call `PhotoCapture.takePicture(callback:)` to shoot still pictures. Create and call a callback class that implements `PhotoCaptureTakePictureCallback` as follows. -``` swift +```swift do { class Callback: PhotoCaptureTakePictureCallback { let callback: (_ fileUrl: String?, _ error: Error?) -> Void @@ -130,180 +134,181 @@ do { ### Properties that can be set for shooting still images -* Exposure Compensation: `setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum)` - -| Value|Correction value |Remarks| -|---|---:|---| -|m20|-2.0f|| -|m17|-1.7f|| -|m13|-1.0f|| -|m07|-0.7f|| -|m03|-0.3f|| -|zero|0.0f|| -|p03|0.3f|| -|p07|0.7f|| -|p13|1.0f|| -|p17|1.7f|| -|p20|2.0f|| - -* Exposure Delay Setting: `setExposureDelay(delay:ThetaRepository.ExposureDelayEnum)` - -Delay between the takePicture command and the start of exposure (= self-timer). - -| Value|Delay time (seconds) |Remarks| -|---|---:|---| -|delayOff|0|| -|delay1|1|| -|delay2|2|| -|delay3|3|| -|delay4|4|| -|delay5|5|| -|delay6|6|| -|delay7|7|| -|delay8|8|| -|delay9|9|| -|delay10|10|| - -* Exposure Program: `setExposureProgram(program:ThetaRepository.ExposureProgramEnum)` - -| Value|Content |Remarks| -|---|---|---| -|manual|Manual|| -|normalProgram|Regular programs|| -|aperturePriority|Aperture priority|| -|shutterPriority|Shutter priority|| -|isoPriority|ISO priority|| - -* File Format: `setFileFormat(fileFormat:ThetaRepository.PhotoFileFormatEnum)` - -| Value|Type| Width|Height |S|SC|SC2|V|Z1|X| -|---|---|--:|--:|:-:|:-:|:-:|:-:|:-:|:-:| -|image2k|Jpeg|2048|1024|+|+|-|-|-|-| -|image5k|Jpeg|5376|2688|+|+|+|+|-|-| -|image67k|Jpeg|6720|3360|-|-|-|-|+|-| -|rawP67k|Raw+|6720|3360|-|-|-|-|+|-| -|image55k|Jpeg|5504|2752|-|-|-|-|-|+| -|image11k|Jpeg|11008|5504|-|-|-|-|-|+| - -* Image Processing: `setFilter(filter:ThetaRepository.Filter)` - -| Value|Content |Remarks| -|---|---|---| -|off|None||| -|noiseReduction|Noise reduction|| -|hdr|HDR|| - -* GPS on/off: `setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum)` +- Exposure Compensation: `setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum)` + +| Value | Correction value | Remarks | +| ----- | ---------------: | ------- | +| m20 | -2.0f | | +| m17 | -1.7f | | +| m13 | -1.0f | | +| m07 | -0.7f | | +| m03 | -0.3f | | +| zero | 0.0f | | +| p03 | 0.3f | | +| p07 | 0.7f | | +| p13 | 1.0f | | +| p17 | 1.7f | | +| p20 | 2.0f | | + +- Exposure Delay Setting: `setExposureDelay(delay:ThetaRepository.ExposureDelayEnum)` + +Delay between the takePicture command and the start of exposure (= self-timer). + +| Value | Delay time (seconds) | Remarks | +| -------- | -------------------: | ------- | +| delayOff | 0 | | +| delay1 | 1 | | +| delay2 | 2 | | +| delay3 | 3 | | +| delay4 | 4 | | +| delay5 | 5 | | +| delay6 | 6 | | +| delay7 | 7 | | +| delay8 | 8 | | +| delay9 | 9 | | +| delay10 | 10 | | + +- Exposure Program: `setExposureProgram(program:ThetaRepository.ExposureProgramEnum)` + +| Value | Content | Remarks | +| ---------------- | ----------------- | ------- | +| manual | Manual | | +| normalProgram | Regular programs | | +| aperturePriority | Aperture priority | | +| shutterPriority | Shutter priority | | +| isoPriority | ISO priority | | + +- File Format: `setFileFormat(fileFormat:ThetaRepository.PhotoFileFormatEnum)` + +| Value | Type | Width | Height | S | SC | SC2 | V | Z1 | X | +| -------- | ---- | ----: | -----: | :-: | :-: | :-: | :-: | :-: | :-: | +| image2k | Jpeg | 2048 | 1024 | + | + | - | - | - | - | +| image5k | Jpeg | 5376 | 2688 | + | + | + | + | - | - | +| image67k | Jpeg | 6720 | 3360 | - | - | - | - | + | - | +| rawP67k | Raw+ | 6720 | 3360 | - | - | - | - | + | - | +| image55k | Jpeg | 5504 | 2752 | - | - | - | - | - | + | +| image11k | Jpeg | 11008 | 5504 | - | - | - | - | - | + | + +- Image Processing: `setFilter(filter:ThetaRepository.Filter)` + +| Value | Content | Remarks | +| -------------- | --------------- | ------- | --- | +| off | None | | | +| noiseReduction | Noise reduction | | +| hdr | HDR | | + +- GPS on/off: `setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum)` Other than THETA X, this setting is ignored. -| Value|Content |Remarks -|---|---|---| -|on|GPS | | -|off|No GPS|| - -* ISO value: `setIso(iso:ThetaRepository.IsoEnum)` - -| Value|ISO value |Remarks| -|---|---:|---| -|isoAuto|0|| -|iso50|50|THETA X only| -|iso64|64|THETA SC2, V and X only| -|iso80|80|THETA S and SC don't support| -|iso100|100|| -|iso125|125|| -|iso160|160|| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|THETA S and SC don't support| -|iso2500|2500|THETA S and SC don't support| -|iso3200|3200|THETA S and SC don't support| -|iso4000|4000|THETA Z1 only| -|iso5000|5000|THETA Z1 only| -|iso6400|6400|THETA Z1 only| - -* ISO upper limit: `setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum)` +| Value | Content | Remarks | +| ----- | ------- | ------- | +| on | GPS | | +| off | No GPS | | + +- ISO value: `setIso(iso:ThetaRepository.IsoEnum)` + +| Value | ISO value | Remarks | +| ------- | --------: | ---------------------------- | +| isoAuto | 0 | | +| iso50 | 50 | THETA X only | +| iso64 | 64 | THETA SC2, V and X only | +| iso80 | 80 | THETA S and SC don't support | +| iso100 | 100 | | +| iso125 | 125 | | +| iso160 | 160 | | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | THETA S and SC don't support | +| iso2500 | 2500 | THETA S and SC don't support | +| iso3200 | 3200 | THETA S and SC don't support | +| iso4000 | 4000 | THETA Z1 only | +| iso5000 | 5000 | THETA Z1 only | +| iso6400 | 6400 | THETA Z1 only | + +- ISO upper limit: `setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum)` This setting is ignored by THETA V Firmware v2.50.1 or earlier, THETA S and THETA SC. -| Value|Upper ISO limit |Remarks| -|---|---:|---| -|iso100|100|THETA X only| -|iso125|125|THETA X only| -|iso160|160|THETA X only| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|| -|iso2500|2500|| -|iso3200|3200|| -|iso4000|4000|THETA Z1 only| -|iso5000|5000|THETA Z1 only| -|iso6400|6400|THETA Z1 only| - -* Aperture setting: `setAperture(aperture:ThetaRepository.ApertureEnum)` - -| Value|Setting value |Remarks| -|---|---:|---| -|apertureAuto|Automatic|| -|aperture20|2.0f|THETA V or prior only| -|aperture21|2.1f|THETA Z1 only| -|aperture24|2.4f|THETA X only| -|aperture35|3.5f|THETA Z1 only| -|aperture56|5.6f|THETA Z1 only| - -* Color temperature setting: `setColorTemperature(kelvin:Int)` - * 2500 to 10000 - -* GPS information: `setGpsInfo(gpsInfo:ThetaRepository.GpsInfo)` +| Value | Upper ISO limit | Remarks | +| ------- | --------------: | ------------- | +| iso100 | 100 | THETA X only | +| iso125 | 125 | THETA X only | +| iso160 | 160 | THETA X only | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | | +| iso2500 | 2500 | | +| iso3200 | 3200 | | +| iso4000 | 4000 | THETA Z1 only | +| iso5000 | 5000 | THETA Z1 only | +| iso6400 | 6400 | THETA Z1 only | + +- Aperture setting: `setAperture(aperture:ThetaRepository.ApertureEnum)` + +| Value | Setting value | Remarks | +| ------------ | ------------: | --------------------- | +| apertureAuto | Automatic | | +| aperture20 | 2.0f | THETA V or prior only | +| aperture21 | 2.1f | THETA Z1 only | +| aperture24 | 2.4f | THETA X only | +| aperture35 | 3.5f | THETA Z1 only | +| aperture56 | 5.6f | THETA Z1 only | + +- Color temperature setting: `setColorTemperature(kelvin:Int)` + + - 2500 to 10000 + +- GPS information: `setGpsInfo(gpsInfo:ThetaRepository.GpsInfo)` GpsInfo shall be prepared as follows: `ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:)` -| Value|Setting value |Remarks| -|---|---|---| -|latitude|Latitude | -90 to 90 or 65535 (disabled)| -|longitude|Longitude | -180 to 180 or 65535 (disabled)| -|altitude|Altitude ||| -|dateTimeZone|Date and time|YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled)| - -* White balance: `setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum)` - -| Value|Setting value |Remarks| -|---|---|---| -|auto|Automatic|| -|daylight|Outdoor|About 5,200| -|shade|Shade|About 7,000| -|cloudyDaylight|Cloudy|About 6,000| -|incandescent|Incandescent light 1|About 3,200| -|warmWhiteFluorescent|Incandescent light 2|| -|daylightFluorescent|Fluorescent light 1 (daylight)|| -|daywhiteFluorescent|Fluorescent light 2 (natural white)|| -|fluorescent|Fluorescent light 3 (white)|About 4,000| -|bulbFluorescent|Fluorescent light 4 (light bulb color)|| -|colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| -|underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| +| Value | Setting value | Remarks | +| ------------ | ------------- | ------------------------------------------------------- | --- | +| latitude | Latitude | -90 to 90 or 65535 (disabled) | +| longitude | Longitude | -180 to 180 or 65535 (disabled) | +| altitude | Altitude | | | +| dateTimeZone | Date and time | YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled) | + +- White balance: `setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum)` + +| Value | Setting value | Remarks | +| -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | +| auto | Automatic | | +| daylight | Outdoor | About 5,200 | +| shade | Shade | About 7,000 | +| cloudyDaylight | Cloudy | About 6,000 | +| incandescent | Incandescent light 1 | About 3,200 | +| warmWhiteFluorescent | Incandescent light 2 | | +| daylightFluorescent | Fluorescent light 1 (daylight) | | +| daywhiteFluorescent | Fluorescent light 2 (natural white) | | +| fluorescent | Fluorescent light 3 (white) | About 4,000 | +| bulbFluorescent | Fluorescent light 4 (light bulb color) | | +| colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | +| underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## Shoot a video First, use the `ThetaRepository.getVideoCaptureBuilder()` to set the shooting and create the `VideoCapture` object. -``` swift +```swift Task { do { let videoCapture: VideoCapture = try await withCheckedThrowingContinuation {continuation in @@ -333,18 +338,21 @@ See [Display preview](#preview) Next, we call `VideoCapture.startCapture(callback:)` to start shooting videos. Create and call a callback class that implements `VideoCaptureStartCaptureCallback` as follows. -``` swift +```swift class Callback: VideoCaptureStartCaptureCallback { let callback: (_ fileUrl: String?, _ error: Error?) -> Void init(_ callback: @escaping (_ fileUrl: String?, _ error: Error?) -> Void) { self.callback = callback } - func onSuccess(fileUrl: String) { + func onCaptureCompleted(fileUrl: String?) { callback(fileUrl, nil) } - func onError(exception: ThetaRepository.ThetaRepositoryException) { + func onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { callback(nil, exception as? Error) } + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + // handle error of stopCapture + } } let videoCapturing = videoCapture.startCapture( callback: Callback {fileUrl, error in @@ -361,203 +369,202 @@ let videoCapturing = videoCapture.startCapture( Next, we call `VideoCapturing.stopCapture()` to finish recording the video. When the MP4 file is created after shooting, the callback function passed to `startCapture(callback:)` is called. -``` swift +```swift VideoCapturing.stopCapture() ``` ### Properties that can be set when shooting videos -* Exposure compensation: `setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum)` +- Exposure compensation: `setExposureCompensation(value:ThetaRepository.ExposureCompensationEnum)` -| Value|Correction value |Remarks| -|---|---:|---| -|m20|-2.0f|| -|m17|-1.7f|| -|m13|-1.0f|| -|m07|-0.7f|| -|m03|-0.3f|| -|zero|0.0f|| -|p03|0.3f|| -|p07|0.7f|| -|p13|1.0f|| -|p17|1.7f|| -|p20|2.0f|| +| Value | Correction value | Remarks | +| ----- | ---------------: | ------- | +| m20 | -2.0f | | +| m17 | -1.7f | | +| m13 | -1.0f | | +| m07 | -0.7f | | +| m03 | -0.3f | | +| zero | 0.0f | | +| p03 | 0.3f | | +| p07 | 0.7f | | +| p13 | 1.0f | | +| p17 | 1.7f | | +| p20 | 2.0f | | -* Exposure delay setting: `setExposureDelay(delay:ThetaRepository.ExposureDelayEnum)` +- Exposure delay setting: `setExposureDelay(delay:ThetaRepository.ExposureDelayEnum)` Delay between the startCapture command and the start of exposure (= self-timer). -| Value|Delay time (seconds) |Remarks| -|---|---:|---| -|delayOff|0|| -|delay1|1|| -|delay2|2|| -|delay3|3|| -|delay4|4|| -|delay5|5|| -|delay6|6|| -|delay7|7|| -|delay8|8|| -|delay9|9|| -|delay10|10|| - -* Exposure program: `setExposureProgram(program:ThetaRepository.ExposureProgramEnum)` - -| Value|Content |Remarks| -|---|---|---| -|manual|Manual|| -|normalProgram|Regular programs|| -|aperturePriority|Aperture priority|| -|shutterPriority|Shutter priority|| -|isoPriority|ISO priority|| - -* File format: `setFileFormat(fileFormat:ThetaRepository.VideoFileFormatEnum)` - -| Value|Type| Width|Height |Frame rate | Codec|S|SC|SC2|V|Z1|X| -|---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:|:-:| -|videoHd|mp4|1280|570|||+|+|-|-|-|-| -|videoFullHd|mp4|1920|1080|||+|+|-|-|-|-| -|video2k|mp4|1920|960||H.264/MPEG-4 AVC|-|-|+|+|+|-| -|video4k|mp4|3840|1920||H.264/MPEG-4 AVC|-|-|+|+|+|-| -|video2k30f|mp4|1920|960|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video2k60f|mp4|1920|960|60|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video4k30f|mp4|3840|1920|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video4k60f|mp4|3840|1920|60|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k2f|mp4|5760|2880|2|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k5f|mp4|5760|2880|5|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k30f|mp4|5760|2880|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k2f|mp4|7680|3840|2|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k5f|mp4|7680|3840|5|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k10f|mp4|7680|3840|10|H.264/MPEG-4 AVC|-|-|-|-|-|+| - -* Maximum recording time setting: `setMaxRecordableTime(time:ThetaRepository.MaxRecordeTimeEnum)` - -| Value|Content |Remarks -|---|---|---| -|recordableTime300|300 seconds|| -|recordableTime1500|1500 seconds|| - -* GPS on/off: `setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum)` +| Value | Delay time (seconds) | Remarks | +| -------- | -------------------: | ------- | +| delayOff | 0 | | +| delay1 | 1 | | +| delay2 | 2 | | +| delay3 | 3 | | +| delay4 | 4 | | +| delay5 | 5 | | +| delay6 | 6 | | +| delay7 | 7 | | +| delay8 | 8 | | +| delay9 | 9 | | +| delay10 | 10 | | + +- Exposure program: `setExposureProgram(program:ThetaRepository.ExposureProgramEnum)` + +| Value | Content | Remarks | +| ---------------- | ----------------- | ------- | +| manual | Manual | | +| normalProgram | Regular programs | | +| aperturePriority | Aperture priority | | +| shutterPriority | Shutter priority | | +| isoPriority | ISO priority | | + +- File format: `setFileFormat(fileFormat:ThetaRepository.VideoFileFormatEnum)` + +| Value | Type | Width | Height | Frame rate | Codec | S | SC | SC2 | V | Z1 | X | +| ----------- | ---- | ----: | -----: | ---------: | ---------------- | :-: | :-: | :-: | :-: | :-: | :-: | +| videoHd | mp4 | 1280 | 570 | | | + | + | - | - | - | - | +| videoFullHd | mp4 | 1920 | 1080 | | | + | + | - | - | - | - | +| video2k | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | - | - | + | + | + | - | +| video4k | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | - | - | + | + | + | - | +| video2k30f | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video2k60f | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video4k30f | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video4k60f | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k2f | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k5f | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k30f | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k2f | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k5f | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k10f | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | - | - | - | - | - | + | + +- Maximum recording time setting: `setMaxRecordableTime(time:ThetaRepository.MaxRecordeTimeEnum)` + +| Value | Content | Remarks | +| ------------------ | ------------ | ------- | +| recordableTime300 | 300 seconds | | +| recordableTime1500 | 1500 seconds | | + +- GPS on/off: `setGpsTagRecording(value:ThetaRepository.GpsTagRecordingEnum)` Other than THETA X, this setting is ignored. -| Value|Content |Remarks| -|---|---|---| -|on|GPS | | -|off|No GPS|| - -* ISO value: `setIso(iso:ThetaRepository.IsoEnum)` - -| Value|ISO value |Remarks| -|---|---:|---| -|isoAuto|0|| -|iso50|50|THETA X only| -|iso64|64|THETA SC2, V and X only| -|iso80|80|THETA S and SC don't support| -|iso100|100|| -|iso125|125|| -|iso160|160|| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|THETA S and SC don't support| -|iso2500|2500|THETA S and SC don't support| -|iso3200|3200|THETA S and SC don't support| -|iso4000|4000|THETA V and Z1 only| -|iso5000|5000|THETA V and Z1 only| -|iso6400|6400|THETA V and Z1 only| - - -* ISO upper limit: `setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum)` +| Value | Content | Remarks | +| ----- | ------- | ------- | +| on | GPS | | +| off | No GPS | | + +- ISO value: `setIso(iso:ThetaRepository.IsoEnum)` + +| Value | ISO value | Remarks | +| ------- | --------: | ---------------------------- | +| isoAuto | 0 | | +| iso50 | 50 | THETA X only | +| iso64 | 64 | THETA SC2, V and X only | +| iso80 | 80 | THETA S and SC don't support | +| iso100 | 100 | | +| iso125 | 125 | | +| iso160 | 160 | | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | THETA S and SC don't support | +| iso2500 | 2500 | THETA S and SC don't support | +| iso3200 | 3200 | THETA S and SC don't support | +| iso4000 | 4000 | THETA V and Z1 only | +| iso5000 | 5000 | THETA V and Z1 only | +| iso6400 | 6400 | THETA V and Z1 only | + +- ISO upper limit: `setIsoAutoHighLimit(iso:ThetaRepository.IsoAutoHighLimitEnum)` THETA V Firmware v2.50.1 or earlier is ignored even if specified in THETA S or THETA SC. -| Value|Upper ISO limit|Remarks| -|---|---:|---| -|iso100|100|THETA X only| -|iso125|125|THETA X only| -|iso160|160|THETA X only| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|| -|iso2500|2500|| -|iso3200|3200|| -|iso4000|4000|THETA V and Z1 only| -|iso5000|5000|THETA V and Z1 only| -|iso6400|6400|THETA V and Z1 only| - - -* Aperture setting: `setAperture(aperture:ThetaRepository.ApertureEnum)` - -| Value|Setting value |Remarks| -|---|---:|---| -|apertureAuto|Automatic|| -|aperture20|2.0f|THETA V or prior only| -|aperture21|2.1f|THETA Z1 only| -|aperture24|2.4f|THETA X only| -|aperture35|3.5f|THETA Z1 only| -|aperture56|5.6f|THETA Z1 only| - -* Color temperature setting: `setColorTemperature(kelvin:Int)` - * 2500 to 10000 - -* GPS information: `setGpsInfo(gpsInfo:ThetaRepository.GpsInfo)` +| Value | Upper ISO limit | Remarks | +| ------- | --------------: | ------------------- | +| iso100 | 100 | THETA X only | +| iso125 | 125 | THETA X only | +| iso160 | 160 | THETA X only | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | | +| iso2500 | 2500 | | +| iso3200 | 3200 | | +| iso4000 | 4000 | THETA V and Z1 only | +| iso5000 | 5000 | THETA V and Z1 only | +| iso6400 | 6400 | THETA V and Z1 only | + +- Aperture setting: `setAperture(aperture:ThetaRepository.ApertureEnum)` + +| Value | Setting value | Remarks | +| ------------ | ------------: | --------------------- | +| apertureAuto | Automatic | | +| aperture20 | 2.0f | THETA V or prior only | +| aperture21 | 2.1f | THETA Z1 only | +| aperture24 | 2.4f | THETA X only | +| aperture35 | 3.5f | THETA Z1 only | +| aperture56 | 5.6f | THETA Z1 only | + +- Color temperature setting: `setColorTemperature(kelvin:Int)` + + - 2500 to 10000 + +- GPS information: `setGpsInfo(gpsInfo:ThetaRepository.GpsInfo)` GpsInfo shall be prepared as follows: `ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:)` -| Value|Setting value |Remarks| -|---|---|---| -|latitude|Latitude | -90 to 90 or 65535 (disabled)| -|longitude|Longitude | -180 to 180 or 65535 (disabled)| -|altitude|Altitude ||| -|dateTimeZone|Date and time|YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled)| - -* White balance: `setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum)` - -| Value|Setting value |Remarks| -|---|---|---| -|auto|Automatic|| -|daylight|Outdoor|About 5,200| -|shade|Shade|About 7,000| -|cloudyDaylight|Cloudy|About 6,000| -|incandescent|Incandescent light 1|About 3,200| -|warmWhiteFluorescent|Incandescent light 2|| -|daylightFluorescent|Fluorescent light 1(daylight)|| -|daywhiteFluorescent|Fluorescent light 2(natural white)|| -|fluorescent|Fluorescent light 3 (white)|About 4,000| -|bulbFluorescent|Fluorescent light 4 (light bulb color)|| -|colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| -|underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| +| Value | Setting value | Remarks | +| ------------ | ------------- | ------------------------------------------------------- | --- | +| latitude | Latitude | -90 to 90 or 65535 (disabled) | +| longitude | Longitude | -180 to 180 or 65535 (disabled) | +| altitude | Altitude | | | +| dateTimeZone | Date and time | YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled) | + +- White balance: `setWhiteBalance(whiteBalance:ThetaRepository.WhiteBalanceEnum)` + +| Value | Setting value | Remarks | +| -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | +| auto | Automatic | | +| daylight | Outdoor | About 5,200 | +| shade | Shade | About 7,000 | +| cloudyDaylight | Cloudy | About 6,000 | +| incandescent | Incandescent light 1 | About 3,200 | +| warmWhiteFluorescent | Incandescent light 2 | | +| daylightFluorescent | Fluorescent light 1(daylight) | | +| daywhiteFluorescent | Fluorescent light 2(natural white) | | +| fluorescent | Fluorescent light 3 (white) | About 4,000 | +| bulbFluorescent | Fluorescent light 4 (light bulb color) | | +| colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | +| underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | ## Display a preview The preview is an equirectangular images of motion JPEG format. -| Model | Width (pixel) | Height (pixel) |Frame rate (fps) | Remarks | -| ---- | --: |--: | -----------: |---- | -| THETA X | 1024 |512 | 30 || -| THETA Z1 |1024 | 512 |30 | | -| THETA V |1024 | 512 |30 | Firmware v2.21.1 or later | -| THETA V |1024 | 512 |8 | Firmware v2.20.1 or earlier | -| THETA SC2 |1024 | 512 |30 | | -| THETA S |640 | 320 |10 | | -| THETA SC |640 | 320 |10 | | +| Model | Width (pixel) | Height (pixel) | Frame rate (fps) | Remarks | +| --------- | ------------: | -------------: | ---------------: | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | Firmware v2.21.1 or later | +| THETA V | 1024 | 512 | 8 | Firmware v2.20.1 or earlier | +| THETA SC2 | 1024 | 512 | 30 | | +| THETA S | 640 | 320 | 10 | | +| THETA SC | 640 | 320 | 10 | | When we call `getLivePreview(frameHandler:completionHandler:)` then the callback function is called each time each frame in the preview is received. The arguments to the callback function are passed to the objects [`Ktor_ByteReadPacket`](https://api.ktor.io/ktor-io/io.kitir.utils.io/-byte-read-packet/index.html) and to the callback function that returns the result. @@ -570,7 +577,7 @@ If CPU usage is high and preview is continued for a long time, the system may de In such a case, adjust the frame rate (approximately 10 fps) so that the frames are discarded before extracting `Data` so as not to place a load on the CPU. An example of receiving a frame is shown below. -``` swift +```swift class FrameHandler: KotlinSuspendFunction1 { static let FrameInterval = CFTimeInterval(1.0/10.0) var last: CFTimeInterval = 0 @@ -637,253 +644,256 @@ Task { Refer to the table below for the items and contents that can be set to "Options". -* Date time: `dateTimeZone:String` +- Date time: `dateTimeZone:String` Format: YYYY:MM:DD hh:mm:ss+(-)hh:mm -hh is 0 to 23, +(-)hh:mm are time zones. +hh is 0 to 23, +(-)hh:mm are time zones. Example: 2014:05:18 01:04:29+08:00 -* Exposure Compensation: `exposureCompensation:ThetaRepository.ExposureCompensationEnum` +- Exposure Compensation: `exposureCompensation:ThetaRepository.ExposureCompensationEnum` -| Value| Correction value | Remarks| -|---|---:|---| -|m20|-2.0f|| -|m17|-1.7f|| -|m13|-1.0f|| -|m07|-0.7f|| -|m03|-0.3f|| -|zero| 0.0f|| -|p03|0.3f|| -|p07|0.7f|| -|p13|1.0f|| -|p17|1.7f|| -|p20|2.0f|| +| Value | Correction value | Remarks | +| ----- | ---------------: | ------- | +| m20 | -2.0f | | +| m17 | -1.7f | | +| m13 | -1.0f | | +| m07 | -0.7f | | +| m03 | -0.3f | | +| zero | 0.0f | | +| p03 | 0.3f | | +| p07 | 0.7f | | +| p13 | 1.0f | | +| p17 | 1.7f | | +| p20 | 2.0f | | -* Exposure Delay Setting: `ExposureDelay:ThetaRepository.ExposureDelayEnum` +- Exposure Delay Setting: `ExposureDelay:ThetaRepository.ExposureDelayEnum` Delay between the startCapture command and the start of exposure (= self-timer). -| Value| Delay time (seconds) | Remarks| -|---|---:|---| -|delayOff|0|| -|delay1|1|| -|delay2|2|| -|delay3|3|| -|delay4|4|| -|delay5|5|| -|delay6|6|| -|delay7|7|| -|delay8|8|| -|delay9|9|| -|delay10|10|| - -* Exposure Program: `ExposureProgram:ThetaRepository.ExposureProgramEnum` - -| Value| Content | Remarks| -|---|---|---| -|manual| Manual|| -|normalProgram| Regular programs|| -|aperturePriority| Aperture priority|| -|shutterPriority| Shutter priority|| -|isoPriority| ISO priority|| - -* File Format: `FileFormat:ThetaRepository.FileFormatEnum` - -| Value| Type| Width| Height | Frame rate | Codec| S| SC| SC2|V| Z1| X| -|---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:|:-:| -|videoHd|mp4|1280|570|||+|+|-|-|-|-| -|videoFullHd|mp4|1920|1080|||+|+|-|-|-|-| -|video2k|mp4|1920|960||H.264/MPEG-4 AVC|-|-|+|+|+|-| -|video4k|mp4|3840|1920||H.264/MPEG-4 AVC|-|-|+|+|+|-| -|video2k30f|mp4|1920|960|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video2k60f|mp4|1920|960|60|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video4k30f|mp4|3840|1920|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video4k60f|mp4|3840|1920|60|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k2f|mp4|5760|2880|2|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k5f|mp4|5760|2880|5|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video57k30f|mp4|5760|2880|30|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k2f|mp4|7680|3840|2|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k5f|mp4|7680|3840|5|H.264/MPEG-4 AVC|-|-|-|-|-|+| -|video7k10f|mp4|7680|3840|10|H.264/MPEG-4 AVC|-|-|-|-|-|+| - -* Image Processing: `filter:ThetaRepository.Filter` - -| Value| Content | Remarks| -|---|---|---| -|off| None||| -|noiseReduction| Noise reduction|| -|hdr| HDR| | - -* GPS ON/OFF: `isGpsOn:Bool` +| Value | Delay time (seconds) | Remarks | +| -------- | -------------------: | ------- | +| delayOff | 0 | | +| delay1 | 1 | | +| delay2 | 2 | | +| delay3 | 3 | | +| delay4 | 4 | | +| delay5 | 5 | | +| delay6 | 6 | | +| delay7 | 7 | | +| delay8 | 8 | | +| delay9 | 9 | | +| delay10 | 10 | | + +- Exposure Program: `ExposureProgram:ThetaRepository.ExposureProgramEnum` + +| Value | Content | Remarks | +| ---------------- | ----------------- | ------- | +| manual | Manual | | +| normalProgram | Regular programs | | +| aperturePriority | Aperture priority | | +| shutterPriority | Shutter priority | | +| isoPriority | ISO priority | | + +- File Format: `FileFormat:ThetaRepository.FileFormatEnum` + +| Value | Type | Width | Height | Frame rate | Codec | S | SC | SC2 | V | Z1 | X | +| ----------- | ---- | ----: | -----: | ---------: | ---------------- | :-: | :-: | :-: | :-: | :-: | :-: | +| videoHd | mp4 | 1280 | 570 | | | + | + | - | - | - | - | +| videoFullHd | mp4 | 1920 | 1080 | | | + | + | - | - | - | - | +| video2k | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | - | - | + | + | + | - | +| video4k | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | - | - | + | + | + | - | +| video2k30f | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video2k60f | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video4k30f | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video4k60f | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k2f | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k5f | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video57k30f | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k2f | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k5f | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | - | - | - | - | - | + | +| video7k10f | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | - | - | - | - | - | + | + +- Image Processing: `filter:ThetaRepository.Filter` + +| Value | Content | Remarks | +| -------------- | --------------- | ------- | --- | +| off | None | | | +| noiseReduction | Noise reduction | | +| hdr | HDR | | + +- GPS ON/OFF: `isGpsOn:Bool` Other than THETA X, it is ignored. -| Value| Content | Remarks| -|---|---|---| -|true| GPS | | -|false| No GPS|| - -* ISO value: `iso:ThetaRepository.IsoEnum` - -| Value| ISO value | Remarks| -|---|---:|---| -|isoAuto|0|| -|iso50|50|| -|iso64|64|| -|iso80|80|| -|iso100|100|| -|iso125|125|| -|iso160|160|| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|| -|iso2500|2500|| -|iso3200|3200|| -|iso4000|4000|| -|iso5000|5000|| -|iso6400|6400|| - -* ISO ceiling: `isoAutoHighLimit:ThetaRepository.IsoAutoHighLimitEnum` +| Value | Content | Remarks | +| ----- | ------- | ------- | +| true | GPS | | +| false | No GPS | | + +- ISO value: `iso:ThetaRepository.IsoEnum` + +| Value | ISO value | Remarks | +| ------- | --------: | ------- | +| isoAuto | 0 | | +| iso50 | 50 | | +| iso64 | 64 | | +| iso80 | 80 | | +| iso100 | 100 | | +| iso125 | 125 | | +| iso160 | 160 | | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | | +| iso2500 | 2500 | | +| iso3200 | 3200 | | +| iso4000 | 4000 | | +| iso5000 | 5000 | | +| iso6400 | 6400 | | + +- ISO ceiling: `isoAutoHighLimit:ThetaRepository.IsoAutoHighLimitEnum` THETA V Firmware v2.50.1 or earlier is ignored even if specified in THETA S or THETA SC. -| Value| Upper ISO limit | Remarks| -|---|---:|---| -|iso100|100|| -|iso125|125|| -|iso160|160|| -|iso200|200|| -|iso250|250|| -|iso320|320|| -|iso400|400|| -|iso500|150|| -|iso640|640|| -|iso800|800|| -|iso1000|1000|| -|iso1250|1250|| -|iso1600|1600|| -|iso2000|2000|| -|iso2500|2500|| -|iso3200|3200|| -|iso4000|4000|| -|iso5000|5000|| -|iso6400|6400|| - -* Aperture: `ThetaRepository.ApertureEnum` - -| Value| Setting value | Remarks| -|---|---:|---| -|apertureAuto| Automatic|| -|aperture20|2.0f|| -|aperture21|2.1f|| -|aperture24|2.4f|| -|aperture35|3.5f|| -|aperture56|5.6f|| - -* Color Temperature Setting: `colorTemperature:Int` - * 2500 to 10000 - -* GPS Information: `gpsInfo:ThetaRepository.GpsInfo` +| Value | Upper ISO limit | Remarks | +| ------- | --------------: | ------- | +| iso100 | 100 | | +| iso125 | 125 | | +| iso160 | 160 | | +| iso200 | 200 | | +| iso250 | 250 | | +| iso320 | 320 | | +| iso400 | 400 | | +| iso500 | 150 | | +| iso640 | 640 | | +| iso800 | 800 | | +| iso1000 | 1000 | | +| iso1250 | 1250 | | +| iso1600 | 1600 | | +| iso2000 | 2000 | | +| iso2500 | 2500 | | +| iso3200 | 3200 | | +| iso4000 | 4000 | | +| iso5000 | 5000 | | +| iso6400 | 6400 | | + +- Aperture: `ThetaRepository.ApertureEnum` + +| Value | Setting value | Remarks | +| ------------ | ------------: | ------- | +| apertureAuto | Automatic | | +| aperture20 | 2.0f | | +| aperture21 | 2.1f | | +| aperture24 | 2.4f | | +| aperture35 | 3.5f | | +| aperture56 | 5.6f | | + +- Color Temperature Setting: `colorTemperature:Int` + + - 2500 to 10000 + +- GPS Information: `gpsInfo:ThetaRepository.GpsInfo` GpsInfo shall be prepared as follows: `ThetaRepository.GpsInfo(latitude:longitude:altitude:dateTimeZone:)` -| Value|Setting value |Remarks| -|---|---|---| -|latitude|Latitude | -90 to 90 or 65535 (disabled)| -|longitude|Longitude | -180 to 180 or 65535 (disabled)| -|altitude|Altitude ||| -|dateTimeZone|Date and time|YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled)| - -* White Balance: `whiteBalance:ThetaRepository.WhiteBalanceEnum` - -| Value| Setting value | Remarks| -|---|---|---| -|auto| Automatic|| -|daylight| Outdoor| About 5,200| -|shade| Shade| About 7,000| -|cloudyDaylight| Cloudy| About 6,000| -|incandescent| Incandescent light 1| About 3,200| -|warmWhiteFluorescent|Incandescent light 2|| -|daylightFluorescent|Fluorescent light 1 (daylight)|| -|daywhiteFluorescent|Fluorescent light 2 (natural white)|| -|fluorescent| Fluorescent light 3 (white)| About 4,000| -|bulbFluorescent|Fluorescent light 4 (light bulb color)|| -|colorTemperature|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| -|underwater|Underwater|RICOH THETA V firmware v3.21.1 or later| - -* Maximum Recording Time Setting: `maxRecordableTime:ThetaRepository.MaxRecorderTimeEnum` - -| Value| Content | Remarks| -|---|---|---| -|recordableTime300| 300 seconds|| -|recordableTime1500| 1500 seconds|| - -* Capture mode: `captureMode:ThetaRepository.CaptureModeEnum` - -| Value| Content | Remarks| -|---|---|---| -|image| Still image shooting mode|| -|video| Video shooting mode|| - -* Language Settings: `language:ThetaRepository.LanguageEnum` +| Value | Setting value | Remarks | +| ------------ | ------------- | ------------------------------------------------------- | --- | +| latitude | Latitude | -90 to 90 or 65535 (disabled) | +| longitude | Longitude | -180 to 180 or 65535 (disabled) | +| altitude | Altitude | | | +| dateTimeZone | Date and time | YYYY:MM:DD hh:mm:ss+(-)hh:mm or empty string (disabled) | + +- White Balance: `whiteBalance:ThetaRepository.WhiteBalanceEnum` + +| Value | Setting value | Remarks | +| -------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | +| auto | Automatic | | +| daylight | Outdoor | About 5,200 | +| shade | Shade | About 7,000 | +| cloudyDaylight | Cloudy | About 6,000 | +| incandescent | Incandescent light 1 | About 3,200 | +| warmWhiteFluorescent | Incandescent light 2 | | +| daylightFluorescent | Fluorescent light 1 (daylight) | | +| daywhiteFluorescent | Fluorescent light 2 (natural white) | | +| fluorescent | Fluorescent light 3 (white) | About 4,000 | +| bulbFluorescent | Fluorescent light 4 (light bulb color) | | +| colorTemperature | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | +| underwater | Underwater | RICOH THETA V firmware v3.21.1 or later | + +- Maximum Recording Time Setting: `maxRecordableTime:ThetaRepository.MaxRecorderTimeEnum` + +| Value | Content | Remarks | +| ------------------ | ------------ | ------- | +| recordableTime300 | 300 seconds | | +| recordableTime1500 | 1500 seconds | | + +- Capture mode: `captureMode:ThetaRepository.CaptureModeEnum` + +| Value | Content | Remarks | +| ----- | ------------------------- | ------- | +| image | Still image shooting mode | | +| video | Video shooting mode | | + +- Language Settings: `language:ThetaRepository.LanguageEnum` Ignored even if specified in THETA S or THETA SC. -|Value|Content|Remarks| -|--|---|---| -|enUs| U.S. English|| -|enGb| English|| -|ja| Japanese ||| -|fr| French ||| -|de| German ||| -|zhTw| Chinese Taiwan ||| -|zhCn| Chinese ||| -|it| Italian|| -|ko| Korean ||| - -* Time to enter sleep mode (minutes): `sleepDelay:ThetaRepository.SleepDelayEnum/SleepDelaySec` - - * ThetaRepository.SleepDelayEnum - -|Value|Set value (minutes)| Remarks| -|--|--:|---| -|disable| 0| Not automatically turned off| -|sleepDelay3m|3|| -|sleepDelay5m|5|| -|sleepDelay7m|7|| -|sleepDelay10m|10|| - - * ThetaRepository.SleepDelaySec(sec:Int) - * 0 to 1800 (seconds) - -* Time from sleep to auto power off (minutes): `OffDelay:ThetaRepository.OffDelayEnum/OffDelaySec` - - * ThetaRepository.OffDelayEnum - -|Value|Set value (minutes)| Remarks| -|--|--:|---| -|disable| 0| Not automatically turned off| -|offDelay5m|5|| -|offDelay10m|10|| -|offDelay15m|15|| -|offDelay30m|30|| - - * ThetaRepository.OffDelaySec(sec:Int) - * 0 to 1800 (seconds) - -* Shutter sound: `shutterVolume:Int` - * 0 - 100 +| Value | Content | Remarks | +| ----- | -------------- | ------- | --- | +| enUs | U.S. English | | +| enGb | English | | +| ja | Japanese | | | +| fr | French | | | +| de | German | | | +| zhTw | Chinese Taiwan | | | +| zhCn | Chinese | | | +| it | Italian | | +| ko | Korean | | | + +- Time to enter sleep mode (minutes): `sleepDelay:ThetaRepository.SleepDelayEnum/SleepDelaySec` + + - ThetaRepository.SleepDelayEnum + +| Value | Set value (minutes) | Remarks | +| ------------- | ------------------: | ---------------------------- | +| disable | 0 | Not automatically turned off | +| sleepDelay3m | 3 | | +| sleepDelay5m | 5 | | +| sleepDelay7m | 7 | | +| sleepDelay10m | 10 | | + +- ThetaRepository.SleepDelaySec(sec:Int) + + - 0 to 1800 (seconds) + +- Time from sleep to auto power off (minutes): `OffDelay:ThetaRepository.OffDelayEnum/OffDelaySec` + + - ThetaRepository.OffDelayEnum + +| Value | Set value (minutes) | Remarks | +| ----------- | ------------------: | ---------------------------- | +| disable | 0 | Not automatically turned off | +| offDelay5m | 5 | | +| offDelay10m | 10 | | +| offDelay15m | 15 | | +| offDelay30m | 30 | | + +- ThetaRepository.OffDelaySec(sec:Int) + + - 0 to 1800 (seconds) + +- Shutter sound: `shutterVolume:Int` + - 0 - 100 ## Acquire camera settings @@ -914,31 +924,31 @@ Task { See the table below for the `ThetaRepository.OptionNameEnum`: -| Value| Meaning | Type | -|----|-----|--| -|aperture| aperture|ThetaRepository.ApertureEnum| -|capturemode| capture mode | ThetaRepository.CaptureModeEnum| -|colortemperature| colortemperature |Int| -|datetimezone| date and time|String| -|exposurecompensation| exposure compensation |ThetaRepository.ExposureCompensationEnum| -|exposuredelay| exposure delay time|ThetaRepository.ExposureDelayEnum| -|exposureprogram| exposure program|ThetaRepository.ExposureProgram| -|fileformat| file format|ThetaRepository.FileFormatEnum| -|filter| image filter | ThetaRepository.FilterEnum| -|gpsinfo| GPS information | ThetaRepository.GpsInfo| -|isgpson| GPS flag | Bool| -|iso| ISO value |ThetaRepository.IsoEnum| -|isoautohighlimit| ISO upper limit|ThetaRepository.IsoAutoHighLimitEnum| -|language| language |ThetaRepository.LanguageEnum| -|maxrecordabletime| maximum recording time|ThetaRepository.MaxRecordableTimeEnum| -|offdelay| power off time|ThetaRepository.Off DelayEnum,OffDelaySec| -|sleepdelay| sleep time | ThetaRepository.SleepDelayEnum,SleepDelaySec| -|remainingpictures| number of remaining images |Int| -|remainingvideoseconds| remaining recording seconds|Int| -|remainingspace| remaining area |Long| -|totalspace| total area |Long| -|shuttervolume| shutter volume |Int| -|whitebalance| white balance|ThetaRepository.WhiteBalanceEnum| +| Value | Meaning | Type | +| --------------------- | --------------------------- | -------------------------------------------- | +| aperture | aperture | ThetaRepository.ApertureEnum | +| capturemode | capture mode | ThetaRepository.CaptureModeEnum | +| colortemperature | colortemperature | Int | +| datetimezone | date and time | String | +| exposurecompensation | exposure compensation | ThetaRepository.ExposureCompensationEnum | +| exposuredelay | exposure delay time | ThetaRepository.ExposureDelayEnum | +| exposureprogram | exposure program | ThetaRepository.ExposureProgram | +| fileformat | file format | ThetaRepository.FileFormatEnum | +| filter | image filter | ThetaRepository.FilterEnum | +| gpsinfo | GPS information | ThetaRepository.GpsInfo | +| isgpson | GPS flag | Bool | +| iso | ISO value | ThetaRepository.IsoEnum | +| isoautohighlimit | ISO upper limit | ThetaRepository.IsoAutoHighLimitEnum | +| language | language | ThetaRepository.LanguageEnum | +| maxrecordabletime | maximum recording time | ThetaRepository.MaxRecordableTimeEnum | +| offdelay | power off time | ThetaRepository.Off DelayEnum,OffDelaySec | +| sleepdelay | sleep time | ThetaRepository.SleepDelayEnum,SleepDelaySec | +| remainingpictures | number of remaining images | Int | +| remainingvideoseconds | remaining recording seconds | Int | +| remainingspace | remaining area | Long | +| totalspace | total area | Long | +| shuttervolume | shutter volume | Int | +| whitebalance | white balance | ThetaRepository.WhiteBalanceEnum | ## List still images and videos in THETA @@ -948,33 +958,32 @@ The return type of `ThetaRepository.listFiles()` is `ThetaRepository.ThetaFiles` The `fileType` is the `ThetaRepository.FileTypeEnum` type, whose contents are as follows: -* ThetaRepository.FileTypeEnum - -|Value|Content| -|---|---| -|image| List of still images (JPEG files)| -|video| List of videos (MP4 files)| -|all| List all files| +- ThetaRepository.FileTypeEnum -* ThetaFiles +| Value | Content | +| ----- | --------------------------------- | +| image | List of still images (JPEG files) | +| video | List of videos (MP4 files) | +| all | List all files | - |Property name|Type|Contents| - |---|---|---| - |fileList|[ThetaRepository.FileInfo]|The list of files in THETA| - |totalEntries|Int32| Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) +- ThetaFiles + | Property name | Type | Contents | + | ------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | [ThetaRepository.FileInfo] | The list of files in THETA | + | totalEntries | Int32 | Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) | -* ThetaRepository.FileInfo +- ThetaRepository.FileInfo -|Property name|Type|Contents| -|---|---|---| -|name| String| Represents the file name| -|size| Long| Indicates the file size (in bytes)| -|dateTime| String| Shooting date and time (YYYY:MM:DD HH:MM:SS)| -|fileUrl| String| Represents the URL of the file| -|thumbnailUrl| String| Represents a thumbnail URL| +| Property name | Type | Contents | +| ------------- | ------ | -------------------------------------------- | +| name | String | Represents the file name | +| size | Long | Indicates the file size (in bytes) | +| dateTime | String | Shooting date and time (YYYY:MM:DD HH:MM:SS) | +| fileUrl | String | Represents the URL of the file | +| thumbnailUrl | String | Represents a thumbnail URL | -``` swift +```swift Task { do { let listFiles: [ThetaRepository.FileInfo] = @@ -1001,7 +1010,7 @@ Task { Use `URLSession` to retrieve data from the URL of still images (JPEG file) or videos (MP4 file) shot. You can download it as `Data` using the following functions. -``` swift +```swift func download(url: String) async throws -> Data { return try await withCheckedThrowingContinuation {continuation in URLSession.shared.dataTask(with: URL(string: url)!) {data, _, error in @@ -1044,7 +1053,7 @@ Task { To reset the connected THETA, call `ThetaRepository.reset(completionHandler:)`. -``` swift +```swift Task { do { let _:Bool = try await withCheckedThrowingContinuation {continuation in @@ -1068,18 +1077,18 @@ To obtain the information about the connected THETA, call `ThetaRepository.getTh Successful calls allow you to obtain `ThetaRepository.ThetaInfo`. This data contains the following information: -* ThetaRepository.ThetaInfo +- ThetaRepository.ThetaInfo -|Property name|Type|Contents| -|---|---|---| -|firmwareVersion|String|Represents the firmware version| -|hasGps|Bool|Indicates whether you have a GPS function.| -|hasGyro|Bool|Indicates whether you have a gyro.| -|model|String|Indicates the model number of THETA.| -|serialNumber|String|Represents the THETA serial number| -|uptime|Integer|This means the number of seconds since the THETA power was turned on.| +| Property name | Type | Contents | +| --------------- | ------- | --------------------------------------------------------------------- | +| firmwareVersion | String | Represents the firmware version | +| hasGps | Bool | Indicates whether you have a GPS function. | +| hasGyro | Bool | Indicates whether you have a gyro. | +| model | String | Indicates the model number of THETA. | +| serialNumber | String | Represents the THETA serial number | +| uptime | Integer | This means the number of seconds since the THETA power was turned on. | -``` swift +```swift Task { do { let thetaInfo: ThetaRepository.ThetaInfo = try await withCheckedThrowingContinuation {continuation in @@ -1105,27 +1114,27 @@ To get the state of the connected THETA, call `ThetaRepository.getThetaState(com Successful calling allows you to get `ThetaRepository.ThetaState`. This data contains the following information: -* ThetaRepository.ThetaState +- ThetaRepository.ThetaState -|Property name|Type|Contents| -|---|---|---| -|batteryLevel|Float|Battery level | -|chargingState|ThetaRepository.ChargingStateEnum|Indicates charging status| -|fingerprint|String|Indicates a unique identifier for each current state| -|isSdCard|Bool|Indicates whether an SD card exists.| -|latestFileUrl|String|Represents the URL of the last acquired media| -|recordableTime|Int32_t|Indicates the number of recordable seconds.| -|recordedTime|Int32_t|Indicates the number of recorded seconds| +| Property name | Type | Contents | +| -------------- | --------------------------------- | ---------------------------------------------------- | +| batteryLevel | Float | Battery level | +| chargingState | ThetaRepository.ChargingStateEnum | Indicates charging status | +| fingerprint | String | Indicates a unique identifier for each current state | +| isSdCard | Bool | Indicates whether an SD card exists. | +| latestFileUrl | String | Represents the URL of the last acquired media | +| recordableTime | Int32_t | Indicates the number of recordable seconds. | +| recordedTime | Int32_t | Indicates the number of recorded seconds | -* ThetaRepository.ChargingStateEnum +- ThetaRepository.ChargingStateEnum -|Property name|Details| -|---|---| -|charging|Indicates charging in progress.| -|charged|Indicates completion of charging.| -|notCharging|Indicates disconnection of the cable.| +| Property name | Details | +| ------------- | ------------------------------------- | +| charging | Indicates charging in progress. | +| charged | Indicates completion of charging. | +| notCharging | Indicates disconnection of the cable. | -``` swift +```swift Task { do { let thetaState: ThetaRepository.ThetaState = try await withCheckedThrowingContinuation {continuation in diff --git a/docs/tutorial-react-native.ja.md b/docs/tutorial-react-native.ja.md index a0f5f7772b..16a689b9fd 100644 --- a/docs/tutorial-react-native.ja.md +++ b/docs/tutorial-react-native.ja.md @@ -1,18 +1,18 @@ -# RICOH360 THETA Clientチュートリアル +# RICOH360 THETA Client チュートリアル ## 使用可能な機種 -* RICOH THETA X -* RICOH THETA Z1 -* RICOH THETA V -* RICOH THETA S (ファームウェアv1.62以降のみ) -* RICOH THETA SC +- RICOH THETA X +- RICOH THETA Z1 +- RICOH THETA V +- RICOH THETA S (ファームウェア v1.62 以降のみ) +- RICOH THETA SC ## 事前準備 -本SDKを使用したアプリケーションが動作するスマートフォンとTHETAを無線LAN接続しておきます。 +本 SDK を使用したアプリケーションが動作するスマートフォンと THETA を無線 LAN 接続しておきます。 -## THETA Clientの初期化 +## THETA Client の初期化 ```Typescript import {initialize} from 'theta-client-react-native'; @@ -36,18 +36,19 @@ initialize('http://:<ポート番号>') }); ``` -* THETA IP ADDRESS - | モード | アドレス | - |-------|---------| - |ダイレクトモード| 192.168.1.1 | - |その他| カメラのIPアドレス| +- THETA IP ADDRESS -* Thetaから画像や動画をダウンロードする場合は、plainな接続となりますので、接続先のアドレス(デフォルト192.168.1.1)に応じて各プラットフォーム毎の設定が必要になります。 + | モード | アドレス | + | ---------------- | -------------------- | + | ダイレクトモード | 192.168.1.1 | + | その他 | カメラの IP アドレス | - * iOS: デフォルトの場合のInfo.plistの例を示します。なお、Xcodeの`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 +- Theta から画像や動画をダウンロードする場合は、plain な接続となりますので、接続先のアドレス(デフォルト 192.168.1.1)に応じて各プラットフォーム毎の設定が必要になります。 - ``` xml + - iOS: デフォルトの場合の Info.plist の例を示します。なお、Xcode の`Signing&Capabilities`→`App Transport Security Exception`でも追加設定することができます。 + + ```xml @@ -73,7 +74,7 @@ initialize('http://:<ポート番号>') ``` - * Android: res/xml/network_security_config.xmlに記述するデフォルトの場合の例を示します。 + - Android: res/xml/network_security_config.xml に記述するデフォルトの場合の例を示します。 ```xml @@ -89,7 +90,7 @@ initialize('http://:<ポート番号>') まず`getPhotoCaptureBuilder()`を使って撮影設定を行い、`PhotoCapture`オブジェクトを生成します。 -``` Typescript +```Typescript import { getPhotoCaptureBuilder, IsoAutoHighLimitEnum, @@ -108,13 +109,13 @@ getPhotoCaptureBuilder() }); ``` -上の例ではISO感度の最大値を1000に、ファイルフォーマットをIMAGE_5Kに設定しています。 +上の例では ISO 感度の最大値を 1000 に、ファイルフォーマットを IMAGE_5K に設定しています。 プレビューを表示する方法は[プレビューを表示する](#プレビューを表示する)をご覧ください。 次に`PhotoCapture.takePicture()`を呼んで静止画を撮影します。 -``` Typescript +```Typescript photoCapture.takePicture() .then(fileUrl => { // fileUrl をGETリクエストを送信してJPEGファイルを受け取る処理 @@ -127,175 +128,176 @@ photoCapture.takePicture() ### 静止画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(value:ExposureCompensationEnum) - - | 値 | 補正値 |備考| - |---|---:|---| - |M_2_0|-2.0f|| - |M_1_7|-1.7f|| - |M_1_3|-1.0f|| - |M_0_7|-0.7f|| - |M_0_3|-0.3f|| - |ZERO|0.0f|デフォルト| - |P_0_3|0.3f|| - |P_0_7|0.7f|| - |P_1_3|1.0f|| - |P_1_7|1.7f|| - |P_2_0|2.0f|| - -* 露出遅延設定 - setExposureDelay(delay:ExposureDelayEnum) - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |DELAY_OFF|0|デフォルト| - |DELAY_1|1|| - |DELAY_2|2|| - |DELAY_3|3|| - |DELAY_4|4|| - |DELAY_5|5|| - |DELAY_6|6|| - |DELAY_7|7|| - |DELAY_8|8|| - |DELAY_9|9|| - |DELAY_10|10|| - -* 露出プログラム - setExposureProgram(program:ExposureProgramEnum) - - | 値 | 内容 |備考| - |---|---|---| - |MANUAL|手動|| - |NORMAL_PROGRAM|通常のプログラム|| - |APERTURE_PRIORITY|絞り優先|| - |SHUTTER_PRIORITY|シャッター優先|| - |ISO_PRIORITY|ISO優先|| - -* ファイルフォーマット - setFileFormat(fileFormat:PhotoFileFormatEnum) - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |IMAGE_2K|jpeg|2048|1024|○|○|×|×|×| - |IMAGE_5K|jpeg|5376|2688|○|○|○|×|×| - |IMAGE_6_7K|jpeg|6720|3360|×|×|×|○|×| - |RAW_P_6_7K|raw+|6720|3360|×|×|×|○|×| - |IMAGE_5_5K|jpeg|5504|2752|×|×|×|×|○| - |IMAGE_11K|jpeg|11008|5504|×|×|×|×|○| - -* 画像処理 - setFilter(filter:FilterEnum) - - | 値 | 内容 | 備考 - |---|---|---| - |OFF|なし|| - |NOISE_REDUCTION|ノイズ軽減|| - |HDR|HDR|デフォルト| - -* GPSオン/オフ - setGpsTagRecording(value:GpsTagRecordingEnum) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |ON|GPSあり|デフォルト| - |OFF|GPSなし|| - -* ISO値 - setIso(iso:IsoEnum) - - | 値 | ISO値 |備考| - |---|---:|---| - |ISO_AUTO|0|| - |ISO_50|50|| - |ISO_64|64|| - |ISO_80|80|| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(iso:IsoAutoHighLimitEnum) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* 絞り設定 - setAperture(aperture:ApertureEnum) - - | 値 | 設定値 |備考| - |---|---:|---| - |APERTURE_AUTO|自動|| - |APERTURE_20|2.0f|| - |APERTURE_21|2.1f|| - |APERTURE_24|2.4f|| - |APERTURE_35|3.5f|| - |APERTURE_56|5.6f|| - -* 色温度設定 - setColorTemperature(kelvin:number) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(gpsInfo:GpsInfo) - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(whiteBalance:WhiteBalanceEnum) - - | 値 | 設定値 |備考| - |---|---|---| - |AUTO|自動|| - |DAYLIGHT|Outdoor|約5,200,000| - |SHADE|Shade|約7,000,000| - |CLOUDY_DAYLIGHT|Cloudy|約6,000,000| - |INCANDESCENT|Incandescent light 1|約3,200,000| - |WARM_WHITE_FLUORESCENT|Incandescent light 2|| - |DAYLIGHT_FLUORESCENT|Fluorescent light 1(daylight)|| - |DAYWHITE_FLUORESCENT|Fluorescent light 2(natural white)|| - |FLUORESCENT|Fluorescent light 3 (white)|約4,000,000| - |BULB_FLUORESCENT|Fluorescent light 4 (light bulb color)|| - |COLOR_TEMPERATURE|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |UNDERWATER|Underwater|RICOH THETA V firmware v3.21.1 or later| +- 露出補正 - setExposureCompensation(value:ExposureCompensationEnum) + + | 値 | 補正値 | 備考 | + | ----- | -----: | ---------- | + | M_2_0 | -2.0f | | + | M_1_7 | -1.7f | | + | M_1_3 | -1.0f | | + | M_0_7 | -0.7f | | + | M_0_3 | -0.3f | | + | ZERO | 0.0f | デフォルト | + | P_0_3 | 0.3f | | + | P_0_7 | 0.7f | | + | P_1_3 | 1.0f | | + | P_1_7 | 1.7f | | + | P_2_0 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(delay:ExposureDelayEnum) + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | --------- | -----------: | ---------- | + | DELAY_OFF | 0 | デフォルト | + | DELAY_1 | 1 | | + | DELAY_2 | 2 | | + | DELAY_3 | 3 | | + | DELAY_4 | 4 | | + | DELAY_5 | 5 | | + | DELAY_6 | 6 | | + | DELAY_7 | 7 | | + | DELAY_8 | 8 | | + | DELAY_9 | 9 | | + | DELAY_10 | 10 | | + +- 露出プログラム - setExposureProgram(program:ExposureProgramEnum) + + | 値 | 内容 | 備考 | + | ----------------- | ---------------- | ---- | + | MANUAL | 手動 | | + | NORMAL_PROGRAM | 通常のプログラム | | + | APERTURE_PRIORITY | 絞り優先 | | + | SHUTTER_PRIORITY | シャッター優先 | | + | ISO_PRIORITY | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(fileFormat:PhotoFileFormatEnum) + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | ---------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | IMAGE_2K | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | IMAGE_5K | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | IMAGE_6_7K | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | RAW_P_6_7K | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | IMAGE_5_5K | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | IMAGE_11K | jpeg | 11008 | 5504 | × | × | × | × | ○ | + +- 画像処理 - setFilter(filter:FilterEnum) + + | 値 | 内容 | 備考 | + | --------------- | ---------- | ---------- | + | OFF | なし | | + | NOISE_REDUCTION | ノイズ軽減 | | + | HDR | HDR | デフォルト | + +- GPS オン/オフ - setGpsTagRecording(value:GpsTagRecordingEnum) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | ON | GPS あり | デフォルト | + | OFF | GPS なし | | + +- ISO 値 - setIso(iso:IsoEnum) + + | 値 | ISO 値 | 備考 | + | -------- | -----: | ---- | + | ISO_AUTO | 0 | | + | ISO_50 | 50 | | + | ISO_64 | 64 | | + | ISO_80 | 80 | | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(iso:IsoAutoHighLimitEnum) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | -------- | ---------: | ---- | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- 絞り設定 - setAperture(aperture:ApertureEnum) + + | 値 | 設定値 | 備考 | + | ------------- | -----: | ---- | + | APERTURE_AUTO | 自動 | | + | APERTURE_20 | 2.0f | | + | APERTURE_21 | 2.1f | | + | APERTURE_24 | 2.4f | | + | APERTURE_35 | 3.5f | | + | APERTURE_56 | 5.6f | | + +- 色温度設定 - setColorTemperature(kelvin:number) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(gpsInfo:GpsInfo) + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(whiteBalance:WhiteBalanceEnum) + + | 値 | 設定値 | 備考 | + | ---------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | AUTO | 自動 | | + | DAYLIGHT | Outdoor | 約 5,200,000 | + | SHADE | Shade | 約 7,000,000 | + | CLOUDY_DAYLIGHT | Cloudy | 約 6,000,000 | + | INCANDESCENT | Incandescent light 1 | 約 3,200,000 | + | WARM_WHITE_FLUORESCENT | Incandescent light 2 | | + | DAYLIGHT_FLUORESCENT | Fluorescent light 1(daylight) | | + | DAYWHITE_FLUORESCENT | Fluorescent light 2(natural white) | | + | FLUORESCENT | Fluorescent light 3 (white) | 約 4,000,000 | + | BULB_FLUORESCENT | Fluorescent light 4 (light bulb color) | | + | COLOR_TEMPERATURE | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | UNDERWATER | Underwater | RICOH THETA V firmware v3.21.1 or later | ## 動画を撮影する まず`getVideoCaptureBuilder()`を使って撮影設定を行い、`VideoCapture`オブジェクトを生成します。 -``` Typescript +```Typescript import { getVideoCaptureBuilder, IsoAutoHighLimitEnum, @@ -314,220 +316,223 @@ getVideoCaptureBuilder() }); ``` -上の例ではISO感度の最大値を800に、ファイルフォーマットを`VIDEO_HD`に設定しています。 +上の例では ISO 感度の最大値を 800 に、ファイルフォーマットを`VIDEO_HD`に設定しています。 表示方法は[プレビューを表示する](#プレビューを表示する)をご覧ください 次に`VideoCapture.startCapture()`を呼んで動画の撮影を開始します。 -``` Typescript -videoCapture.startCapture() - .then(fileUrl => { - // GETリクエストを送信してMP4ファイルを受け取る処理 +```Typescript +VideoCapture + .startCapture((error) => { + // handle error of stopCapture }) - .catch(error => { - // handle error + .then((fileUrl) => { + // send GET requests and receiving MP4 files + }) + .catch((error) => { + // handle error of startCapture }); ``` -次に`VideoCapture.stopCapture()`を呼び出して動画の撮影を終了します。成功すると上記の通り、撮影したファイルのURLを引数にthenが呼び出されます。 +次に`VideoCapture.stopCapture()`を呼び出して動画の撮影を終了します。成功すると上記の通り、撮影したファイルの URL を引数に then が呼び出されます。 -``` Typescript +```Typescript videoCapture.stopCapture(); ``` ### 動画撮影時に設定できる項目 -* 露出補正 - setExposureCompensation(value:ExposureCompensationEnum) - - | 値 | 補正値 |備考| - |---|---:|---| - |M_2_0|-2.0f|| - |M_1_7|-1.7f|| - |M_1_3|-1.0f|| - |M_0_7|-0.7f|| - |M_0_3|-0.3f|| - |ZERO|0.0f|デフォルト| - |P_0_3|0.3f|| - |P_0_7|0.7f|| - |P_1_3|1.0f|| - |P_1_7|1.7f|| - |P_2_0|2.0f|| - -* 露出遅延設定 - setExposureDelay(delay:ExposureDelayEnum) - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |DELAY_OFF|0|デフォルト| - |DELAY_1|1|| - |DELAY_2|2|| - |DELAY_3|3|| - |DELAY_4|4|| - |DELAY_5|5|| - |DELAY_6|6|| - |DELAY_7|7|| - |DELAY_8|8|| - |DELAY_9|9|| - |DELAY_10|10|| - -* 露出プログラム - setExposureProgram(program:ExposureProgramEnum) - - | 値 | 内容 |備考| - |---|---|---| - |MANUAL|手動|| - |NORMAL_PROGRAM|通常のプログラム|| - |APERTURE_PRIORITY|絞り優先|| - |SHUTTER_PRIORITY|シャッター優先|| - |ISO_PRIORITY|ISO優先|| - -* ファイルフォーマット - setFileFormat(fileFormat:VideoFileFormatEnum) - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |VIDEO_HD|mp4|1280|570|||○|○|×|×|×| - |VIDEO_FULL_Hd|mp4|1920|1080|||○|○|×|×|×| - |VIDEO_2K|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |VIDEO_4K|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |VIDEO_2K_30F|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_2K_60F|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_4K_30F|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_4K_60F|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_2F|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_5F|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_30F|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_2F|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_5F|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_10F|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 最大録画時間設定 - setMaxRecordableTime(time:MaxRecordableTimeEnum) - - | 値 | 内容 | 備考 - |---|---|---| - |RECORDABLE_TIME300|300秒|| - |RECORDABLE_TIME1500|1500秒|| - -* GPSオン/オフ - setGpsTagRecording(value:GpsTagRecordingEnum) - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |ON|GPSあり|デフォルト| - |OFF|GPSなし|| - -* ISO値 - setIso(iso:IsoEnum) - - | 値 | ISO値 |備考| - |---|---:|---| - |ISO_AUTO|0|| - |ISO_50|50|| - |ISO_64|64|| - |ISO_80|80|| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* ISO上限 - setIsoAutoHighLimit(iso:IsoAutoHighLimitEnum) - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* 絞り設定 - setAperture(aperture:ApertureEnum) - - | 値 | 設定値 |備考| - |---|---:|---| - |APERTURE_AUTO|自動|| - |APERTURE_20|2.0f|| - |APERTURE_21|2.1f|| - |APERTURE_24|2.4f|| - |APERTURE_35|3.5f|| - |APERTURE_56|5.6f|| - -* 色温度設定 - setColorTemperature(kelvin:number) - * 2500 ~ 10000 - -* GPS 情報 - setGpsInfo(gpsInfo:GpsInfo) - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - setWhiteBalance(whiteBalance:WhiteBalanceEnum) - - | 値 | 設定値 |備考| - |---|---|---| - |AUTO|自動|| - |DAYLIGHT|Outdoor|約5,200,000| - |SHADE|Shade|約7,000,000| - |CLOUDY_DAYLIGHT|Cloudy|約6,000,000| - |INCANDESCENT|Incandescent light 1|約3,200,000| - |WARM_WHITE_FLUORESCENT|Incandescent light 2|| - |DAYLIGHT_FLUORESCENT|Fluorescent light 1(daylight)|| - |DAYWHITE_FLUORESCENT|Fluorescent light 2(natural white)|| - |FLUORESCENT|Fluorescent light 3 (white)|約4,000,000| - |BULB_FLUORESCENT|Fluorescent light 4 (light bulb color)|| - |COLOR_TEMPERATURE|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |UNDERWATER|Underwater|RICOH THETA V firmware v3.21.1 or later| - +- 露出補正 - setExposureCompensation(value:ExposureCompensationEnum) + + | 値 | 補正値 | 備考 | + | ----- | -----: | ---------- | + | M_2_0 | -2.0f | | + | M_1_7 | -1.7f | | + | M_1_3 | -1.0f | | + | M_0_7 | -0.7f | | + | M_0_3 | -0.3f | | + | ZERO | 0.0f | デフォルト | + | P_0_3 | 0.3f | | + | P_0_7 | 0.7f | | + | P_1_3 | 1.0f | | + | P_1_7 | 1.7f | | + | P_2_0 | 2.0f | | + +- 露出遅延設定 - setExposureDelay(delay:ExposureDelayEnum) + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | --------- | -----------: | ---------- | + | DELAY_OFF | 0 | デフォルト | + | DELAY_1 | 1 | | + | DELAY_2 | 2 | | + | DELAY_3 | 3 | | + | DELAY_4 | 4 | | + | DELAY_5 | 5 | | + | DELAY_6 | 6 | | + | DELAY_7 | 7 | | + | DELAY_8 | 8 | | + | DELAY_9 | 9 | | + | DELAY_10 | 10 | | + +- 露出プログラム - setExposureProgram(program:ExposureProgramEnum) + + | 値 | 内容 | 備考 | + | ----------------- | ---------------- | ---- | + | MANUAL | 手動 | | + | NORMAL_PROGRAM | 通常のプログラム | | + | APERTURE_PRIORITY | 絞り優先 | | + | SHUTTER_PRIORITY | シャッター優先 | | + | ISO_PRIORITY | ISO 優先 | | + +- ファイルフォーマット - setFileFormat(fileFormat:VideoFileFormatEnum) + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | -------------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | VIDEO_HD | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | VIDEO_FULL_Hd | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | VIDEO_2K | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | VIDEO_4K | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | VIDEO_2K_30F | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_2K_60F | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_4K_30F | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_4K_60F | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_2F | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_5F | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_30F | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_2F | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_5F | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_10F | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 最大録画時間設定 - setMaxRecordableTime(time:MaxRecordableTimeEnum) + + | 値 | 内容 | 備考 | + | ------------------- | ------- | ---- | + | RECORDABLE_TIME300 | 300 秒 | | + | RECORDABLE_TIME1500 | 1500 秒 | | + +- GPS オン/オフ - setGpsTagRecording(value:GpsTagRecordingEnum) + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | --- | -------- | ---------- | + | ON | GPS あり | デフォルト | + | OFF | GPS なし | | + +- ISO 値 - setIso(iso:IsoEnum) + + | 値 | ISO 値 | 備考 | + | -------- | -----: | ---- | + | ISO_AUTO | 0 | | + | ISO_50 | 50 | | + | ISO_64 | 64 | | + | ISO_80 | 80 | | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- ISO 上限 - setIsoAutoHighLimit(iso:IsoAutoHighLimitEnum) + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | -------- | ---------: | ---- | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- 絞り設定 - setAperture(aperture:ApertureEnum) + + | 値 | 設定値 | 備考 | + | ------------- | -----: | ---- | + | APERTURE_AUTO | 自動 | | + | APERTURE_20 | 2.0f | | + | APERTURE_21 | 2.1f | | + | APERTURE_24 | 2.4f | | + | APERTURE_35 | 3.5f | | + | APERTURE_56 | 5.6f | | + +- 色温度設定 - setColorTemperature(kelvin:number) + + - 2500 ~ 10000 + +- GPS 情報 - setGpsInfo(gpsInfo:GpsInfo) + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - setWhiteBalance(whiteBalance:WhiteBalanceEnum) + + | 値 | 設定値 | 備考 | + | ---------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | AUTO | 自動 | | + | DAYLIGHT | Outdoor | 約 5,200,000 | + | SHADE | Shade | 約 7,000,000 | + | CLOUDY_DAYLIGHT | Cloudy | 約 6,000,000 | + | INCANDESCENT | Incandescent light 1 | 約 3,200,000 | + | WARM_WHITE_FLUORESCENT | Incandescent light 2 | | + | DAYLIGHT_FLUORESCENT | Fluorescent light 1(daylight) | | + | DAYWHITE_FLUORESCENT | Fluorescent light 2(natural white) | | + | FLUORESCENT | Fluorescent light 3 (white) | 約 4,000,000 | + | BULB_FLUORESCENT | Fluorescent light 4 (light bulb color) | | + | COLOR_TEMPERATURE | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | UNDERWATER | Underwater | RICOH THETA V firmware v3.21.1 or later | ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 -| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | -| ---- | --: | --: | -----------: | ---- | -| THETA X | 1024 | 512 | 30 | | -| THETA Z1 | 1024 | 512 | 30 | | -| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1以降 | -| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1以前 | -| THETA S | 640 | 320 | 10 | | -| THETA SC | 640 | 320 | 10 | | +| 機種 | 横(pixel) | 縦(pixel) | フレームレート(fps) | 備考 | +| -------- | --------: | --------: | ------------------: | --------------------------- | +| THETA X | 1024 | 512 | 30 | | +| THETA Z1 | 1024 | 512 | 30 | | +| THETA V | 1024 | 512 | 30 | ファームウェア v2.21.1 以降 | +| THETA V | 1024 | 512 | 8 | ファームウェア v2.20.1 以前 | +| THETA S | 640 | 320 | 10 | | +| THETA SC | 640 | 320 | 10 | | -`getLivePreview()`を呼ぶと、プレビューの各フレームの受信が完了する度に、イベントTHETA_FRAME_EVENTが発生します。 -イベントデータにはフレームデータ(JPEG)のDATA URLが渡されます。 +`getLivePreview()`を呼ぶと、プレビューの各フレームの受信が完了する度に、イベント THETA_FRAME_EVENT が発生します。 +イベントデータにはフレームデータ(JPEG)の DATA URL が渡されます。 プレビューを終了する場合は`stopLivePreview()`を呼び出します。 -``` Typescript +```Typescript import * as React from 'react'; import {NativeModules, NativeEventEmitter} from 'react-native'; import { @@ -564,6 +569,7 @@ const LivePreview = () => { ); }; ``` + ## カメラを設定する カメラを設定するには、設定したい内容を`Options`に設定して`SetOptions()`を呼び出します。 @@ -589,259 +595,262 @@ setOptions(options) `Options`に設定できる項目と内容は以下を参照してください。 -* 日付時刻 - dateTimeZone:string +- 日付時刻 - dateTimeZone:string 形式: YYYY:MM:DD hh:mm:ss+(-)hh:mm hh 0-23、+(-)hh:mm は、タイムゾーン 例: 2014:05:18 01:04:29+08:00 -* 露出補正 - exposureCompensation:ExposureCompensationEnum - - | 値 | 補正値 |備考| - |---|---:|---| - |M_2_0|-2.0f|| - |M_1_7|-1.7f|| - |M_1_3|-1.0f|| - |M_0_7|-0.7f|| - |M_0_3|-0.3f|| - |ZERO|0.0f|デフォルト| - |P_0_3|0.3f|| - |P_0_7|0.7f|| - |P_1_3|1.0f|| - |P_1_7|1.7f|| - |P_2_0|2.0f|| - -* 露出遅延設定 - exposureDelay:ExposureDelayEnum - takePictureコマンドと露出開始間の遅延時間(=セルフタイマー) - - | 値 | 遅延時間(秒) |備考| - |---|---:|---| - |DELAY_OFF|0|デフォルト| - |DELAY_1|1|| - |DELAY_2|2|| - |DELAY_3|3|| - |DELAY_4|4|| - |DELAY_5|5|| - |DELAY_6|6|| - |DELAY_7|7|| - |DELAY_8|8|| - |DELAY_9|9|| - |DELAY_10|10|| - -* 露出プログラム - exposureProgram:ExposureProgramEnum - - | 値 | 内容 |備考| - |---|---|---| - |MANUAL|手動|| - |NORMAL_PROGRAM|通常のプログラム|| - |APERTURE_PRIORITY|絞り優先|| - |SHUTTER_PRIORITY|シャッター優先|| - |ISO_PRIORITY|ISO優先|| - -* ファイルフォーマット - fileFormat:PhotoFileFormatEnum|VideoFileFormatEnum - - * PhotoFileFormatEnum - - | 値 | タイプ| 幅 | 高さ |S|SC|V|Z1|X| - |---|---|--:|--:|:-:|:-:|:-:|:-:|:-:| - |IMAGE_2K|jpeg|2048|1024|○|○|×|×|×| - |IMAGE_5K|jpeg|5376|2688|○|○|○|×|×| - |IMAGE_6_7K|jpeg|6720|3360|×|×|×|○|×| - |RAW_P_6_7K|raw+|6720|3360|×|×|×|○|×| - |IMAGE_5_5K|jpeg|5504|2752|×|×|×|×|○| - |IMAGE_11K|jpeg|11008|5504|×|×|×|×|○| - - * VideoFileFormatEnum - - | 値 | タイプ| 幅 | 高さ |フレームレート|Codec|S|SC|V|Z1|X| - |---|---|--:|--:|--:|--|:-:|:-:|:-:|:-:|:-:| - |VIDEO_HD|mp4|1280|570|||○|○|×|×|×| - |VIDEO_FULL_Hd|mp4|1920|1080|||○|○|×|×|×| - |VIDEO_2K|mp4|1920|960||H.264/MPEG-4 AVC|×|×|○|○|×| - |VIDEO_4K|mp4|3840|1920||H.264/MPEG-4 AVC|×|×|○|○|×| - |VIDEO_2K_30F|mp4|1920|960|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_2K_60F|mp4|1920|960|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_4K_30F|mp4|3840|1920|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_4K_60F|mp4|3840|1920|60|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_2F|mp4|5760|2880|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_5F|mp4|5760|2880|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_5_7K_30F|mp4|5760|2880|30|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_2F|mp4|7680|3840|2|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_5F|mp4|7680|3840|5|H.264/MPEG-4 AVC|×|×|×|×|○| - |VIDEO_7K_10F|mp4|7680|3840|10|H.264/MPEG-4 AVC|×|×|×|×|○| - -* 画像処理 - filter:FilterEnum - - | 値 | 内容 | 備考 - |---|---|---| - |OFF|なし|| - |NOISE_REDUCTION|ノイズ軽減|| - |HDR|HDR|デフォルト| - -* GPSオン/オフ - isGpsOn:boolean - THETA X以外は指定しても無視される - - | 値 | 内容 | 備考 - |---|---|---| - |true|GPSあり|デフォルト| - |false|GPSなし|| - -* ISO値 - iso:IsoEnum - - | 値 | ISO値 |備考| - |---|---:|---| - |ISO_AUTO|0|| - |ISO_50|50|| - |ISO_64|64|| - |ISO_80|80|| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* ISO上限 - isoAutoHighLimit:IsoAutoHighLimitEnum - THETA V ファームウェア v2.50.1以前、THETA S、THETA SCでは指定しても無視される - - | 値 | ISO上限値 |備考| - |---|---:|---| - |ISO_100|100|| - |ISO_125|125|| - |ISO_160|160|| - |ISO_200|200|| - |ISO_250|250|| - |ISO_320|320|| - |ISO_400|400|| - |ISO_500|150|| - |ISO_640|640|| - |ISO_800|800|| - |ISO_1000|1000|| - |ISO_1250|1250|| - |ISO_1600|1600|| - |ISO_2000|2000|| - |ISO_2500|2500|| - |ISO_3200|3200|| - |ISO_4000|4000|| - |ISO_5000|5000|| - |ISO_6400|6400|| - -* 絞り設定 - aperture:ApertureEnum - - | 値 | 設定値 |備考| - |---|---:|---| - |APERTURE_AUTO|自動|| - |APERTURE_20|2.0f|| - |APERTURE_21|2.1f|| - |APERTURE_24|2.4f|| - |APERTURE_35|3.5f|| - |APERTURE_56|5.6f|| - -* 色温度設定 - colorTemperature:number - * 2500 ~ 10000 - -* GPS 情報 - gpsInfo:GpsInfo - GpsInfoは以下の内容のObjectとして作成する。 - - | 値 | 設定値 |備考| - |---|---|---| - |latitude|緯度|65535でオフ| - |longitude|経度|65535でオフ| - |altitude|高度|| - |dateTimeZone|日付時刻|| - -* ホワイトバランス - whiteBalance:WhiteBalanceEnum - - | 値 | 設定値 |備考| - |---|---|---| - |AUTO|自動|| - |DAYLIGHT|Outdoor|約5,200,000| - |SHADE|Shade|約7,000,000| - |CLOUDY_DAYLIGHT|Cloudy|約6,000,000| - |INCANDESCENT|Incandescent light 1|約3,200,000| - |WARM_WHITE_FLUORESCENT|Incandescent light 2|| - |DAYLIGHT_FLUORESCENT|Fluorescent light 1(daylight)|| - |DAYWHITE_FLUORESCENT|Fluorescent light 2(natural white)|| - |FLUORESCENT|Fluorescent light 3 (white)|約4,000,000| - |BULB_FLUORESCENT|Fluorescent light 4 (light bulb color)|| - |COLOR_TEMPERATURE|CT settings (specified by the colorTemperature option)|RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later| - |UNDERWATER|Underwater|RICOH THETA V firmware v3.21.1 or later| - -* 最大録画時間設定 - maxRecordableTime:MaxRecordableTimeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |RECORDABLE_TIME300|300秒|| - |RECORDABLE_TIME1500|1500秒|| - -* キャプチャモード - captureMode:CaptureModeEnum - - | 値 | 内容 | 備考 - |---|---|---| - |IMAGE|静止画撮影モード|| - |VIDEO|動画撮影モード|| - -* 言語設定 - language:LanguageEnum -(THETA S, THETA SCでは指定しても無視される) - - |値|内容|備考| - |--|---|---| - |EN_US|米国英語|| - |EN_GB|英国英語|| - |JA|日本語|| - |FR|フランス語|| - |DE|ドイツ語|| - |ZH_TW|中国語台湾|| - |ZH_CN|中国語|| - |IT|イタリア語|| - |KO|韓国語|| - -* スリープモードに入るまでの時間(分) - sleepDelay:SleepDelayEnum (number含む) - * SleepDelayEnum - - |値|設定値(分)|備考| - |--|--:|---| - |DISABLE|0|自動オフしない| - |SLEEP_DELAY_3M|3|| - |SLEEP_DELAY_5M|5|| - |SLEEP_DELAY_7M|7|| - |SLEEP_DELAY_10M|10|| - - * number - * 0〜1800 (秒) - -* スリープから自動電源オフまでの時間(秒) offDelay:OffDelayEnum (number含む) - - * OffDelayEnum - - |値|設定値(秒)|備考| - |--|--:|---| - |DISABLE|65535|自動オフしない| - |OFF_DELAY_5M|300|| - |OFF_DELAY_10M|600|| - |OFF_DELAY_15M|900|| - |OFF_DELAY_30M|1800|| - - * number - * 0〜1800 (秒) デフォルトは3分(180秒) - -* シャッター音 - shutterVolume:number - * 0 - 100 +- 露出補正 - exposureCompensation:ExposureCompensationEnum + + | 値 | 補正値 | 備考 | + | ----- | -----: | ---------- | + | M_2_0 | -2.0f | | + | M_1_7 | -1.7f | | + | M_1_3 | -1.0f | | + | M_0_7 | -0.7f | | + | M_0_3 | -0.3f | | + | ZERO | 0.0f | デフォルト | + | P_0_3 | 0.3f | | + | P_0_7 | 0.7f | | + | P_1_3 | 1.0f | | + | P_1_7 | 1.7f | | + | P_2_0 | 2.0f | | + +- 露出遅延設定 - exposureDelay:ExposureDelayEnum + takePicture コマンドと露出開始間の遅延時間(=セルフタイマー) + + | 値 | 遅延時間(秒) | 備考 | + | --------- | -----------: | ---------- | + | DELAY_OFF | 0 | デフォルト | + | DELAY_1 | 1 | | + | DELAY_2 | 2 | | + | DELAY_3 | 3 | | + | DELAY_4 | 4 | | + | DELAY_5 | 5 | | + | DELAY_6 | 6 | | + | DELAY_7 | 7 | | + | DELAY_8 | 8 | | + | DELAY_9 | 9 | | + | DELAY_10 | 10 | | + +- 露出プログラム - exposureProgram:ExposureProgramEnum + + | 値 | 内容 | 備考 | + | ----------------- | ---------------- | ---- | + | MANUAL | 手動 | | + | NORMAL_PROGRAM | 通常のプログラム | | + | APERTURE_PRIORITY | 絞り優先 | | + | SHUTTER_PRIORITY | シャッター優先 | | + | ISO_PRIORITY | ISO 優先 | | + +- ファイルフォーマット - fileFormat:PhotoFileFormatEnum|VideoFileFormatEnum + + - PhotoFileFormatEnum + + | 値 | タイプ | 幅 | 高さ | S | SC | V | Z1 | X | + | ---------- | ------ | ----: | ---: | :-: | :-: | :-: | :-: | :-: | + | IMAGE_2K | jpeg | 2048 | 1024 | ○ | ○ | × | × | × | + | IMAGE_5K | jpeg | 5376 | 2688 | ○ | ○ | ○ | × | × | + | IMAGE_6_7K | jpeg | 6720 | 3360 | × | × | × | ○ | × | + | RAW_P_6_7K | raw+ | 6720 | 3360 | × | × | × | ○ | × | + | IMAGE_5_5K | jpeg | 5504 | 2752 | × | × | × | × | ○ | + | IMAGE_11K | jpeg | 11008 | 5504 | × | × | × | × | ○ | + + - VideoFileFormatEnum + + | 値 | タイプ | 幅 | 高さ | フレームレート | Codec | S | SC | V | Z1 | X | + | -------------- | ------ | ---: | ---: | -------------: | ---------------- | :-: | :-: | :-: | :-: | :-: | + | VIDEO_HD | mp4 | 1280 | 570 | | | ○ | ○ | × | × | × | + | VIDEO_FULL_Hd | mp4 | 1920 | 1080 | | | ○ | ○ | × | × | × | + | VIDEO_2K | mp4 | 1920 | 960 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | VIDEO_4K | mp4 | 3840 | 1920 | | H.264/MPEG-4 AVC | × | × | ○ | ○ | × | + | VIDEO_2K_30F | mp4 | 1920 | 960 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_2K_60F | mp4 | 1920 | 960 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_4K_30F | mp4 | 3840 | 1920 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_4K_60F | mp4 | 3840 | 1920 | 60 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_2F | mp4 | 5760 | 2880 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_5F | mp4 | 5760 | 2880 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_5_7K_30F | mp4 | 5760 | 2880 | 30 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_2F | mp4 | 7680 | 3840 | 2 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_5F | mp4 | 7680 | 3840 | 5 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + | VIDEO_7K_10F | mp4 | 7680 | 3840 | 10 | H.264/MPEG-4 AVC | × | × | × | × | ○ | + +- 画像処理 - filter:FilterEnum + + | 値 | 内容 | 備考 | + | --------------- | ---------- | ---------- | + | OFF | なし | | + | NOISE_REDUCTION | ノイズ軽減 | | + | HDR | HDR | デフォルト | + +- GPS オン/オフ - isGpsOn:boolean + THETA X 以外は指定しても無視される + + | 値 | 内容 | 備考 | + | ----- | -------- | ---------- | + | true | GPS あり | デフォルト | + | false | GPS なし | | + +- ISO 値 - iso:IsoEnum + + | 値 | ISO 値 | 備考 | + | -------- | -----: | ---- | + | ISO_AUTO | 0 | | + | ISO_50 | 50 | | + | ISO_64 | 64 | | + | ISO_80 | 80 | | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- ISO 上限 - isoAutoHighLimit:IsoAutoHighLimitEnum + THETA V ファームウェア v2.50.1 以前、THETA S、THETA SC では指定しても無視される + + | 値 | ISO 上限値 | 備考 | + | -------- | ---------: | ---- | + | ISO_100 | 100 | | + | ISO_125 | 125 | | + | ISO_160 | 160 | | + | ISO_200 | 200 | | + | ISO_250 | 250 | | + | ISO_320 | 320 | | + | ISO_400 | 400 | | + | ISO_500 | 150 | | + | ISO_640 | 640 | | + | ISO_800 | 800 | | + | ISO_1000 | 1000 | | + | ISO_1250 | 1250 | | + | ISO_1600 | 1600 | | + | ISO_2000 | 2000 | | + | ISO_2500 | 2500 | | + | ISO_3200 | 3200 | | + | ISO_4000 | 4000 | | + | ISO_5000 | 5000 | | + | ISO_6400 | 6400 | | + +- 絞り設定 - aperture:ApertureEnum + + | 値 | 設定値 | 備考 | + | ------------- | -----: | ---- | + | APERTURE_AUTO | 自動 | | + | APERTURE_20 | 2.0f | | + | APERTURE_21 | 2.1f | | + | APERTURE_24 | 2.4f | | + | APERTURE_35 | 3.5f | | + | APERTURE_56 | 5.6f | | + +- 色温度設定 - colorTemperature:number + + - 2500 ~ 10000 + +- GPS 情報 - gpsInfo:GpsInfo + GpsInfo は以下の内容の Object として作成する。 + + | 値 | 設定値 | 備考 | + | ------------ | -------- | ------------ | + | latitude | 緯度 | 65535 でオフ | + | longitude | 経度 | 65535 でオフ | + | altitude | 高度 | | + | dateTimeZone | 日付時刻 | | + +- ホワイトバランス - whiteBalance:WhiteBalanceEnum + + | 値 | 設定値 | 備考 | + | ---------------------- | ------------------------------------------------------ | ---------------------------------------------------------------------------------- | + | AUTO | 自動 | | + | DAYLIGHT | Outdoor | 約 5,200,000 | + | SHADE | Shade | 約 7,000,000 | + | CLOUDY_DAYLIGHT | Cloudy | 約 6,000,000 | + | INCANDESCENT | Incandescent light 1 | 約 3,200,000 | + | WARM_WHITE_FLUORESCENT | Incandescent light 2 | | + | DAYLIGHT_FLUORESCENT | Fluorescent light 1(daylight) | | + | DAYWHITE_FLUORESCENT | Fluorescent light 2(natural white) | | + | FLUORESCENT | Fluorescent light 3 (white) | 約 4,000,000 | + | BULB_FLUORESCENT | Fluorescent light 4 (light bulb color) | | + | COLOR_TEMPERATURE | CT settings (specified by the colorTemperature option) | RICOH THETA S firmware v01.82 or later and RICOH THETA SC firmware v01.10 or later | + | UNDERWATER | Underwater | RICOH THETA V firmware v3.21.1 or later | + +- 最大録画時間設定 - maxRecordableTime:MaxRecordableTimeEnum + + | 値 | 内容 | 備考 | + | ------------------- | ------- | ---- | + | RECORDABLE_TIME300 | 300 秒 | | + | RECORDABLE_TIME1500 | 1500 秒 | | + +- キャプチャモード - captureMode:CaptureModeEnum + + | 値 | 内容 | 備考 | + | ----- | ---------------- | ---- | + | IMAGE | 静止画撮影モード | | + | VIDEO | 動画撮影モード | | + +- 言語設定 - language:LanguageEnum + (THETA S, THETA SC では指定しても無視される) + + | 値 | 内容 | 備考 | + | ----- | ---------- | ---- | + | EN_US | 米国英語 | | + | EN_GB | 英国英語 | | + | JA | 日本語 | | + | FR | フランス語 | | + | DE | ドイツ語 | | + | ZH_TW | 中国語台湾 | | + | ZH_CN | 中国語 | | + | IT | イタリア語 | | + | KO | 韓国語 | | + +- スリープモードに入るまでの時間(分) - sleepDelay:SleepDelayEnum (number 含む) + + - SleepDelayEnum + + | 値 | 設定値(分) | 備考 | + | --------------- | ---------: | -------------- | + | DISABLE | 0 | 自動オフしない | + | SLEEP_DELAY_3M | 3 | | + | SLEEP_DELAY_5M | 5 | | + | SLEEP_DELAY_7M | 7 | | + | SLEEP_DELAY_10M | 10 | | + + - number + - 0〜1800 (秒) + +- スリープから自動電源オフまでの時間(秒) offDelay:OffDelayEnum (number 含む) + + - OffDelayEnum + + | 値 | 設定値(秒) | 備考 | + | ------------- | ---------: | -------------- | + | DISABLE | 65535 | 自動オフしない | + | OFF_DELAY_5M | 300 | | + | OFF_DELAY_10M | 600 | | + | OFF_DELAY_15M | 900 | | + | OFF_DELAY_30M | 1800 | | + + - number + - 0〜1800 (秒) デフォルトは 3 分(180 秒) + +- シャッター音 - shutterVolume:number + - 0 - 100 ## カメラの設定を取得する + カメラの設定を取得するには、取得したいオプションの一覧を指定して`getOptions()`を呼び出します。 ```Typescript @@ -866,65 +875,66 @@ getOptions(optionNames) ``` -* OptionNameEnum は以下の表を参照してください - - | 値 | 意味 |型| - |----|-----|--| - |Aperture|絞り|ApertureEnum| - |CaptureMode|キャプチャーモード|CaptureModeEnum| - |ColorTemperature|色温度|number| - |DateTimeZone|日時|string| - |ExposureCompensation|露出補正|ExposureCompensationEnum| - |ExposureDelay|露出遅延時間|ExposureDelayEnum| - |ExposureProgram|露出プログラム|ExposureProgram| - |FileFormat|ファイルフォーマット|PhotoFileFormatEnum \| VideoFileFormatEnum| - |Filter|画像フィルタ|FilterEnum| - |GpsInfo|GPS情報|GpsInfo| - |IsGpsOn|GPSフラグ|boolean| - |Iso|ISO値|IsoEnum| - |IsoAutoHighLimit|ISO上限|IsoAutoHighLimitEnum| - |Language|言語|LanguageEnum| - |MaxRecordableTime|最長録画時間|MaxRecordableTimeEnum| - |OffDelay|電源オフ時間|OffDelayEnum| - |SleepDelay|スリープ時間|SleepDelayEnum| - |RemainingPictures|残り画像数|number| - |RemainingVideoSeconds|残り録画秒数|number| - |RemainingSpace|残領域|number| - |TotalSpace|合計領域|number| - |ShutterVolume|シャッター音量|number| - |WhiteBalance|ホワイトバランス|WhiteBalanceEnum| - -## THETA内の静止画・動画を一覧する -THETA内の静止画(JPEGファイル)や動画(MP4ファイル)の一覧は`listFiles(fileType:FileTypeEnum, startPosition:number, entryCount:number)`を使って取得できます。 -`listFiles()`の戻り値型は`ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` がTHETA内のファイル一覧です。 +- OptionNameEnum は以下の表を参照してください + + | 値 | 意味 | 型 | + | --------------------- | -------------------- | ------------------------------------------ | + | Aperture | 絞り | ApertureEnum | + | CaptureMode | キャプチャーモード | CaptureModeEnum | + | ColorTemperature | 色温度 | number | + | DateTimeZone | 日時 | string | + | ExposureCompensation | 露出補正 | ExposureCompensationEnum | + | ExposureDelay | 露出遅延時間 | ExposureDelayEnum | + | ExposureProgram | 露出プログラム | ExposureProgram | + | FileFormat | ファイルフォーマット | PhotoFileFormatEnum \| VideoFileFormatEnum | + | Filter | 画像フィルタ | FilterEnum | + | GpsInfo | GPS 情報 | GpsInfo | + | IsGpsOn | GPS フラグ | boolean | + | Iso | ISO 値 | IsoEnum | + | IsoAutoHighLimit | ISO 上限 | IsoAutoHighLimitEnum | + | Language | 言語 | LanguageEnum | + | MaxRecordableTime | 最長録画時間 | MaxRecordableTimeEnum | + | OffDelay | 電源オフ時間 | OffDelayEnum | + | SleepDelay | スリープ時間 | SleepDelayEnum | + | RemainingPictures | 残り画像数 | number | + | RemainingVideoSeconds | 残り録画秒数 | number | + | RemainingSpace | 残領域 | number | + | TotalSpace | 合計領域 | number | + | ShutterVolume | シャッター音量 | number | + | WhiteBalance | ホワイトバランス | WhiteBalanceEnum | + +## THETA 内の静止画・動画を一覧する + +THETA 内の静止画(JPEG ファイル)や動画(MP4 ファイル)の一覧は`listFiles(fileType:FileTypeEnum, startPosition:number, entryCount:number)`を使って取得できます。 +`listFiles()`の戻り値型は`ThetaFiles`で、`ThetaFiles`のプロパティ`fileList` が THETA 内のファイル一覧です。 `fileList`は `FileInfo`のリストです。 -* FileTypeEnum +- FileTypeEnum - |値|内容| - |---|---| - |IMAGE|静止画(JPEGファイル)を一覧| - |VIDEO|動画(MP4ファイル)を一覧| - |ALL|全てのファイルを一覧| + | 値 | 内容 | + | ----- | ----------------------------- | + | IMAGE | 静止画(JPEG ファイル)を一覧 | + | VIDEO | 動画(MP4 ファイル)を一覧 | + | ALL | 全てのファイルを一覧 | -* ThetaFiles +- ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|THETA内のファイル一覧| - |totalEntries|number| THETA内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) + | Property name | Type | Contents | + | ------------- | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | THETA 内のファイル一覧 | + | totalEntries | number | THETA 内のファイル数 ([api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)参照) | -* FileInfo +- FileInfo - |プロパティ名|型|内容| - |---|---|---| - |name|string|ファイル名を表します| - |size|number|ファイルサイズ(バイト数)を表します| - |dateTime|string|撮影日時(YYYY:MM:DD HH:MM:SS)を表します| - |fileUrl|string|ファイルのURLを表します| - |thumbnailUrl|string|サムネールのURLを表します| + | プロパティ名 | 型 | 内容 | + | ------------ | ------ | ----------------------------------------- | + | name | string | ファイル名を表します | + | size | number | ファイルサイズ(バイト数)を表します | + | dateTime | string | 撮影日時(YYYY:MM:DD HH:MM:SS)を表します | + | fileUrl | string | ファイルの URL を表します | + | thumbnailUrl | string | サムネールの URL を表します | -``` Typescript +```Typescript import {listFiles, FileTypeEnum} from 'theta-client-react-native'; await listFiles(FileTypeEnum.IMAGE, 0, 1000) @@ -937,10 +947,11 @@ await listFiles(FileTypeEnum.IMAGE, 0, 1000) ``` ## 静止画・動画を取得する -撮影した静止画(JPEGファイル)や動画(MP4ファイル)のURLからデータを取得します。以下のような関数を使えばダウンロードできます。 -なお、静止画像の場合は、Imageコンポーネントのsource属性にURLを設定することでダウンロード後表示することができます。 -``` Typescript +撮影した静止画(JPEG ファイル)や動画(MP4 ファイル)の URL からデータを取得します。以下のような関数を使えばダウンロードできます。 +なお、静止画像の場合は、Image コンポーネントの source 属性に URL を設定することでダウンロード後表示することができます。 + +```Typescript fetch(url) .then((res) => { if (!res.ok) { @@ -959,9 +970,10 @@ fetch(url) ``` ## サムネイルを取得する -THETA内のファイルのサムネイルは、`listFiles`を使って取得した `FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 -``` Typescript +THETA 内のファイルのサムネイルは、`listFiles`を使って取得した `FileInfo`の`thumbnailUrl`を使って以下のようにダウンロードすることができます。 + +```Typescript fetch(fileInfo.thumbnailUrl) .then((res) => { if (!res.ok) { @@ -981,13 +993,13 @@ fetch(fileInfo.thumbnailUrl) ## プレビューを表示する -プレビューはequirectangular形式のmotion JPEGです。 +プレビューは equirectangular 形式の motion JPEG です。 それぞれのフレーム処理を行うコールバック関数を引数として `getLivePreview()`を呼び出します。 プレビュー中のフレームは、イベントのパラメータとして受け取ることができます。 -``` Typescript +```Typescript import * as React from 'react'; import {NativeModules, NativeEventEmitter} from 'react-native'; import { @@ -1025,10 +1037,11 @@ const LivePreview = () => { }; ``` -## THETAをリセットする -接続しているThetaをリセットするには、`reset()`を呼び出します。 +## THETA をリセットする + +接続している Theta をリセットするには、`reset()`を呼び出します。 -``` Typescript +```Typescript import {reset} from 'theta-client-react-native'; reset() @@ -1040,21 +1053,21 @@ reset() }); ``` -## THETAの情報を取得する -接続しているThetaの情報を取得するには、`getThetaInfo()`を呼び出します。呼び出しに成功すると`ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 +## THETA の情報を取得する -* ThetaInfo +接続している Theta の情報を取得するには、`getThetaInfo()`を呼び出します。呼び出しに成功すると`ThetaInfo`を取得できます。このデータは以下のような情報を含みます。 +- ThetaInfo |プロパティ名|型|内容| |---|---|---| |firmwareVersion|string|ファームウェアバージョンを表します| - |hasGps|boolean|GPS機能を持っているかどうかを表します| + |hasGps|boolean|GPS 機能を持っているかどうかを表します| |hasGyro|boolean|ジャイロを持っているかどうかを表します| - |model|string|Thetaの型番を表します| - |serialNumber|string|Thetaのシリアル番号を表します| - |uptime|number|Thetaの電源を入れてからの秒数を表します| + |model|string|Theta の型番を表します| + |serialNumber|string|Theta のシリアル番号を表します| + |uptime|number|Theta の電源を入れてからの秒数を表します| -``` Typescript +```Typescript import {getThetaInfo} from 'theta-client-react-native'; getThetaInfo() @@ -1066,31 +1079,31 @@ getThetaInfo() }); ``` -## THETAの状態を取得する -接続しているThetaの状態を取得するには、`getThetaState()`を呼び出します。呼び出しに成功すると`ThetaState`を取得できます。このデータは以下のような情報を含みます。 +## THETA の状態を取得する -* ThetaState +接続している Theta の状態を取得するには、`getThetaState()`を呼び出します。呼び出しに成功すると`ThetaState`を取得できます。このデータは以下のような情報を含みます。 - |プロパティ名|型|内容| - |---|---|---| - |batteryLevel|number|バッテリーレベルを表します| - |chargingState|ChargingStateEnum|充電状態を表します| - |fingerprint|string|現在の状態ごとに一意に決まる識別子を表します| - |isSdCard|boolean|SDカードが存在するかどうかを表します| - |latestFileUrl|string|最後の取得したメディアのURLを表します| - |recordableTime|number|録画可能秒数を表します| - |recordedTime|number|録画済み秒数を表します| +- ThetaState -* ChargingStateEnum + | プロパティ名 | 型 | 内容 | + | -------------- | ----------------- | -------------------------------------------- | + | batteryLevel | number | バッテリーレベルを表します | + | chargingState | ChargingStateEnum | 充電状態を表します | + | fingerprint | string | 現在の状態ごとに一意に決まる識別子を表します | + | isSdCard | boolean | SD カードが存在するかどうかを表します | + | latestFileUrl | string | 最後の取得したメディアの URL を表します | + | recordableTime | number | 録画可能秒数を表します | + | recordedTime | number | 録画済み秒数を表します | - |プロパティ名|内容| - |---|---| - |CHARGING|充電中を表します| - |CHARGED|充電完了を表します| - |NOT_CHARGING|ケーブル未接続を表します| +- ChargingStateEnum + | プロパティ名 | 内容 | + | ------------ | ------------------------ | + | CHARGING | 充電中を表します | + | CHARGED | 充電完了を表します | + | NOT_CHARGING | ケーブル未接続を表します | -``` Typescript +```Typescript import {getThetaState} from 'theta-client-react-native'; getThetaState() diff --git a/docs/tutorial-react-native.md b/docs/tutorial-react-native.md index 1e7472ee5f..7be514fe6e 100644 --- a/docs/tutorial-react-native.md +++ b/docs/tutorial-react-native.md @@ -1,9 +1,11 @@ # THETA Client Tutorial for React Native ## Advance preparation + Connect the wireless LAN between the smartphone and THETA that runs on the application using this SDK. ## Initialize THETA Client + ```Typescript import {initialize} from 'theta-client-react-native'; @@ -26,19 +28,19 @@ initialize('http://:') }); ``` -* THETA IP ADDRESS +- THETA IP ADDRESS -| Mode |Address | -|-------|---------| -|Direct mode|192.168.1.1| -|Other| Camera IP address| +| Mode | Address | +| ----------- | ----------------- | +| Direct mode | 192.168.1.1 | +| Other | Camera IP address | -* When downloading images or videos from THETA, the connection is plain, so the settings for each platform are required depending on the destination address (default 192.168.1.1). +- When downloading images or videos from THETA, the connection is plain, so the settings for each platform are required depending on the destination address (default 192.168.1.1). -* iOS: shows an example of Info.plist by default. -Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. +- iOS: shows an example of Info.plist by default. + Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be added. -``` xml +```xml @@ -64,7 +66,8 @@ Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be ``` -* Android: Res/xml/network_security_config.xml shows an example of the default case. +- Android: Res/xml/network_security_config.xml shows an example of the default case. + ```xml @@ -79,7 +82,7 @@ Xcode `Signing & Capabilities` -> `App Transport Security Exception` can also be First, shooting settings are performed using `getPhotoCaptureBuilder()` to create `PhotoCapture` objects. -``` Typescript +```Typescript import { getPhotoCaptureBuilder, IsoAutoHighLimitEnum, @@ -103,7 +106,7 @@ The above example sets the maximum ISO sensitivity to 1000 and the file format t See [Display a preview](#preview) for instructions on how to view preview. Next, we call `PhotoCapture.takePicture()` to shoot still pictures. -``` Typescript +```Typescript photoCapture.takePicture() .then(fileUrl => { // HTTP GET to fileUrl and receiving a JPEG file @@ -117,7 +120,7 @@ photoCapture.takePicture() First, you set up shooting using `getVideoCaptureBuilder()` and create `VideoCapture` objects. -``` Typescript +```Typescript import { getVideoCaptureBuilder, IsoAutoHighLimitEnum, @@ -142,20 +145,23 @@ See [Display a preview](#preview) for instructions on how to view preview. Next, we call `VideoCapture.startCapture()` to start recording videos. -``` Typescript -VideoCapture.startCapture() - .then(fileUrl => { +```Typescript +VideoCapture + .startCapture((error) => { + // handle error of stopCapture + }) + .then((fileUrl) => { // send GET requests and receiving MP4 files }) - .catch(error => { - // handle error + .catch((error) => { + // handle error of startCapture }); - ``` - +``` + Next, call `VideoCapture.stopCapture()` to finish recording the video. If successful, then the URL of the shot file is called then as shown above. -``` Typescript +```Typescript VideoCapture.stopCapture(); ``` @@ -167,7 +173,7 @@ When calling `getLivePreview()`, the event THETA_FRAME_EVENT occurs every time e The DATA URL of the frame data (JPEG) is passed to the event data. To exit the preview, call `stopLivePreview()`. -``` Typescript +```Typescript import * as React from 'react'; import {NativeModules, NativeEventEmitter} from 'react-native'; import { @@ -252,39 +258,38 @@ getOptions(optionNames) }); ``` - ## List still images and videos in THETA The list of still pictures (JPEG file) and videos (MP4 file) in THETA can be obtained using `listFiles(fileType:FileType Enum, startPosition:number, entryCount:number)` The return type of `listFiles()` is `ThetaFiles`, and property `fileList` of `ThetaFiles` is the list of files in THETA. `fileList` is a list of `FileInfo`. -* FileTypeEnum +- FileTypeEnum - |Value|Content| - |---|---| - |IMAGE|List of still images (JPEG files)| - |VIDEO|List of videos (MP4 files)| - |ALL|List all files| + | Value | Content | + | ----- | --------------------------------- | + | IMAGE | List of still images (JPEG files) | + | VIDEO | List of videos (MP4 files) | + | ALL | List all files | -* ThetaFiles +- ThetaFiles - |Property name|Type|Contents| - |---|---|---| - |fileList|List\|The list of files in THETA| - |totalEntries|number| Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) + | Property name | Type | Contents | + | ------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | + | fileList | List\ | The list of files in THETA | + | totalEntries | number | Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) | -* FileInfo +- FileInfo - |Property name|Type|Contents| - |---|---|---| - |name|string|Represents the file name| - |size|number|Indicates the file size (in bytes)| - |dateTime|string|Shooting date and time (YYYY:MM:DD HH:MM:SS)| - |fileUrl|string|Represents the URL of the file| - |thumbnailUrl|string|Represents a thumbnail URL| + | Property name | Type | Contents | + | ------------- | ------ | -------------------------------------------- | + | name | string | Represents the file name | + | size | number | Indicates the file size (in bytes) | + | dateTime | string | Shooting date and time (YYYY:MM:DD HH:MM:SS) | + | fileUrl | string | Represents the URL of the file | + | thumbnailUrl | string | Represents a thumbnail URL | -``` Typescript +```Typescript import {listFiles, FileTypeEnum} from 'theta-client-react-native'; await listFiles(FileTypeEnum.IMAGE, 0, 1000) @@ -295,12 +300,14 @@ await listFiles(FileTypeEnum.IMAGE, 0, 1000) // handle error }); ``` + ## Download still images and videos + You can retrieve data from the URL of still pictures (JPEG file) or videos (MP4 file). You can download using the following functions. For still images, you can download the image and display it by setting the URL to the source attribute of the Image component. -``` Typescript +```Typescript fetch(url) .then((res) => { if (!res.ok) { @@ -317,10 +324,12 @@ fetch(url) // handle error }); ``` + ## Obtain thumbnails + Thumbnails of the files in THETA can be downloaded using the `thumbnailUrl` of the `FileInfo` aquired by `listFiles()` as follows: -``` Typescript +```Typescript fetch(fileInfo.thumbnailUrl) .then((res) => { if (!res.ok) { @@ -338,13 +347,12 @@ fetch(fileInfo.thumbnailUrl) }); ``` - ## Get the THETA information To obtain the information about the connected THETA, call `getThetaInfo()`. If the call is successful, you can get `ThetaInfo`. -``` Typescript +```Typescript import {getThetaInfo} from 'theta-client-react-native'; getThetaInfo() @@ -361,7 +369,7 @@ getThetaInfo() To get the state of the connected THETA, call `getThetaState()`. Successful calling allows you to acquire the `ThetaState`. -``` Typescript +```Typescript import {getThetaState} from 'theta-client-react-native'; getThetaState() @@ -377,7 +385,7 @@ getThetaState() Call `reset()` to reset the connected THETA. -``` Typescript +```Typescript import {reset} from 'theta-client-react-native'; reset() diff --git a/docs/tutorial.md b/docs/tutorial.md index ba1a3eb9f1..68ab6256fd 100644 --- a/docs/tutorial.md +++ b/docs/tutorial.md @@ -1,55 +1,55 @@ -# THETA client tutorial - -* [Advance preparation](#preparation) -* [Create an instance of THETA client](#instance) -* [Take photos](#takephoto) -* [Take videos](#takevideo) -* [Display a preview](#preview) -* [List photos and videos in THETA](#list) -* [Get the metadata of a photo](#metadata) -* [Delete files in THETA](#delete) -* [Get the information of THETA](#info) -* [Get the state of THETA](#state) -* [Reset the THETA](#reset) +# THETA client tutorial + +- [Advance preparation](#preparation) +- [Create an instance of THETA client](#instance) +- [Take photos](#takephoto) +- [Take videos](#takevideo) +- [Display a preview](#preview) +- [List photos and videos in THETA](#list) +- [Get the metadata of a photo](#metadata) +- [Delete files in THETA](#delete) +- [Get the information of THETA](#info) +- [Get the state of THETA](#state) +- [Reset the THETA](#reset) ## Advance preparation -* Update your RICOH THETA to the newest firmware version. -* Connect the wireless LAN between THETA and the smartphone on which an application using this SDK runs. +- Update your RICOH THETA to the newest firmware version. +- Connect the wireless LAN between THETA and the smartphone on which an application using this SDK runs. ## Create an instance of THETA client `ThetaRepository.Config` is a class for configuration of THETA. You can set following properties if necessary: -| Property | Value | Note | -| -------- | ----- | ------- | -| dateTime | YYYY:MM:DD hh:mm:ss+(-)hh:mm | Current date and time | -| language | DE, EN_GB, EN_US, FR, IT, JA, KO, ZH_CN, ZH_TW | Ignored by THETA S, SC and SC2 | -| offDelay | OFF_DELAY_5M, OFF_DELAY_10M, OFF_DELAY_15M, OFF_DELAY_30M, DISABLE | Time from sleep to automatic power-off (minutes)| -| sleepDelay |SLEEP_DELAY_3M, SLEEP_DELAY_5M, SLEEP_DELAY_7M, SLEEP_DELAY_10M, DISABLE | Time to enter sleep mode (minutes) | -| shutterVolume | 0 to 100 || +| Property | Value | Note | +| ------------- | ------------------------------------------------------------------------ | ------------------------------------------------ | +| dateTime | YYYY:MM:DD hh:mm:ss+(-)hh:mm | Current date and time | +| language | DE, EN_GB, EN_US, FR, IT, JA, KO, ZH_CN, ZH_TW | Ignored by THETA S, SC and SC2 | +| offDelay | OFF_DELAY_5M, OFF_DELAY_10M, OFF_DELAY_15M, OFF_DELAY_30M, DISABLE | Time from sleep to automatic power-off (minutes) | +| sleepDelay | SLEEP_DELAY_3M, SLEEP_DELAY_5M, SLEEP_DELAY_7M, SLEEP_DELAY_10M, DISABLE | Time to enter sleep mode (minutes) | +| shutterVolume | 0 to 100 | | Call `ThetaRepository.newInstance(endpoint, config)` to create an instance of THETA client. `endpoint` is the endpoint of THETA Web API, typically "http://192.168.1.1:80". -## Take photos +## Take photos First, call `ThetaRepository.getPhotoCaptureBuilder()` to get `PhotoCapture.Builder` object and configure shooting settings if necessary. -| Function | Parameters | Note | -| -------- | ---------- | ---- | -| setExposureCompensation() | M2_0, M1_7, M1_3, M1_0, M0_7, M0_3, ZERO, P0_3, P0_7, P1_0, P1_3, P1_7, P2_0 | -2.0 to +2.0 | -| setExposureDelay() | DELAY_OFF, DELAY_1, DELAY_2, DELAY_3, DELAY_4, DELAY_5, DELAY_6, DELAY_7, DELAY_8, DELAY_9, DELAY_10 | Operating time (sec.) of the self-timer. 0 to 10 seconds | -| setExposureProgram() | MANUAL, NORMAL_PROGRAM, APERTURE_PRIORITY, SHUTTER_PRIORITY, ISO_PRIORITY | APERTURE_PRIORITY is supported only by THETA Z1 | -|setAperture() | APERTURE_AUTO, APERTURE_2_1, APERTURE_3_5, APERTURE_5_6 | Other than APERTURE_AUTO is supported only by THETA Z1 | -| setColorTemperature() | 2500 to 10000 | 100 kelvin steps | -| setGpsInfo() | latitude (-90 to 90), longitude (-180 to 180), altitude (meters), dateTimeZone (YYYY:MM:DD hh:mm:ss+(-)hh:mm) | When GPS is disabled, assign 65535 to latitude and longitude, 0 to altitude, and empty string to dateTimeZone. | -| setGpsTagRecording() | ON, OFF | Turns position information assigning ON/OFF. Supported only by THETA X. | -| setIso() |ISO_AUTO, ISO_100, ISO_200, ISO_400, ISO_800, ISO_1600, ISO_3200 (SC2, V, Z1, X), ISO_6400 (Z1) | Set ISO sensitivity. | -| setIsoAutoHighLimit() | Same as setIso() | Set ISO sensitivity upper limit when ISO sensitivity is set to automatic. Supported only by THETA SC2, V, Z1 and X. ISO_100 is supported only by THETA X. | -| setWhiteBalance() | AUTO, DAYLIGHT, SHADE, CLOUDY_DAYLIGHT, INCANDESCENT, WARM_WHITE_FLUORESCENT, DAYLIGHT_FLUORESCENT, DAYWHITE_FLUORESCENT, FLUORESCENT, BULB_FLUORESCENT, COLOR_TEMPERATURE, UNDERWATER (SC2, V, Z1, X) | | -| setFilter() | OFF, NOISE_REDUCTION, HDR | Image processing filter | -| setFileFormat() | IMAGE_2K (S, SC), IMAGE_5K (S, SC, SC2, V), IMAGE_6_7K (Z1), RAW_P_6_7K (Z1), IMAGE_5_5K (X), IMAGE_11K (X) | | +| Function | Parameters | Note | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | +| setExposureCompensation() | M2_0, M1_7, M1_3, M1_0, M0_7, M0_3, ZERO, P0_3, P0_7, P1_0, P1_3, P1_7, P2_0 | -2.0 to +2.0 | +| setExposureDelay() | DELAY_OFF, DELAY_1, DELAY_2, DELAY_3, DELAY_4, DELAY_5, DELAY_6, DELAY_7, DELAY_8, DELAY_9, DELAY_10 | Operating time (sec.) of the self-timer. 0 to 10 seconds | +| setExposureProgram() | MANUAL, NORMAL_PROGRAM, APERTURE_PRIORITY, SHUTTER_PRIORITY, ISO_PRIORITY | APERTURE_PRIORITY is supported only by THETA Z1 | +| setAperture() | APERTURE_AUTO, APERTURE_2_1, APERTURE_3_5, APERTURE_5_6 | Other than APERTURE_AUTO is supported only by THETA Z1 | +| setColorTemperature() | 2500 to 10000 | 100 kelvin steps | +| setGpsInfo() | latitude (-90 to 90), longitude (-180 to 180), altitude (meters), dateTimeZone (YYYY:MM:DD hh:mm:ss+(-)hh:mm) | When GPS is disabled, assign 65535 to latitude and longitude, 0 to altitude, and empty string to dateTimeZone. | +| setGpsTagRecording() | ON, OFF | Turns position information assigning ON/OFF. Supported only by THETA X. | +| setIso() | ISO_AUTO, ISO_100, ISO_200, ISO_400, ISO_800, ISO_1600, ISO_3200 (SC2, V, Z1, X), ISO_6400 (Z1) | Set ISO sensitivity. | +| setIsoAutoHighLimit() | Same as setIso() | Set ISO sensitivity upper limit when ISO sensitivity is set to automatic. Supported only by THETA SC2, V, Z1 and X. ISO_100 is supported only by THETA X. | +| setWhiteBalance() | AUTO, DAYLIGHT, SHADE, CLOUDY_DAYLIGHT, INCANDESCENT, WARM_WHITE_FLUORESCENT, DAYLIGHT_FLUORESCENT, DAYWHITE_FLUORESCENT, FLUORESCENT, BULB_FLUORESCENT, COLOR_TEMPERATURE, UNDERWATER (SC2, V, Z1, X) | | +| setFilter() | OFF, NOISE_REDUCTION, HDR | Image processing filter | +| setFileFormat() | IMAGE_2K (S, SC), IMAGE_5K (S, SC, SC2, V), IMAGE_6_7K (Z1), RAW_P_6_7K (Z1), IMAGE_5_5K (X), IMAGE_11K (X) | | After settings are performed, call `PhotoCapture.Builder.build()` so that you get `PhotoCapture` object. @@ -67,35 +67,33 @@ You can HTTP GET `fileUrl` for the JPEG file. Detail information of each file format is following: -| File format | File type | Width (px) | Height (px) | S | SC | SC2 | V | Z1 | X | -| ----------- | --------- | ----- | ------ | - | -- | --- | - | -- | - | -| IMAGE_2K | JPEG | 2,048 | 1,024 | + | + | - | - | - | - | -| IMAGE_5K | JPEG | 5,376 | 2,688 | + | + | + | + | - | - | -| IMAGE_6_7K | JPEG | 6,720 | 3,360 | - | - | - | - | + | - | -| RAW_P_6_7K | RAW (DNG)| 6,720 | 3,360 | - | - | - | - | + | - | -| IMAGE_5_5K | JPEG | 5,504 | 2,752 | - | - | - | - | - | + | -| IMAGE_11K | JPEG | 11,008 | 5,504 | - | - | - | - | - | + | - +| File format | File type | Width (px) | Height (px) | S | SC | SC2 | V | Z1 | X | +| ----------- | --------- | ---------- | ----------- | --- | --- | --- | --- | --- | --- | +| IMAGE_2K | JPEG | 2,048 | 1,024 | + | + | - | - | - | - | +| IMAGE_5K | JPEG | 5,376 | 2,688 | + | + | + | + | - | - | +| IMAGE_6_7K | JPEG | 6,720 | 3,360 | - | - | - | - | + | - | +| RAW_P_6_7K | RAW (DNG) | 6,720 | 3,360 | - | - | - | - | + | - | +| IMAGE_5_5K | JPEG | 5,504 | 2,752 | - | - | - | - | - | + | +| IMAGE_11K | JPEG | 11,008 | 5,504 | - | - | - | - | - | + | ## Take videos First, call `ThetaRepository.getVideoCaptureBuilder()` to get `VideoCapture.Builder` object and configure shooting settings if necessary. -| Function | Parameters | Note | -| -------- | ---------- | ---- | -| setExposureCompensation() | M2_0, M1_7, M1_3, M1_0, M0_7, M0_3, ZERO, P0_3, P0_7, P1_0, P1_3, P1_7, P2_0 | -2.0 to +2.0 | -| setExposureDelay() | DELAY_OFF, DELAY_1, DELAY_2, DELAY_3, DELAY_4, DELAY_5, DELAY_6, DELAY_7, DELAY_8, DELAY_9, DELAY_10 | Operating time (sec.) of the self-timer. 0 to 10 seconds. THETA S and SC supports only DELAY_OFF. | -| setExposureProgram() | MANUAL, NORMAL_PROGRAM, APERTURE_PRIORITY, SHUTTER_PRIORITY, ISO_PRIORITY | APERTURE_PRIORITY is supported only by THETA Z1 | -|setAperture() | APERTURE_AUTO, APERTURE_2_1, APERTURE_3_5, APERTURE_5_6 | Other than APERTURE_AUTO is supported only THETA Z1 | -| setColorTemperature() | 2500 to 10000 | 100 kelvin steps | -| setGpsInfo() | latitude (-90 to 90), longitude (-180 to 180), altitude (meters), dateTimeZone (YYYY:MM:DD hh:mm:ss+(-)hh:mm) | When GPS is disabled, assign 65535 to latitude and longitude, 0 to altitude, and empty string to dateTimeZone. | -| setGpsTagRecording() | ON, OFF | Turns position information assigning ON/OFF. Supported only by THETA X. | -| setIso() |ISO_AUTO, ISO_100, ISO_200, ISO_400, ISO_800, ISO_1600, ISO_3200 (SC2, V, Z1, X), ISO_6400 (SC2, V, Z1) | Set ISO sensitivity. | -| setIsoAutoHighLimit() | Same as setIso() | Set ISO sensitivity upper limit when ISO sensitivity is set to automatic. ISO_100 is supported only by THETA X. | -| setWhiteBalance() | AUTO, DAYLIGHT, SHADE, CLOUDY_DAYLIGHT, INCANDESCENT, WARM_WHITE_FLUORESCENT, DAYLIGHT_FLUORESCENT, DAYWHITE_FLUORESCENT, FLUORESCENT, BULB_FLUORESCENT, COLOR_TEMPERATURE, UNDERWATER (SC2, V, Z1, X) | | -| setMaxRecordableTime() | RECORDABLE_TIME_180, RECORDABLE_TIME_300, RECORDABLE_TIME_1500 | Maximum recordable time (in seconds) of the camera, 300 seconds or 1500 seconds (other than SC2). SC2 supports 180 only. | -| setFileFormat() | VIDEO_HD (S, SC), VIDEO_FULL_HD (S, SC), VIDEO_2K (SC2, V, Z1), VIDEO_4K (SC2, V, Z1), VIDEO_2K_30F (X), VIDEO_2K_60F (X), VIDEO_4K_30F (X), VIDEO_4K_60F (X), VIDEO_5_7K_2F (X), VIDEO_5_7K_5F (X), VIDEO_5_7K_30F (X), VIDEO_7K_2F (X), VIDEO_7K_5F (X), VIDEO_7K_10F (X) | | - +| Function | Parameters | Note | +| ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------ | +| setExposureCompensation() | M2_0, M1_7, M1_3, M1_0, M0_7, M0_3, ZERO, P0_3, P0_7, P1_0, P1_3, P1_7, P2_0 | -2.0 to +2.0 | +| setExposureDelay() | DELAY_OFF, DELAY_1, DELAY_2, DELAY_3, DELAY_4, DELAY_5, DELAY_6, DELAY_7, DELAY_8, DELAY_9, DELAY_10 | Operating time (sec.) of the self-timer. 0 to 10 seconds. THETA S and SC supports only DELAY_OFF. | +| setExposureProgram() | MANUAL, NORMAL_PROGRAM, APERTURE_PRIORITY, SHUTTER_PRIORITY, ISO_PRIORITY | APERTURE_PRIORITY is supported only by THETA Z1 | +| setAperture() | APERTURE_AUTO, APERTURE_2_1, APERTURE_3_5, APERTURE_5_6 | Other than APERTURE_AUTO is supported only THETA Z1 | +| setColorTemperature() | 2500 to 10000 | 100 kelvin steps | +| setGpsInfo() | latitude (-90 to 90), longitude (-180 to 180), altitude (meters), dateTimeZone (YYYY:MM:DD hh:mm:ss+(-)hh:mm) | When GPS is disabled, assign 65535 to latitude and longitude, 0 to altitude, and empty string to dateTimeZone. | +| setGpsTagRecording() | ON, OFF | Turns position information assigning ON/OFF. Supported only by THETA X. | +| setIso() | ISO_AUTO, ISO_100, ISO_200, ISO_400, ISO_800, ISO_1600, ISO_3200 (SC2, V, Z1, X), ISO_6400 (SC2, V, Z1) | Set ISO sensitivity. | +| setIsoAutoHighLimit() | Same as setIso() | Set ISO sensitivity upper limit when ISO sensitivity is set to automatic. ISO_100 is supported only by THETA X. | +| setWhiteBalance() | AUTO, DAYLIGHT, SHADE, CLOUDY_DAYLIGHT, INCANDESCENT, WARM_WHITE_FLUORESCENT, DAYLIGHT_FLUORESCENT, DAYWHITE_FLUORESCENT, FLUORESCENT, BULB_FLUORESCENT, COLOR_TEMPERATURE, UNDERWATER (SC2, V, Z1, X) | | +| setMaxRecordableTime() | RECORDABLE_TIME_180, RECORDABLE_TIME_300, RECORDABLE_TIME_1500 | Maximum recordable time (in seconds) of the camera, 300 seconds or 1500 seconds (other than SC2). SC2 supports 180 only. | +| setFileFormat() | VIDEO_HD (S, SC), VIDEO_FULL_HD (S, SC), VIDEO_2K (SC2, V, Z1), VIDEO_4K (SC2, V, Z1), VIDEO_2K_30F (X), VIDEO_2K_60F (X), VIDEO_4K_30F (X), VIDEO_4K_60F (X), VIDEO_5_7K_2F (X), VIDEO_5_7K_5F (X), VIDEO_5_7K_30F (X), VIDEO_7K_2F (X), VIDEO_7K_5F (X), VIDEO_7K_10F (X) | | After shooting settings are performed, call `VideoCapture.Builder.build()` so that you get `VideoCapture` object. @@ -110,28 +108,27 @@ interface StartCaptureCallback { `VideoCapture.startCapture(callback)` returns `VideoCapturing` object. To finish recording the video, call `VideoCapturing.stopCapture()`. -When the MP4 file is created after shooting, `onSuccess()` of the callback is called. +When the MP4 file is created after shooting, `onSuccess()` of the callback is called. You can HTTP GET `fileUrl` for the MP4 file. Detail information of each file format is following: -| File format | File type | Width (px) | Height (px) | Frame rate (fps) | S | SC | SC2 | V | Z1 | X | -| ----------- | --------- | ---------- | ----- | ------ | - | -- | --- | - | -- | - | -| VIDEO_HD | MP4 | 1,280 |720 | 15 | + | + | - | - | - | - | -| VIDEO_FULL_HD | MP4 | 1,920 | 1,080 | 30 | + | + | - | - | - | - | -| VIDEO_2K | MP4 | 1,920 | 960 | 30 | - | - | + | + | + | - | -| VIDEO_4K | MP4 | 3,840 | 1,920 | 30 | - | - | + | + | + | - | -| VIDEO_2K_30F | MP4 | 1,920 | 960 | 30 | - | - | - | - | - | + | -| VIDEO_2K_60F | MP4 | 1,920 | 960 | 60 | - | - | - | - | - | + | -| VIDEO_4K_30F | MP4 | 3,840 | 1,920 | 30 | - | - | - | - | - | + | -| VIDEO_4K_60F | MP4 | 3,840 | 1,920 | 60 | - | - | - | - | - | + | -| VIDEO_5_7K_2F | MP4 | 5,760 | 2,880 | 2 | - | - | - | - | - | + | -| VIDEO_5_7K_5F | MP4 | 5,760 | 2,880 | 5 | - | - | - | - | - | + | -| VIDEO_5_7K_30F | MP4 | 5,760 | 2,880 | 30 | - | - | - | - | - | + | -| VIDEO_7K_2F | MP4 | 7,680 | 3,840 | 2 | - | - | - | - | - | + | -| VIDEO_7K_5F | MP4 | 7,680 | 3,840 | 5 | - | - | - | - | - | + | -| VIDEO_7K_10F | MP4 | 7,680 | 3,840 | 10 | - | - | - | - | - | + | - +| File format | File type | Width (px) | Height (px) | Frame rate (fps) | S | SC | SC2 | V | Z1 | X | +| -------------- | --------- | ---------- | ----------- | ---------------- | --- | --- | --- | --- | --- | --- | +| VIDEO_HD | MP4 | 1,280 | 720 | 15 | + | + | - | - | - | - | +| VIDEO_FULL_HD | MP4 | 1,920 | 1,080 | 30 | + | + | - | - | - | - | +| VIDEO_2K | MP4 | 1,920 | 960 | 30 | - | - | + | + | + | - | +| VIDEO_4K | MP4 | 3,840 | 1,920 | 30 | - | - | + | + | + | - | +| VIDEO_2K_30F | MP4 | 1,920 | 960 | 30 | - | - | - | - | - | + | +| VIDEO_2K_60F | MP4 | 1,920 | 960 | 60 | - | - | - | - | - | + | +| VIDEO_4K_30F | MP4 | 3,840 | 1,920 | 30 | - | - | - | - | - | + | +| VIDEO_4K_60F | MP4 | 3,840 | 1,920 | 60 | - | - | - | - | - | + | +| VIDEO_5_7K_2F | MP4 | 5,760 | 2,880 | 2 | - | - | - | - | - | + | +| VIDEO_5_7K_5F | MP4 | 5,760 | 2,880 | 5 | - | - | - | - | - | + | +| VIDEO_5_7K_30F | MP4 | 5,760 | 2,880 | 30 | - | - | - | - | - | + | +| VIDEO_7K_2F | MP4 | 7,680 | 3,840 | 2 | - | - | - | - | - | + | +| VIDEO_7K_5F | MP4 | 7,680 | 3,840 | 5 | - | - | - | - | - | + | +| VIDEO_7K_10F | MP4 | 7,680 | 3,840 | 10 | - | - | - | - | - | + | ## Display a preview @@ -139,7 +136,7 @@ The preview is an equirectangular data of motion JPEG format. Only available in the still image shooting mode for THETA S and THETA SC. Data acquisition stops when THETA is operated, shooting is started, or the shooting mode is changed. -On Android, call `ThetaRepository.getLivePreview()` then Flow<[io.ktor.utils.io.core.ByteReadPacket](https://api.ktor.io/ktor-io/io.ktor.utils.io.core/-byte-read-packet.html)> returns, from which you get the JPEG data for each frame. +On Android, call `ThetaRepository.getLivePreview()` then Flow<[io.ktor.utils.io.core.ByteReadPacket](https://api.ktor.io/ktor-io/io.ktor.utils.io.core/-byte-read-packet.html)> returns, from which you get the JPEG data for each frame. On other environment, call `ThetaRepository.getLivePreview(frameHandler)`. `frameHandler` is a callback function called when each frame is acquired. @@ -148,11 +145,10 @@ For details, see source codes of demo applications. Detail information of preview is following: -| Model | Width (px) | Height (px) | Frame rate (fps) | -| ----- | ----- | ------ | ---------- | -| SC2, V, Z1, X | 1,024 | 512 | 30 | -| S, SC | 640 | 320 | 10 | - +| Model | Width (px) | Height (px) | Frame rate (fps) | +| ------------- | ---------- | ----------- | ---------------- | +| SC2, V, Z1, X | 1,024 | 512 | 30 | +| S, SC | 640 | 320 | 10 | ## List still images and videos in THETA @@ -160,11 +156,11 @@ The list of still images (JPEG files) and videos (MP4 files) in THETA can be obt The `fileType` specifies the file types to acquire: -|Value| File types| -|---|---| -|IMAGE| still images (JPEG files)| -|VIDEO| videos (MP4 files)| -|ALL| all files| +| Value | File types | +| ----- | ------------------------- | +| IMAGE | still images (JPEG files) | +| VIDEO | videos (MP4 files) | +| ALL | all files | The `startPosition` is the position to start acquiring the file list. If a number larger than the number of existing files is specified, an empty list is acquired. @@ -175,21 +171,20 @@ If the number of existing files is smaller than the specified number of files, a `listFiles()` returns `ThetaFiles` Object: - |Property name|Value| - |---|---| - |fileList|The list of files in THETA| - |totalEntries|Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) +| Property name | Value | +| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | +| fileList | The list of files in THETA | +| totalEntries | Number of files in THETA (see [api spec](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md)) | Property `fileList` of `ThetaFiles` is a list of `ThetaRepository.FileInfo`: -| Property | Value | -| -------- | ----- | -| name | File name | -| size | File size (bytes) | -| dateTime | File creation or update time. Local time.| -| fileUrl | JPEG or MP4 file URL | -| thumbnailUrl | Thumbnail file URL | - +| Property | Value | +| ------------ | ----------------------------------------- | +| name | File name | +| size | File size (bytes) | +| dateTime | File creation or update time. Local time. | +| fileUrl | JPEG or MP4 file URL | +| thumbnailUrl | Thumbnail file URL | You can HTTP GET `FileInfo.fileUrl` to acquire a JPEG file or a MP4 file. Also, you can HTTP GET `FileInfo.thumbnailUrl` to acquire thumbnail JPEG file. @@ -198,50 +193,50 @@ Also, you can HTTP GET `FileInfo.thumbnailUrl` to acquire thumbnail JPEG file. Call `ThetaRepository.getMetadata(fileUrl)` then you can get the meta information for the specified still image. Its contents are following: -| Object class | Property name | Content | -| ----------- | ------------- | ------- | -|Exif| exifVersion | EXIF Support version | -|Exif| dateTime | Time created or updated | -|Exif| imageWidth | Width (px). THETA X is not supported | -|Exif| imageLength | Height (px). THETA X is not supported | -|Exif| gpsLatitude | Latitude | -|Exif| gpsLongitude | Longitude | -|Xmp| fullPanoWidthPixels | Width (px) when the actual image size is based on a panoramic image | -|Xmp| fullPanoHeightPixels | Height (px) when the actual image size is based on a panoramic image | -|Xmp| poseHeadingDegrees | Compass heading, measured in degrees clockwise from North, for the center the image. Only THETA SC2, V and Z1 are supported. | +| Object class | Property name | Content | +| ------------ | -------------------- | ---------------------------------------------------------------------------------------------------------------------------- | +| Exif | exifVersion | EXIF Support version | +| Exif | dateTime | Time created or updated | +| Exif | imageWidth | Width (px). THETA X is not supported | +| Exif | imageLength | Height (px). THETA X is not supported | +| Exif | gpsLatitude | Latitude | +| Exif | gpsLongitude | Longitude | +| Xmp | fullPanoWidthPixels | Width (px) when the actual image size is based on a panoramic image | +| Xmp | fullPanoHeightPixels | Height (px) when the actual image size is based on a panoramic image | +| Xmp | poseHeadingDegrees | Compass heading, measured in degrees clockwise from North, for the center the image. Only THETA SC2, V and Z1 are supported. | ## Delete files in THETA -To delete files in THETA, call `ThetaRepository.deleteFiles(fileUrls: List)`. The argument is a list of URLs of deleting files. +To delete files in THETA, call `ThetaRepository.deleteFiles(fileUrls: List)`. The argument is a list of URLs of deleting files. If you want to delete all files, call one of the functions: `deleteAllFiles()`, `deleteAllImageFiles()` and `deleteAllVideoFiles()`. -## Get the information of THETA +## Get the information of THETA -Call `ThetaRepository.getThetaInfo()` then you can get `ThetaInfo` object. Its contents are following: +Call `ThetaRepository.getThetaInfo()` then you can get `ThetaInfo` object. Its contents are following: -| Name | Content | -| ---- | ------- | -| model | model name | -| serialNumber | serial number | -| firmwareVersion | firmware version | -| hasGps | presence of GPS | -| hasGyro | presence of gyroscope | -| uptime | elapsed time after startup (seconds) | +| Name | Content | +| --------------- | ------------------------------------ | +| model | model name | +| serialNumber | serial number | +| firmwareVersion | firmware version | +| hasGps | presence of GPS | +| hasGyro | presence of gyroscope | +| uptime | elapsed time after startup (seconds) | +## Get the state of THETA -## Get the state of THETA Call `ThetaRepository.getThetaState()` then you can get `ThetaState` object. Its contents are following: -| Name | Content | Note | -| ---- | ------- | ---- | -| batteryLevel | 0.0 to 1.0 | Battery level. When using an external power source, 1.0 | -| chargingState | CHARGING, COMPLETED, NOT_CHARGING | Battery charging state | -| recordedTime | recorded time of video (seconds) || -| recordableTime | remaining time of video (seconds) || -| latestFileUrl | URL of the last saved file || -| isSdCard | record to SD card or not | THETA X only | -| fingerprint | Unique value of current state | If the state changes, fingerprint also changes. | +| Name | Content | Note | +| -------------- | --------------------------------- | ------------------------------------------------------- | +| batteryLevel | 0.0 to 1.0 | Battery level. When using an external power source, 1.0 | +| chargingState | CHARGING, COMPLETED, NOT_CHARGING | Battery charging state | +| recordedTime | recorded time of video (seconds) | | +| recordableTime | remaining time of video (seconds) | | +| latestFileUrl | URL of the last saved file | | +| isSdCard | record to SD card or not | THETA X only | +| fingerprint | Unique value of current state | If the state changes, fingerprint also changes. | ## Reset the THETA diff --git a/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ConvertUtil.kt b/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ConvertUtil.kt index 34d1ae8a1d..02aae6bf36 100644 --- a/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ConvertUtil.kt +++ b/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ConvertUtil.kt @@ -13,6 +13,7 @@ const val KEY_NOTIFY_ID = "id" const val KEY_NOTIFY_PARAMS = "params" const val KEY_NOTIFY_PARAM_COMPLETION = "completion" const val KEY_NOTIFY_PARAM_IMAGE = "image" +const val KEY_NOTIFY_PARAM_MESSAGE = "message" fun toResult(thetaInfo: ThetaInfo): Map { return mapOf( @@ -474,10 +475,13 @@ fun toConfigParam(data: Map): Config { OptionNameEnum.DateTimeZone.name -> config.dateTime = value.toString() OptionNameEnum.Language.name -> config.language = getOptionValueEnum(OptionNameEnum.Language, value as String) as LanguageEnum? + OptionNameEnum.OffDelay.name -> config.offDelay = getOptionValueEnum(OptionNameEnum.OffDelay, value as String) as OffDelayEnum? + OptionNameEnum.SleepDelay.name -> config.sleepDelay = getOptionValueEnum(OptionNameEnum.SleepDelay, value as String) as SleepDelayEnum? + OptionNameEnum.ShutterVolume.name -> config.shutterVolume = value as Int KEY_CLIENT_MODE -> config.clientMode = toDigestAuthParam(value as Map<*, *>) } @@ -598,3 +602,9 @@ fun toPreviewNotifyParam(imageData: ByteArray): Map { KEY_NOTIFY_PARAM_IMAGE to imageData ) } + +fun toMessageNotifyParam(message: String): Map { + return mapOf( + KEY_NOTIFY_PARAM_MESSAGE to message + ) +} diff --git a/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ThetaClientFlutterPlugin.kt b/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ThetaClientFlutterPlugin.kt index 1a668878b7..377325d1f1 100644 --- a/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ThetaClientFlutterPlugin.kt +++ b/flutter/android/src/main/kotlin/com/ricoh360/thetaclient/theta_client_flutter/ThetaClientFlutterPlugin.kt @@ -51,6 +51,7 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { const val eventNameNotify = "theta_client_flutter/theta_notify" const val notifyIdLivePreview = 10001 const val notifyIdTimeShiftProgress = 10002 + const val notifyIdVideoCaptureStopError = 10003 } fun sendNotifyEvent(id: Int, params: Map) { @@ -82,224 +83,273 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { "getPlatformVersion" -> { result.success("Android ${android.os.Build.VERSION.RELEASE}") } + "initialize" -> { scope.launch { initialize(call, result) } } + "isInitialized" -> { isInitialized(result) } + "restoreSettings" -> { scope.launch { restoreSettings(result) } } + "getThetaModel" -> { getThetaModel(result) } + "getThetaInfo" -> { scope.launch { getThetaInfo(result) } } + "getThetaState" -> { scope.launch { getThetaState(result) } } + "getLivePreview" -> { scope.launch { getLivePreview(result) } } + "stopLivePreview" -> { stopLivePreview(result) } + "listFiles" -> { scope.launch { listFiles(call, result) } } + "deleteFiles" -> { scope.launch { deleteFiles(call, result) } } + "deleteAllFiles" -> { scope.launch { deleteAllFiles(result) } } + "deleteAllImageFiles" -> { scope.launch { deleteAllImageFiles(result) } } + "deleteAllVideoFiles" -> { scope.launch { deleteAllVideoFiles(result) } } + "getPhotoCaptureBuilder" -> { getPhotoCaptureBuilder(result) } + "buildPhotoCapture" -> { scope.launch { buildPhotoCapture(call, result) } } + "takePicture" -> { takePicture(result) } + "getTimeShiftCaptureBuilder" -> { getTimeShiftCaptureBuilder(result) } + "buildTimeShiftCapture" -> { scope.launch { buildTimeShiftCapture(call, result) } } + "startTimeShiftCapture" -> { startTimeShiftCapture(result) } + "stopTimeShiftCapture" -> { stopTimeShiftCapture(result) } + "getVideoCaptureBuilder" -> { getVideoCaptureBuilder(result) } + "buildVideoCapture" -> { scope.launch { buildVideoCapture(call, result) } } + "startVideoCapture" -> { startVideoCapture(result) } + "stopVideoCapture" -> { stopVideoCapture(result) } + "getOptions" -> { scope.launch { getOptions(call, result) } } + "setOptions" -> { scope.launch { setOptions(call, result) } } + "getMetadata" -> { scope.launch { getMetadata(call, result) } } + "reset" -> { scope.launch { reset(result) } } + "stopSelfTimer" -> { scope.launch { stopSelfTimer(result) } } + "convertVideoFormats" -> { scope.launch { convertVideoFormats(call, result) } } + "cancelVideoConvert" -> { scope.launch { cancelVideoConvert(result) } } + "finishWlan" -> { scope.launch { finishWlan(result) } } + "listAccessPoints" -> { scope.launch { listAccessPoints(result) } } + "setAccessPointDynamically" -> { scope.launch { setAccessPointDynamically(call, result) } } + "setAccessPointStatically" -> { scope.launch { setAccessPointStatically(call, result) } } + "deleteAccessPoint" -> { scope.launch { deleteAccessPoint(call, result) } } + "getMySetting" -> { scope.launch { getMySetting(call, result) } } + "getMySettingFromOldModel" -> { scope.launch { getMySettingFromOldModel(call, result) } } + "setMySetting" -> { scope.launch { setMySetting(call, result) } } + "deleteMySetting" -> { scope.launch { deleteMySetting(call, result) } } + "listPlugins" -> { scope.launch { listPlugins(call, result) } } + "setPlugin" -> { scope.launch { setPlugin(call, result) } } + "startPlugin" -> { scope.launch { startPlugin(call, result) } } + "stopPlugin" -> { scope.launch { stopPlugin(call, result) } } + "getPluginLicense" -> { scope.launch { getPluginLicense(call, result) } } + "getPluginOrders" -> { scope.launch { getPluginOrders(call, result) } } + "setPluginOrders" -> { scope.launch { setPluginOrders(call, result) } } + "setBluetoothDevice" -> { scope.launch { setBluetoothDevice(call, result) } } + else -> { result.notImplemented() } @@ -547,18 +597,27 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { } fun startVideoCapture(result: Result) { - if (thetaRepository == null || videoCapture == null) { + val theta = thetaRepository + val capture = videoCapture + if (theta == null || capture == null) { result.error(errorCode, messageNotInit, null) return } - videoCapturing = videoCapture!!.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + videoCapturing = capture.startCapture(object : VideoCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { result.success(fileUrl) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { result.error(exception.javaClass.simpleName, exception.message, null) } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + sendNotifyEvent( + notifyIdVideoCaptureStopError, + toMessageNotifyParam(exception.message ?: exception.toString()) + ) + } }) } diff --git a/flutter/ios/Classes/ConvertUtil.swift b/flutter/ios/Classes/ConvertUtil.swift index 9328443921..f93ce3a307 100644 --- a/flutter/ios/Classes/ConvertUtil.swift +++ b/flutter/ios/Classes/ConvertUtil.swift @@ -1,18 +1,18 @@ import Flutter -import UIKit import THETAClient +import UIKit let KEY_CLIENT_MODE = "clientMode" let KEY_NOTIFY_ID = "id" let KEY_NOTIFY_PARAMS = "params" let KEY_NOTIFY_PARAM_COMPLETION = "completion" let KEY_NOTIFY_PARAM_IMAGE = "image" +let KEY_NOTIFY_PARAM_MESSAGE = "message" -public class ConvertUtil: NSObject { -} +public class ConvertUtil: NSObject {} func getEnumValue>(values: KotlinArray, name: String) -> E? { - for i in 0..>(values: KotlinArray, name: String) -> func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] { var resultList = [[String: Any]]() - fileInfoList.forEach({ fileInfo in + fileInfoList.forEach { fileInfo in var item = [ "name": fileInfo.name, "fileUrl": fileInfo.fileUrl, @@ -86,12 +86,12 @@ func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] item["storageID"] = storageID } resultList.append(item) - }) + } return resultList } func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { - return [ + return [ "manufacturer": thetaInfo.manufacturer, "model": thetaInfo.model, "serialNumber": thetaInfo.serialNumber, @@ -123,7 +123,7 @@ func convertResult(cameraErrorList: [ThetaRepository.CameraErrorEnum]?) -> [Stri } func convertResult(thetaState: ThetaRepository.ThetaState) -> [String: Any?] { - return [ + return [ "fingerprint": thetaState.fingerprint, "batteryLevel": thetaState.batteryLevel, "storageUri": thetaState.storageUri, @@ -147,7 +147,7 @@ func convertResult(thetaState: ThetaRepository.ThetaState) -> [String: Any?] { ] } -func setCaptureBuilderParams(params: [String : Any], builder: CaptureBuilder) { +func setCaptureBuilderParams(params: [String: Any], builder: CaptureBuilder) { if let value = params[ThetaRepository.OptionNameEnum.aperture.name] as? String { if let enumValue = getEnumValue(values: ThetaRepository.ApertureEnum.values(), name: value) { builder.setAperture(aperture: enumValue) @@ -171,7 +171,7 @@ func setCaptureBuilderParams(params: [String : Any], builder: CaptureBuilder< builder.setExposureProgram(program: enumValue) } } - if let value = params[ThetaRepository.OptionNameEnum.gpsinfo.name] as? [String : Any] { + if let value = params[ThetaRepository.OptionNameEnum.gpsinfo.name] as? [String: Any] { builder.setGpsInfo(gpsInfo: toGpsInfo(params: value)) } if let value = params["GpsTagRecording"] as? String { @@ -196,13 +196,13 @@ func setCaptureBuilderParams(params: [String : Any], builder: CaptureBuilder< } } -func setPhotoCaptureBuilderParams(params: [String : Any], builder: PhotoCapture.Builder) { +func setPhotoCaptureBuilderParams(params: [String: Any], builder: PhotoCapture.Builder) { if let value = params[ThetaRepository.OptionNameEnum.filter.name] as? String { if let enumValue = getEnumValue(values: ThetaRepository.FilterEnum.values(), name: value) { builder.setFilter(filter: enumValue) } } - if let value = params["PhotoFileFormat"] as? String { + if let value = params["PhotoFileFormat"] as? String { if let enumValue = getEnumValue(values: ThetaRepository.PhotoFileFormatEnum.values(), name: value) { builder.setFileFormat(fileFormat: enumValue) } @@ -214,12 +214,13 @@ func setPhotoCaptureBuilderParams(params: [String : Any], builder: PhotoCapture. } } -func setTimeShiftCaptureBuilderParams(params: [String : Any], builder: TimeShiftCapture.Builder) { +func setTimeShiftCaptureBuilderParams(params: [String: Any], builder: TimeShiftCapture.Builder) { if let interval = params["_capture_interval"] as? Int, - interval >= 0 { + interval >= 0 + { builder.setCheckStatusCommandInterval(timeMillis: Int64(interval)) } - if let timeShiftParams = params[ThetaRepository.OptionNameEnum.timeshift.name] as? [String : Any] { + if let timeShiftParams = params[ThetaRepository.OptionNameEnum.timeshift.name] as? [String: Any] { let timeShift = toTimeShift(params: timeShiftParams) if let isFrontFirst = timeShift.isFrontFirst { builder.setIsFrontFirst(isFrontFirst: isFrontFirst.boolValue) @@ -233,7 +234,7 @@ func setTimeShiftCaptureBuilderParams(params: [String : Any], builder: TimeShift } } -func setVideoCaptureBuilderParams(params: [String : Any], builder: VideoCapture.Builder) { +func setVideoCaptureBuilderParams(params: [String: Any], builder: VideoCapture.Builder) { if let value = params[ThetaRepository.OptionNameEnum.maxrecordabletime.name] as? String { if let enumValue = getEnumValue(values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value) { builder.setMaxRecordableTime(time: enumValue) @@ -258,37 +259,37 @@ func toBitrate(value: Any) -> ThetaRepositoryBitrate? { } } -func toBurstOption(params: [String : Any]) -> ThetaRepository.BurstOption { - var burstCaptureNum: ThetaRepository.BurstCaptureNumEnum? = nil; +func toBurstOption(params: [String: Any]) -> ThetaRepository.BurstOption { + var burstCaptureNum: ThetaRepository.BurstCaptureNumEnum? = nil if let name = params["burstCaptureNum"] as? String { burstCaptureNum = getEnumValue(values: ThetaRepository.BurstCaptureNumEnum.values(), name: name) } - - var burstBracketStep: ThetaRepository.BurstBracketStepEnum? = nil; + + var burstBracketStep: ThetaRepository.BurstBracketStepEnum? = nil if let name = params["burstBracketStep"] as? String { burstBracketStep = getEnumValue(values: ThetaRepository.BurstBracketStepEnum.values(), name: name) } - - var burstCompensation: ThetaRepository.BurstCompensationEnum? = nil; + + var burstCompensation: ThetaRepository.BurstCompensationEnum? = nil if let name = params["burstCompensation"] as? String { burstCompensation = getEnumValue(values: ThetaRepository.BurstCompensationEnum.values(), name: name) } - - var burstMaxExposureTime: ThetaRepository.BurstMaxExposureTimeEnum? = nil; + + var burstMaxExposureTime: ThetaRepository.BurstMaxExposureTimeEnum? = nil if let name = params["burstMaxExposureTime"] as? String { burstMaxExposureTime = getEnumValue(values: ThetaRepository.BurstMaxExposureTimeEnum.values(), name: name) } - - var burstEnableIsoControl: ThetaRepository.BurstEnableIsoControlEnum? = nil; + + var burstEnableIsoControl: ThetaRepository.BurstEnableIsoControlEnum? = nil if let name = params["burstEnableIsoControl"] as? String { burstEnableIsoControl = getEnumValue(values: ThetaRepository.BurstEnableIsoControlEnum.values(), name: name) } - - var burstOrder: ThetaRepository.BurstOrderEnum? = nil; + + var burstOrder: ThetaRepository.BurstOrderEnum? = nil if let name = params["burstOrder"] as? String { burstOrder = getEnumValue(values: ThetaRepository.BurstOrderEnum.values(), name: name) } - + return ThetaRepository.BurstOption( burstCaptureNum: burstCaptureNum, burstBracketStep: burstBracketStep, @@ -304,22 +305,22 @@ func convertResult(burstOption: ThetaRepository.BurstOption) -> [String: Any] { "burstCaptureNum": burstOption.burstCaptureNum?.value.name, "burstBracketStep": burstOption.burstBracketStep?.value.name, "burstCompensation": burstOption.burstCompensation?.value.name, - "burstMaxExposureTime": burstOption.burstEnableIsoControl?.value.name, + "burstMaxExposureTime": burstOption.burstMaxExposureTime?.value.name, "burstEnableIsoControl": burstOption.burstEnableIsoControl?.value.name, "burstOrder": burstOption.burstOrder?.value.name, ] } -func toGpsInfo(params: [String : Any]) -> ThetaRepository.GpsInfo { +func toGpsInfo(params: [String: Any]) -> ThetaRepository.GpsInfo { return ThetaRepository.GpsInfo( - latitude: params["latitude"] as! Float, - longitude: params["longitude"] as! Float, - altitude: params["altitude"] as! Float, + latitude: Float(params["latitude"] as? Double ?? 0), + longitude: Float(params["longitude"] as? Double ?? 0), + altitude: Float(params["altitude"] as? Double ?? 0), dateTimeZone: params["dateTimeZone"] as! String ) } -func toProxy(params: [String : Any]) -> ThetaRepository.Proxy { +func toProxy(params: [String: Any]) -> ThetaRepository.Proxy { return ThetaRepository.Proxy( use: params["use"] as? Bool ?? false, url: params["url"] as? String, @@ -329,17 +330,17 @@ func toProxy(params: [String : Any]) -> ThetaRepository.Proxy { ) } -func toTimeShift(params: [String : Any]) -> ThetaRepository.TimeShiftSetting { - var firstInterval: ThetaRepository.TimeShiftIntervalEnum? = nil; +func toTimeShift(params: [String: Any]) -> ThetaRepository.TimeShiftSetting { + var firstInterval: ThetaRepository.TimeShiftIntervalEnum? = nil if let name = params["firstInterval"] as? String { firstInterval = getEnumValue(values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name) } - - var secondInterval: ThetaRepository.TimeShiftIntervalEnum? = nil; + + var secondInterval: ThetaRepository.TimeShiftIntervalEnum? = nil if let name = params["secondInterval"] as? String { secondInterval = getEnumValue(values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name) } - + return ThetaRepository.TimeShiftSetting( isFrontFirst: toKotlinBoolean(value: params["isFrontFirst"]), firstInterval: firstInterval, @@ -386,21 +387,21 @@ func convertResult(timeshift: ThetaRepository.TimeShiftSetting) -> [String: Any] func convertResult(options: ThetaRepository.Options) -> [String: Any] { var result = [String: Any]() let nameList = ThetaRepository.OptionNameEnum.values() - for i in 0.. { let enumValue = value as! KotlinEnum result[name.name] = enumValue.name } else if value is KotlinBoolean { - result[name.name] = value as! Bool ? true: false + result[name.name] = value as! Bool ? true : false } else if value is NSNumber || value is String { result[name.name] = value } else if let bitrate = value as? ThetaRepository.BitrateNumber { result[name.name] = bitrate.value } else if value is ThetaRepository.BurstOption, let burstOption = value as? ThetaRepository.BurstOption { result[name.name] = convertResult(burstOption: burstOption) - } else if value is ThetaRepository.GpsInfo { + } else if value is ThetaRepository.GpsInfo { let gpsInfo = value as! ThetaRepository.GpsInfo result[name.name] = convertResult(gpsInfo: gpsInfo) } else if value is ThetaRepository.Proxy, let proxy = value as? ThetaRepository.Proxy { @@ -416,7 +417,7 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { func convertKotlinBooleanToBool(value: Any?) -> Bool? { guard let value = value else { return nil } guard value is KotlinBoolean, let numVal = value as? NSNumber else { return false } - + return numVal.boolValue } @@ -443,183 +444,132 @@ func convertSetOptionsParam(params: [String: Any]) -> ThetaRepository.Options { } func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) { - switch (name) { + switch name { case ThetaRepository.OptionNameEnum.aiautothumbnail.name: options.aiAutoThumbnail = getEnumValue(values: ThetaRepository.AiAutoThumbnailEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.aperture.name: options.aperture = getEnumValue(values: ThetaRepository.ApertureEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.bitrate.name: options.bitrate = toBitrate(value: value) - break case ThetaRepository.OptionNameEnum.bluetoothpower.name: options.bluetoothPower = getEnumValue(values: ThetaRepository.BluetoothPowerEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.burstmode.name: options.burstMode = getEnumValue(values: ThetaRepository.BurstModeEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.burstoption.name: - if let params = value as? [String : Any] { + if let params = value as? [String: Any] { options.burstOption = toBurstOption(params: params) } - break case ThetaRepository.OptionNameEnum.cameracontrolsource.name: options.cameraControlSource = getEnumValue(values: ThetaRepository.CameraControlSourceEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.cameramode.name: options.cameraMode = getEnumValue(values: ThetaRepository.CameraModeEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.captureinterval.name: options.captureInterval = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.capturemode.name: options.captureMode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.capturenumber.name: options.captureNumber = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.colortemperature.name: options.colorTemperature = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.compositeshootingoutputinterval.name: options.compositeShootingOutputInterval = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.compositeshootingtime.name: options.compositeShootingTime = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.continuousnumber.name: options.continuousNumber = getEnumValue(values: ThetaRepository.ContinuousNumberEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.datetimezone.name: options.dateTimeZone = value as? String - break case ThetaRepository.OptionNameEnum.exposurecompensation.name: options.exposureCompensation = getEnumValue(values: ThetaRepository.ExposureCompensationEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.exposuredelay.name: options.exposureDelay = getEnumValue(values: ThetaRepository.ExposureDelayEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.exposureprogram.name: options.exposureProgram = getEnumValue(values: ThetaRepository.ExposureProgramEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.facedetect.name: options.faceDetect = getEnumValue(values: ThetaRepository.FaceDetectEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.fileformat.name: options.fileFormat = getEnumValue(values: ThetaRepository.FileFormatEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.filter.name: options.filter = getEnumValue(values: ThetaRepository.FilterEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.function.name: options.function = getEnumValue(values: ThetaRepository.ShootingFunctionEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.gain.name: options.gain = getEnumValue(values: ThetaRepository.GainEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.gpsinfo.name: - options.gpsInfo = toGpsInfo(params: value as! [String : Any]) - break + options.gpsInfo = toGpsInfo(params: value as! [String: Any]) case ThetaRepository.OptionNameEnum.imagestitching.name: options.imageStitching = getEnumValue(values: ThetaRepository.ImageStitchingEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.isgpson.name: - options.isGpsOn = (value as! Bool) ? true: false - break + options.isGpsOn = (value as! Bool) ? true : false case ThetaRepository.OptionNameEnum.iso.name: options.iso = getEnumValue(values: ThetaRepository.IsoEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.isoautohighlimit.name: options.isoAutoHighLimit = getEnumValue(values: ThetaRepository.IsoAutoHighLimitEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.language.name: options.language = getEnumValue(values: ThetaRepository.LanguageEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.maxrecordabletime.name: options.maxRecordableTime = getEnumValue(values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.networktype.name: options.networkType = getEnumValue(values: ThetaRepository.NetworkTypeEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.offdelay.name: options.offDelay = getEnumValue(values: ThetaRepository.OffDelayEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.password.name: options.password = value as? String - break; case ThetaRepository.OptionNameEnum.powersaving.name: options.powerSaving = getEnumValue(values: ThetaRepository.PowerSavingEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.preset.name: options.preset = getEnumValue(values: ThetaRepository.PresetEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.previewformat.name: options.previewFormat = getEnumValue(values: ThetaRepository.PreviewFormatEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.proxy.name: - if let params = value as? [String : Any] { + if let params = value as? [String: Any] { options.proxy = toProxy(params: params) } - break case ThetaRepository.OptionNameEnum.remainingpictures.name: options.remainingPictures = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.remainingvideoseconds.name: options.remainingVideoSeconds = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.remainingspace.name: options.remainingSpace = KotlinLong(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.shootingmethod.name: options.shootingMethod = getEnumValue(values: ThetaRepository.ShootingMethodEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.shutterspeed.name: options.shutterSpeed = getEnumValue(values: ThetaRepository.ShutterSpeedEnum.values(), name: value as! String)! - break; case ThetaRepository.OptionNameEnum.shuttervolume.name: options.shutterVolume = KotlinInt(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.sleepdelay.name: options.sleepDelay = getEnumValue(values: ThetaRepository.SleepDelayEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.timeshift.name: - if let params = value as? [String : Any] { + if let params = value as? [String: Any] { options.timeShift = toTimeShift(params: params) } - break case ThetaRepository.OptionNameEnum.totalspace.name: options.totalSpace = KotlinLong(integerLiteral: value as! Int) - break case ThetaRepository.OptionNameEnum.username.name: options.username = value as? String - break; case ThetaRepository.OptionNameEnum.whitebalance.name: options.whiteBalance = getEnumValue(values: ThetaRepository.WhiteBalanceEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.whitebalanceautostrength.name: options.whiteBalanceAutoStrength = getEnumValue(values: ThetaRepository.WhiteBalanceAutoStrengthEnum.values(), name: value as! String)! - break case ThetaRepository.OptionNameEnum.wlanfrequency.name: options.wlanFrequency = getEnumValue(values: ThetaRepository.WlanFrequencyEnum.values(), name: value as! String)! - break default: break } } -func toDigetAuth(params: [String : String?]?) -> DigestAuth? { +func toDigetAuth(params: [String: String?]?) -> DigestAuth? { guard let params = params, let username = params["username"] as? String else { return nil } - + let password = params["password"] as? String return DigestAuth(username: username, password: password) } -func toConfig(params: [String : Any]) -> ThetaRepository.Config { +func toConfig(params: [String: Any]) -> ThetaRepository.Config { let config = ThetaRepository.Config() params.forEach { key, value in - switch (key) { + switch key { case ThetaRepository.OptionNameEnum.datetimezone.name: config.dateTime = value as? String case ThetaRepository.OptionNameEnum.language.name: @@ -631,7 +581,7 @@ func toConfig(params: [String : Any]) -> ThetaRepository.Config { case ThetaRepository.OptionNameEnum.shuttervolume.name: config.shutterVolume = KotlinInt(integerLiteral: value as! Int) case KEY_CLIENT_MODE: - config.clientMode = toDigetAuth(params: value as? [String : String?]) + config.clientMode = toDigetAuth(params: value as? [String: String?]) default: break } @@ -639,7 +589,7 @@ func toConfig(params: [String : Any]) -> ThetaRepository.Config { return config } -func toTimeout(params: [String : Any]) -> ThetaRepository.Timeout { +func toTimeout(params: [String: Any]) -> ThetaRepository.Timeout { return ThetaRepository.Timeout( connectTimeout: params["connectTimeout"] as! Int64, requestTimeout: params["requestTimeout"] as! Int64, @@ -677,7 +627,7 @@ func convertResult(xmp: ThetaRepository.Xmp) -> [String: Any] { } func convertResult(metadata: KotlinPair) -> [String: Any] { - return [ + return [ "exif": convertResult(exif: metadata.first!), "xmp": convertResult(xmp: metadata.second!), ] @@ -685,7 +635,7 @@ func convertResult(metadata: KotlinPair [[String: Any]] { var resultList = [[String: Any]]() - accessPointList.forEach({ accessPoint in + accessPointList.forEach { accessPoint in var result = [String: Any]() result["ssid"] = accessPoint.ssid result["ssidStealth"] = accessPoint.ssidStealth @@ -705,13 +655,13 @@ func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: result["proxy"] = convertResult(proxy: proxy) } resultList.append(result) - }) + } return resultList } func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[String: Any]] { var resultList = [[String: Any]]() - pluginInfoList.forEach({ pluginInfo in + pluginInfoList.forEach { pluginInfo in let item = [ "name": pluginInfo.name, "packageName": pluginInfo.packageName, @@ -725,13 +675,13 @@ func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[Stri "message": pluginInfo.message, ] resultList.append(item) - }) + } return resultList } -func toNotify(id: Int, params: [String : Any]?) -> [String: Any] { +func toNotify(id: Int, params: [String: Any]?) -> [String: Any] { var result: [String: Any] = [ - KEY_NOTIFY_ID: id + KEY_NOTIFY_ID: id, ] if let params = params { result[KEY_NOTIFY_PARAMS] = params @@ -741,12 +691,18 @@ func toNotify(id: Int, params: [String : Any]?) -> [String: Any] { func toCaptureProgressNotifyParam(value: Float) -> [String: Any] { return [ - KEY_NOTIFY_PARAM_COMPLETION: value + KEY_NOTIFY_PARAM_COMPLETION: value, ] } func toPreviewNotifyParam(imageData: FlutterStandardTypedData) -> [String: Any] { return [ - KEY_NOTIFY_PARAM_IMAGE: imageData + KEY_NOTIFY_PARAM_IMAGE: imageData, + ] +} + +func toMessageNotifyParam(message: String) -> [String: Any] { + return [ + KEY_NOTIFY_PARAM_MESSAGE: message, ] } diff --git a/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift b/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift index 2ae28c1841..51fe9c982a 100644 --- a/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift +++ b/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift @@ -1,22 +1,23 @@ import Flutter -import UIKit import THETAClient +import UIKit let EVENT_NOTIFY = "theta_client_flutter/theta_notify" let NOTIFY_LIVE_PREVIEW = 10001 let NOTIFY_TIME_SHIFT_PROGRESS = 10002 +let NOTIFY_VIDEO_CAPTURE_STOP_ERROR = 10003 public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { - public func onListen(withArguments arguments: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { - self.eventSink = events + public func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { + eventSink = events return nil } - - public func onCancel(withArguments arguments: Any?) -> FlutterError? { + + public func onCancel(withArguments _: Any?) -> FlutterError? { print("onCancel") return nil } - + var thetaRepository: ThetaRepository? = nil static let errorCode: String = "Error" static let messageNotInit: String = "Not initialized." @@ -25,7 +26,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre static var endPoint: String = "http://192.168.1.1" var eventSink: FlutterEventSink? = nil var previewing = false - + var photoCaptureBuilder: PhotoCapture.Builder? = nil var photoCapture: PhotoCapture? = nil var timeShiftCaptureBuilder: TimeShiftCapture.Builder? = nil @@ -34,17 +35,17 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre var videoCaptureBuilder: VideoCapture.Builder? = nil var videoCapture: VideoCapture? = nil var videoCapturing: VideoCapturing? = nil - + public static func register(with registrar: FlutterPluginRegistrar) { let channel = FlutterMethodChannel(name: "theta_client_flutter", binaryMessenger: registrar.messenger()) let instance = SwiftThetaClientFlutterPlugin() registrar.addMethodCallDelegate(instance, channel: channel) - - let eventChannel = FlutterEventChannel(name: EVENT_NOTIFY, binaryMessenger: registrar.messenger()) + + let eventChannel = FlutterEventChannel(name: EVENT_NOTIFY, binaryMessenger: registrar.messenger()) eventChannel.setStreamHandler(instance) } - func sendNotifyEvent(id: Int, params: [String : Any]?) { + func sendNotifyEvent(id: Int, params: [String: Any]?) { if let eventSink = eventSink { eventSink(toNotify(id: id, params: params)) } @@ -61,27 +62,27 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre case "isInitialized": result(thetaRepository != nil) case "restoreSettings": - self.restoreSettings(result: result) + restoreSettings(result: result) case "getThetaModel": - self.getThetaModel(result: result) + getThetaModel(result: result) case "getThetaInfo": - self.getThetaInfo(result: result) + getThetaInfo(result: result) case "getThetaState": - self.getThetaState(result: result) + getThetaState(result: result) case "getLivePreview": - self.getLivePreview(result: result) + getLivePreview(result: result) case "stopLivePreview": previewing = false case "listFiles": - self.listFiles(call: call, result: result) + listFiles(call: call, result: result) case "deleteFiles": - self.deleteFiles(call: call, result: result) + deleteFiles(call: call, result: result) case "deleteAllFiles": - self.deleteAllFiles(result: result) + deleteAllFiles(result: result) case "deleteAllImageFiles": - self.deleteAllImageFiles(result: result) + deleteAllImageFiles(result: result) case "deleteAllVideoFiles": - self.deleteAllVideoFiles(result: result) + deleteAllVideoFiles(result: result) case "getPhotoCaptureBuilder": getPhotoCaptureBuilder(result: result) case "buildPhotoCapture": @@ -156,7 +157,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre result("Error. no method: " + call.method) } } - + func initialize(call: FlutterMethodCall, result: @escaping FlutterResult) async throws { thetaRepository = nil photoCaptureBuilder = nil @@ -168,27 +169,27 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre videoCapture = nil videoCapturing = nil previewing = false - - thetaRepository = try await withCheckedThrowingContinuation {continuation in - let arguments = call.arguments as! [String : Any] + + thetaRepository = try await withCheckedThrowingContinuation { continuation in + let arguments = call.arguments as! [String: Any] Self.endPoint = arguments["endpoint"] as! String let config: ThetaRepository.Config? = { - if let configParam = arguments["config"] as? [String : Any] { + if let configParam = arguments["config"] as? [String: Any] { return toConfig(params: configParam) } return nil }() let timeout: ThetaRepository.Timeout? = { - if let configParam = arguments["timeout"] as? [String : Any] { + if let configParam = arguments["timeout"] as? [String: Any] { return toTimeout(params: configParam) } return nil }() ThetaRepository.Companion.shared.doNewInstance( - endpoint:Self.endPoint, - config:config, - timeout:timeout - ) {resp, error in + endpoint: Self.endPoint, + config: config, + timeout: timeout + ) { resp, error in if let response = resp { continuation.resume(returning: response) result(nil) @@ -200,14 +201,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func restoreSettings(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.restoreSettings() { error in + thetaRepository!.restoreSettings { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -216,23 +217,23 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func getThetaModel(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } result(thetaRepository?.cameraModel?.name) } - + func getThetaInfo(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.getThetaInfo() { response, error in + thetaRepository!.getThetaInfo { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -245,14 +246,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func getThetaState(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.getThetaState() { response, error in + thetaRepository!.getThetaState { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -262,31 +263,32 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func getLivePreview(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - + class FrameHandler: KotlinSuspendFunction1 { weak var plugin: SwiftThetaClientFlutterPlugin? init(plugin: SwiftThetaClientFlutterPlugin) { self.plugin = plugin } + func invoke(p1: Any?) async throws -> Any? { if let frameData = p1 as? KotlinPair { let nsData = PlatformKt.frameFrom( packet: frameData ) - let data = FlutterStandardTypedData.init(bytes: nsData) + let data = FlutterStandardTypedData(bytes: nsData) plugin?.sendNotifyEvent(id: NOTIFY_LIVE_PREVIEW, params: toPreviewNotifyParam(imageData: data)) } return plugin?.previewing } } - + previewing = true thetaRepository!.getLivePreview(frameHandler: FrameHandler(plugin: self)) { error in self.previewing = false @@ -298,15 +300,15 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func listFiles(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - - guard let arguments = call.arguments as? [String : Any], + + guard let arguments = call.arguments as? [String: Any], let fileTypeName = arguments["fileType"] as? String, let fileType = getEnumValue(values: ThetaRepository.FileTypeEnum.values(), name: fileTypeName), let startPosition: Int32 = arguments["startPosition"] as? Int32, @@ -334,12 +336,12 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func deleteFiles(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } let arguments = call.arguments as! [String] thetaRepository!.deleteFiles(fileUrls: arguments) { error in @@ -351,14 +353,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func deleteAllFiles(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.deleteAllFiles() { error in + thetaRepository!.deleteAllFiles { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -367,14 +369,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func deleteAllImageFiles(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.deleteAllImageFiles() { error in + thetaRepository!.deleteAllImageFiles { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -383,14 +385,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func deleteAllVideoFiles(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.deleteAllVideoFiles() { error in + thetaRepository!.deleteAllVideoFiles { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -399,24 +401,24 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func getPhotoCaptureBuilder(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } photoCaptureBuilder = thetaRepository!.getPhotoCaptureBuilder() result(nil) } - + func buildPhotoCapture(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil || photoCaptureBuilder == nil) { + if thetaRepository == nil || photoCaptureBuilder == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - let arguments = call.arguments as! [String : Any] + let arguments = call.arguments as! [String: Any] setCaptureBuilderParams(params: arguments, builder: photoCaptureBuilder!) setPhotoCaptureBuilderParams(params: arguments, builder: photoCaptureBuilder!) photoCaptureBuilder!.build(completionHandler: { capture, error in @@ -429,30 +431,32 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func takePicture(result: @escaping FlutterResult) { - if (thetaRepository == nil || photoCapture == nil) { + if thetaRepository == nil || photoCapture == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - + class Callback: PhotoCaptureTakePictureCallback { let callback: (_ url: String?, _ error: Error?) -> Void init(_ callback: @escaping (_ url: String?, _ error: Error?) -> Void) { self.callback = callback } + func onSuccess(fileUrl: String) { callback(fileUrl, nil) } - func onProgress(completion: Float) { - } + + func onProgress(completion _: Float) {} + func onError(exception: ThetaRepository.ThetaRepositoryException) { callback(nil, exception as? Error) } } photoCapture!.takePicture( - callback: Callback {fileUrl, error in + callback: Callback { fileUrl, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -479,7 +483,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre result(flutterError) return } - let arguments = call.arguments as! [String : Any] + let arguments = call.arguments as! [String: Any] setCaptureBuilderParams(params: arguments, builder: builder) setTimeShiftCaptureBuilderParams(params: arguments, builder: builder) builder.build(completionHandler: { capture, error in @@ -506,14 +510,15 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre self.callback = callback self.plugin = plugin } + func onError(exception: ThetaRepository.ThetaRepositoryException) { callback(nil, exception as? Error) } - + func onProgress(completion: Float) { plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) } - + func onSuccess(fileUrl_ fileUrl: String?) { callback(fileUrl, nil) } @@ -521,14 +526,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre timeShiftCapturing = capture.startCapture( callback: Callback({ fileUrl, error in - if let thetaError = error { - let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) - result(flutterError) - } else { - result(fileUrl) - } - }, - plugin: self) + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + result(fileUrl) + } + }, + plugin: self) ) } @@ -543,22 +548,22 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func getVideoCaptureBuilder(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } videoCaptureBuilder = thetaRepository!.getVideoCaptureBuilder() result(nil) } - + func buildVideoCapture(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil || videoCaptureBuilder == nil) { + if thetaRepository == nil || videoCaptureBuilder == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - let arguments = call.arguments as! [String : Any] + let arguments = call.arguments as! [String: Any] setCaptureBuilderParams(params: arguments, builder: videoCaptureBuilder!) setVideoCaptureBuilderParams(params: arguments, builder: videoCaptureBuilder!) videoCaptureBuilder!.build(completionHandler: { capture, error in @@ -571,53 +576,63 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func startVideoCapture(result: @escaping FlutterResult) { - if (thetaRepository == nil || videoCapture == nil) { + if thetaRepository == nil || videoCapture == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - + class Callback: VideoCaptureStartCaptureCallback { let callback: (_ url: String?, _ error: Error?) -> Void - init(_ callback: @escaping (_ url: String?, _ error: Error?) -> Void) { + weak var plugin: SwiftThetaClientFlutterPlugin? + init(_ callback: @escaping (_ url: String?, _ error: Error?) -> Void, plugin: SwiftThetaClientFlutterPlugin) { self.callback = callback + self.plugin = plugin } - func onSuccess(fileUrl: String) { + + func onCaptureCompleted(fileUrl: String?) { callback(fileUrl, nil) } - func onError(exception: ThetaRepository.ThetaRepositoryException) { + + func onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { callback(nil, exception as? Error) } + + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + let error = exception.asError() + plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_STOP_ERROR, params: toMessageNotifyParam(message: error.localizedDescription)) + } } videoCapturing = videoCapture!.startCapture( - callback: Callback {fileUrl, error in - if let thetaError = error { - let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) - result(flutterError) - } else { - result(fileUrl) - } - } + callback: Callback({ fileUrl, error in + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + result(fileUrl) + } + }, + plugin: self) ) } - + func stopVideoCapture(result: @escaping FlutterResult) { - if (thetaRepository == nil || videoCapturing == nil) { + if thetaRepository == nil || videoCapturing == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } videoCapturing!.stopCapture() result(nil) } - + func getOptions(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } let arguments = call.arguments as! [String] let params = convertGetOptionsParam(params: arguments) @@ -630,12 +645,12 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func setOptions(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } let arguments = call.arguments as! [String: Any] let params = convertSetOptionsParam(params: arguments) @@ -648,12 +663,12 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func getMetadata(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } let arguments = call.arguments as! String thetaRepository!.getMetadata(fileUrl: arguments, completionHandler: { response, error in @@ -665,14 +680,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func reset(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.reset() { error in + thetaRepository!.reset { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -681,14 +696,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func stopSelfTimer(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.stopSelfTimer() { error in + thetaRepository!.stopSelfTimer { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -697,14 +712,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func convertVideoFormats(call: FlutterMethodCall, result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - let arguments = call.arguments as! [String : Any] + let arguments = call.arguments as! [String: Any] let fileUrl = arguments["fileUrl"] as! String let toLowResolution = arguments["toLowResolution"] as! Bool let applyTopBottomCorrection = arguments["applyTopBottomCorrection"] as! Bool @@ -717,14 +732,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func cancelVideoConvert(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.cancelVideoConvert() { error in + thetaRepository!.cancelVideoConvert { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -733,14 +748,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func finishWlan(result: @escaping FlutterResult) { - if (thetaRepository == nil) { + if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) - return; + return } - thetaRepository!.finishWlan() { error in + thetaRepository!.finishWlan { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -749,14 +764,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func listAccessPoints(result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - thetaRepository.listAccessPoints() { response, error in + thetaRepository.listAccessPoints { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -770,7 +785,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func setAccessPointDynamically(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) @@ -778,23 +793,24 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre return } guard - let arguments = call.arguments as? [String : Any], + let arguments = call.arguments as? [String: Any], let ssid = arguments["ssid"] as? String, let ssidStealth = arguments["ssidStealth"] as? Bool, let authModeName = arguments["authMode"] as? String, let authMode = getEnumValue(values: ThetaRepository.AuthModeEnum.values(), name: authModeName), let password = arguments["password"] as? String, - let connectionPriority = arguments["connectionPriority"] as? Int32 else { + let connectionPriority = arguments["connectionPriority"] as? Int32 + else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + var proxy: ThetaRepository.Proxy? if let proxyMap = arguments["proxy"] as? [String: Any] { proxy = toProxy(params: proxyMap) } - + thetaRepository.setAccessPointDynamically( ssid: ssid, ssidStealth: ssidStealth, @@ -809,9 +825,10 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } else { result(nil) } - }) + } + ) } - + func setAccessPointStatically(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) @@ -819,7 +836,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre return } guard - let arguments = call.arguments as? [String : Any], + let arguments = call.arguments as? [String: Any], let ssid = arguments["ssid"] as? String, let ssidStealth = arguments["ssidStealth"] as? Bool, let authModeName = arguments["authMode"] as? String, @@ -827,19 +844,20 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let connectionPriority = arguments["connectionPriority"] as? Int32, let ipAddress = arguments["ipAddress"] as? String, let subnetMask = arguments["subnetMask"] as? String, - let defaultGateway = arguments["defaultGateway"] as? String else { + let defaultGateway = arguments["defaultGateway"] as? String + else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + let password = arguments["password"] as? String - + var proxy: ThetaRepository.Proxy? if let proxyMap = arguments["proxy"] as? [String: Any] { proxy = toProxy(params: proxyMap) } - + thetaRepository.setAccessPointStatically( ssid: ssid, ssidStealth: ssidStealth, @@ -857,9 +875,10 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } else { result(nil) } - }) + } + ) } - + func deleteAccessPoint(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) @@ -880,26 +899,27 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func getMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - - guard let arguments = call.arguments as? [String : Any] else { + + guard let arguments = call.arguments as? [String: Any] else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + var captureMode: ThetaRepository.CaptureModeEnum? if let captureModeName = arguments["captureMode"] as? String, - let mode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName) as? ThetaRepository.CaptureModeEnum { + let mode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName) as? ThetaRepository.CaptureModeEnum + { captureMode = mode } - + if let captureMode = captureMode { thetaRepository.getMySetting(captureMode: captureMode, completionHandler: { options, error in if let thetaError = error { @@ -919,26 +939,27 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre result(flutterError) } } - + func getMySettingFromOldModel(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - - guard let arguments = call.arguments as? [String : Any] else { + + guard let arguments = call.arguments as? [String: Any] else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + var optionNames: [ThetaRepository.OptionNameEnum]? if let optionNameStrAry = arguments["optionNames"] as? [String], - let names = convertGetOptionsParam(params: optionNameStrAry) as? [ThetaRepository.OptionNameEnum] { + let names = convertGetOptionsParam(params: optionNameStrAry) as? [ThetaRepository.OptionNameEnum] + { optionNames = names } - + if let optionNames = optionNames { thetaRepository.getMySetting(optionNames: optionNames, completionHandler: { options, error in if let thetaError = error { @@ -953,23 +974,24 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre result(flutterError) } } - + func setMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - - guard let arguments = call.arguments as? [String : Any], + + guard let arguments = call.arguments as? [String: Any], let captureModeName = arguments["captureMode"] as? String, let captureMode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName) as? ThetaRepository.CaptureModeEnum, - let optionDic = arguments["options"] as? [String: Any] else { + let optionDic = arguments["options"] as? [String: Any] + else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + let options = convertSetOptionsParam(params: optionDic) thetaRepository.setMySetting(captureMode: captureMode, options: options, completionHandler: { error in if let thetaError = error { @@ -980,22 +1002,23 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func deleteMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - - guard let arguments = call.arguments as? [String : Any], + + guard let arguments = call.arguments as? [String: Any], let captureModeName = arguments["captureMode"] as? String, - let captureMode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName) as? ThetaRepository.CaptureModeEnum else { + let captureMode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName) as? ThetaRepository.CaptureModeEnum + else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + thetaRepository.deleteMySetting(captureMode: captureMode, completionHandler: { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1005,15 +1028,15 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func listPlugins(result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - - thetaRepository.listPlugins() { response, error in + + thetaRepository.listPlugins { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) @@ -1027,20 +1050,20 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func setPlugin(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + guard let arguments = call.arguments as? String else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + thetaRepository.setPlugin(packageName: arguments, completionHandler: { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1050,14 +1073,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func startPlugin(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + let arguments = call.arguments as? String thetaRepository.startPlugin(packageName: arguments, completionHandler: { error in if let thetaError = error { @@ -1068,14 +1091,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func stopPlugin(result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + thetaRepository.stopPlugin(completionHandler: { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1085,20 +1108,20 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func getPluginLicense(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + guard let arguments = call.arguments as? String else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + thetaRepository.getPluginLicense(packageName: arguments, completionHandler: { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1113,14 +1136,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func getPluginOrders(result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + thetaRepository.getPluginOrders(completionHandler: { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1135,20 +1158,20 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func setPluginOrders(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + guard let arguments = call.arguments as? [String] else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + thetaRepository.setPluginOrders(plugins: arguments, completionHandler: { error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1158,20 +1181,20 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } }) } - + func setBluetoothDevice(call: FlutterMethodCall, result: @escaping FlutterResult) { guard let thetaRepository = thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } - + guard let arguments = call.arguments as? String else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) result(flutterError) return } - + thetaRepository.setBluetoothDevice(uuid: arguments, completionHandler: { response, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) diff --git a/flutter/lib/capture/capture.dart b/flutter/lib/capture/capture.dart index 6d15d0df76..54be62f27f 100644 --- a/flutter/lib/capture/capture.dart +++ b/flutter/lib/capture/capture.dart @@ -124,12 +124,13 @@ class VideoCapture extends Capture { } /// Starts video capture. - VideoCapturing startCapture(void Function(String fileUrl) onSuccess, - void Function(Exception exception) onError) { + VideoCapturing startCapture(void Function(String? fileUrl) onCaptureCompleted, + void Function(Exception exception) onCaptureFailed, + {void Function(Exception exception)? onStopFailed}) { ThetaClientFlutterPlatform.instance - .startVideoCapture() - .then((value) => onSuccess(value!)) - .onError((error, stackTrace) => onError(error as Exception)); + .startVideoCapture(onStopFailed) + .then((value) => onCaptureCompleted(value!)) + .onError((error, stackTrace) => onCaptureFailed(error as Exception)); return VideoCapturing(); } } diff --git a/flutter/lib/theta_client_flutter.dart b/flutter/lib/theta_client_flutter.dart index bb509e082a..570cb7a6c6 100644 --- a/flutter/lib/theta_client_flutter.dart +++ b/flutter/lib/theta_client_flutter.dart @@ -1,13 +1,14 @@ +import 'dart:async'; + import 'package:flutter/foundation.dart'; import 'package:theta_client_flutter/digest_auth.dart'; import 'capture/capture_builder.dart'; import 'theta_client_flutter_platform_interface.dart'; -import 'dart:async'; export 'capture/capture.dart'; -export 'capture/capturing.dart'; export 'capture/capture_builder.dart'; +export 'capture/capturing.dart'; /// Handle Theta web APIs. class ThetaClientFlutter { @@ -22,8 +23,11 @@ class ThetaClientFlutter { /// - @param timeout Timeout of HTTP call. /// - @throws If an error occurs in THETA. Future initialize( - [String endpoint = 'http://192.168.1.1:80/', ThetaConfig? config, ThetaTimeout? timeout]) { - return ThetaClientFlutterPlatform.instance.initialize(endpoint, config, timeout); + [String endpoint = 'http://192.168.1.1:80/', + ThetaConfig? config, + ThetaTimeout? timeout]) { + return ThetaClientFlutterPlatform.instance + .initialize(endpoint, config, timeout); } /// Returns whether it is initialized or not. @@ -196,8 +200,8 @@ class ThetaClientFlutter { /// - @throws Command is currently disabled. Future convertVideoFormats(String fileUrl, bool toLowResolution, [bool applyTopBottomCorrection = true]) { - return ThetaClientFlutterPlatform.instance - .convertVideoFormats(fileUrl, toLowResolution, applyTopBottomCorrection); + return ThetaClientFlutterPlatform.instance.convertVideoFormats( + fileUrl, toLowResolution, applyTopBottomCorrection); } /// Cancels the movie format conversion. @@ -265,8 +269,16 @@ class ThetaClientFlutter { required String subnetMask, required String defaultGateway, Proxy? proxy}) { - return ThetaClientFlutterPlatform.instance.setAccessPointStatically(ssid, ssidStealth, authMode, - password, connectionPriority, ipAddress, subnetMask, defaultGateway, proxy); + return ThetaClientFlutterPlatform.instance.setAccessPointStatically( + ssid, + ssidStealth, + authMode, + password, + connectionPriority, + ipAddress, + subnetMask, + defaultGateway, + proxy); } /// Deletes access point information used in client mode. @@ -303,7 +315,8 @@ class ThetaClientFlutter { /// - @exception ThetaWebApiException When an invalid option is specified. /// - @exception NotConnectedException Future getMySettingFromOldModel(List optionNames) { - return ThetaClientFlutterPlatform.instance.getMySettingFromOldModel(optionNames); + return ThetaClientFlutterPlatform.instance + .getMySettingFromOldModel(optionNames); } /// Registers shooting conditions in My Settings. @@ -313,7 +326,8 @@ class ThetaClientFlutter { /// - @exception ThetaWebApiException When an invalid option is specified. /// - @exception NotConnectedException Future setMySetting(CaptureModeEnum captureMode, Options options) { - return ThetaClientFlutterPlatform.instance.setMySetting(captureMode, options); + return ThetaClientFlutterPlatform.instance + .setMySetting(captureMode, options); } /// Delete shooting conditions in My Settings. Supported just by Theta X and Z1. @@ -441,9 +455,9 @@ enum ThetaModel { } static ThetaModel? getValue(String? rawValue) { - return ThetaModel.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ThetaModel.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -576,9 +590,9 @@ enum CodecEnum { } static CodecEnum? getValue(String rawValue) { - return CodecEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return CodecEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -603,9 +617,9 @@ enum ProjectionTypeEnum { } static ProjectionTypeEnum? getValue(String rawValue) { - return ProjectionTypeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ProjectionTypeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -735,9 +749,9 @@ enum BluetoothPowerEnum { } static BluetoothPowerEnum? getValue(String rawValue) { - return BluetoothPowerEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return BluetoothPowerEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -763,9 +777,9 @@ enum BurstModeEnum { } static BurstModeEnum? getValue(String rawValue) { - return BurstModeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return BurstModeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -791,8 +805,13 @@ class BurstOption { /// see [BurstOrderEnum] BurstOrderEnum? burstOrder; - BurstOption(this.burstCaptureNum, this.burstBracketStep, this.burstCompensation, - this.burstMaxExposureTime, this.burstEnableIsoControl, this.burstOrder); + BurstOption( + this.burstCaptureNum, + this.burstBracketStep, + this.burstCompensation, + this.burstMaxExposureTime, + this.burstEnableIsoControl, + this.burstOrder); @override bool operator ==(Object other) => hashCode == other.hashCode; @@ -827,9 +846,9 @@ enum BurstCaptureNumEnum { } static BurstCaptureNumEnum? getValue(String rawValue) { - return BurstCaptureNumEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return BurstCaptureNumEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -857,9 +876,9 @@ enum BurstBracketStepEnum { } static BurstBracketStepEnum? getValue(String rawValue) { - return BurstBracketStepEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return BurstBracketStepEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -911,7 +930,8 @@ enum BurstCompensationEnum { static BurstCompensationEnum? getValue(String rawValue) { return BurstCompensationEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -953,7 +973,8 @@ enum BurstMaxExposureTimeEnum { static BurstMaxExposureTimeEnum? getValue(String rawValue) { return BurstMaxExposureTimeEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -975,7 +996,8 @@ enum BurstEnableIsoControlEnum { static BurstEnableIsoControlEnum? getValue(String rawValue) { return BurstEnableIsoControlEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -995,9 +1017,9 @@ enum BurstOrderEnum { } static BurstOrderEnum? getValue(String rawValue) { - return BurstOrderEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return BurstOrderEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1037,9 +1059,9 @@ enum CaptureStatusEnum { } static CaptureStatusEnum? getValue(String rawValue) { - return CaptureStatusEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return CaptureStatusEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1064,9 +1086,9 @@ enum ChargingStateEnum { } static ChargingStateEnum? getValue(String rawValue) { - return ChargingStateEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ChargingStateEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1097,9 +1119,9 @@ enum ShootingFunctionEnum { } static ShootingFunctionEnum? getValue(String rawValue) { - return ShootingFunctionEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ShootingFunctionEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1124,9 +1146,9 @@ enum MicrophoneOptionEnum { } static MicrophoneOptionEnum? getValue(String rawValue) { - return MicrophoneOptionEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return MicrophoneOptionEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1231,9 +1253,9 @@ enum CameraErrorEnum { } static CameraErrorEnum? getValue(String rawValue) { - return CameraErrorEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return CameraErrorEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1342,8 +1364,8 @@ class Exif { /// GPS longitude if exists. double? gpsLongitude; - Exif(this.exifVersion, this.dateTime, this.imageWidth, this.imageLength, this.gpsLatitude, - this.gpsLongitude); + Exif(this.exifVersion, this.dateTime, this.imageWidth, this.imageLength, + this.gpsLatitude, this.gpsLongitude); } /// Photo sphere XMP metadata of a still image. @@ -1357,7 +1379,8 @@ class Xmp { /// Image height (pixel). int fullPanoHeightPixels; - Xmp(this.poseHeadingDegrees, this.fullPanoWidthPixels, this.fullPanoHeightPixels); + Xmp(this.poseHeadingDegrees, this.fullPanoWidthPixels, + this.fullPanoHeightPixels); } /// Metadata of a still image @@ -1392,9 +1415,9 @@ enum AuthModeEnum { } static AuthModeEnum? getValue(String rawValue) { - return AuthModeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return AuthModeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1427,8 +1450,16 @@ class AccessPoint { /// Proxy information to be used for the access point. Proxy? proxy; - AccessPoint(this.ssid, this.ssidStealth, this.authMode, this.connectionPriority, this.usingDhcp, - this.ipAddress, this.subnetMask, this.defaultGateway, this.proxy); + AccessPoint( + this.ssid, + this.ssidStealth, + this.authMode, + this.connectionPriority, + this.usingDhcp, + this.ipAddress, + this.subnetMask, + this.defaultGateway, + this.proxy); } /// Camera setting options name. @@ -1585,7 +1616,8 @@ enum OptionNameEnum { whiteBalance('WhiteBalance', WhiteBalanceEnum), /// Option name WhiteBalanceAutoStrength - whiteBalanceAutoStrength('WhiteBalanceAutoStrength', WhiteBalanceAutoStrengthEnum), + whiteBalanceAutoStrength( + 'WhiteBalanceAutoStrength', WhiteBalanceAutoStrengthEnum), // Option name wlanfrequency wlanFrequency('WlanFrequency', WlanFrequencyEnum); @@ -1601,9 +1633,9 @@ enum OptionNameEnum { } static OptionNameEnum? getValue(String rawValue) { - return OptionNameEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return OptionNameEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1627,9 +1659,9 @@ enum AiAutoThumbnailEnum { } static AiAutoThumbnailEnum? getValue(String rawValue) { - return AiAutoThumbnailEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return AiAutoThumbnailEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1673,9 +1705,9 @@ enum ApertureEnum { } static ApertureEnum? getValue(String rawValue) { - return ApertureEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ApertureEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1780,7 +1812,8 @@ enum CameraControlSourceEnum { static CameraControlSourceEnum? getValue(String rawValue) { return CameraControlSourceEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1808,9 +1841,9 @@ enum CameraModeEnum { } static CameraModeEnum? getValue(String rawValue) { - return CameraModeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return CameraModeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1841,9 +1874,9 @@ enum CaptureModeEnum { } static CaptureModeEnum? getValue(String rawValue) { - return CaptureModeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return CaptureModeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1935,9 +1968,9 @@ enum ContinuousNumberEnum { } static ContinuousNumberEnum? getValue(String rawValue) { - return ContinuousNumberEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ContinuousNumberEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -1994,7 +2027,8 @@ enum ExposureCompensationEnum { static ExposureCompensationEnum? getValue(String rawValue) { return ExposureCompensationEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2043,9 +2077,9 @@ enum ExposureDelayEnum { } static ExposureDelayEnum? getValue(String rawValue) { - return ExposureDelayEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ExposureDelayEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2090,9 +2124,9 @@ enum ExposureProgramEnum { } static ExposureProgramEnum? getValue(String rawValue) { - return ExposureProgramEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ExposureProgramEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2117,9 +2151,9 @@ enum FaceDetectEnum { } static FaceDetectEnum? getValue(String rawValue) { - return FaceDetectEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return FaceDetectEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2297,9 +2331,9 @@ enum FileFormatEnum { } static FileFormatEnum? getValue(String rawValue) { - return FileFormatEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return FileFormatEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2335,9 +2369,9 @@ enum FilterEnum { } static FilterEnum? getValue(String rawValue) { - return FilterEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return FilterEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2368,9 +2402,9 @@ enum GainEnum { } static GainEnum? getValue(String rawValue) { - return GainEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return GainEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2406,6 +2440,7 @@ enum ImageStitchingEnum { none('NONE'); final String rawValue; + const ImageStitchingEnum(this.rawValue); @override @@ -2415,7 +2450,7 @@ enum ImageStitchingEnum { static ImageStitchingEnum? getValue(String rawValue) { return ImageStitchingEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, + (element) => element?.rawValue == rawValue, orElse: () => null); } } @@ -2527,9 +2562,9 @@ enum IsoEnum { } static IsoEnum? getValue(String rawValue) { - return IsoEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return IsoEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2637,9 +2672,9 @@ enum IsoAutoHighLimitEnum { } static IsoAutoHighLimitEnum? getValue(String rawValue) { - return IsoAutoHighLimitEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return IsoAutoHighLimitEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2691,9 +2726,9 @@ enum LanguageEnum { } static LanguageEnum? getValue(String rawValue) { - return LanguageEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return LanguageEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2729,7 +2764,8 @@ enum MaxRecordableTimeEnum { static MaxRecordableTimeEnum? getValue(String rawValue) { return MaxRecordableTimeEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2757,9 +2793,9 @@ enum NetworkTypeEnum { } static NetworkTypeEnum? getValue(String rawValue) { - return NetworkTypeEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return NetworkTypeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2799,9 +2835,9 @@ enum OffDelayEnum { } static OffDelayEnum? getValue(String rawValue) { - return OffDelayEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return OffDelayEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2825,9 +2861,9 @@ enum PowerSavingEnum { } static PowerSavingEnum? getValue(String rawValue) { - return PowerSavingEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return PowerSavingEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2848,9 +2884,9 @@ enum PresetEnum { } static PresetEnum? getValue(String rawValue) { - return PresetEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return PresetEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2899,9 +2935,9 @@ enum PreviewFormatEnum { } static PreviewFormatEnum? getValue(String rawValue) { - return PreviewFormatEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return PreviewFormatEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -2948,9 +2984,9 @@ enum ShootingMethodEnum { } static ShootingMethodEnum? getValue(String rawValue) { - return ShootingMethodEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ShootingMethodEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3189,9 +3225,9 @@ enum ShutterSpeedEnum { } static ShutterSpeedEnum? getValue(String rawValue) { - return ShutterSpeedEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return ShutterSpeedEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3229,9 +3265,9 @@ enum SleepDelayEnum { } static SleepDelayEnum? getValue(String rawValue) { - return SleepDelayEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return SleepDelayEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3302,9 +3338,9 @@ enum WhiteBalanceEnum { } static WhiteBalanceEnum? getValue(String rawValue) { - return WhiteBalanceEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return WhiteBalanceEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3330,7 +3366,8 @@ class TimeShift { bool operator ==(Object other) => hashCode == other.hashCode; @override - int get hashCode => Object.hashAll([isFrontFirst, firstInterval, secondInterval]); + int get hashCode => + Object.hashAll([isFrontFirst, firstInterval, secondInterval]); @override String toString() { @@ -3385,7 +3422,8 @@ enum TimeShiftIntervalEnum { static TimeShiftIntervalEnum? getValue(String rawValue) { return TimeShiftIntervalEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3415,7 +3453,8 @@ enum WhiteBalanceAutoStrengthEnum { static WhiteBalanceAutoStrengthEnum? getValue(String rawValue) { return WhiteBalanceAutoStrengthEnum.values .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3437,9 +3476,9 @@ enum WlanFrequencyEnum { } static WlanFrequencyEnum? getValue(String rawValue) { - return WlanFrequencyEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, orElse: () => null); + return WlanFrequencyEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); } } @@ -3494,7 +3533,8 @@ class GpsInfo { bool operator ==(Object other) => hashCode == other.hashCode; @override - int get hashCode => Object.hashAll([latitude, longitude, altitude, dateTimeZone]); + int get hashCode => + Object.hashAll([latitude, longitude, altitude, dateTimeZone]); } /// Proxy information to be used when wired LAN is enabled. @@ -4143,6 +4183,15 @@ class PluginInfo { /// Message String message; - PluginInfo(this.name, this.packageName, this.version, this.isPreInstalled, this.isRunning, - this.isForeground, this.isBoot, this.hasWebServer, this.exitStatus, this.message); + PluginInfo( + this.name, + this.packageName, + this.version, + this.isPreInstalled, + this.isRunning, + this.isForeground, + this.isBoot, + this.hasWebServer, + this.exitStatus, + this.message); } diff --git a/flutter/lib/theta_client_flutter_method_channel.dart b/flutter/lib/theta_client_flutter_method_channel.dart index 792887829c..eda7c46943 100644 --- a/flutter/lib/theta_client_flutter_method_channel.dart +++ b/flutter/lib/theta_client_flutter_method_channel.dart @@ -9,6 +9,7 @@ import 'theta_client_flutter_platform_interface.dart'; const notifyIdLivePreview = 10001; const notifyIdTimeShiftProgress = 10002; +const notifyIdVideoCaptureStopError = 10003; /// An implementation of [ThetaClientFlutterPlatform] that uses method channels. class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @@ -58,12 +59,14 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @override Future getPlatformVersion() async { - final version = await methodChannel.invokeMethod('getPlatformVersion'); + final version = + await methodChannel.invokeMethod('getPlatformVersion'); return version; } @override - Future initialize(String endpoint, ThetaConfig? config, ThetaTimeout? timeout) async { + Future initialize( + String endpoint, ThetaConfig? config, ThetaTimeout? timeout) async { clearNotify(); disableNotifyEventReceiver(); enableNotifyEventReceiver(); @@ -101,7 +104,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @override Future getThetaModel() async { var completer = Completer(); - final thetaModel = await methodChannel.invokeMethod('getThetaModel'); + final thetaModel = + await methodChannel.invokeMethod('getThetaModel'); completer.complete(ThetaModel.getValue(thetaModel)); return completer.future; } @@ -111,8 +115,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { var completer = Completer(); try { debugPrint('call getThetaInfo'); - var result = await methodChannel.invokeMethod>('getThetaInfo') - as Map; + var result = await methodChannel.invokeMethod>( + 'getThetaInfo') as Map; var thetaInfo = ConvertUtils.convertThetaInfo(result); completer.complete(thetaInfo); } catch (e) { @@ -126,8 +130,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { var completer = Completer(); try { debugPrint('call getThetaState'); - var result = await methodChannel.invokeMethod>('getThetaState') - as Map; + var result = await methodChannel.invokeMethod>( + 'getThetaState') as Map; var thetaState = ConvertUtils.convertThetaState(result); completer.complete(thetaState); } catch (e) { @@ -158,8 +162,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future listFiles( - FileTypeEnum fileType, int entryCount, int startPosition, StorageEnum? storage) async { + Future listFiles(FileTypeEnum fileType, int entryCount, + int startPosition, StorageEnum? storage) async { var completer = Completer(); try { final Map params = { @@ -171,8 +175,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { if (storage != null) { params['storage'] = storage.rawValue; } - var result = await methodChannel.invokeMethod>('listFiles', params) - as Map; + var result = await methodChannel.invokeMethod>( + 'listFiles', params) as Map; var thetaFiles = ConvertUtils.convertThetaFiles(result); completer.complete(thetaFiles); } catch (e) { @@ -272,8 +276,28 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future startVideoCapture() async { - return methodChannel.invokeMethod('startVideoCapture'); + Future startVideoCapture( + void Function(Exception exception)? onStopFailed) async { + var completer = Completer(); + try { + enableNotifyEventReceiver(); + if (onStopFailed != null) { + addNotify(notifyIdVideoCaptureStopError, (params) { + final message = params?['message'] as String?; + if (message != null) { + onStopFailed(Exception(message)); + } + }); + } + final fileUrl = + await methodChannel.invokeMethod('startVideoCapture'); + removeNotify(notifyIdVideoCaptureStopError); + completer.complete(fileUrl); + } catch (e) { + removeNotify(notifyIdVideoCaptureStopError); + completer.completeError(e); + } + return completer.future; } @override @@ -311,7 +335,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future getMetadata(String fileUrl) async { var completer = Completer(); try { - var data = await methodChannel.invokeMethod>('getMetadata', fileUrl); + var data = await methodChannel.invokeMethod>( + 'getMetadata', fileUrl); completer.complete(ConvertUtils.convertMetadata(data!)); } catch (e) { completer.completeError(e); @@ -330,8 +355,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future convertVideoFormats( - String fileUrl, bool toLowResolution, bool applyTopBottomCorrection) async { + Future convertVideoFormats(String fileUrl, bool toLowResolution, + bool applyTopBottomCorrection) async { var completer = Completer(); try { final Map params = { @@ -339,7 +364,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { 'toLowResolution': toLowResolution, 'applyTopBottomCorrection': applyTopBottomCorrection, }; - var result = await methodChannel.invokeMethod('convertVideoFormats', params); + var result = await methodChannel.invokeMethod( + 'convertVideoFormats', params); completer.complete(result); } catch (e) { completer.completeError(e); @@ -361,9 +387,10 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future> listAccessPoints() async { var completer = Completer>(); try { - var result = - await methodChannel.invokeMethod>('listAccessPoints') as List; - var fileInfoList = ConvertUtils.toAccessPointList(result.cast>()); + var result = await methodChannel + .invokeMethod>('listAccessPoints') as List; + var fileInfoList = + ConvertUtils.toAccessPointList(result.cast>()); completer.complete(fileInfoList); } catch (e) { completer.completeError(e); @@ -372,8 +399,13 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future setAccessPointDynamically(String ssid, bool ssidStealth, AuthModeEnum authMode, - String password, int connectionPriority, Proxy? proxy) async { + Future setAccessPointDynamically( + String ssid, + bool ssidStealth, + AuthModeEnum authMode, + String password, + int connectionPriority, + Proxy? proxy) async { final Map params = { 'ssid': ssid, 'ssidStealth': ssidStealth, @@ -382,7 +414,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { 'connectionPriority': connectionPriority, 'proxy': proxy != null ? ConvertUtils.convertProxyParam(proxy) : null }; - return methodChannel.invokeMethod('setAccessPointDynamically', params); + return methodChannel.invokeMethod( + 'setAccessPointDynamically', params); } @override @@ -422,7 +455,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { final Map params = { 'captureMode': captureMode.rawValue, }; - var options = await methodChannel.invokeMethod>('getMySetting', params); + var options = await methodChannel.invokeMethod>( + 'getMySetting', params); if (options != null) { completer.complete(ConvertUtils.convertOptions(options)); @@ -437,7 +471,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future getMySettingFromOldModel(List optionNames) async { + Future getMySettingFromOldModel( + List optionNames) async { var completer = Completer(); try { final Map params = { @@ -459,7 +494,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future setMySetting(CaptureModeEnum captureMode, Options options) async { + Future setMySetting( + CaptureModeEnum captureMode, Options options) async { var completer = Completer(); final Map params = { 'captureMode': captureMode.rawValue, @@ -491,8 +527,10 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future> listPlugins() async { var completer = Completer>(); try { - var result = await methodChannel.invokeMethod>('listPlugins') as List; - var plugins = ConvertUtils.toPluginInfoList(result.cast>()); + var result = await methodChannel + .invokeMethod>('listPlugins') as List; + var plugins = + ConvertUtils.toPluginInfoList(result.cast>()); completer.complete(plugins); } catch (e) { completer.completeError(e); @@ -519,7 +557,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future getPluginLicense(String packageName) async { var completer = Completer(); try { - var result = await methodChannel.invokeMethod('getPluginLicense', packageName); + var result = await methodChannel.invokeMethod( + 'getPluginLicense', packageName); completer.complete(result); } catch (e) { completer.completeError(e); @@ -531,8 +570,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future> getPluginOrders() async { var completer = Completer>(); try { - var result = - await methodChannel.invokeMethod>('getPluginOrders') as List; + var result = await methodChannel + .invokeMethod>('getPluginOrders') as List; completer.complete(ConvertUtils.convertStringList(result)); } catch (e) { completer.completeError(e); @@ -549,7 +588,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { Future setBluetoothDevice(String uuid) async { var completer = Completer(); try { - var result = await methodChannel.invokeMethod('setBluetoothDevice', uuid); + var result = + await methodChannel.invokeMethod('setBluetoothDevice', uuid); completer.complete(result); } catch (e) { completer.completeError(e); diff --git a/flutter/lib/theta_client_flutter_platform_interface.dart b/flutter/lib/theta_client_flutter_platform_interface.dart index b4fd337128..c4c84b8793 100644 --- a/flutter/lib/theta_client_flutter_platform_interface.dart +++ b/flutter/lib/theta_client_flutter_platform_interface.dart @@ -10,7 +10,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { static final Object _token = Object(); - static ThetaClientFlutterPlatform _instance = MethodChannelThetaClientFlutter(); + static ThetaClientFlutterPlatform _instance = + MethodChannelThetaClientFlutter(); /// The default instance of [ThetaClientFlutterPlatform] to use. /// @@ -29,7 +30,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { throw UnimplementedError('platformVersion() has not been implemented.'); } - Future initialize(String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { + Future initialize( + String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { throw UnimplementedError('initialize() has not been implemented.'); } @@ -57,8 +59,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { throw UnimplementedError('getLivePreview() has not been implemented.'); } - Future listFiles( - FileTypeEnum fileType, int entryCount, int startPosition, StorageEnum? storage) { + Future listFiles(FileTypeEnum fileType, int entryCount, + int startPosition, StorageEnum? storage) { throw UnimplementedError('listFiles() has not been implemented.'); } @@ -82,7 +84,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { } Future getPhotoCaptureBuilder() { - throw UnimplementedError('getPhotoCaptureBuilder() has not been implemented.'); + throw UnimplementedError( + 'getPhotoCaptureBuilder() has not been implemented.'); } Future buildPhotoCapture(Map options) { @@ -94,30 +97,37 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { } Future getTimeShiftCaptureBuilder() { - throw UnimplementedError('getTimeShiftCaptureBuilder() has not been implemented.'); + throw UnimplementedError( + 'getTimeShiftCaptureBuilder() has not been implemented.'); } - Future buildTimeShiftCapture(Map options, int interval) { - throw UnimplementedError('buildTimeShiftCapture() has not been implemented.'); + Future buildTimeShiftCapture( + Map options, int interval) { + throw UnimplementedError( + 'buildTimeShiftCapture() has not been implemented.'); } Future startTimeShiftCapture(void Function(double)? onProgress) { - throw UnimplementedError('startTimeShiftCapture() has not been implemented.'); + throw UnimplementedError( + 'startTimeShiftCapture() has not been implemented.'); } Future stopTimeShiftCapture() { - throw UnimplementedError('stopTimeShiftCapture() has not been implemented.'); + throw UnimplementedError( + 'stopTimeShiftCapture() has not been implemented.'); } Future getVideoCaptureBuilder() { - throw UnimplementedError('getVideoCaptureBuilder() has not been implemented.'); + throw UnimplementedError( + 'getVideoCaptureBuilder() has not been implemented.'); } Future buildVideoCapture(Map options) { throw UnimplementedError('buildVideoCapture() has not been implemented.'); } - Future startVideoCapture() { + Future startVideoCapture( + void Function(Exception exception)? onStopFailed) { throw UnimplementedError('startVideoCapture() has not been implemented.'); } @@ -162,9 +172,15 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { throw UnimplementedError('listAccessPoints() has not been implemented.'); } - Future setAccessPointDynamically(String ssid, bool ssidStealth, AuthModeEnum authMode, - String password, int connectionPriority, Proxy? proxy) { - throw UnimplementedError('setAccessPointDynamically() has not been implemented.'); + Future setAccessPointDynamically( + String ssid, + bool ssidStealth, + AuthModeEnum authMode, + String password, + int connectionPriority, + Proxy? proxy) { + throw UnimplementedError( + 'setAccessPointDynamically() has not been implemented.'); } Future setAccessPointStatically( @@ -177,7 +193,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { String subnetMask, String defaultGateway, Proxy? proxy) { - throw UnimplementedError('setAccessPointStatically() has not been implemented.'); + throw UnimplementedError( + 'setAccessPointStatically() has not been implemented.'); } Future deleteAccessPoint(String ssid) { diff --git a/flutter/lib/utils/convert_utils.dart b/flutter/lib/utils/convert_utils.dart index 012c9248af..7502d90acd 100644 --- a/flutter/lib/utils/convert_utils.dart +++ b/flutter/lib/utils/convert_utils.dart @@ -32,7 +32,8 @@ class ConvertUtils { static ThetaFiles convertThetaFiles(Map data) { var inputList = data['fileList'] as List; var fileList = List.empty(growable: true); - for (Map element in inputList.cast>()) { + for (Map element + in inputList.cast>()) { var info = FileInfo( element['name'], element['fileUrl'], @@ -50,15 +51,17 @@ class ConvertUtils { element['recordTime'], element['isProcessed'], element['previewUrl'], - (element['codec'] != null) ? CodecEnum.getValue(element['codec'] as String) : null, - (element['projectionType'] != null) ? ProjectionTypeEnum.getValue( - element['projectionType'] as String) : null, + (element['codec'] != null) + ? CodecEnum.getValue(element['codec'] as String) + : null, + (element['projectionType'] != null) + ? ProjectionTypeEnum.getValue(element['projectionType'] as String) + : null, element['continuousShootingGroupId'], element['frameRate'], element['favorite'], element['imageDescription'], - element['storageID'] - ); + element['storageID']); fileList.add(info); } return ThetaFiles(fileList, data['totalEntries']); @@ -70,9 +73,7 @@ class ConvertUtils { apiList.add(str); }); Endpoints endpoints = Endpoints( - data['endpoints']['httpPort'], - data['endpoints']['httpUpdatesPort'] - ); + data['endpoints']['httpPort'], data['endpoints']['httpUpdatesPort']); var apiLevelList = List.empty(growable: true); data['apiLevel'].forEach((n) { apiLevelList.add(n); @@ -91,8 +92,7 @@ class ConvertUtils { apiList, endpoints, apiLevelList, - ThetaModel.getValue(data['thetaModel']) - ); + ThetaModel.getValue(data['thetaModel'])); return thetaInfo; } @@ -138,7 +138,8 @@ class ConvertUtils { result[entry.key] = convertGpsInfoParam(entry.value); } else if (entry.value.runtimeType == TimeShift) { result[entry.key] = convertTimeShiftParam(entry.value); - } else if (entry.value.runtimeType == int || entry.value.runtimeType == double) { + } else if (entry.value.runtimeType == int || + entry.value.runtimeType == double) { result[entry.key] = entry.value; } else { result[entry.key] = entry.value.toString(); @@ -232,8 +233,9 @@ class ConvertUtils { result.aperture = ApertureEnum.getValue(entry.value); break; case OptionNameEnum.bitrate: - result.bitrate = - (entry.value is int) ? BitrateNumber(entry.value) : Bitrate.getValue(entry.value); + result.bitrate = (entry.value is int) + ? BitrateNumber(entry.value) + : Bitrate.getValue(entry.value); break; case OptionNameEnum.bluetoothPower: result.bluetoothPower = BluetoothPowerEnum.getValue(entry.value); @@ -245,7 +247,8 @@ class ConvertUtils { result.burstOption = convertBurstOption(entry.value); break; case OptionNameEnum.cameraControlSource: - result.cameraControlSource = CameraControlSourceEnum.getValue(entry.value); + result.cameraControlSource = + CameraControlSourceEnum.getValue(entry.value); break; case OptionNameEnum.cameraMode: result.cameraMode = CameraModeEnum.getValue(entry.value); @@ -275,7 +278,8 @@ class ConvertUtils { result.dateTimeZone = entry.value; break; case OptionNameEnum.exposureCompensation: - result.exposureCompensation = ExposureCompensationEnum.getValue(entry.value); + result.exposureCompensation = + ExposureCompensationEnum.getValue(entry.value); break; case OptionNameEnum.exposureDelay: result.exposureDelay = ExposureDelayEnum.getValue(entry.value); @@ -317,7 +321,8 @@ class ConvertUtils { result.language = LanguageEnum.getValue(entry.value); break; case OptionNameEnum.maxRecordableTime: - result.maxRecordableTime = MaxRecordableTimeEnum.getValue(entry.value); + result.maxRecordableTime = + MaxRecordableTimeEnum.getValue(entry.value); break; case OptionNameEnum.networkType: result.networkType = NetworkTypeEnum.getValue(entry.value); @@ -374,7 +379,8 @@ class ConvertUtils { result.whiteBalance = WhiteBalanceEnum.getValue(entry.value); break; case OptionNameEnum.whiteBalanceAutoStrength: - result.whiteBalanceAutoStrength = WhiteBalanceAutoStrengthEnum.getValue(entry.value); + result.whiteBalanceAutoStrength = + WhiteBalanceAutoStrengthEnum.getValue(entry.value); break; case OptionNameEnum.wlanFrequency: result.wlanFrequency = WlanFrequencyEnum.getValue(entry.value); @@ -466,7 +472,10 @@ class ConvertUtils { return value.rawValue; } else if (value is WlanFrequencyEnum) { return value.rawValue; - } else if (value is int || value is double || value is String || value is bool) { + } else if (value is int || + value is double || + value is String || + value is bool) { return value; } else if (value is GpsInfo) { return convertGpsInfoParam(value); @@ -520,31 +529,19 @@ class ConvertUtils { } static Exif convertExif(Map data) { - var exif = Exif( - data['exifVersion'], - data['dateTime'], - data['imageWidth'], - data['imageLength'], - data['gpsLatitude'], - data['gpsLongitude'] - ); + var exif = Exif(data['exifVersion'], data['dateTime'], data['imageWidth'], + data['imageLength'], data['gpsLatitude'], data['gpsLongitude']); return exif; } static Xmp convertXmp(Map data) { - var xmp = Xmp( - data['poseHeadingDegrees'], - data['fullPanoWidthPixels'], - data['fullPanoHeightPixels'] - ); + var xmp = Xmp(data['poseHeadingDegrees'], data['fullPanoWidthPixels'], + data['fullPanoHeightPixels']); return xmp; } static Metadata convertMetadata(Map data) { - var metadata = Metadata( - convertExif(data['exif']), - convertXmp(data['xmp']) - ); + var metadata = Metadata(convertExif(data['exif']), convertXmp(data['xmp'])); return metadata; } diff --git a/flutter/test/capture/video_capture_method_channel_test.dart b/flutter/test/capture/video_capture_method_channel_test.dart index fad657fad5..32e4fefb43 100644 --- a/flutter/test/capture/video_capture_method_channel_test.dart +++ b/flutter/test/capture/video_capture_method_channel_test.dart @@ -73,6 +73,6 @@ void main() { channel.setMockMethodCallHandler((MethodCall methodCall) async { return fileUrl; }); - expect(await platform.startVideoCapture(), fileUrl); + expect(await platform.startVideoCapture(null), fileUrl); }); } diff --git a/flutter/test/capture/video_capture_test.dart b/flutter/test/capture/video_capture_test.dart index fdf48f4b4b..119a4fae4a 100644 --- a/flutter/test/capture/video_capture_test.dart +++ b/flutter/test/capture/video_capture_test.dart @@ -113,20 +113,22 @@ void main() { onCallGetVideoCaptureBuilder = Future.value; onCallBuildVideoCapture = Future.value; - onCallStartVideoCapture = () { + onCallStartVideoCapture = (onStopFailed) { return Future.value(imageUrl); }; var builder = thetaClientPlugin.getVideoCaptureBuilder(); var capture = await builder.build(); String? fileUrl; + capture.startCapture((value) { expect(value, imageUrl); fileUrl = value; }, (exception) { expect(false, isTrue, reason: 'Error. startCapture'); - }); - await Future.delayed(const Duration(milliseconds: 10), () {}); + }, onStopFailed: (exception) {}); + + await Future.delayed(const Duration(milliseconds: 100), () {}); expect(fileUrl, imageUrl); expect(capture.getAperture(), isNull); }); @@ -140,7 +142,7 @@ void main() { onCallGetVideoCaptureBuilder = Future.value; onCallBuildVideoCapture = Future.value; var completer = Completer(); - onCallStartVideoCapture = () { + onCallStartVideoCapture = (onStopFailed) { return completer.future; }; onCallStopVideoCapture = () { @@ -154,7 +156,7 @@ void main() { expect(false, isTrue, reason: 'startCapture'); }, (exception) { expect(exception, isNotNull, reason: 'Error. startCapture'); - }); + }, onStopFailed: (exception) {}); capturing.stopCapture(); await Future.delayed(const Duration(milliseconds: 10), () {}); expect(capture.getAperture(), isNull); @@ -171,7 +173,7 @@ void main() { onCallGetVideoCaptureBuilder = Future.value; onCallBuildVideoCapture = Future.value; var completer = Completer(); - onCallStartVideoCapture = () { + onCallStartVideoCapture = (onStopFailed) { return completer.future; }; onCallStopVideoCapture = () { @@ -187,7 +189,7 @@ void main() { fileUrl = value; }, (exception) { expect(false, isTrue, reason: 'Error. startCapture'); - }); + }, onStopFailed: (exception) {}); capturing.stopCapture(); await Future.delayed(const Duration(milliseconds: 10), () {}); expect(fileUrl, imageUrl); diff --git a/flutter/test/commands/get_live_preview_method_channel_test.dart b/flutter/test/commands/get_live_preview_method_channel_test.dart index 782fa251db..792cae24d9 100644 --- a/flutter/test/commands/get_live_preview_method_channel_test.dart +++ b/flutter/test/commands/get_live_preview_method_channel_test.dart @@ -91,6 +91,7 @@ void main() { expect(error.toString().contains('test error'), true); } expect(previewCount, 0); - expect(platform.notifyList.containsKey(10002), false, reason: 'remove notify progress'); + expect(platform.notifyList.containsKey(10002), false, + reason: 'remove notify progress'); }); } diff --git a/flutter/test/commands/list_files_test.dart b/flutter/test/commands/list_files_test.dart index ec4a086c3d..5f65edec74 100644 --- a/flutter/test/commands/list_files_test.dart +++ b/flutter/test/commands/list_files_test.dart @@ -7,7 +7,8 @@ import '../theta_client_flutter_test.dart'; void main() { test('listFiles', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const name = 'R0013336.JPG'; @@ -42,7 +43,8 @@ void main() { return Future.value(input); }; - var result = await thetaClientPlugin.listFiles(FileTypeEnum.image, 10, 10, StorageEnum.current); + var result = await thetaClientPlugin.listFiles( + FileTypeEnum.image, 10, 10, StorageEnum.current); expect(result, input); }); @@ -66,7 +68,8 @@ void main() { expect(data.length, ProjectionTypeEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); - expect(ProjectionTypeEnum.getValue(data[i][1]), data[i][0], reason: data[i][1]); + expect(ProjectionTypeEnum.getValue(data[i][1]), data[i][0], + reason: data[i][1]); } }); } diff --git a/flutter/test/enum_name_test.dart b/flutter/test/enum_name_test.dart index a317ba67fc..6fe942c124 100644 --- a/flutter/test/enum_name_test.dart +++ b/flutter/test/enum_name_test.dart @@ -36,7 +36,8 @@ void main() { [BurstCaptureNumEnum.burstCaptureNum_7, 'BURST_CAPTURE_NUM_7'], [BurstCaptureNumEnum.burstCaptureNum_9, 'BURST_CAPTURE_NUM_9'], ]; - expect(data.length, BurstCaptureNumEnum.values.length, reason: 'enum count'); + expect(data.length, BurstCaptureNumEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -55,7 +56,8 @@ void main() { [BurstBracketStepEnum.bracketStep_2_7, 'BRACKET_STEP_2_7'], [BurstBracketStepEnum.bracketStep_3_0, 'BRACKET_STEP_3_0'], ]; - expect(data.length, BurstBracketStepEnum.values.length, reason: 'enum count'); + expect(data.length, BurstBracketStepEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -63,39 +65,130 @@ void main() { test('BurstCompensationEnum', () async { List> data = [ - [BurstCompensationEnum.burstCompensationDown_5_0, 'BURST_COMPENSATION_DOWN_5_0'], - [BurstCompensationEnum.burstCompensationDown_4_7, 'BURST_COMPENSATION_DOWN_4_7'], - [BurstCompensationEnum.burstCompensationDown_4_3, 'BURST_COMPENSATION_DOWN_4_3'], - [BurstCompensationEnum.burstCompensationDown_4_0, 'BURST_COMPENSATION_DOWN_4_0'], - [BurstCompensationEnum.burstCompensationDown_3_7, 'BURST_COMPENSATION_DOWN_3_7'], - [BurstCompensationEnum.burstCompensationDown_3_3, 'BURST_COMPENSATION_DOWN_3_3'], - [BurstCompensationEnum.burstCompensationDown_3_0, 'BURST_COMPENSATION_DOWN_3_0'], - [BurstCompensationEnum.burstCompensationDown_2_7, 'BURST_COMPENSATION_DOWN_2_7'], - [BurstCompensationEnum.burstCompensationDown_2_3, 'BURST_COMPENSATION_DOWN_2_3'], - [BurstCompensationEnum.burstCompensationDown_2_0, 'BURST_COMPENSATION_DOWN_2_0'], - [BurstCompensationEnum.burstCompensationDown_1_7, 'BURST_COMPENSATION_DOWN_1_7'], - [BurstCompensationEnum.burstCompensationDown_1_3, 'BURST_COMPENSATION_DOWN_1_3'], - [BurstCompensationEnum.burstCompensationDown_1_0, 'BURST_COMPENSATION_DOWN_1_0'], - [BurstCompensationEnum.burstCompensationDown_0_7, 'BURST_COMPENSATION_DOWN_0_7'], - [BurstCompensationEnum.burstCompensationDown_0_3, 'BURST_COMPENSATION_DOWN_0_3'], + [ + BurstCompensationEnum.burstCompensationDown_5_0, + 'BURST_COMPENSATION_DOWN_5_0' + ], + [ + BurstCompensationEnum.burstCompensationDown_4_7, + 'BURST_COMPENSATION_DOWN_4_7' + ], + [ + BurstCompensationEnum.burstCompensationDown_4_3, + 'BURST_COMPENSATION_DOWN_4_3' + ], + [ + BurstCompensationEnum.burstCompensationDown_4_0, + 'BURST_COMPENSATION_DOWN_4_0' + ], + [ + BurstCompensationEnum.burstCompensationDown_3_7, + 'BURST_COMPENSATION_DOWN_3_7' + ], + [ + BurstCompensationEnum.burstCompensationDown_3_3, + 'BURST_COMPENSATION_DOWN_3_3' + ], + [ + BurstCompensationEnum.burstCompensationDown_3_0, + 'BURST_COMPENSATION_DOWN_3_0' + ], + [ + BurstCompensationEnum.burstCompensationDown_2_7, + 'BURST_COMPENSATION_DOWN_2_7' + ], + [ + BurstCompensationEnum.burstCompensationDown_2_3, + 'BURST_COMPENSATION_DOWN_2_3' + ], + [ + BurstCompensationEnum.burstCompensationDown_2_0, + 'BURST_COMPENSATION_DOWN_2_0' + ], + [ + BurstCompensationEnum.burstCompensationDown_1_7, + 'BURST_COMPENSATION_DOWN_1_7' + ], + [ + BurstCompensationEnum.burstCompensationDown_1_3, + 'BURST_COMPENSATION_DOWN_1_3' + ], + [ + BurstCompensationEnum.burstCompensationDown_1_0, + 'BURST_COMPENSATION_DOWN_1_0' + ], + [ + BurstCompensationEnum.burstCompensationDown_0_7, + 'BURST_COMPENSATION_DOWN_0_7' + ], + [ + BurstCompensationEnum.burstCompensationDown_0_3, + 'BURST_COMPENSATION_DOWN_0_3' + ], [BurstCompensationEnum.burstCompensation_0_0, 'BURST_COMPENSATION_0_0'], - [BurstCompensationEnum.burstCompensationUp_0_3, 'BURST_COMPENSATION_UP_0_3'], - [BurstCompensationEnum.burstCompensationUp_0_7, 'BURST_COMPENSATION_UP_0_7'], - [BurstCompensationEnum.burstCompensationUp_1_0, 'BURST_COMPENSATION_UP_1_0'], - [BurstCompensationEnum.burstCompensationUp_1_3, 'BURST_COMPENSATION_UP_1_3'], - [BurstCompensationEnum.burstCompensationUp_1_7, 'BURST_COMPENSATION_UP_1_7'], - [BurstCompensationEnum.burstCompensationUp_2_0, 'BURST_COMPENSATION_UP_2_0'], - [BurstCompensationEnum.burstCompensationUp_2_3, 'BURST_COMPENSATION_UP_2_3'], - [BurstCompensationEnum.burstCompensationUp_2_7, 'BURST_COMPENSATION_UP_2_7'], - [BurstCompensationEnum.burstCompensationUp_3_0, 'BURST_COMPENSATION_UP_3_0'], - [BurstCompensationEnum.burstCompensationUp_3_3, 'BURST_COMPENSATION_UP_3_3'], - [BurstCompensationEnum.burstCompensationUp_3_7, 'BURST_COMPENSATION_UP_3_7'], - [BurstCompensationEnum.burstCompensationUp_4_0, 'BURST_COMPENSATION_UP_4_0'], - [BurstCompensationEnum.burstCompensationUp_4_3, 'BURST_COMPENSATION_UP_4_3'], - [BurstCompensationEnum.burstCompensationUp_4_7, 'BURST_COMPENSATION_UP_4_7'], - [BurstCompensationEnum.burstCompensationUp_5_0, 'BURST_COMPENSATION_UP_5_0'], - ]; - expect(data.length, BurstCompensationEnum.values.length, reason: 'enum count'); + [ + BurstCompensationEnum.burstCompensationUp_0_3, + 'BURST_COMPENSATION_UP_0_3' + ], + [ + BurstCompensationEnum.burstCompensationUp_0_7, + 'BURST_COMPENSATION_UP_0_7' + ], + [ + BurstCompensationEnum.burstCompensationUp_1_0, + 'BURST_COMPENSATION_UP_1_0' + ], + [ + BurstCompensationEnum.burstCompensationUp_1_3, + 'BURST_COMPENSATION_UP_1_3' + ], + [ + BurstCompensationEnum.burstCompensationUp_1_7, + 'BURST_COMPENSATION_UP_1_7' + ], + [ + BurstCompensationEnum.burstCompensationUp_2_0, + 'BURST_COMPENSATION_UP_2_0' + ], + [ + BurstCompensationEnum.burstCompensationUp_2_3, + 'BURST_COMPENSATION_UP_2_3' + ], + [ + BurstCompensationEnum.burstCompensationUp_2_7, + 'BURST_COMPENSATION_UP_2_7' + ], + [ + BurstCompensationEnum.burstCompensationUp_3_0, + 'BURST_COMPENSATION_UP_3_0' + ], + [ + BurstCompensationEnum.burstCompensationUp_3_3, + 'BURST_COMPENSATION_UP_3_3' + ], + [ + BurstCompensationEnum.burstCompensationUp_3_7, + 'BURST_COMPENSATION_UP_3_7' + ], + [ + BurstCompensationEnum.burstCompensationUp_4_0, + 'BURST_COMPENSATION_UP_4_0' + ], + [ + BurstCompensationEnum.burstCompensationUp_4_3, + 'BURST_COMPENSATION_UP_4_3' + ], + [ + BurstCompensationEnum.burstCompensationUp_4_7, + 'BURST_COMPENSATION_UP_4_7' + ], + [ + BurstCompensationEnum.burstCompensationUp_5_0, + 'BURST_COMPENSATION_UP_5_0' + ], + ]; + expect(data.length, BurstCompensationEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -104,8 +197,14 @@ void main() { test('BurstMaxExposureTimeEnum', () async { List> data = [ [BurstMaxExposureTimeEnum.maxExposureTime_0_5, 'MAX_EXPOSURE_TIME_0_5'], - [BurstMaxExposureTimeEnum.maxExposureTime_0_625, 'MAX_EXPOSURE_TIME_0_625'], - [BurstMaxExposureTimeEnum.maxExposureTime_0_76923076, 'MAX_EXPOSURE_TIME_0_76923076'], + [ + BurstMaxExposureTimeEnum.maxExposureTime_0_625, + 'MAX_EXPOSURE_TIME_0_625' + ], + [ + BurstMaxExposureTimeEnum.maxExposureTime_0_76923076, + 'MAX_EXPOSURE_TIME_0_76923076' + ], [BurstMaxExposureTimeEnum.maxExposureTime_1, 'MAX_EXPOSURE_TIME_1'], [BurstMaxExposureTimeEnum.maxExposureTime_1_3, 'MAX_EXPOSURE_TIME_1_3'], [BurstMaxExposureTimeEnum.maxExposureTime_1_6, 'MAX_EXPOSURE_TIME_1_6'], @@ -126,7 +225,8 @@ void main() { [BurstMaxExposureTimeEnum.maxExposureTime_50, 'MAX_EXPOSURE_TIME_50'], [BurstMaxExposureTimeEnum.maxExposureTime_60, 'MAX_EXPOSURE_TIME_60'], ]; - expect(data.length, BurstMaxExposureTimeEnum.values.length, reason: 'enum count'); + expect(data.length, BurstMaxExposureTimeEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -137,7 +237,8 @@ void main() { [BurstEnableIsoControlEnum.off, 'OFF'], [BurstEnableIsoControlEnum.on, 'ON'], ]; - expect(data.length, BurstEnableIsoControlEnum.values.length, reason: 'enum count'); + expect(data.length, BurstEnableIsoControlEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -163,7 +264,10 @@ void main() { [CaptureStatusEnum.converting, 'CONVERTING'], [CaptureStatusEnum.timeShiftShooting, 'TIME_SHIFT_SHOOTING'], [CaptureStatusEnum.continuousShooting, 'CONTINUOUS_SHOOTING'], - [CaptureStatusEnum.retrospectiveImageRecording, 'RETROSPECTIVE_IMAGE_RECORDING'], + [ + CaptureStatusEnum.retrospectiveImageRecording, + 'RETROSPECTIVE_IMAGE_RECORDING' + ], ]; expect(data.length, CaptureStatusEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { @@ -208,7 +312,8 @@ void main() { [ContinuousNumberEnum.max20, 'MAX_20'], [ContinuousNumberEnum.unsupported, 'UNSUPPORTED'], ]; - expect(data.length, ContinuousNumberEnum.values.length, reason: 'enum count'); + expect(data.length, ContinuousNumberEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -220,7 +325,8 @@ void main() { [ShootingFunctionEnum.selfTimer, 'SELF_TIMER'], [ShootingFunctionEnum.mySetting, 'MY_SETTING'], ]; - expect(data.length, ShootingFunctionEnum.values.length, reason: 'enum count'); + expect(data.length, ShootingFunctionEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -232,7 +338,8 @@ void main() { [MicrophoneOptionEnum.internal, 'INTERNAL'], [MicrophoneOptionEnum.external, 'EXTERNAL'], ]; - expect(data.length, MicrophoneOptionEnum.values.length, reason: 'enum count'); + expect(data.length, MicrophoneOptionEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -286,7 +393,8 @@ void main() { [AiAutoThumbnailEnum.on, 'ON'], [AiAutoThumbnailEnum.off, 'OFF'], ]; - expect(data.length, AiAutoThumbnailEnum.values.length, reason: 'enum count'); + expect(data.length, AiAutoThumbnailEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -312,7 +420,8 @@ void main() { [CameraControlSourceEnum.camera, 'CAMERA'], [CameraControlSourceEnum.app, 'APP'], ]; - expect(data.length, CameraControlSourceEnum.values.length, reason: 'enum count'); + expect(data.length, CameraControlSourceEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -361,7 +470,8 @@ void main() { [ExposureCompensationEnum.p1_7, 'P1_7'], [ExposureCompensationEnum.p2_0, 'P2_0'], ]; - expect(data.length, ExposureCompensationEnum.values.length, reason: 'enum count'); + expect(data.length, ExposureCompensationEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -395,7 +505,8 @@ void main() { [ExposureProgramEnum.shutterPriority, 'SHUTTER_PRIORITY'], [ExposureProgramEnum.isoPriority, 'ISO_PRIORITY'], ]; - expect(data.length, ExposureProgramEnum.values.length, reason: 'enum count'); + expect(data.length, ExposureProgramEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -479,7 +590,8 @@ void main() { [PhotoFileFormatEnum.image_5_5K, 'IMAGE_5_5K'], [PhotoFileFormatEnum.image_11K, 'IMAGE_11K'], ]; - expect(data.length, PhotoFileFormatEnum.values.length, reason: 'enum count'); + expect(data.length, PhotoFileFormatEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -502,7 +614,8 @@ void main() { [VideoFileFormatEnum.video_7K_5F, 'VIDEO_7K_5F'], [VideoFileFormatEnum.video_7K_10F, 'VIDEO_7K_10F'], ]; - expect(data.length, VideoFileFormatEnum.values.length, reason: 'enum count'); + expect(data.length, VideoFileFormatEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -539,7 +652,8 @@ void main() { [GpsTagRecordingEnum.on, 'ON'], [GpsTagRecordingEnum.off, 'OFF'], ]; - expect(data.length, GpsTagRecordingEnum.values.length, reason: 'enum count'); + expect(data.length, GpsTagRecordingEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -599,7 +713,8 @@ void main() { [IsoAutoHighLimitEnum.iso5000, 'ISO_5000'], [IsoAutoHighLimitEnum.iso6400, 'ISO_6400'], ]; - expect(data.length, IsoAutoHighLimitEnum.values.length, reason: 'enum count'); + expect(data.length, IsoAutoHighLimitEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -629,9 +744,13 @@ void main() { [MaxRecordableTimeEnum.time_300, 'RECORDABLE_TIME_300'], [MaxRecordableTimeEnum.time_1500, 'RECORDABLE_TIME_1500'], [MaxRecordableTimeEnum.time_7200, 'RECORDABLE_TIME_7200'], - [MaxRecordableTimeEnum.doNotUpdateMySettingCondition, 'DO_NOT_UPDATE_MY_SETTING_CONDITION'], + [ + MaxRecordableTimeEnum.doNotUpdateMySettingCondition, + 'DO_NOT_UPDATE_MY_SETTING_CONDITION' + ], ]; - expect(data.length, MaxRecordableTimeEnum.values.length, reason: 'enum count'); + expect(data.length, MaxRecordableTimeEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -779,7 +898,8 @@ void main() { [TimeShiftIntervalEnum.interval_9, 'INTERVAL_9'], [TimeShiftIntervalEnum.interval_10, 'INTERVAL_10'], ]; - expect(data.length, TimeShiftIntervalEnum.values.length, reason: 'enum count'); + expect(data.length, TimeShiftIntervalEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } @@ -811,7 +931,8 @@ void main() { [WhiteBalanceAutoStrengthEnum.on, 'ON'], [WhiteBalanceAutoStrengthEnum.off, 'OFF'], ]; - expect(data.length, WhiteBalanceAutoStrengthEnum.values.length, reason: 'enum count'); + expect(data.length, WhiteBalanceAutoStrengthEnum.values.length, + reason: 'enum count'); for (int i = 0; i < data.length; i++) { expect(data[i][0].toString(), data[i][1], reason: data[i][1]); } diff --git a/flutter/test/options/option_shutter_speed_test.dart b/flutter/test/options/option_shutter_speed_test.dart index 1dfc307127..9be8b89b0f 100644 --- a/flutter/test/options/option_shutter_speed_test.dart +++ b/flutter/test/options/option_shutter_speed_test.dart @@ -57,22 +57,70 @@ void main() { [ShutterSpeedEnum.shutterSpeedOneOver_500, 'SHUTTER_SPEED_ONE_OVER_500'], [ShutterSpeedEnum.shutterSpeedOneOver_640, 'SHUTTER_SPEED_ONE_OVER_640'], [ShutterSpeedEnum.shutterSpeedOneOver_800, 'SHUTTER_SPEED_ONE_OVER_800'], - [ShutterSpeedEnum.shutterSpeedOneOver_1000, 'SHUTTER_SPEED_ONE_OVER_1000'], - [ShutterSpeedEnum.shutterSpeedOneOver_1250, 'SHUTTER_SPEED_ONE_OVER_1250'], - [ShutterSpeedEnum.shutterSpeedOneOver_1600, 'SHUTTER_SPEED_ONE_OVER_1600'], - [ShutterSpeedEnum.shutterSpeedOneOver_2000, 'SHUTTER_SPEED_ONE_OVER_2000'], - [ShutterSpeedEnum.shutterSpeedOneOver_2500, 'SHUTTER_SPEED_ONE_OVER_2500'], - [ShutterSpeedEnum.shutterSpeedOneOver_3200, 'SHUTTER_SPEED_ONE_OVER_3200'], - [ShutterSpeedEnum.shutterSpeedOneOver_4000, 'SHUTTER_SPEED_ONE_OVER_4000'], - [ShutterSpeedEnum.shutterSpeedOneOver_5000, 'SHUTTER_SPEED_ONE_OVER_5000'], - [ShutterSpeedEnum.shutterSpeedOneOver_6400, 'SHUTTER_SPEED_ONE_OVER_6400'], - [ShutterSpeedEnum.shutterSpeedOneOver_8000, 'SHUTTER_SPEED_ONE_OVER_8000'], - [ShutterSpeedEnum.shutterSpeedOneOver_10000, 'SHUTTER_SPEED_ONE_OVER_10000'], - [ShutterSpeedEnum.shutterSpeedOneOver_12500, 'SHUTTER_SPEED_ONE_OVER_12500'], - [ShutterSpeedEnum.shutterSpeedOneOver_12800, 'SHUTTER_SPEED_ONE_OVER_12800'], - [ShutterSpeedEnum.shutterSpeedOneOver_16000, 'SHUTTER_SPEED_ONE_OVER_16000'], - [ShutterSpeedEnum.shutterSpeedOneOver_20000, 'SHUTTER_SPEED_ONE_OVER_20000'], - [ShutterSpeedEnum.shutterSpeedOneOver_25000, 'SHUTTER_SPEED_ONE_OVER_25000'], + [ + ShutterSpeedEnum.shutterSpeedOneOver_1000, + 'SHUTTER_SPEED_ONE_OVER_1000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_1250, + 'SHUTTER_SPEED_ONE_OVER_1250' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_1600, + 'SHUTTER_SPEED_ONE_OVER_1600' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_2000, + 'SHUTTER_SPEED_ONE_OVER_2000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_2500, + 'SHUTTER_SPEED_ONE_OVER_2500' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_3200, + 'SHUTTER_SPEED_ONE_OVER_3200' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_4000, + 'SHUTTER_SPEED_ONE_OVER_4000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_5000, + 'SHUTTER_SPEED_ONE_OVER_5000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_6400, + 'SHUTTER_SPEED_ONE_OVER_6400' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_8000, + 'SHUTTER_SPEED_ONE_OVER_8000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_10000, + 'SHUTTER_SPEED_ONE_OVER_10000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_12500, + 'SHUTTER_SPEED_ONE_OVER_12500' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_12800, + 'SHUTTER_SPEED_ONE_OVER_12800' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_16000, + 'SHUTTER_SPEED_ONE_OVER_16000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_20000, + 'SHUTTER_SPEED_ONE_OVER_20000' + ], + [ + ShutterSpeedEnum.shutterSpeedOneOver_25000, + 'SHUTTER_SPEED_ONE_OVER_25000' + ], ]; expect(data.length, ShutterSpeedEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { diff --git a/flutter/test/theta_client_flutter_method_channel_test.dart b/flutter/test/theta_client_flutter_method_channel_test.dart index 07ee7c2c46..31ffcb43fb 100644 --- a/flutter/test/theta_client_flutter_method_channel_test.dart +++ b/flutter/test/theta_client_flutter_method_channel_test.dart @@ -122,10 +122,14 @@ void main() { const isMySettingChanged = true; const currentMicrophone = MicrophoneOptionEnum.auto; const isSdCard = true; - const cameraError = [CameraErrorEnum.batteryChargeFail, CameraErrorEnum.batteryHighTemperature]; + const cameraError = [ + CameraErrorEnum.batteryChargeFail, + CameraErrorEnum.batteryHighTemperature + ]; const isBatteryInsert = false; - List convertCameraErrorParam(List cameraErrorList) { + List convertCameraErrorParam( + List cameraErrorList) { var stringList = List.empty(growable: true); for (CameraErrorEnum element in cameraErrorList) { stringList.add(element.rawValue); @@ -169,7 +173,8 @@ void main() { expect(thetaState.recordedTime, recordedTime); expect(thetaState.recordableTime, recordableTime); expect(thetaState.capturedPictures, capturedPictures); - expect(thetaState.compositeShootingElapsedTime, compositeShootingElapsedTime); + expect( + thetaState.compositeShootingElapsedTime, compositeShootingElapsedTime); expect(thetaState.latestFileUrl, latestFileUrl); expect(thetaState.chargingState, chargingState); expect(thetaState.apiVersion, apiVersion); @@ -234,7 +239,11 @@ void main() { }); test('listFiles', () async { - const fileTypes = [FileTypeEnum.all, FileTypeEnum.image, FileTypeEnum.video]; + const fileTypes = [ + FileTypeEnum.all, + FileTypeEnum.image, + FileTypeEnum.video + ]; const entryCount = 10; const startPosition = 0; const name = 'R0013336.JPG'; @@ -275,8 +284,8 @@ void main() { for (int i = 0; i < fileTypes.length; i++) { index = i; - var thetaFiles = - await platform.listFiles(fileTypes[i], entryCount, startPosition, StorageEnum.current); + var thetaFiles = await platform.listFiles( + fileTypes[i], entryCount, startPosition, StorageEnum.current); expect(thetaFiles.fileList.length, 2); var fileInfo = thetaFiles.fileList[0]; expect(fileInfo.name, name); @@ -293,7 +302,11 @@ void main() { }); test('listFiles StorageEnum', () async { - const storages = [StorageEnum.internal, StorageEnum.sd, StorageEnum.current]; + const storages = [ + StorageEnum.internal, + StorageEnum.sd, + StorageEnum.current + ]; const entryCount = 10; const startPosition = 0; const name = 'R0013336.JPG'; @@ -335,8 +348,8 @@ void main() { for (int i = 0; i < storages.length; i++) { index = i; - var thetaFiles = - await platform.listFiles(FileTypeEnum.image, entryCount, startPosition, storages[index]); + var thetaFiles = await platform.listFiles( + FileTypeEnum.image, entryCount, startPosition, storages[index]); expect(thetaFiles.fileList.length, 2); var fileInfo = thetaFiles.fileList[0]; expect(fileInfo.name, name); @@ -370,10 +383,25 @@ void main() { 'secondInterval': TimeShiftIntervalEnum.interval_10.toString() }; List> data = [ - [OptionNameEnum.aiAutoThumbnail, 'AiAutoThumbnail', AiAutoThumbnailEnum.off, 'OFF'], - [OptionNameEnum.aperture, 'Aperture', ApertureEnum.aperture_2_0, 'APERTURE_2_0'], + [ + OptionNameEnum.aiAutoThumbnail, + 'AiAutoThumbnail', + AiAutoThumbnailEnum.off, + 'OFF' + ], + [ + OptionNameEnum.aperture, + 'Aperture', + ApertureEnum.aperture_2_0, + 'APERTURE_2_0' + ], [OptionNameEnum.bitrate, 'Bitrate', Bitrate.fine, 'FINE'], - [OptionNameEnum.bluetoothPower, 'BluetoothPower', BluetoothPowerEnum.off, 'OFF'], + [ + OptionNameEnum.bluetoothPower, + 'BluetoothPower', + BluetoothPowerEnum.off, + 'OFF' + ], [OptionNameEnum.burstMode, 'BurstMode', BurstModeEnum.on, 'ON'], [ OptionNameEnum.cameraControlSource, @@ -381,14 +409,34 @@ void main() { CameraControlSourceEnum.camera, 'CAMERA' ], - [OptionNameEnum.cameraMode, 'CameraMode', CameraModeEnum.capture, 'CAPTURE'], - [OptionNameEnum.captureMode, 'CaptureMode', CaptureModeEnum.image, 'IMAGE'], + [ + OptionNameEnum.cameraMode, + 'CameraMode', + CameraModeEnum.capture, + 'CAPTURE' + ], + [ + OptionNameEnum.captureMode, + 'CaptureMode', + CaptureModeEnum.image, + 'IMAGE' + ], [OptionNameEnum.captureInterval, 'CaptureInterval', 6, 6], [OptionNameEnum.captureNumber, 'CaptureNumber', 0, 0], [OptionNameEnum.colorTemperature, 'ColorTemperature', 2, 2], - [OptionNameEnum.compositeShootingOutputInterval, 'CompositeShootingOutputInterval', 60, 60], + [ + OptionNameEnum.compositeShootingOutputInterval, + 'CompositeShootingOutputInterval', + 60, + 60 + ], [OptionNameEnum.compositeShootingTime, 'CompositeShootingTime', 600, 600], - [OptionNameEnum.continuousNumber, 'ContinuousNumber', ContinuousNumberEnum.max11, 'MAX_11'], + [ + OptionNameEnum.continuousNumber, + 'ContinuousNumber', + ContinuousNumberEnum.max11, + 'MAX_11' + ], [ OptionNameEnum.dateTimeZone, 'DateTimeZone', @@ -401,7 +449,12 @@ void main() { ExposureCompensationEnum.m0_3, 'M0_3' ], - [OptionNameEnum.exposureDelay, 'ExposureDelay', ExposureDelayEnum.delay1, 'DELAY_1'], + [ + OptionNameEnum.exposureDelay, + 'ExposureDelay', + ExposureDelayEnum.delay1, + 'DELAY_1' + ], [ OptionNameEnum.exposureProgram, 'ExposureProgram', @@ -409,9 +462,19 @@ void main() { 'APERTURE_PRIORITY' ], [OptionNameEnum.faceDetect, 'FaceDetect', FaceDetectEnum.off, 'OFF'], - [OptionNameEnum.fileFormat, 'FileFormat', FileFormatEnum.image_2K, 'IMAGE_2K'], + [ + OptionNameEnum.fileFormat, + 'FileFormat', + FileFormatEnum.image_2K, + 'IMAGE_2K' + ], [OptionNameEnum.filter, 'Filter', FilterEnum.hdr, 'HDR'], - [OptionNameEnum.function, 'Function', ShootingFunctionEnum.normal, 'NORMAL'], + [ + OptionNameEnum.function, + 'Function', + ShootingFunctionEnum.normal, + 'NORMAL' + ], [OptionNameEnum.gain, 'Gain', GainEnum.normal, 'NORMAL'], [ OptionNameEnum.gpsInfo, @@ -419,10 +482,20 @@ void main() { GpsInfo(1.0, 2.0, 3.0, '2022:01:01 00:01:00+09:00'), gpsInfoMap ], - [OptionNameEnum.imageStitching, 'ImageStitching', ImageStitchingEnum.auto, 'AUTO'], + [ + OptionNameEnum.imageStitching, + 'ImageStitching', + ImageStitchingEnum.auto, + 'AUTO' + ], [OptionNameEnum.isGpsOn, 'IsGpsOn', true, true], [OptionNameEnum.iso, 'Iso', IsoEnum.iso50, 'ISO_50'], - [OptionNameEnum.isoAutoHighLimit, 'IsoAutoHighLimit', IsoAutoHighLimitEnum.iso200, 'ISO_200'], + [ + OptionNameEnum.isoAutoHighLimit, + 'IsoAutoHighLimit', + IsoAutoHighLimitEnum.iso200, + 'ISO_200' + ], [OptionNameEnum.language, 'Language', LanguageEnum.de, 'DE'], [ OptionNameEnum.maxRecordableTime, @@ -430,8 +503,18 @@ void main() { MaxRecordableTimeEnum.time_1500, 'RECORDABLE_TIME_1500' ], - [OptionNameEnum.networkType, 'NetworkType', NetworkTypeEnum.client, 'CLIENT'], - [OptionNameEnum.offDelay, 'OffDelay', OffDelayEnum.offDelay_10m, 'OFF_DELAY_10M'], + [ + OptionNameEnum.networkType, + 'NetworkType', + NetworkTypeEnum.client, + 'CLIENT' + ], + [ + OptionNameEnum.offDelay, + 'OffDelay', + OffDelayEnum.offDelay_10m, + 'OFF_DELAY_10M' + ], [OptionNameEnum.password, 'Password', 'password', 'password'], [OptionNameEnum.powerSaving, 'PowerSaving', PowerSavingEnum.on, 'ON'], [OptionNameEnum.preset, 'Preset', PresetEnum.room, 'ROOM'], @@ -445,7 +528,12 @@ void main() { [OptionNameEnum.remainingPictures, 'RemainingPictures', 3, 3], [OptionNameEnum.remainingVideoSeconds, 'RemainingVideoSeconds', 4, 4], [OptionNameEnum.remainingSpace, 'RemainingSpace', 5, 5], - [OptionNameEnum.shootingMethod, 'ShootingMethod', ShootingMethodEnum.normal, 'NORMAL'], + [ + OptionNameEnum.shootingMethod, + 'ShootingMethod', + ShootingMethodEnum.normal, + 'NORMAL' + ], [ OptionNameEnum.shutterSpeed, 'ShutterSpeed', @@ -453,7 +541,12 @@ void main() { 'SHUTTER_SPEED_ONE_OVER_10' ], [OptionNameEnum.shutterVolume, 'ShutterVolume', 7, 7], - [OptionNameEnum.sleepDelay, 'SleepDelay', SleepDelayEnum.sleepDelay_10m, 'SLEEP_DELAY_10M'], + [ + OptionNameEnum.sleepDelay, + 'SleepDelay', + SleepDelayEnum.sleepDelay_10m, + 'SLEEP_DELAY_10M' + ], [ OptionNameEnum.timeShift, 'TimeShift', @@ -477,7 +570,12 @@ void main() { WhiteBalanceAutoStrengthEnum.off, 'OFF' ], - [OptionNameEnum.wlanFrequency, 'WlanFrequency', WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4'], + [ + OptionNameEnum.wlanFrequency, + 'WlanFrequency', + WlanFrequencyEnum.ghz_2_4, + 'GHZ_2_4' + ], ]; Map optionMap = {}; @@ -527,17 +625,47 @@ void main() { 'secondInterval': TimeShiftIntervalEnum.interval_10.toString() }; List> data = [ - [OptionNameEnum.aiAutoThumbnail, 'AiAutoThumbnail', AiAutoThumbnailEnum.on, 'ON'], - [OptionNameEnum.aperture, 'Aperture', ApertureEnum.aperture_2_0, 'APERTURE_2_0'], + [ + OptionNameEnum.aiAutoThumbnail, + 'AiAutoThumbnail', + AiAutoThumbnailEnum.on, + 'ON' + ], + [ + OptionNameEnum.aperture, + 'Aperture', + ApertureEnum.aperture_2_0, + 'APERTURE_2_0' + ], [OptionNameEnum.bitrate, 'Bitrate', Bitrate.fine, 'FINE'], - [OptionNameEnum.bluetoothPower, 'BluetoothPower', BluetoothPowerEnum.on, 'ON'], + [ + OptionNameEnum.bluetoothPower, + 'BluetoothPower', + BluetoothPowerEnum.on, + 'ON' + ], [OptionNameEnum.burstMode, 'BurstMode', BurstModeEnum.on, 'ON'], - [OptionNameEnum.cameraMode, 'CameraMode', CameraModeEnum.capture, 'CAPTURE'], - [OptionNameEnum.captureMode, 'CaptureMode', CaptureModeEnum.image, 'IMAGE'], + [ + OptionNameEnum.cameraMode, + 'CameraMode', + CameraModeEnum.capture, + 'CAPTURE' + ], + [ + OptionNameEnum.captureMode, + 'CaptureMode', + CaptureModeEnum.image, + 'IMAGE' + ], [OptionNameEnum.captureInterval, 'CaptureInterval', 4, 4], [OptionNameEnum.captureNumber, 'CaptureNumber', 2, 2], [OptionNameEnum.colorTemperature, 'ColorTemperature', 2, 2], - [OptionNameEnum.compositeShootingOutputInterval, 'CompositeShootingOutputInterval', 60, 60], + [ + OptionNameEnum.compositeShootingOutputInterval, + 'CompositeShootingOutputInterval', + 60, + 60 + ], [OptionNameEnum.compositeShootingTime, 'CompositeShootingTime', 600, 600], [ OptionNameEnum.dateTimeZone, @@ -551,7 +679,12 @@ void main() { ExposureCompensationEnum.m0_3, 'M0_3' ], - [OptionNameEnum.exposureDelay, 'ExposureDelay', ExposureDelayEnum.delay1, 'DELAY_1'], + [ + OptionNameEnum.exposureDelay, + 'ExposureDelay', + ExposureDelayEnum.delay1, + 'DELAY_1' + ], [ OptionNameEnum.exposureProgram, 'ExposureProgram', @@ -559,9 +692,19 @@ void main() { 'APERTURE_PRIORITY' ], [OptionNameEnum.faceDetect, 'FaceDetect', FaceDetectEnum.on, 'ON'], - [OptionNameEnum.fileFormat, 'FileFormat', FileFormatEnum.image_2K, 'IMAGE_2K'], + [ + OptionNameEnum.fileFormat, + 'FileFormat', + FileFormatEnum.image_2K, + 'IMAGE_2K' + ], [OptionNameEnum.filter, 'Filter', FilterEnum.hdr, 'HDR'], - [OptionNameEnum.function, 'Function', ShootingFunctionEnum.selfTimer, 'SELF_TIMER'], + [ + OptionNameEnum.function, + 'Function', + ShootingFunctionEnum.selfTimer, + 'SELF_TIMER' + ], [OptionNameEnum.gain, 'Gain', GainEnum.megaVolume, 'MEGA_VOLUME'], [ OptionNameEnum.gpsInfo, @@ -569,10 +712,20 @@ void main() { GpsInfo(1.0, 2.0, 3.0, '2022:01:01 00:01:00+09:00'), gpsInfoMap ], - [OptionNameEnum.imageStitching, 'ImageStitching', ImageStitchingEnum.auto, 'AUTO'], + [ + OptionNameEnum.imageStitching, + 'ImageStitching', + ImageStitchingEnum.auto, + 'AUTO' + ], [OptionNameEnum.isGpsOn, 'IsGpsOn', true, true], [OptionNameEnum.iso, 'Iso', IsoEnum.iso50, 'ISO_50'], - [OptionNameEnum.isoAutoHighLimit, 'IsoAutoHighLimit', IsoAutoHighLimitEnum.iso200, 'ISO_200'], + [ + OptionNameEnum.isoAutoHighLimit, + 'IsoAutoHighLimit', + IsoAutoHighLimitEnum.iso200, + 'ISO_200' + ], [OptionNameEnum.language, 'Language', LanguageEnum.de, 'DE'], [ OptionNameEnum.maxRecordableTime, @@ -580,8 +733,18 @@ void main() { MaxRecordableTimeEnum.time_1500, 'RECORDABLE_TIME_1500' ], - [OptionNameEnum.networkType, 'NetworkType', NetworkTypeEnum.client, 'CLIENT'], - [OptionNameEnum.offDelay, 'OffDelay', OffDelayEnum.offDelay_15m, 'OFF_DELAY_15M'], + [ + OptionNameEnum.networkType, + 'NetworkType', + NetworkTypeEnum.client, + 'CLIENT' + ], + [ + OptionNameEnum.offDelay, + 'OffDelay', + OffDelayEnum.offDelay_15m, + 'OFF_DELAY_15M' + ], [OptionNameEnum.password, 'Password', 'password', 'password'], [OptionNameEnum.powerSaving, 'PowerSaving', PowerSavingEnum.on, 'ON'], [OptionNameEnum.preset, 'Preset', PresetEnum.room, 'ROOM'], @@ -595,7 +758,12 @@ void main() { [OptionNameEnum.remainingPictures, 'RemainingPictures', 3, 3], [OptionNameEnum.remainingVideoSeconds, 'RemainingVideoSeconds', 4, 4], [OptionNameEnum.remainingSpace, 'RemainingSpace', 5, 5], - [OptionNameEnum.shootingMethod, 'ShootingMethod', ShootingMethodEnum.normal, 'NORMAL'], + [ + OptionNameEnum.shootingMethod, + 'ShootingMethod', + ShootingMethodEnum.normal, + 'NORMAL' + ], [ OptionNameEnum.shutterSpeed, 'ShutterSpeed', @@ -603,7 +771,12 @@ void main() { 'SHUTTER_SPEED_ONE_OVER_20' ], [OptionNameEnum.shutterVolume, 'ShutterVolume', 7, 7], - [OptionNameEnum.sleepDelay, 'SleepDelay', SleepDelayEnum.sleepDelay_10m, 'SLEEP_DELAY_10M'], + [ + OptionNameEnum.sleepDelay, + 'SleepDelay', + SleepDelayEnum.sleepDelay_10m, + 'SLEEP_DELAY_10M' + ], [ OptionNameEnum.timeShift, 'TimeShift', @@ -627,7 +800,12 @@ void main() { WhiteBalanceAutoStrengthEnum.on, 'ON' ], - [OptionNameEnum.wlanFrequency, 'WlanFrequency', WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4'], + [ + OptionNameEnum.wlanFrequency, + 'WlanFrequency', + WlanFrequencyEnum.ghz_2_4, + 'GHZ_2_4' + ], ]; Map optionMap = {}; @@ -885,7 +1063,9 @@ void main() { expect(arguments['applyTopBottomCorrection'], applyTopBottomCorrection); return Future.value(result); }); - expect(await platform.convertVideoFormats(fileUrl, toLowResolution, applyTopBottomCorrection), + expect( + await platform.convertVideoFormats( + fileUrl, toLowResolution, applyTopBottomCorrection), result); }); @@ -946,16 +1126,21 @@ void main() { for (int i = 0; i < resultList.length; i++) { expect(resultList[i].ssid, data[i]['ssid']); expect(resultList[i].ssidStealth, data[i]['ssidStealth']); - expect(resultList[i].authMode, AuthModeEnum.getValue(data[i]['authMode'] as String)); + expect(resultList[i].authMode, + AuthModeEnum.getValue(data[i]['authMode'] as String)); expect(resultList[i].connectionPriority, data[i]['connectionPriority']); expect(resultList[i].usingDhcp, data[i]['usingDhcp']); expect(resultList[i].ipAddress, data[i]['ipAddress']); expect(resultList[i].subnetMask, data[i]['subnetMask']); expect(resultList[i].defaultGateway, data[i]['defaultGateway']); - expect(resultList[i].proxy?.use, (data[i]['proxy'] as Map)['use']); - expect(resultList[i].proxy?.url, (data[i]['proxy'] as Map)['url']); - expect(resultList[i].proxy?.port, (data[i]['proxy'] as Map)['port']); - expect(resultList[i].proxy?.userid, (data[i]['proxy'] as Map)['userid']); + expect(resultList[i].proxy?.use, + (data[i]['proxy'] as Map)['use']); + expect(resultList[i].proxy?.url, + (data[i]['proxy'] as Map)['url']); + expect(resultList[i].proxy?.port, + (data[i]['proxy'] as Map)['port']); + expect(resultList[i].proxy?.userid, + (data[i]['proxy'] as Map)['userid']); } }); @@ -1017,8 +1202,16 @@ void main() { expect(arguments['proxy']['password'], proxy.password); return Future.value(); }); - await platform.setAccessPointStatically(ssid, ssidStealth, authMode, password, - connectionPriority, ipAddress, subnetMask, defaultGateway, proxy); + await platform.setAccessPointStatically( + ssid, + ssidStealth, + authMode, + password, + connectionPriority, + ipAddress, + subnetMask, + defaultGateway, + proxy); }); test('deleteAccessPoint', () async { @@ -1033,7 +1226,12 @@ void main() { test('getMySetting', () async { List> data = [ - [OptionNameEnum.aperture, 'Aperture', ApertureEnum.apertureAuto, 'APERTURE_AUTO'], + [ + OptionNameEnum.aperture, + 'Aperture', + ApertureEnum.apertureAuto, + 'APERTURE_AUTO' + ], [OptionNameEnum.colorTemperature, 'ColorTemperature', 5000, 5000], [ OptionNameEnum.exposureCompensation, @@ -1041,14 +1239,24 @@ void main() { ExposureCompensationEnum.zero, 'ZERO' ], - [OptionNameEnum.exposureDelay, 'ExposureDelay', ExposureDelayEnum.delayOff, 'DELAY_OFF'], + [ + OptionNameEnum.exposureDelay, + 'ExposureDelay', + ExposureDelayEnum.delayOff, + 'DELAY_OFF' + ], [ OptionNameEnum.exposureProgram, 'ExposureProgram', ExposureProgramEnum.normalProgram, 'NORMAL_PROGRAM' ], - [OptionNameEnum.fileFormat, 'FileFormat', FileFormatEnum.image_6_7K, 'IMAGE_6_7K'], + [ + OptionNameEnum.fileFormat, + 'FileFormat', + FileFormatEnum.image_6_7K, + 'IMAGE_6_7K' + ], [OptionNameEnum.filter, 'Filter', FilterEnum.off, 'OFF'], [OptionNameEnum.iso, 'Iso', IsoEnum.isoAuto, 'ISO_AUTO'], [ @@ -1057,7 +1265,12 @@ void main() { IsoAutoHighLimitEnum.iso6400, 'ISO_6400' ], - [OptionNameEnum.whiteBalance, 'WhiteBalance', WhiteBalanceEnum.auto, 'AUTO'], + [ + OptionNameEnum.whiteBalance, + 'WhiteBalance', + WhiteBalanceEnum.auto, + 'AUTO' + ], [ OptionNameEnum.whiteBalanceAutoStrength, 'WhiteBalanceAutoStrength', @@ -1091,7 +1304,12 @@ void main() { test('getMySettingFromOldModel', () async { List> data = [ - [OptionNameEnum.aperture, 'Aperture', ApertureEnum.apertureAuto, 'APERTURE_AUTO'], + [ + OptionNameEnum.aperture, + 'Aperture', + ApertureEnum.apertureAuto, + 'APERTURE_AUTO' + ], [OptionNameEnum.colorTemperature, 'ColorTemperature', 5000, 5000], [ OptionNameEnum.exposureCompensation, @@ -1099,14 +1317,24 @@ void main() { ExposureCompensationEnum.zero, 'ZERO' ], - [OptionNameEnum.exposureDelay, 'ExposureDelay', ExposureDelayEnum.delayOff, 'DELAY_OFF'], + [ + OptionNameEnum.exposureDelay, + 'ExposureDelay', + ExposureDelayEnum.delayOff, + 'DELAY_OFF' + ], [ OptionNameEnum.exposureProgram, 'ExposureProgram', ExposureProgramEnum.normalProgram, 'NORMAL_PROGRAM' ], - [OptionNameEnum.fileFormat, 'FileFormat', FileFormatEnum.image_6_7K, 'IMAGE_6_7K'], + [ + OptionNameEnum.fileFormat, + 'FileFormat', + FileFormatEnum.image_6_7K, + 'IMAGE_6_7K' + ], [OptionNameEnum.filter, 'Filter', FilterEnum.off, 'OFF'], [OptionNameEnum.iso, 'Iso', IsoEnum.isoAuto, 'ISO_AUTO'], [ @@ -1115,7 +1343,12 @@ void main() { IsoAutoHighLimitEnum.iso6400, 'ISO_6400' ], - [OptionNameEnum.whiteBalance, 'WhiteBalance', WhiteBalanceEnum.auto, 'AUTO'], + [ + OptionNameEnum.whiteBalance, + 'WhiteBalance', + WhiteBalanceEnum.auto, + 'AUTO' + ], [ OptionNameEnum.whiteBalanceAutoStrength, 'WhiteBalanceAutoStrength', @@ -1151,7 +1384,12 @@ void main() { test('setMySetting', () async { List> data = [ - [OptionNameEnum.aperture, 'Aperture', ApertureEnum.apertureAuto, 'APERTURE_AUTO'], + [ + OptionNameEnum.aperture, + 'Aperture', + ApertureEnum.apertureAuto, + 'APERTURE_AUTO' + ], [OptionNameEnum.colorTemperature, 'ColorTemperature', 100, 100], [ OptionNameEnum.exposureCompensation, @@ -1159,14 +1397,24 @@ void main() { ExposureCompensationEnum.p0_3, 'P0_3' ], - [OptionNameEnum.exposureDelay, 'ExposureDelay', ExposureDelayEnum.delay2, 'DELAY_2'], + [ + OptionNameEnum.exposureDelay, + 'ExposureDelay', + ExposureDelayEnum.delay2, + 'DELAY_2' + ], [ OptionNameEnum.exposureProgram, 'ExposureProgram', ExposureProgramEnum.shutterPriority, 'SHUTTER_PRIORITY' ], - [OptionNameEnum.fileFormat, 'FileFormat', FileFormatEnum.image_2K, 'IMAGE_2K'], + [ + OptionNameEnum.fileFormat, + 'FileFormat', + FileFormatEnum.image_2K, + 'IMAGE_2K' + ], [OptionNameEnum.filter, 'Filter', FilterEnum.hdr, 'HDR'], [OptionNameEnum.iso, 'Iso', IsoEnum.iso100, 'ISO_100'], [ @@ -1175,7 +1423,12 @@ void main() { IsoAutoHighLimitEnum.iso1250, 'ISO_1250' ], - [OptionNameEnum.whiteBalance, 'WhiteBalance', WhiteBalanceEnum.auto, 'AUTO'], + [ + OptionNameEnum.whiteBalance, + 'WhiteBalance', + WhiteBalanceEnum.auto, + 'AUTO' + ], [ OptionNameEnum.whiteBalanceAutoStrength, 'WhiteBalanceAutoStrength', @@ -1214,7 +1467,8 @@ void main() { test('deleteMySetting', () async { channel.setMockMethodCallHandler((MethodCall methodCall) async { expect(methodCall.method, 'deleteMySetting'); - expect(methodCall.arguments['captureMode'], CaptureModeEnum.image.rawValue); + expect( + methodCall.arguments['captureMode'], CaptureModeEnum.image.rawValue); return Future.value(); }); await platform.deleteMySetting(CaptureModeEnum.image); diff --git a/flutter/test/theta_client_flutter_test.dart b/flutter/test/theta_client_flutter_test.dart index 8ee10c6118..87733c472e 100644 --- a/flutter/test/theta_client_flutter_test.dart +++ b/flutter/test/theta_client_flutter_test.dart @@ -1,10 +1,10 @@ import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:theta_client_flutter/theta_client_flutter.dart'; -import 'package:theta_client_flutter/theta_client_flutter_platform_interface.dart'; import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; -import 'package:plugin_platform_interface/plugin_platform_interface.dart'; +import 'package:theta_client_flutter/theta_client_flutter_platform_interface.dart'; class MockThetaClientFlutterPlatform with MockPlatformInterfaceMixin @@ -33,7 +33,8 @@ class MockThetaClientFlutterPlatform } @override - Future initialize(String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { + Future initialize( + String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { return onCallInitialize(); } @@ -43,8 +44,8 @@ class MockThetaClientFlutterPlatform } @override - Future listFiles( - FileTypeEnum fileType, int entryCount, int startPosition, StorageEnum? storage) { + Future listFiles(FileTypeEnum fileType, int entryCount, + int startPosition, StorageEnum? storage) { return onCallListFiles(); } @@ -95,8 +96,9 @@ class MockThetaClientFlutterPlatform } @override - Future startVideoCapture() { - return onCallStartVideoCapture(); + Future startVideoCapture( + void Function(Exception exception)? onStopFailed) { + return onCallStartVideoCapture(onStopFailed); } @override @@ -176,8 +178,13 @@ class MockThetaClientFlutterPlatform } @override - Future setAccessPointDynamically(String ssid, bool ssidStealth, AuthModeEnum authMode, - String password, int connectionPriority, Proxy? proxy) { + Future setAccessPointDynamically( + String ssid, + bool ssidStealth, + AuthModeEnum authMode, + String password, + int connectionPriority, + Proxy? proxy) { return Future.value(); } @@ -269,15 +276,20 @@ Future Function() onGetThetaState = Future.value; Future Function() onCallGetLivePreview = Future.value; Future Function() onCallListFiles = Future.value; Future Function() onCallGetPhotoCaptureBuilder = Future.value; -Future Function(Map options) onCallBuildPhotoCapture = Future.value; +Future Function(Map options) onCallBuildPhotoCapture = + Future.value; Future Function() onCallTakePicture = Future.value; Future Function() onCallGetTimeShiftCaptureBuilder = Future.value; -Future Function(Map options, int interval) onCallBuildTimeShiftCapture = (options, interval) => Future.value(); -Future Function(void Function(double)? onProgress) onCallStartTimeShiftCapture = (onProgress) => Future.value(); +Future Function(Map options, int interval) + onCallBuildTimeShiftCapture = (options, interval) => Future.value(); +Future Function(void Function(double)? onProgress) + onCallStartTimeShiftCapture = (onProgress) => Future.value(); Future Function() onCallStopTimeShiftCapture = Future.value; Future Function() onCallGetVideoCaptureBuilder = Future.value; -Future Function(Map options) onCallBuildVideoCapture = Future.value; -Future Function() onCallStartVideoCapture = Future.value; +Future Function(Map options) onCallBuildVideoCapture = + Future.value; +Future Function(void Function(Exception exception)? onStopFailed) + onCallStartVideoCapture = (onStopFailed) => Future.value(); Future Function() onCallStopVideoCapture = Future.value; Future Function(List optionNames) onCallGetOptions = (optionNames) => Future.value(Options()); @@ -288,7 +300,8 @@ Future Function(String str) onCallGetString = Future.value; Future> Function() onCallGetStringList = Future.value; void main() { - final ThetaClientFlutterPlatform initialPlatform = ThetaClientFlutterPlatform.instance; + final ThetaClientFlutterPlatform initialPlatform = + ThetaClientFlutterPlatform.instance; test('$MethodChannelThetaClientFlutter is the default instance', () { expect(initialPlatform, isInstanceOf()); @@ -296,7 +309,8 @@ void main() { test('getPlatformVersion', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; expect(await thetaClientPlugin.getPlatformVersion(), '42'); @@ -304,7 +318,8 @@ void main() { test('initialize', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallInitialize = Future.value; @@ -315,7 +330,8 @@ void main() { test('isInitialized', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallIsInitialized = () { @@ -326,7 +342,8 @@ void main() { test('getThetaModel', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const thetaModel = ThetaModel.thetaZ1; @@ -340,7 +357,8 @@ void main() { test('getThetaInfo', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const model = 'RICOH THETA Z1'; @@ -349,8 +367,21 @@ void main() { const apiLevel = [2]; var endpoints = Endpoints(80, 80); onGetThetaInfo = () { - return Future.value(ThetaInfo('RICOH', model, 'serialNo', 'wlanMac', 'blMac', 'firmVersion', - 'supportUrl', true, true, 1, api, endpoints, apiLevel, thetaModel)); + return Future.value(ThetaInfo( + 'RICOH', + model, + 'serialNo', + 'wlanMac', + 'blMac', + 'firmVersion', + 'supportUrl', + true, + true, + 1, + api, + endpoints, + apiLevel, + thetaModel)); }; var thetaInfo = await thetaClientPlugin.getThetaInfo(); @@ -360,7 +391,8 @@ void main() { test('getThetaState', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const fingerprint = 'fingerprint_1'; @@ -381,7 +413,10 @@ void main() { const isMySettingChanged = true; const currentMicrophone = MicrophoneOptionEnum.auto; const isSdCard = true; - const cameraError = [CameraErrorEnum.batteryChargeFail, CameraErrorEnum.batteryHighTemperature]; + const cameraError = [ + CameraErrorEnum.batteryChargeFail, + CameraErrorEnum.batteryHighTemperature + ]; const isBatteryInsert = false; onGetThetaState = () { return Future.value(ThetaState( @@ -417,7 +452,8 @@ void main() { expect(thetaState.recordedTime, recordedTime); expect(thetaState.recordableTime, recordableTime); expect(thetaState.capturedPictures, capturedPictures); - expect(thetaState.compositeShootingElapsedTime, compositeShootingElapsedTime); + expect( + thetaState.compositeShootingElapsedTime, compositeShootingElapsedTime); expect(thetaState.latestFileUrl, latestFileUrl); expect(thetaState.chargingState, chargingState); expect(thetaState.apiVersion, apiVersion); @@ -433,7 +469,8 @@ void main() { test('getOptions', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallGetOptions = (optionNames) { @@ -451,7 +488,8 @@ void main() { test('setOptions', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallSetOptions = Future.value; @@ -465,7 +503,8 @@ void main() { test('restoreSettings', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallRestoreSettings = Future.value; @@ -476,7 +515,8 @@ void main() { test('getMySetting', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallGetOptions = (optionNames) { @@ -489,7 +529,8 @@ void main() { test('getMySettingFromOldModel', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallGetOptions = (optionNames) { @@ -507,7 +548,8 @@ void main() { test('setMySetting', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; onCallSetOptions = Future.value; @@ -521,7 +563,8 @@ void main() { test('deleteMySetting', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; thetaClientPlugin.deleteMySetting(CaptureModeEnum.image); @@ -530,7 +573,8 @@ void main() { test('listPlugins', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var infoList = List.empty(growable: true); @@ -557,7 +601,8 @@ void main() { test('setPlugin', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; thetaClientPlugin.setPlugin('com.theta360.usbstorage'); @@ -566,7 +611,8 @@ void main() { test('startPlugin', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; thetaClientPlugin.startPlugin('com.theta360.usbstorage'); @@ -575,7 +621,8 @@ void main() { test('stopPlugin', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; thetaClientPlugin.stopPlugin(); @@ -584,7 +631,8 @@ void main() { test('getPluginLicense', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var license = """ @@ -610,7 +658,8 @@ void main() { test('getPluginOrders', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var strList = List.empty(growable: true); @@ -626,7 +675,8 @@ void main() { test('setPluginOrders', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; thetaClientPlugin.setPluginOrders(['com.theta360.usbstorage']); @@ -635,7 +685,8 @@ void main() { test('setBluetoothDevice', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); - MockThetaClientFlutterPlatform fakePlatform = MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var name = '10107709'; diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartReader.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartReader.kt index 2aeed0c6c2..782feb82b7 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartReader.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartReader.kt @@ -3,9 +3,10 @@ */ package com.ricoh360.thetaclient -import io.ktor.http.* -import io.ktor.utils.io.* -import io.ktor.utils.io.core.* +import io.ktor.http.Headers +import io.ktor.http.HttpHeaders +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.core.ByteReadPacket /** * Reader for HTTP multipart response body Theta sends. diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt index 35360af8b5..996d08c2b1 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt @@ -8,11 +8,22 @@ import io.ktor.http.HttpMethod import io.ktor.http.HttpStatusCode import io.ktor.http.auth.HttpAuthHeader import io.ktor.http.auth.parseAuthorizationHeader -import io.ktor.network.selector.* -import io.ktor.network.sockets.* -import io.ktor.utils.io.* -import io.ktor.utils.io.charsets.* -import io.ktor.utils.io.core.* +import io.ktor.network.selector.SelectorManager +import io.ktor.network.sockets.ASocket +import io.ktor.network.sockets.InetSocketAddress +import io.ktor.network.sockets.aSocket +import io.ktor.network.sockets.openReadChannel +import io.ktor.network.sockets.openWriteChannel +import io.ktor.network.sockets.tcpNoDelay +import io.ktor.utils.io.ByteReadChannel +import io.ktor.utils.io.ByteWriteChannel +import io.ktor.utils.io.cancel +import io.ktor.utils.io.charsets.Charsets +import io.ktor.utils.io.close +import io.ktor.utils.io.core.String +import io.ktor.utils.io.core.toByteArray +import io.ktor.utils.io.discard +import io.ktor.utils.io.writeFully import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withTimeout @@ -427,6 +438,7 @@ internal class PreviewClientImpl : PreviewClient { } } ?: client } + else -> client } } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt index cad4adc424..dd5ec2a84d 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt @@ -181,6 +181,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? HttpStatusCode.Unauthorized -> { throw ThetaUnauthorizedException(e.message ?: e.toString()) } + else -> { throw ThetaWebApiException.create(e) } @@ -4054,6 +4055,9 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @return [OffDelayEnum] or [OffDelay] */ fun get(sec: Int): OffDelay { + if (sec == 0) { + return DISABLE + } return values().firstOrNull { it.sec == sec } ?: OffDelaySec(sec) } } @@ -4786,6 +4790,9 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @return [SleepDelayEnum] or [SleepDelaySec] */ fun get(sec: Int): SleepDelay { + if (sec == 0) { + return DISABLE + } return values().firstOrNull { it.sec == sec } ?: SleepDelaySec(sec) } } @@ -5944,9 +5951,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? size = VideoFormat.VIDEO_4K ) } + ThetaModel.isBeforeThetaV(cameraModel) -> { return fileUrl } + else -> { ConvertVideoFormatsParams( fileUrl = fileUrl, @@ -6633,11 +6642,13 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? plugins.size > SIZE_OF_SET_PLUGIN_ORDERS_ARGUMENT_LIST_FOR_Z1 -> { throw ArgumentException("Argument list must have $SIZE_OF_SET_PLUGIN_ORDERS_ARGUMENT_LIST_FOR_Z1 or less elements for RICOH THETA Z1") } + plugins.size < SIZE_OF_SET_PLUGIN_ORDERS_ARGUMENT_LIST_FOR_Z1 -> { do { // autocomplete plugins += "" } while (plugins.size < SIZE_OF_SET_PLUGIN_ORDERS_ARGUMENT_LIST_FOR_Z1) } + else -> {} } } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapture.kt index 7cd4f8f238..12959c8782 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapture.kt @@ -150,6 +150,7 @@ class TimeShiftCapture private constructor( _timeShift = TimeShift(firstShooting = FirstShootingEnum.FRONT, firstInterval = SC2B_DEFAULT_FIRST_INTERVAL, secondInterval = SC2B_DEFAULT_SECOND_INTERVAL), exposureDelay = SC2B_DEFAULT_EXPOSURE_DELAY, // without this option, sometimes shooting is normal but time-shift ) + else -> Options(captureMode = CaptureMode.IMAGE) } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapturing.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapturing.kt index b194e68450..9a5dedc595 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapturing.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCapturing.kt @@ -3,8 +3,8 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.StopCaptureResponse -import io.ktor.client.plugins.* -import io.ktor.serialization.* +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapture.kt index 7574205441..efe5b2f5d7 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapture.kt @@ -7,15 +7,22 @@ import io.ktor.client.plugins.* import io.ktor.serialization.* import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay import kotlinx.coroutines.launch +internal const val CHECK_STATE_INTERVAL = 1000L +internal const val CHECK_STATE_RETRY = 3 +internal const val CHECK_SHOOTING_IDLE_COUNT = 2 +internal const val ERROR_GET_CAPTURE_STATUS = "Capture status cannot be retrieved." + /* * VideoCapture * * @property endpoint URL of Theta web API endpoint * @property options option of video capture */ -class VideoCapture private constructor(private val endpoint: String, options: Options) : Capture(options) { +class VideoCapture private constructor(private val endpoint: String, options: Options) : + Capture(options) { private val scope = CoroutineScope(Dispatchers.Default) @@ -45,19 +52,42 @@ class VideoCapture private constructor(private val endpoint: String, options: Op * Callback of startCapture */ interface StartCaptureCallback { + /** - * Called when successful. + * Called when stopCapture error occurs. * - * @param fileUrl URL of the video capture + * @param exception Exception of error occurs */ - fun onSuccess(fileUrl: String) + fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) /** * Called when error occurs. * * @param exception Exception of error occurs */ - fun onError(exception: ThetaRepository.ThetaRepositoryException) + fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) + + /** + * Called when successful. + * + * @param fileUrl URL of the video capture + */ + fun onCaptureCompleted(fileUrl: String?) + } + + internal suspend fun getCaptureStatus(): CaptureStatus? { + var retry = CHECK_STATE_RETRY + while (retry > 0) { + try { + val stateResponse = ThetaApi.callStateApi(endpoint) + return stateResponse.state._captureStatus + } catch (e: Exception) { + println("getCaptureStatus retry: $retry") + delay(CHECK_STATE_INTERVAL) + } + retry -= 1 + } + return null } /** @@ -66,21 +96,86 @@ class VideoCapture private constructor(private val endpoint: String, options: Op * @param callback Success or failure of the call */ fun startCapture(callback: StartCaptureCallback): VideoCapturing { + var isEndCapture = false + + fun callOnCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + if (isEndCapture) { + return + } + isEndCapture = true + callback.onCaptureFailed(exception) + } + + fun callOnCaptureCompleted(fileUrl: String?) { + println("call callOnCaptureCompleted: $fileUrl") + if (isEndCapture) { + return + } + isEndCapture = true + callback.onCaptureCompleted(fileUrl) + } + + val captureCallback = object : StartCaptureCallback { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + if (!isEndCapture) { + callback.onStopFailed(exception) + } + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + } + + override fun onCaptureCompleted(fileUrl: String?) { + callOnCaptureCompleted(fileUrl) + } + + } scope.launch { try { ThetaApi.callStartCaptureCommand(endpoint, StartCaptureParams()).error?.let { - callback.onError(ThetaRepository.ThetaWebApiException(it.message)) + callOnCaptureFailed(ThetaRepository.ThetaWebApiException(it.message)) } } catch (e: JsonConvertException) { - callback.onError(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) + callOnCaptureFailed(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) } catch (e: ResponseException) { - callback.onError(ThetaRepository.ThetaWebApiException.create(e)) + callOnCaptureFailed(ThetaRepository.ThetaWebApiException.create(e)) } catch (e: Exception) { - callback.onError(ThetaRepository.NotConnectedException(e.message ?: e.toString())) + callOnCaptureFailed( + ThetaRepository.NotConnectedException( + e.message ?: e.toString() + ) + ) } - } - return VideoCapturing(endpoint, callback) + var idleCount = CHECK_SHOOTING_IDLE_COUNT + while (!isEndCapture) { + delay(CHECK_STATE_INTERVAL) + when (getCaptureStatus()) { + null -> { + callOnCaptureFailed( + ThetaRepository.ThetaWebApiException( + ERROR_GET_CAPTURE_STATUS + ) + ) + break + } + + CaptureStatus.IDLE -> { + idleCount -= 1 + // In the case of SC2, it becomes idle in the middle, so wait multiple times + if (idleCount <= 0) { + break + } + } + + else -> { + idleCount = CHECK_SHOOTING_IDLE_COUNT + } + } + } + callOnCaptureCompleted(null) + } + return VideoCapturing(endpoint, captureCallback) } /* diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapturing.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapturing.kt index 2deef5e7bf..d156717730 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapturing.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/VideoCapturing.kt @@ -3,8 +3,8 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.StopCaptureResponse -import io.ktor.client.plugins.* -import io.ktor.serialization.* +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -32,22 +32,22 @@ class VideoCapturing internal constructor( try { response = ThetaApi.callStopCaptureCommand(endpoint) response.error?.let { - callback.onError(ThetaRepository.ThetaWebApiException(it.message)) + callback.onStopFailed(ThetaRepository.ThetaWebApiException(it.message)) return@launch } } catch (e: JsonConvertException) { - callback.onError(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) + callback.onStopFailed(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) return@launch } catch (e: ResponseException) { - callback.onError(ThetaRepository.ThetaWebApiException.create(e)) + callback.onStopFailed(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) return@launch } catch (e: Exception) { - callback.onError(ThetaRepository.NotConnectedException(e.message ?: e.toString())) + callback.onStopFailed(ThetaRepository.NotConnectedException(e.message ?: e.toString())) return@launch } val fileUrl = response.results?.fileUrls?.firstOrNull() ?: response.results?.fileUrl ?: "" - callback.onSuccess(fileUrl) + callback.onCaptureCompleted(fileUrl) } } } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/infoApi.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/infoApi.kt index 42f544734d..1986dc831d 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/infoApi.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/infoApi.kt @@ -3,7 +3,7 @@ */ package com.ricoh360.thetaclient.transferred -import io.ktor.http.* +import io.ktor.http.HttpMethod import kotlinx.serialization.Serializable /** diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setBluetoothDeveiceCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setBluetoothDeveiceCommand.kt index 950928d927..a7ad34a5cc 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setBluetoothDeveiceCommand.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setBluetoothDeveiceCommand.kt @@ -4,7 +4,6 @@ package com.ricoh360.thetaclient.transferred import kotlinx.serialization.Serializable -import kotlinx.serialization.json.JsonElement @Serializable internal data class SetBluetoothDeviceRequest( diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/statusApi.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/statusApi.kt index 9b6217d586..f0f8c399ee 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/statusApi.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/statusApi.kt @@ -3,7 +3,7 @@ */ package com.ricoh360.thetaclient.transferred -import io.ktor.http.* +import io.ktor.http.HttpMethod import kotlinx.serialization.Serializable import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json @@ -73,54 +73,71 @@ internal fun decodeStatusApiResponse(body: String?): CommandApiResponse { "camera._cancelVideoConvert" -> { return js.decodeFromString(body) } + "camera._convertVideoFormats" -> { return js.decodeFromString(body) } + "camera.delete" -> { return js.decodeFromString(body) } + "camera._finishWlan" -> { return js.decodeFromString(body) } + "camera._getMetadata" -> { return js.decodeFromString(body) } + "camera.listFiles" -> { return js.decodeFromString(body) } + "camera.reset" -> { return js.decodeFromString(body) } + "camera._listAccessPoints" -> { return js.decodeFromString(body) } + "camera.startCapture" -> { return js.decodeFromString(body) } + "camera.stopCapture" -> { return js.decodeFromString(body) } + "camera.takePicture" -> { return js.decodeFromString(body) } + "camera._deleteAccessPoint" -> { return js.decodeFromString(body) } + "camera._setAccessPoint" -> { return js.decodeFromString(body) } + "camera._stopSelfTimer" -> { return js.decodeFromString(body) } + "camera.setOptions" -> { return js.decodeFromString(body) } + "camera.getOptions" -> { return js.decodeFromString(body) } + "camera.startSession" -> { return js.decodeFromString(body) } + else -> { return UnknownResponse(name = name) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt index 6cab379c72..e94793d162 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt @@ -33,6 +33,28 @@ internal class CheckRequest { assertEquals(requestData.name, command, "command name") } + fun getCommandName(request: HttpRequestData): String? { + if (request.url.encodedPath != "/osc/commands/execute") { + return null + } + + val body = request.body as TextContent + val js = Json { + encodeDefaults = true // Encode properties with default value. + explicitNulls = false // Don't encode properties with null value. + ignoreUnknownKeys = true // Ignore unknown keys on decode. + } + + @Serializable + data class CommandApiRequestAny( + override val name: String, + override val parameters: JsonObject + ) : CommandApiRequest + + val requestData = js.decodeFromString(body.text) + return requestData.name + } + fun checkSetOptions( request: HttpRequestData, aiAutoThumbnail: AiAutoThumbnail? = null, diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/LocalMockApiTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/LocalMockApiTest.kt index 87669114d9..4ad77538cc 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/LocalMockApiTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/LocalMockApiTest.kt @@ -1,6 +1,7 @@ package com.ricoh360.thetaclient -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest class LocalMockApiTest { val endpoint = "http://localhost:8000/" @@ -12,404 +13,404 @@ class LocalMockApiTest { @AfterTest fun teardown() { } -/* - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun infoApiTest() = runTest { - val info = ThetaApi.callInfoApi(endpoint) - assertEquals("RICOH", info.manufacturer, "info.manufacturer") - assertEquals("RICOH THETA S", info.model, "info.model") - assertEquals("00001234", info.serialNumber, "info.serialNumber") - assertEquals("3c:22:fb:7f:0b:cb", info._wlanMacAddress, "info._wlanMacAddress") - assertEquals( - "3c:22:fb:7f:0b:cb", info._bluetoothMacAddress, - "info._bluetoothMacAddress" - ) - assertEquals("1.62", info.firmwareVersion, "info.firmwareVersion") - assertEquals( - "https://theta360.com/en/support/", info.supportUrl, - "info.supportUrl" - ) - assertEquals(false, info.gps, "info.gps") - assertEquals(false, info.gyro, "info.gyro") - assertEquals(67, info.uptime, "info.uptime") - assertEquals( - listOf( - "/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status" - ), - info.api, "info.api" - ) - assertEquals(listOf(1, 2), info.apiLevel, "info.apiLevel") - - assertEquals(80, info.endpoints.httpPort, "endpoint.httpPort") - assertEquals(80, info.endpoints.httpUpdatesPort, "endpoint.httpUpdatePort") - } - - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun stateApiTest() = runTest { - val response = ThetaApi.callStateApi(endpoint) - assertEquals("12EGA33", response.fingerprint, "fingerprint") - val state = response.state - assertEquals(0.33, state.batteryLevel, "state.batteryLevel") - assertEquals("${endpoint}files/abcde/", state.storageUri, "state.storageUri") - assertEquals("storage ID", state._storageID, "state._storageID") - assertEquals(CaptureStatus.IDLE, state._captureStatus, "state._captureStatus") - assertEquals(0, state._recordedTime, "state._recordedTime") - assertEquals(300, state._recordableTime, "state._recordableTime") - assertEquals(500, state._capturedPictures, "state._capturedPictures") - assertEquals( - 10, state._compositeShootingElapsedTime, - "state._compositeShootingElapsedTime" - ) - assertEquals( - "${endpoint}files/abcde/100RICOH/R0010015.JPG", - state._latestFileUrl, "state._latestFileUrl" - ) - assertEquals(ChargingState.DISCONNECT, state._batteryState, "state._batteryState") - assertEquals(2, state._apiVersion, "state._apiVersion") - assertEquals(false, state._pluginRunning, "state._pluginRunning") - assertEquals(false, state._pluginWebServer, "state._pluginWebServer") - assertEquals(ShootingFunction.NORMAL, state._function, "state._function") - assertEquals(false, state._mySettingChanged, "state._mySettingChanged") - assertEquals( - MicrophoneOption.AUTO, state._currentMicrophone, - "state._currentMicrophone" - ) - assertEquals(StorageOption.IN, state._currentStorage, "state._currentStorage") - assertEquals(listOf(), state._cameraError, "state._cameraError") - assertEquals(false, state._batteryInsert, "state._batterylnsert") - } - - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun listFilesTest() = runTest { - val params = ListFilesParams( - fileType = FileType.ALL, - startPosition = 0, - _startFileUrl = "${endpoint}files", - entryCount = 3, - maxThumbSize = 10, - _detail = false, - _sort = SortOrder.OLDEST - ) - val response = ThetaApi.callListFilesCommand(endpoint, params) - assertEquals("camera.listFiles", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results, "results") - assertNull(response.progress, "progress") - assertNull(response.error, "error") - - assertEquals(3, response.results?.totalEntries, "totalEntries") - assertFalse(response.results!!.entries.isEmpty(), "entries") - var c = 0 - response.results!!.entries.forEach { - assertEquals("R00${1001 + c}.JPG", it.name, "name") + /* + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun infoApiTest() = runTest { + val info = ThetaApi.callInfoApi(endpoint) + assertEquals("RICOH", info.manufacturer, "info.manufacturer") + assertEquals("RICOH THETA S", info.model, "info.model") + assertEquals("00001234", info.serialNumber, "info.serialNumber") + assertEquals("3c:22:fb:7f:0b:cb", info._wlanMacAddress, "info._wlanMacAddress") assertEquals( - "${endpoint}files/R00${1001 + c}.JPG", it.fileUrl, - "fileUrl" + "3c:22:fb:7f:0b:cb", info._bluetoothMacAddress, + "info._bluetoothMacAddress" ) - c++ - assertEquals(4051440, it.size, "size") - assertEquals("2015:07:10 11:05:18+09:00", it.dateTimeZone, "dateTimeZone") - assertEquals("2015:07:10 11:05:18", it.dateTime, "dateTime") - assertEquals(50.5324f, it.lat, "lat") - assertEquals(-120.2332f, it.lng, "lng") - assertEquals(5376, it.width, "width") - assertEquals(2688, it.height, "height") - assertEquals("thumbnail base64 string", it.thumbnail, "thumbnail") - assertEquals(3348, it._thumbSize, "_thumbSize") - assertEquals("123", it._intervalCaptureGroupId, "_intervalCaptureGroupId") - assertEquals("XYZ", it._compositeShootingGroupId, "_compositeShootingGroupId") - assertEquals("ABZ", it._autoBracketGroupId, "_autoBracketGroupId") - assertEquals(34, it._recordTime, "_recordTime") - assertEquals(true, it.isProcessed, "isProcessed") - assertEquals("preview url", it.previewUrl, "previewUrl") - assertEquals("H264", it._codec, "_codec") + assertEquals("1.62", info.firmwareVersion, "info.firmwareVersion") assertEquals( - _ProjectionType.EQUIRECTANGULAR, it._projectionType, - "_projectionType" + "https://theta360.com/en/support/", info.supportUrl, + "info.supportUrl" ) + assertEquals(false, info.gps, "info.gps") + assertEquals(false, info.gyro, "info.gyro") + assertEquals(67, info.uptime, "info.uptime") assertEquals( - "CSH", it._continuousShootingGroupId, - "_continuousShootingGroupId" + listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), + info.api, "info.api" ) - assertEquals(60, it._frameRate, "_frameRate") - assertEquals(false, it._favorite, "_favorite") - assertEquals("good image", it._imageDescription, "_imageDescription") + assertEquals(listOf(1, 2), info.apiLevel, "info.apiLevel") + + assertEquals(80, info.endpoints.httpPort, "endpoint.httpPort") + assertEquals(80, info.endpoints.httpUpdatesPort, "endpoint.httpUpdatePort") + } - // Test to get a photo + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun stateApiTest() = runTest { + val response = ThetaApi.callStateApi(endpoint) + assertEquals("12EGA33", response.fingerprint, "fingerprint") + val state = response.state + assertEquals(0.33, state.batteryLevel, "state.batteryLevel") + assertEquals("${endpoint}files/abcde/", state.storageUri, "state.storageUri") + assertEquals("storage ID", state._storageID, "state._storageID") + assertEquals(CaptureStatus.IDLE, state._captureStatus, "state._captureStatus") + assertEquals(0, state._recordedTime, "state._recordedTime") + assertEquals(300, state._recordableTime, "state._recordableTime") + assertEquals(500, state._capturedPictures, "state._capturedPictures") + assertEquals( + 10, state._compositeShootingElapsedTime, + "state._compositeShootingElapsedTime" + ) assertEquals( - HttpStatusCode.OK, - ThetaApi.httpClient.get(it.fileUrl).status + "${endpoint}files/abcde/100RICOH/R0010015.JPG", + state._latestFileUrl, "state._latestFileUrl" ) + assertEquals(ChargingState.DISCONNECT, state._batteryState, "state._batteryState") + assertEquals(2, state._apiVersion, "state._apiVersion") + assertEquals(false, state._pluginRunning, "state._pluginRunning") + assertEquals(false, state._pluginWebServer, "state._pluginWebServer") + assertEquals(ShootingFunction.NORMAL, state._function, "state._function") + assertEquals(false, state._mySettingChanged, "state._mySettingChanged") assertEquals( - HttpStatusCode.OK, - ThetaApi.httpClient.get(it.getThumnailUrl()).status + MicrophoneOption.AUTO, state._currentMicrophone, + "state._currentMicrophone" ) + assertEquals(StorageOption.IN, state._currentStorage, "state._currentStorage") + assertEquals(listOf(), state._cameraError, "state._cameraError") + assertEquals(false, state._batteryInsert, "state._batterylnsert") } - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun getLivePreviewTest() = runTest { - var count = 3 // read three frames - ThetaApi.callGetLivePreviewCommand(endpoint) { - if (!isActive) { - println("CoroutineScope.isActive is false") - return@callGetLivePreviewCommand false + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun listFilesTest() = runTest { + val params = ListFilesParams( + fileType = FileType.ALL, + startPosition = 0, + _startFileUrl = "${endpoint}files", + entryCount = 3, + maxThumbSize = 10, + _detail = false, + _sort = SortOrder.OLDEST + ) + val response = ThetaApi.callListFilesCommand(endpoint, params) + assertEquals("camera.listFiles", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results, "results") + assertNull(response.progress, "progress") + assertNull(response.error, "error") + + assertEquals(3, response.results?.totalEntries, "totalEntries") + assertFalse(response.results!!.entries.isEmpty(), "entries") + var c = 0 + response.results!!.entries.forEach { + assertEquals("R00${1001 + c}.JPG", it.name, "name") + assertEquals( + "${endpoint}files/R00${1001 + c}.JPG", it.fileUrl, + "fileUrl" + ) + c++ + assertEquals(4051440, it.size, "size") + assertEquals("2015:07:10 11:05:18+09:00", it.dateTimeZone, "dateTimeZone") + assertEquals("2015:07:10 11:05:18", it.dateTime, "dateTime") + assertEquals(50.5324f, it.lat, "lat") + assertEquals(-120.2332f, it.lng, "lng") + assertEquals(5376, it.width, "width") + assertEquals(2688, it.height, "height") + assertEquals("thumbnail base64 string", it.thumbnail, "thumbnail") + assertEquals(3348, it._thumbSize, "_thumbSize") + assertEquals("123", it._intervalCaptureGroupId, "_intervalCaptureGroupId") + assertEquals("XYZ", it._compositeShootingGroupId, "_compositeShootingGroupId") + assertEquals("ABZ", it._autoBracketGroupId, "_autoBracketGroupId") + assertEquals(34, it._recordTime, "_recordTime") + assertEquals(true, it.isProcessed, "isProcessed") + assertEquals("preview url", it.previewUrl, "previewUrl") + assertEquals("H264", it._codec, "_codec") + assertEquals( + _ProjectionType.EQUIRECTANGULAR, it._projectionType, + "_projectionType" + ) + assertEquals( + "CSH", it._continuousShootingGroupId, + "_continuousShootingGroupId" + ) + assertEquals(60, it._frameRate, "_frameRate") + assertEquals(false, it._favorite, "_favorite") + assertEquals("good image", it._imageDescription, "_imageDescription") + + // Test to get a photo + assertEquals( + HttpStatusCode.OK, + ThetaApi.httpClient.get(it.fileUrl).status + ) + assertEquals( + HttpStatusCode.OK, + ThetaApi.httpClient.get(it.getThumnailUrl()).status + ) } - val byteArray = it.readBytes() - println("Got a preview frame ${byteArray::class.simpleName}") - it.release() - assertTrue(true, "Preview callback") - return@callGetLivePreviewCommand if (--count > 0) true else false + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") } - } - - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun takePictureTest() = runTest { - val response = ThetaApi.callTakePictureCommand(endpoint) - assertTrue(true, "take picture") - assertEquals("camera.takePicture", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.id, "id") - assertNotNull(response.results?.fileUrl, "fileUrl") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun cancelVideoConvertTest() = runTest { - val response = ThetaApi.callCancelVideoConvertCommand(endpoint) - assertEquals("camera._cancelVideoConvert", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun getLivePreviewTest() = runTest { + var count = 3 // read three frames + ThetaApi.callGetLivePreviewCommand(endpoint) { + if (!isActive) { + println("CoroutineScope.isActive is false") + return@callGetLivePreviewCommand false + } + val byteArray = it.readBytes() + println("Got a preview frame ${byteArray::class.simpleName}") + it.release() + assertTrue(true, "Preview callback") + return@callGetLivePreviewCommand if (--count > 0) true else false + } + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun convertVideoFormatsTest() = runTest { - val params = ConvertVideoFormatsParams( - fileUrl = "file url to convert", - size = VideoFormat.VIDEO_4K, - projectionType = _ProjectionType.EQUIRECTANGULAR, - codec = "H.264/MPEG-4 AVC", - topBottomCorrection = TopBottomCorrection.APPLY - ) - val response = ThetaApi.callConvertVideoFormatsCommand(endpoint, params) - assertEquals("camera._convertVideoFormats", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun takePictureTest() = runTest { + val response = ThetaApi.callTakePictureCommand(endpoint) + assertTrue(true, "take picture") + assertEquals("camera.takePicture", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.id, "id") + assertNotNull(response.results?.fileUrl, "fileUrl") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun deleteTest() = runTest { - val fileUrls: List = listOf("file1", "file2", "file3") - val params = DeleteParams(fileUrls = fileUrls) - val response = ThetaApi.callDeleteCommand(endpoint, params) - assertEquals("camera.delete", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun cancelVideoConvertTest() = runTest { + val response = ThetaApi.callCancelVideoConvertCommand(endpoint) + assertEquals("camera._cancelVideoConvert", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun finishWlanTest() = runTest { - val response = ThetaApi.callFinishWlanCommand(endpoint) - assertEquals("camera._finishWlan", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun convertVideoFormatsTest() = runTest { + val params = ConvertVideoFormatsParams( + fileUrl = "file url to convert", + size = VideoFormat.VIDEO_4K, + projectionType = _ProjectionType.EQUIRECTANGULAR, + codec = "H.264/MPEG-4 AVC", + topBottomCorrection = TopBottomCorrection.APPLY + ) + val response = ThetaApi.callConvertVideoFormatsCommand(endpoint, params) + assertEquals("camera._convertVideoFormats", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun getMetadataTest() = runTest { - val params = GetMetadataParams(fileUrl = "file1") - val response = ThetaApi.callGetMetadataCommand(endpoint, params) - assertEquals("camera._getMetadata", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.exif, "results.exif") - val exif = response.results!!.exif - assertEquals("0230", exif.ExifVersion, "exif.ExifVersion") - assertEquals("image description", exif.ImageDescription, "exif.ImageDescription") - assertEquals("2021:06:09 19:34:17", exif.DateTime, "exif.DateTime") - assertEquals(5376, exif.ImageWidth, "exif.ImageWidth") - assertEquals(2688, exif.ImageLength, "exif.ImageLength") - assertEquals(1, exif.ColorSpace, "exif.ColorSpace") - assertEquals(6, exif.Compression, "exif.Compression") - assertEquals(1, exif.Orientation, "exif.Orientation") - assertEquals(0, exif.Flash, "exif.Flash") - assertEquals(0.75f, exif.FocalLength, "exif.FocalLength") - assertEquals(0, exif.WhiteBalance, "exif.WhiteBalance") - assertEquals(0.0333, exif.ExposureTime, "exif.ExposureTime") - assertEquals(2.0, exif.FNumber, "exif.FNumber") - assertEquals(2, exif.ExposureProgram, "exif.ExposureProgram") - assertEquals(1, exif.PhotographicSensitivity, "exif.PhotographicSensitivity") - assertEquals(2.0, exif.ApertureValue, "exif.ApertureValue") - assertEquals(0.5f, exif.BrightnessValue, "exif.BrightnessValue") - assertEquals(0.0f, exif.ExposureBiasValue, "exif.ExposureBiasValue") - assertEquals("north", exif.GPSLatitudeRef, "exif.GPSLatitudeRef") - assertEquals(12.5, exif.GPSLatitude, "exif.GPSLatitude") - assertEquals("west", exif.GPSLongitudeRef, "exif.GPSLongitudeRef") - assertEquals(10.5, exif.GPSLongitude, "exif.GPSLongitude") - assertEquals("RICOH", exif.Make, "exif.Make") - assertEquals("RICOH THETA S", exif.Model, "exif.Model") - assertEquals("RICOH THETA S Ver 1.11", exif.Software, "exif.Software") - assertEquals("2022 ricoh co ltd.", exif.Copyright, "exif.Copyright") - assertNotNull(response.results?.xmp, "results.xmp") - val xmp = response.results!!.xmp - assertEquals( - ProjectionType.EQUIRECTANGULAR, xmp.ProjectionType, - "xmp.ProjectionType" - ) - assertEquals(true, xmp.UsePanoramaViewer, "xmp.UsePanoramaViewer") - assertEquals(2.5, xmp.PoseHeadingDegrees, "xmp.PoseHeadingDegrees") - assertEquals( - 5376, xmp.CroppedAreaImageWidthPixels, - "xmp.CroppedAreaImageWidthPixels" - ) - assertEquals( - 2688, xmp.CroppedAreaImageHeightPixels, - "xmp.CroppedAreaImageHeightPixels" - ) - assertEquals(5376, xmp.FullPanoWidthPixels, "xmp.FullPanoWidthPixels") - assertEquals(2688, xmp.FullPanoHeightPixels, "xmp.FullPanoHeightPixels") - assertEquals(0, xmp.CroppedAreaLeftPixels, "xmp.CroppedAreaLeftPixels") - assertEquals(0, xmp.CroppedAreaTopPixels, "xmp.CroppedAreaTopPixels") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun deleteTest() = runTest { + val fileUrls: List = listOf("file1", "file2", "file3") + val params = DeleteParams(fileUrls = fileUrls) + val response = ThetaApi.callDeleteCommand(endpoint, params) + assertEquals("camera.delete", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun resetTest() = runTest { - val response = ThetaApi.callResetCommand(endpoint) - assertEquals("camera.reset", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun finishWlanTest() = runTest { + val response = ThetaApi.callFinishWlanCommand(endpoint) + assertEquals("camera._finishWlan", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun listAccessPointsTest() = runTest { - val response = ThetaApi.callListAccessPointsCommand(endpoint) - assertEquals("camera._listAccessPoints", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.accessPoints, "accessPoints") - response.results!!.accessPoints.forEach { - assertEquals("dummy ssid", it.ssid, "xmp.ssid") - assertEquals(true, it.ssidStealth, "xmp.ssidStealth") - assertEquals(AuthenticationMode.WPA_WPA2_PSK, it.security, "xmp.security") - assertEquals(0, it.connectionPriority, "xmp.connectionPriority") + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun getMetadataTest() = runTest { + val params = GetMetadataParams(fileUrl = "file1") + val response = ThetaApi.callGetMetadataCommand(endpoint, params) + assertEquals("camera._getMetadata", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.exif, "results.exif") + val exif = response.results!!.exif + assertEquals("0230", exif.ExifVersion, "exif.ExifVersion") + assertEquals("image description", exif.ImageDescription, "exif.ImageDescription") + assertEquals("2021:06:09 19:34:17", exif.DateTime, "exif.DateTime") + assertEquals(5376, exif.ImageWidth, "exif.ImageWidth") + assertEquals(2688, exif.ImageLength, "exif.ImageLength") + assertEquals(1, exif.ColorSpace, "exif.ColorSpace") + assertEquals(6, exif.Compression, "exif.Compression") + assertEquals(1, exif.Orientation, "exif.Orientation") + assertEquals(0, exif.Flash, "exif.Flash") + assertEquals(0.75f, exif.FocalLength, "exif.FocalLength") + assertEquals(0, exif.WhiteBalance, "exif.WhiteBalance") + assertEquals(0.0333, exif.ExposureTime, "exif.ExposureTime") + assertEquals(2.0, exif.FNumber, "exif.FNumber") + assertEquals(2, exif.ExposureProgram, "exif.ExposureProgram") + assertEquals(1, exif.PhotographicSensitivity, "exif.PhotographicSensitivity") + assertEquals(2.0, exif.ApertureValue, "exif.ApertureValue") + assertEquals(0.5f, exif.BrightnessValue, "exif.BrightnessValue") + assertEquals(0.0f, exif.ExposureBiasValue, "exif.ExposureBiasValue") + assertEquals("north", exif.GPSLatitudeRef, "exif.GPSLatitudeRef") + assertEquals(12.5, exif.GPSLatitude, "exif.GPSLatitude") + assertEquals("west", exif.GPSLongitudeRef, "exif.GPSLongitudeRef") + assertEquals(10.5, exif.GPSLongitude, "exif.GPSLongitude") + assertEquals("RICOH", exif.Make, "exif.Make") + assertEquals("RICOH THETA S", exif.Model, "exif.Model") + assertEquals("RICOH THETA S Ver 1.11", exif.Software, "exif.Software") + assertEquals("2022 ricoh co ltd.", exif.Copyright, "exif.Copyright") + assertNotNull(response.results?.xmp, "results.xmp") + val xmp = response.results!!.xmp + assertEquals( + ProjectionType.EQUIRECTANGULAR, xmp.ProjectionType, + "xmp.ProjectionType" + ) + assertEquals(true, xmp.UsePanoramaViewer, "xmp.UsePanoramaViewer") + assertEquals(2.5, xmp.PoseHeadingDegrees, "xmp.PoseHeadingDegrees") + assertEquals( + 5376, xmp.CroppedAreaImageWidthPixels, + "xmp.CroppedAreaImageWidthPixels" + ) assertEquals( - IpAddressAllocation.STATIC, it.ipAddressAllocation, - "xmp.ipAddressAllocation" + 2688, xmp.CroppedAreaImageHeightPixels, + "xmp.CroppedAreaImageHeightPixels" ) - assertEquals("172.16.1.13", it.ipAddress, "xmp.ipAddress") - assertEquals("255.255.0.0", it.subnetMask, "xmp.subnetMask") - assertEquals("172.16.1.1", it.defaultGateway, "xmp.defaultGateway") + assertEquals(5376, xmp.FullPanoWidthPixels, "xmp.FullPanoWidthPixels") + assertEquals(2688, xmp.FullPanoHeightPixels, "xmp.FullPanoHeightPixels") + assertEquals(0, xmp.CroppedAreaLeftPixels, "xmp.CroppedAreaLeftPixels") + assertEquals(0, xmp.CroppedAreaTopPixels, "xmp.CroppedAreaTopPixels") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") } - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun startCaptureTest() = runTest { - val params = StartCaptureParams(_mode = ShootingMode.INTERVAL_SHOOTING) - val response = ThetaApi.callStartCaptureCommand(endpoint, params) - assertEquals("camera.startCapture", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.fileUrls, "fileUrls") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun resetTest() = runTest { + val response = ThetaApi.callResetCommand(endpoint) + assertEquals("camera.reset", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun stopCaptureTest() = runTest { - val response = ThetaApi.callStopCaptureCommand(endpoint) - assertEquals("camera.stopCapture", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.fileUrls, "fileUrls") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun listAccessPointsTest() = runTest { + val response = ThetaApi.callListAccessPointsCommand(endpoint) + assertEquals("camera._listAccessPoints", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.accessPoints, "accessPoints") + response.results!!.accessPoints.forEach { + assertEquals("dummy ssid", it.ssid, "xmp.ssid") + assertEquals(true, it.ssidStealth, "xmp.ssidStealth") + assertEquals(AuthenticationMode.WPA_WPA2_PSK, it.security, "xmp.security") + assertEquals(0, it.connectionPriority, "xmp.connectionPriority") + assertEquals( + IpAddressAllocation.STATIC, it.ipAddressAllocation, + "xmp.ipAddressAllocation" + ) + assertEquals("172.16.1.13", it.ipAddress, "xmp.ipAddress") + assertEquals("255.255.0.0", it.subnetMask, "xmp.subnetMask") + assertEquals("172.16.1.1", it.defaultGateway, "xmp.defaultGateway") + } + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun deleteAccessPointTest() = runTest { - val params = DeleteAccessPointParams(ssid = "ssid to delete") - val response = ThetaApi.callDeleteAccessPointCommand(endpoint, params) - assertEquals("camera._deleteAccessPoint", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun startCaptureTest() = runTest { + val params = StartCaptureParams(_mode = ShootingMode.INTERVAL_SHOOTING) + val response = ThetaApi.callStartCaptureCommand(endpoint, params) + assertEquals("camera.startCapture", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.fileUrls, "fileUrls") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun setAccessPointTest() = runTest { - val params = SetAccessPointParams( - ssid = "ssid to set", - ssidStealth = false, - security = AuthenticationMode.WEP, - password = "password", - connectionPriority = 1, - ipAddressAllocation = IpAddressAllocation.STATIC, - ipAddress = "172.16.1.14", - subnetMask = "255.255.0.0", - defaultGateway = "172.16.1.1" - ) - val response = ThetaApi.callSetAccessPointCommand(endpoint, params) - assertEquals("camera._setAccessPoint", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun stopCaptureTest() = runTest { + val response = ThetaApi.callStopCaptureCommand(endpoint) + assertEquals("camera.stopCapture", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.fileUrls, "fileUrls") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun stopSelfTimerTestTest() = runTest { - val response = ThetaApi.callStopSelfTimerCommand(endpoint) - assertEquals("camera._stopSelfTimer", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun deleteAccessPointTest() = runTest { + val params = DeleteAccessPointParams(ssid = "ssid to delete") + val response = ThetaApi.callDeleteAccessPointCommand(endpoint, params) + assertEquals("camera._deleteAccessPoint", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun exceptionTest() = runTest { - try { - ThetaApi.httpClient.get("${endpoint}not/found/data") - assertTrue(false, "should throw exception") - } catch (t: Throwable) { - assertTrue(t is ClientRequestException, "exception type") - assertEquals(404, t.response.status.value, "received status") + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun setAccessPointTest() = runTest { + val params = SetAccessPointParams( + ssid = "ssid to set", + ssidStealth = false, + security = AuthenticationMode.WEP, + password = "password", + connectionPriority = 1, + ipAddressAllocation = IpAddressAllocation.STATIC, + ipAddress = "172.16.1.14", + subnetMask = "255.255.0.0", + defaultGateway = "172.16.1.1" + ) + val response = ThetaApi.callSetAccessPointCommand(endpoint, params) + assertEquals("camera._setAccessPoint", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") } - } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun startSessionTest() = runTest { - val response = ThetaApi.callStartSessionCommand(endpoint) - assertEquals("camera.startSession", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.sessionId, "sessionId") - assertEquals(180, response.results?.timeout, "timeout") - val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) - assertEquals(response, sres, "statusapi") - } -*/ + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun stopSelfTimerTestTest() = runTest { + val response = ThetaApi.callStopSelfTimerCommand(endpoint) + assertEquals("camera._stopSelfTimer", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } + + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun exceptionTest() = runTest { + try { + ThetaApi.httpClient.get("${endpoint}not/found/data") + assertTrue(false, "should throw exception") + } catch (t: Throwable) { + assertTrue(t is ClientRequestException, "exception type") + assertEquals(404, t.response.status.value, "received status") + } + } + + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun startSessionTest() = runTest { + val response = ThetaApi.callStartSessionCommand(endpoint) + assertEquals("camera.startSession", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.sessionId, "sessionId") + assertEquals(180, response.results?.timeout, "timeout") + val sres = ThetaApi.callStatusApi(endpoint, StatusApiParams(name = response.name)) + assertEquals(response, sres, "statusapi") + } + */ } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt index d5df8bfa47..c4f2b10097 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt @@ -19,12 +19,12 @@ internal object MockApiClient { var useMock = true var onPreviewRequest: ( ( - endpoint: String, - method: String, - path: String, - body: String, - contentType: String - ) -> PreviewClient + endpoint: String, + method: String, + path: String, + body: String, + contentType: String + ) -> PreviewClient )? = null var onPreviewClose: (() -> Unit)? = null var onPreviewHasNextPart: (() -> Boolean)? = null diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/OptionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/OptionTest.kt index c23034e77b..16ec2a77fd 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/OptionTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/OptionTest.kt @@ -1,6 +1,7 @@ package com.ricoh360.thetaclient -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest class OptionTest { val endpoint = "http://localhost:8000/" @@ -12,530 +13,530 @@ class OptionTest { @AfterTest fun teardown() { } -/* - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun getOptionsTest() = runTest { - val optionNames = - listOf("_aiAutoThumbnail", - "_aiAutoThumbnailSupport", - "aperture", - "apertureSupport", - "_cameraControlSource", - "_cameraControlSourceSupport", - "clientVersion", - "clientVersionSupport", - "captureMode", - "captureModeSupport", - "dateTimeZone", - "exposureCompensation", - "exposureCompensationSupport", - "exposureDelay", - "exposureDelaySupport", - "exposureProgram", - "exposureProgramSupport", - "fileFormat", - "fileFormatSupport", - "_filter", - "_filterSupport", - "gpsInfo", - "_gpsTagRecording", - "_imageStitching", - "_imageStitchingSupport", - "iso", - "isoSupport", - "isoAutoHighLimit", - "isoAutoHighLimitSupport", - "_language", - "_languageSupport", - "_maxRecordableTime", - "_maxRecordableTimeSupport", - "_microphone", - "_microphoneSupport", - "_microphoneChannel", - "_microphoneChannelSupport", - "_networkType", - "_networkTypeSupport", - "offDelay", - "offDelaySupport", - "remainingPictures", - "previewFormat", - "previewFormatSupport", - "remainingSpace", - "remainingVideoSeconds", - "_shootingMethod", - "_shootingMethodSupport", - "shutterSpeed", - "shutterSpeedSupport", - "_shutterVolume", - "_shutterVolumeSupport", - "sleepDelay", - "sleepDelaySupport", - "_topBottomCorrection", - "_topBottomCorrectionSupport", - "totalSpace", - "videoStitching", - "videoStitchingSupport", - "_visibilityReduction", - "_visibilityReductionSupport", - "whiteBalance", - "whiteBalanceSupport", - ) - val params = GetOptionsParams(optionNames = optionNames) - val response = ThetaApi.callGetOptionsCommand(endpoint, params) - assertEquals("camera.getOptions", response.name, "name") - assertEquals(CommandState.DONE, response.state, "state") - assertNotNull(response.results?.options, "results.options") - val options = response.results!!.options - assertEquals(AiAutoThumbnail.OFF, options._aiAutoThumbnail, "_aiAutoThumbnail") - options._aiAutoThumbnailSupport!!.forEach { - when (it) { - AiAutoThumbnail.ON -> { - assertTrue(true, "aiAutoThumbnail.ON") - } - AiAutoThumbnail.OFF -> { - assertTrue(true, "aiAutoThumbnail.OFF") - } - else -> { - assertTrue (false, "aiAutoThunbmailSupport") + /* + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun getOptionsTest() = runTest { + val optionNames = + listOf("_aiAutoThumbnail", + "_aiAutoThumbnailSupport", + "aperture", + "apertureSupport", + "_cameraControlSource", + "_cameraControlSourceSupport", + "clientVersion", + "clientVersionSupport", + "captureMode", + "captureModeSupport", + "dateTimeZone", + "exposureCompensation", + "exposureCompensationSupport", + "exposureDelay", + "exposureDelaySupport", + "exposureProgram", + "exposureProgramSupport", + "fileFormat", + "fileFormatSupport", + "_filter", + "_filterSupport", + "gpsInfo", + "_gpsTagRecording", + "_imageStitching", + "_imageStitchingSupport", + "iso", + "isoSupport", + "isoAutoHighLimit", + "isoAutoHighLimitSupport", + "_language", + "_languageSupport", + "_maxRecordableTime", + "_maxRecordableTimeSupport", + "_microphone", + "_microphoneSupport", + "_microphoneChannel", + "_microphoneChannelSupport", + "_networkType", + "_networkTypeSupport", + "offDelay", + "offDelaySupport", + "remainingPictures", + "previewFormat", + "previewFormatSupport", + "remainingSpace", + "remainingVideoSeconds", + "_shootingMethod", + "_shootingMethodSupport", + "shutterSpeed", + "shutterSpeedSupport", + "_shutterVolume", + "_shutterVolumeSupport", + "sleepDelay", + "sleepDelaySupport", + "_topBottomCorrection", + "_topBottomCorrectionSupport", + "totalSpace", + "videoStitching", + "videoStitchingSupport", + "_visibilityReduction", + "_visibilityReductionSupport", + "whiteBalance", + "whiteBalanceSupport", + ) + val params = GetOptionsParams(optionNames = optionNames) + val response = ThetaApi.callGetOptionsCommand(endpoint, params) + assertEquals("camera.getOptions", response.name, "name") + assertEquals(CommandState.DONE, response.state, "state") + assertNotNull(response.results?.options, "results.options") + val options = response.results!!.options + assertEquals(AiAutoThumbnail.OFF, options._aiAutoThumbnail, "_aiAutoThumbnail") + options._aiAutoThumbnailSupport!!.forEach { + when (it) { + AiAutoThumbnail.ON -> { + assertTrue(true, "aiAutoThumbnail.ON") + } + AiAutoThumbnail.OFF -> { + assertTrue(true, "aiAutoThumbnail.OFF") + } + else -> { + assertTrue (false, "aiAutoThunbmailSupport") + } } } - } - assertEquals(2.0f, options.aperture, "aperture") - assertNotNull(options.apertureSupport, "apertureSupport") - assertEquals(CameraControlSource.CAMERA, options._cameraControlSource, "_cameraControlSource") - options._cameraControlSourceSupport!!.forEach { - when (it) { - CameraControlSource.CAMERA -> { - assertTrue(true, "CameraControlSource.CAMEARA") - } - CameraControlSource.APP -> { - assertTrue(true, "CameraControlSource.APP") - } - else -> { - assertTrue (false, "CameraControlSource failed") + assertEquals(2.0f, options.aperture, "aperture") + assertNotNull(options.apertureSupport, "apertureSupport") + assertEquals(CameraControlSource.CAMERA, options._cameraControlSource, "_cameraControlSource") + options._cameraControlSourceSupport!!.forEach { + when (it) { + CameraControlSource.CAMERA -> { + assertTrue(true, "CameraControlSource.CAMEARA") + } + CameraControlSource.APP -> { + assertTrue(true, "CameraControlSource.APP") + } + else -> { + assertTrue (false, "CameraControlSource failed") + } } } - } - assertEquals(1, options.clientVersion, "clientVersion") - assertEquals(2, options.clientVersionSupport?.size, "clientVersionSupport") - options.clientVersionSupport!!.forEach { - when (it) { - 1 -> { - assertTrue(true, "clientVersion 1") - } - 2 -> { - assertTrue(true, "clientVersion 2") - } - else -> { - assertTrue (false, "clientVersion failed") + assertEquals(1, options.clientVersion, "clientVersion") + assertEquals(2, options.clientVersionSupport?.size, "clientVersionSupport") + options.clientVersionSupport!!.forEach { + when (it) { + 1 -> { + assertTrue(true, "clientVersion 1") + } + 2 -> { + assertTrue(true, "clientVersion 2") + } + else -> { + assertTrue (false, "clientVersion failed") + } } } - } - assertEquals(CaptureMode.IMAGE, options.captureMode, "captureMode") - assertEquals(2, options.captureModeSupport?.size, "captureModeSupport") - options.captureModeSupport!!.forEach { - when (it) { - CaptureMode.IMAGE -> { - assertTrue(true, "CaptureMode.IMAGE") - } - CaptureMode.VIDEO -> { - assertTrue(true, "CaptureMode.VIDEO") - } - else -> { - assertTrue (false, "CaptureMode failed") + assertEquals(CaptureMode.IMAGE, options.captureMode, "captureMode") + assertEquals(2, options.captureModeSupport?.size, "captureModeSupport") + options.captureModeSupport!!.forEach { + when (it) { + CaptureMode.IMAGE -> { + assertTrue(true, "CaptureMode.IMAGE") + } + CaptureMode.VIDEO -> { + assertTrue(true, "CaptureMode.VIDEO") + } + else -> { + assertTrue (false, "CaptureMode failed") + } } } - } - assertEquals("2020:01:01 00:00:00+09:00", options.dateTimeZone, "dateTimeZone") - assertEquals(0f, options.exposureCompensation, "exposureCompensation") - assertEquals(13, options.exposureCompensationSupport!!.size, "exposureCompensationSupport") - assertEquals(0, options.exposureDelay, "exposureDelay") - assertEquals(1, options.exposureDelaySupport!!.size, "exposureDelaySupport") - assertEquals(2, options.exposureProgram, "exposureProgram") - assertEquals(4, options.exposureProgramSupport!!.size, "exposureProgramSupport") - assertNotNull(options.fileFormat, "fileFormat") - val fileFormat = options.fileFormat!! - assertEquals(MediaType.JPEG, fileFormat.type, "fileformat.type") - assertEquals(5376, fileFormat.width, "fileformat.width") - assertEquals(2688, fileFormat.height, "fileformat.height") - assertEquals(2, options.fileFormatSupport!!.size, "fileFormatSupport") - assertEquals(ImageFilter.OFF, options._filter, "_filter") - assertEquals(5, options._filterSupport!!.size, "_filterSupport") - options._filterSupport!!.forEach { - when (it) { - ImageFilter.OFF -> { - assertTrue(true, "imageFilter.OFF") - } - ImageFilter.DR_COMP -> { - assertTrue(true, "imageFilter.DR_COMP") - } - ImageFilter.NOISE_REDUCTION -> { - assertTrue(true, "imageFilter.NOISE_REDUCTION") - } - ImageFilter.HDR -> { - assertTrue(true, "imageFilter.HDR") - } - ImageFilter.HH_HDR -> { - assertTrue(true, "imageFilter.HH_HDR") - } - else -> { - assertTrue(false, "imageFilter failed") + assertEquals("2020:01:01 00:00:00+09:00", options.dateTimeZone, "dateTimeZone") + assertEquals(0f, options.exposureCompensation, "exposureCompensation") + assertEquals(13, options.exposureCompensationSupport!!.size, "exposureCompensationSupport") + assertEquals(0, options.exposureDelay, "exposureDelay") + assertEquals(1, options.exposureDelaySupport!!.size, "exposureDelaySupport") + assertEquals(2, options.exposureProgram, "exposureProgram") + assertEquals(4, options.exposureProgramSupport!!.size, "exposureProgramSupport") + assertNotNull(options.fileFormat, "fileFormat") + val fileFormat = options.fileFormat!! + assertEquals(MediaType.JPEG, fileFormat.type, "fileformat.type") + assertEquals(5376, fileFormat.width, "fileformat.width") + assertEquals(2688, fileFormat.height, "fileformat.height") + assertEquals(2, options.fileFormatSupport!!.size, "fileFormatSupport") + assertEquals(ImageFilter.OFF, options._filter, "_filter") + assertEquals(5, options._filterSupport!!.size, "_filterSupport") + options._filterSupport!!.forEach { + when (it) { + ImageFilter.OFF -> { + assertTrue(true, "imageFilter.OFF") + } + ImageFilter.DR_COMP -> { + assertTrue(true, "imageFilter.DR_COMP") + } + ImageFilter.NOISE_REDUCTION -> { + assertTrue(true, "imageFilter.NOISE_REDUCTION") + } + ImageFilter.HDR -> { + assertTrue(true, "imageFilter.HDR") + } + ImageFilter.HH_HDR -> { + assertTrue(true, "imageFilter.HH_HDR") + } + else -> { + assertTrue(false, "imageFilter failed") + } } } - } - assertNotNull(options.gpsInfo, "gpsInfo") - val gps = options.gpsInfo!! - assertEquals(23.532F, gps.lat, "lat") - assertEquals(23.532F, gps.lng, "lng") - assertEquals(999f, gps._altitude, "_altitude") - assertEquals("2014:05:18 01:04:29+08:00", gps._dateTimeZone, "_dateTimeZone") - assertEquals("WGS84", gps._datum, "_datum") - assertEquals(GpsTagRecording.OFF, options._gpsTagRecording, "_gpsTagRecording") - assertEquals(ImageStitching.AUTO, options._imageStitching, "_imageStitching") - assertEquals(7, options._imageStitchingSupport!!.size, "_imageStitchingSupport") - options._imageStitchingSupport!!.forEach { - when (it) { - ImageStitching.AUTO -> { - assertTrue(true, "imageStitching.AUTO") - } - ImageStitching.STATIC -> { - assertTrue(true, "imageStitching.STATIC") - } - ImageStitching.DYNAMIC_AUTO -> { - assertTrue(true, "imageStitching.DYNAMIC_AUTO") - } - ImageStitching.DYNAMIC_SEMIAUTO -> { - assertTrue(true, "imageStitching.DYNAMIC_SEMIAUTO") - } - ImageStitching.DYNAMIC_SAVE -> { - assertTrue(true, "imageStitching.DYNAMIC_SAVE") - } - ImageStitching.DYNAMIC_LOAD -> { - assertTrue(true, "imageStitching.DYNAMIC_LOAD") - } - ImageStitching.NONE -> { - assertTrue(true, "imageStitching.NONE") - } - else -> { - assertTrue(false, "imageStitching unknown") + assertNotNull(options.gpsInfo, "gpsInfo") + val gps = options.gpsInfo!! + assertEquals(23.532F, gps.lat, "lat") + assertEquals(23.532F, gps.lng, "lng") + assertEquals(999f, gps._altitude, "_altitude") + assertEquals("2014:05:18 01:04:29+08:00", gps._dateTimeZone, "_dateTimeZone") + assertEquals("WGS84", gps._datum, "_datum") + assertEquals(GpsTagRecording.OFF, options._gpsTagRecording, "_gpsTagRecording") + assertEquals(ImageStitching.AUTO, options._imageStitching, "_imageStitching") + assertEquals(7, options._imageStitchingSupport!!.size, "_imageStitchingSupport") + options._imageStitchingSupport!!.forEach { + when (it) { + ImageStitching.AUTO -> { + assertTrue(true, "imageStitching.AUTO") + } + ImageStitching.STATIC -> { + assertTrue(true, "imageStitching.STATIC") + } + ImageStitching.DYNAMIC_AUTO -> { + assertTrue(true, "imageStitching.DYNAMIC_AUTO") + } + ImageStitching.DYNAMIC_SEMIAUTO -> { + assertTrue(true, "imageStitching.DYNAMIC_SEMIAUTO") + } + ImageStitching.DYNAMIC_SAVE -> { + assertTrue(true, "imageStitching.DYNAMIC_SAVE") + } + ImageStitching.DYNAMIC_LOAD -> { + assertTrue(true, "imageStitching.DYNAMIC_LOAD") + } + ImageStitching.NONE -> { + assertTrue(true, "imageStitching.NONE") + } + else -> { + assertTrue(false, "imageStitching unknown") + } } } - } - assertEquals(640, options.iso, "iso") - assertEquals(18, options.isoSupport!!.size, "isoSupport") - assertEquals(1600, options.isoAutoHighLimit, "isoAutoHighLimit") - assertEquals(13, options.isoAutoHighLimitSupport!!.size, "isoAutoHighLimitSupport") - assertEquals(Language.US, options._language, "_language") - assertEquals(9, options._languageSupport!!.size, "_languageSupport") - options._languageSupport!!.forEach { - when (it) { - Language.US -> { - assertTrue(true, "language.US") - } - Language.GB -> { - assertTrue(true, "language.GB") - } - Language.JA -> { - assertTrue(true, "language.JA") - } - Language.FR -> { - assertTrue(true, "language.FR") - } - Language.DE -> { - assertTrue(true, "language.DE") - } - Language.TW -> { - assertTrue(true, "language.TW") - } - Language.CN -> { - assertTrue(true, "language.CN") - } - Language.IT -> { - assertTrue(true, "language.IT") - } - Language.KO -> { - assertTrue(true, "language.KO") - } - else -> { - assertTrue(false, "language unknown") + assertEquals(640, options.iso, "iso") + assertEquals(18, options.isoSupport!!.size, "isoSupport") + assertEquals(1600, options.isoAutoHighLimit, "isoAutoHighLimit") + assertEquals(13, options.isoAutoHighLimitSupport!!.size, "isoAutoHighLimitSupport") + assertEquals(Language.US, options._language, "_language") + assertEquals(9, options._languageSupport!!.size, "_languageSupport") + options._languageSupport!!.forEach { + when (it) { + Language.US -> { + assertTrue(true, "language.US") + } + Language.GB -> { + assertTrue(true, "language.GB") + } + Language.JA -> { + assertTrue(true, "language.JA") + } + Language.FR -> { + assertTrue(true, "language.FR") + } + Language.DE -> { + assertTrue(true, "language.DE") + } + Language.TW -> { + assertTrue(true, "language.TW") + } + Language.CN -> { + assertTrue(true, "language.CN") + } + Language.IT -> { + assertTrue(true, "language.IT") + } + Language.KO -> { + assertTrue(true, "language.KO") + } + else -> { + assertTrue(false, "language unknown") + } } } - } - assertEquals(180, options._maxRecordableTime, "_maxRecordableTime") - assertEquals(1, options._maxRecordableTimeSupport!!.size, "_MaxRecordableTimeSupport") - assertEquals(MicrophoneOption.AUTO, options._microphone, "_microphone") - assertEquals(3, options._microphoneSupport!!.size, "_microphoneSupport") - options._microphoneSupport!!.forEach { - when (it) { - MicrophoneOption.AUTO -> { - assertTrue(true, "_microphone.AUTO") - } - MicrophoneOption.INTERNAL -> { - assertTrue(true, "_microphone.INTERNAL") - } - MicrophoneOption.EXTERNAL -> { - assertTrue(true, "_microphone.EXTERNAL") - } - else -> { - assertTrue(false, "_microphone unknown") + assertEquals(180, options._maxRecordableTime, "_maxRecordableTime") + assertEquals(1, options._maxRecordableTimeSupport!!.size, "_MaxRecordableTimeSupport") + assertEquals(MicrophoneOption.AUTO, options._microphone, "_microphone") + assertEquals(3, options._microphoneSupport!!.size, "_microphoneSupport") + options._microphoneSupport!!.forEach { + when (it) { + MicrophoneOption.AUTO -> { + assertTrue(true, "_microphone.AUTO") + } + MicrophoneOption.INTERNAL -> { + assertTrue(true, "_microphone.INTERNAL") + } + MicrophoneOption.EXTERNAL -> { + assertTrue(true, "_microphone.EXTERNAL") + } + else -> { + assertTrue(false, "_microphone unknown") + } } } - } - assertEquals(MicrophoneChannel.SPATIAL, options._microphoneChannel, - "_microphoneChannel") - assertEquals(2, options._microphoneChannelSupport!!.size, - "_microphoneChannelSupport") - options._microphoneChannelSupport!!.forEach { - when (it) { - MicrophoneChannel.SPATIAL -> { - assertTrue(true, "microphoneChannel.SPATIAL") - } - MicrophoneChannel.MONAURAL -> { - assertTrue(true, "microphoneChannel.MONAURAL") - } - else -> { - assertTrue(false, "microphoneChannel unknown") + assertEquals(MicrophoneChannel.SPATIAL, options._microphoneChannel, + "_microphoneChannel") + assertEquals(2, options._microphoneChannelSupport!!.size, + "_microphoneChannelSupport") + options._microphoneChannelSupport!!.forEach { + when (it) { + MicrophoneChannel.SPATIAL -> { + assertTrue(true, "microphoneChannel.SPATIAL") + } + MicrophoneChannel.MONAURAL -> { + assertTrue(true, "microphoneChannel.MONAURAL") + } + else -> { + assertTrue(false, "microphoneChannel unknown") + } } } - } - assertEquals(NetworkType.DIRECT, options._networkType, "_networkType") - assertEquals(2, options._networkTypeSupport!!.size, "_networkTypeSupport") - options._networkTypeSupport!!.forEach { - when (it) { - NetworkType.DIRECT -> { - assertTrue(true, "networkType.DIRECT") - } - NetworkType.CLIENT -> { - assertTrue(true, "networkType.CLIENT") - } - else -> { - assertTrue(false, "networkType unknown") + assertEquals(NetworkType.DIRECT, options._networkType, "_networkType") + assertEquals(2, options._networkTypeSupport!!.size, "_networkTypeSupport") + options._networkTypeSupport!!.forEach { + when (it) { + NetworkType.DIRECT -> { + assertTrue(true, "networkType.DIRECT") + } + NetworkType.CLIENT -> { + assertTrue(true, "networkType.CLIENT") + } + else -> { + assertTrue(false, "networkType unknown") + } } } - } - assertEquals(1000, options.offDelay, "offDelay") - assertEquals(4, options.offDelaySupport!!.size, "offDelaySupport") - assertEquals(1378, options.remainingPictures, "remainingPictures") - assertNotNull(options.previewFormat, "previewFormat") - val previewFormat = options.previewFormat!! - assertEquals(0, previewFormat.width, "previewFormat.width") - assertEquals(0, previewFormat.height, "previewFormat.height") - assertEquals(0, previewFormat.framerate, "previewFormat.framerate") - assertEquals(1, options.previewFormatSupport!!.size, "previewFormatSupport") - assertEquals(6864153600, options.remainingSpace, "remainingSpace") - assertEquals(2182, options.remainingVideoSeconds, "remainingVideoSeconds") - assertEquals(ShootingMethod.NORMAL, options._shootingMethod, "_shootingMethod") - assertEquals(9, options._shootingMethodSupport!!.size, "_shootingMethodSupport") - options._shootingMethodSupport!!.forEach { - when (it) { - ShootingMethod.NORMAL -> { - assertTrue(true, "shootingMethod.NORMAL") - } - ShootingMethod.INTERVAL -> { - assertTrue(true, "shootingMethod.INTERVAL") - } - ShootingMethod.MOVE_INTERVAL -> { - assertTrue(true, "shootingMethod.MOVE_INTERVAL") - } - ShootingMethod.FIXED_INTERVAL -> { - assertTrue(true, "shootingMethod.FIXED_INTERVAL") - } - ShootingMethod.BRACKET -> { - assertTrue(true, "shootingMethod.BRACKET") - } - ShootingMethod.COMPOSITE -> { - assertTrue(true, "shootingMethod.COMPOSITE") - } - ShootingMethod.CONTINUOUS -> { - assertTrue(true, "shootingMethod.CONTINUOUS") - } - ShootingMethod.TIMESHIFT -> { - assertTrue(true, "shootingMethod.TIMESHIFT") - } - ShootingMethod.BURST -> { - assertTrue(true, "shootingMethod.BURST") - } - else -> { - assertTrue(false, "shootingMethod unknown") + assertEquals(1000, options.offDelay, "offDelay") + assertEquals(4, options.offDelaySupport!!.size, "offDelaySupport") + assertEquals(1378, options.remainingPictures, "remainingPictures") + assertNotNull(options.previewFormat, "previewFormat") + val previewFormat = options.previewFormat!! + assertEquals(0, previewFormat.width, "previewFormat.width") + assertEquals(0, previewFormat.height, "previewFormat.height") + assertEquals(0, previewFormat.framerate, "previewFormat.framerate") + assertEquals(1, options.previewFormatSupport!!.size, "previewFormatSupport") + assertEquals(6864153600, options.remainingSpace, "remainingSpace") + assertEquals(2182, options.remainingVideoSeconds, "remainingVideoSeconds") + assertEquals(ShootingMethod.NORMAL, options._shootingMethod, "_shootingMethod") + assertEquals(9, options._shootingMethodSupport!!.size, "_shootingMethodSupport") + options._shootingMethodSupport!!.forEach { + when (it) { + ShootingMethod.NORMAL -> { + assertTrue(true, "shootingMethod.NORMAL") + } + ShootingMethod.INTERVAL -> { + assertTrue(true, "shootingMethod.INTERVAL") + } + ShootingMethod.MOVE_INTERVAL -> { + assertTrue(true, "shootingMethod.MOVE_INTERVAL") + } + ShootingMethod.FIXED_INTERVAL -> { + assertTrue(true, "shootingMethod.FIXED_INTERVAL") + } + ShootingMethod.BRACKET -> { + assertTrue(true, "shootingMethod.BRACKET") + } + ShootingMethod.COMPOSITE -> { + assertTrue(true, "shootingMethod.COMPOSITE") + } + ShootingMethod.CONTINUOUS -> { + assertTrue(true, "shootingMethod.CONTINUOUS") + } + ShootingMethod.TIMESHIFT -> { + assertTrue(true, "shootingMethod.TIMESHIFT") + } + ShootingMethod.BURST -> { + assertTrue(true, "shootingMethod.BURST") + } + else -> { + assertTrue(false, "shootingMethod unknown") + } } } - } - assertEquals(0.0, options.shutterSpeed, "shutterSpeed") - assertEquals(1, options.shutterSpeedSupport!!.size, "shutterSpeedSupport") - assertEquals(100, options._shutterVolume, "_shutterVolume") - assertNotNull(options._shutterVolumeSupport, "_shutterVolumeSupport") - val shutterVolume = options._shutterVolumeSupport!! - assertEquals(0, shutterVolume.minShutterVolume, "minShutterVolume") - assertEquals(100, shutterVolume.maxShutterVolume, "maxShutterVolume") - assertEquals(1000, options.sleepDelay, "sleepDelay") - assertEquals(4, options.sleepDelaySupport!!.size, "sleepDelaySupport") - assertEquals(TopBottomCorrectionOption.APPLY, options._topBottomCorrection, - "_topBottomCorrection") - assertEquals(7, options._topBottomCorrectionSupport!!.size, - "_topBottomCorrectionSupport") - options._topBottomCorrectionSupport!!.forEach { - when (it) { - TopBottomCorrectionOption.APPLY -> { - assertTrue(true, "topBottomCorrection.APPLY") - } - TopBottomCorrectionOption.APPLY_AUTO -> { - assertTrue(true, "topBottomCorrection.APPLY_AUTO") - } - TopBottomCorrectionOption.APPLY_SEMIAUTO -> { - assertTrue(true, "topBottomCorrection.APPLY_SEMIAUTO") - } - TopBottomCorrectionOption.APPLY_SAVE -> { - assertTrue(true, "topBottomCorrection.APPLY_SAVE") - } - TopBottomCorrectionOption.APPLY_LOAD -> { - assertTrue(true, "topBottomCorrection.APPLY_LOAD") - } - TopBottomCorrectionOption.DISAPPLY -> { - assertTrue(true, "topBottomCorrection.DISAPPLY") - } - TopBottomCorrectionOption.MANUAL -> { - assertTrue(true, "topBottomCorrection.MANUAL") - } - else -> { - assertTrue(false, "topBottomCorrection unknown") + assertEquals(0.0, options.shutterSpeed, "shutterSpeed") + assertEquals(1, options.shutterSpeedSupport!!.size, "shutterSpeedSupport") + assertEquals(100, options._shutterVolume, "_shutterVolume") + assertNotNull(options._shutterVolumeSupport, "_shutterVolumeSupport") + val shutterVolume = options._shutterVolumeSupport!! + assertEquals(0, shutterVolume.minShutterVolume, "minShutterVolume") + assertEquals(100, shutterVolume.maxShutterVolume, "maxShutterVolume") + assertEquals(1000, options.sleepDelay, "sleepDelay") + assertEquals(4, options.sleepDelaySupport!!.size, "sleepDelaySupport") + assertEquals(TopBottomCorrectionOption.APPLY, options._topBottomCorrection, + "_topBottomCorrection") + assertEquals(7, options._topBottomCorrectionSupport!!.size, + "_topBottomCorrectionSupport") + options._topBottomCorrectionSupport!!.forEach { + when (it) { + TopBottomCorrectionOption.APPLY -> { + assertTrue(true, "topBottomCorrection.APPLY") + } + TopBottomCorrectionOption.APPLY_AUTO -> { + assertTrue(true, "topBottomCorrection.APPLY_AUTO") + } + TopBottomCorrectionOption.APPLY_SEMIAUTO -> { + assertTrue(true, "topBottomCorrection.APPLY_SEMIAUTO") + } + TopBottomCorrectionOption.APPLY_SAVE -> { + assertTrue(true, "topBottomCorrection.APPLY_SAVE") + } + TopBottomCorrectionOption.APPLY_LOAD -> { + assertTrue(true, "topBottomCorrection.APPLY_LOAD") + } + TopBottomCorrectionOption.DISAPPLY -> { + assertTrue(true, "topBottomCorrection.DISAPPLY") + } + TopBottomCorrectionOption.MANUAL -> { + assertTrue(true, "topBottomCorrection.MANUAL") + } + else -> { + assertTrue(false, "topBottomCorrection unknown") + } } } - } - assertEquals(8060403712, options.totalSpace, "totalSpace") - assertEquals(VideoStitching.NONE, options.videoStitching, "videoStitching") - assertEquals(2, options.videoStitchingSupport!!.size, "videoStitchingSupport") - options.videoStitchingSupport!!.forEach { - when (it) { - VideoStitching.NONE -> { - assertTrue(true, "videoStitching.NONE") - } - VideoStitching.ONDEVICE -> { - assertTrue(true, "videoStitching.ONDEVICE") - } - else -> { - assertTrue(true, "videoStitching unknown") + assertEquals(8060403712, options.totalSpace, "totalSpace") + assertEquals(VideoStitching.NONE, options.videoStitching, "videoStitching") + assertEquals(2, options.videoStitchingSupport!!.size, "videoStitchingSupport") + options.videoStitchingSupport!!.forEach { + when (it) { + VideoStitching.NONE -> { + assertTrue(true, "videoStitching.NONE") + } + VideoStitching.ONDEVICE -> { + assertTrue(true, "videoStitching.ONDEVICE") + } + else -> { + assertTrue(true, "videoStitching unknown") + } } } - } - assertEquals(VisibilityReduction.ON, options._visibilityReduction, "_visibilityRecution") - assertEquals(2, options._visibilityReductionSupport!!.size, - "_visibilityReductionSupport") - options._visibilityReductionSupport!!.forEach { - when (it) { - VisibilityReduction.ON -> { - assertTrue(true, "visibilityReduction.ON") - } - VisibilityReduction.OFF -> { - assertTrue(true, "visibilityReduction.OFF") - } - else -> { - assertTrue(true, "visibilityReduction unknown") + assertEquals(VisibilityReduction.ON, options._visibilityReduction, "_visibilityRecution") + assertEquals(2, options._visibilityReductionSupport!!.size, + "_visibilityReductionSupport") + options._visibilityReductionSupport!!.forEach { + when (it) { + VisibilityReduction.ON -> { + assertTrue(true, "visibilityReduction.ON") + } + VisibilityReduction.OFF -> { + assertTrue(true, "visibilityReduction.OFF") + } + else -> { + assertTrue(true, "visibilityReduction unknown") + } } } - } - assertEquals(WhiteBalance.AUTO, options.whiteBalance, "whiteBalance") - assertEquals(12, options.whiteBalanceSupport!!.size, "whiteBalanceSupport") - options.whiteBalanceSupport!!.forEach { - when (it) { - WhiteBalance.AUTO -> { - assertTrue(true, "whiteBalance.AUTO") - } - WhiteBalance.DAYLIGHT -> { - assertTrue(true, "whiteBalance.DAYLIGHT") - } - WhiteBalance.SHADE -> { - assertTrue(true, "whiteBalance.SHADE") - } - WhiteBalance.CLOUDY_DAYLIGHT -> { - assertTrue(true, "whiteBalance.CLOUDY_DAYLIGHT") - } - WhiteBalance.INCANDESCENT -> { - assertTrue(true, "whiteBalance.INCANDESCENT") - } - WhiteBalance._WARM_WHITE_FLUORESCENT -> { - assertTrue(true, "whiteBalance._WARM_WHITE_FLUORESCENT") - } - WhiteBalance._DAYLIGHT_FLUORESCENT -> { - assertTrue(true, "whiteBalance._DAYLIGHT_FLUORESCENT") - } - WhiteBalance._DAYWHITE_FLUORESCENT -> { - assertTrue(true, "whiteBalance._DAYWHITE_FLUORESCENT") - } - WhiteBalance.FLUORESCENT -> { - assertTrue(true, "whiteBalance.FLUORESCENT") - } - WhiteBalance._BULB_FLUORESCENT -> { - assertTrue(true, "whiteBalance._BULB_FLUORESCENT") - } - WhiteBalance._COLOR_TEMPERATURE -> { - assertTrue(true, "whiteBalance._COLOR_TEMPERATURE") - } - WhiteBalance._UNDERWATER -> { - assertTrue(true, "whiteBalance._UNDERWATER") - } - else -> { - assertTrue(true, "whiteBalance unknown") + assertEquals(WhiteBalance.AUTO, options.whiteBalance, "whiteBalance") + assertEquals(12, options.whiteBalanceSupport!!.size, "whiteBalanceSupport") + options.whiteBalanceSupport!!.forEach { + when (it) { + WhiteBalance.AUTO -> { + assertTrue(true, "whiteBalance.AUTO") + } + WhiteBalance.DAYLIGHT -> { + assertTrue(true, "whiteBalance.DAYLIGHT") + } + WhiteBalance.SHADE -> { + assertTrue(true, "whiteBalance.SHADE") + } + WhiteBalance.CLOUDY_DAYLIGHT -> { + assertTrue(true, "whiteBalance.CLOUDY_DAYLIGHT") + } + WhiteBalance.INCANDESCENT -> { + assertTrue(true, "whiteBalance.INCANDESCENT") + } + WhiteBalance._WARM_WHITE_FLUORESCENT -> { + assertTrue(true, "whiteBalance._WARM_WHITE_FLUORESCENT") + } + WhiteBalance._DAYLIGHT_FLUORESCENT -> { + assertTrue(true, "whiteBalance._DAYLIGHT_FLUORESCENT") + } + WhiteBalance._DAYWHITE_FLUORESCENT -> { + assertTrue(true, "whiteBalance._DAYWHITE_FLUORESCENT") + } + WhiteBalance.FLUORESCENT -> { + assertTrue(true, "whiteBalance.FLUORESCENT") + } + WhiteBalance._BULB_FLUORESCENT -> { + assertTrue(true, "whiteBalance._BULB_FLUORESCENT") + } + WhiteBalance._COLOR_TEMPERATURE -> { + assertTrue(true, "whiteBalance._COLOR_TEMPERATURE") + } + WhiteBalance._UNDERWATER -> { + assertTrue(true, "whiteBalance._UNDERWATER") + } + else -> { + assertTrue(true, "whiteBalance unknown") + } } } } - } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun setOptionsTest() = runTest { - val optionNames = - listOf("_aiAutoThumbnail", - "aperture", - "_cameraControlSource", - "captureMode", - "dateTimeZone", - "exposureCompensation", - "exposureDelay", - "exposureProgram", - "fileFormat", - "_filter", - "gpsInfo", - "_gpsTagRecording", - "_imageStitching", - "iso", - "isoAutoHighLimit", - "_language", - "_maxRecordableTime", - "_microphone", - "_microphoneChannel", - "_networkType", - "offDelay", - "remainingPictures", - "previewFormat", - "remainingSpace", - "remainingVideoSeconds", - "_shootingMethod", - "shutterSpeed", - "_shutterVolume", - "sleepDelay", - "_topBottomCorrection", - "totalSpace", - "videoStitching", - "_visibilityReduction", - "whiteBalance", - ) - val params = GetOptionsParams(optionNames = optionNames) - val response = ThetaApi.callGetOptionsCommand(endpoint, params) - val params2 = SetOptionsParams(sessionId = "12345", - options = response.results!!.options) - val response2 = ThetaApi.callSetOptionsCommand(endpoint, params2) - assertEquals("camera.setOptions", response2.name, "name") - assertEquals(CommandState.DONE, response2.state, "state") - } + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun setOptionsTest() = runTest { + val optionNames = + listOf("_aiAutoThumbnail", + "aperture", + "_cameraControlSource", + "captureMode", + "dateTimeZone", + "exposureCompensation", + "exposureDelay", + "exposureProgram", + "fileFormat", + "_filter", + "gpsInfo", + "_gpsTagRecording", + "_imageStitching", + "iso", + "isoAutoHighLimit", + "_language", + "_maxRecordableTime", + "_microphone", + "_microphoneChannel", + "_networkType", + "offDelay", + "remainingPictures", + "previewFormat", + "remainingSpace", + "remainingVideoSeconds", + "_shootingMethod", + "shutterSpeed", + "_shutterVolume", + "sleepDelay", + "_topBottomCorrection", + "totalSpace", + "videoStitching", + "_visibilityReduction", + "whiteBalance", + ) + val params = GetOptionsParams(optionNames = optionNames) + val response = ThetaApi.callGetOptionsCommand(endpoint, params) + val params2 = SetOptionsParams(sessionId = "12345", + options = response.results!!.options) + val response2 = ThetaApi.callSetOptionsCommand(endpoint, params2) + assertEquals("camera.setOptions", response2.name, "name") + assertEquals(CommandState.DONE, response2.state, "state") + } - @kotlinx.coroutines.ExperimentalCoroutinesApi - @Test - fun optionsCopyTest() = runTest { - val opt1 = Options(iso = 100) - val opt2 = opt1.copy(isoAutoHighLimit = 200) - assertEquals(opt1.iso, opt2.iso, "iso copyed") - assertNotEquals(opt1.isoAutoHighLimit, opt2.isoAutoHighLimit, "isoAutoHighLimit") - } -*/ + @kotlinx.coroutines.ExperimentalCoroutinesApi + @Test + fun optionsCopyTest() = runTest { + val opt1 = Options(iso = 100) + val opt2 = opt1.copy(isoAutoHighLimit = 200) + assertEquals(opt1.iso, opt2.iso, "iso copyed") + assertNotEquals(opt1.isoAutoHighLimit, opt2.isoAutoHighLimit, "isoAutoHighLimit") + } + */ } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt index 88d7fe0d50..c0892f6f21 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt @@ -40,46 +40,46 @@ class PreviewTest { assertEquals(listFilesRequest.name, "camera.getLivePreview", "command name") } -/* - @Test - fun getLivePreviewTest1() = runTest { - MockApiClient.onRequest = { request -> - // check request - assertEquals(request.url.encodedPath, "/osc/commands/execute", "request path") - checkRequest(request) - ByteReadChannel(Resource("src/commonTest/resources/getLivePreview/preview_dummy_500.txt").readText()) - } + /* + @Test + fun getLivePreviewTest1() = runTest { + MockApiClient.onRequest = { request -> + // check request + assertEquals(request.url.encodedPath, "/osc/commands/execute", "request path") + checkRequest(request) + ByteReadChannel(Resource("src/commonTest/resources/getLivePreview/preview_dummy_500.txt").readText()) + } - val READ_COUNT = 1000 - var count = 0 - ThetaApi.callGetLivePreviewCommand(endpoint).collect { byteReadPacket -> - byteReadPacket.release() - if(++ count == READ_COUNT) { - assertTrue(true, "Read ${READ_COUNT} parts") - this.cancel() + val READ_COUNT = 1000 + var count = 0 + ThetaApi.callGetLivePreviewCommand(endpoint).collect { byteReadPacket -> + byteReadPacket.release() + if(++ count == READ_COUNT) { + assertTrue(true, "Read ${READ_COUNT} parts") + this.cancel() + } } } - } - @Test - fun getLivePreviewTest2() = runTest { - MockApiClient.onRequest = { request -> - // check request - assertEquals(request.url.encodedPath, "/osc/commands/execute", "request path") - checkRequest(request) - ByteReadChannel(Resource("src/commonTest/resources/getLivePreview/preview_dummy_500.txt").readText()) - } + @Test + fun getLivePreviewTest2() = runTest { + MockApiClient.onRequest = { request -> + // check request + assertEquals(request.url.encodedPath, "/osc/commands/execute", "request path") + checkRequest(request) + ByteReadChannel(Resource("src/commonTest/resources/getLivePreview/preview_dummy_500.txt").readText()) + } - val READ_COUNT = 1000 - var count = 0 - ThetaApi.callGetLivePreviewCommand(endpoint) handler@ { byteReadPacket -> - byteReadPacket.release() - if(++count == READ_COUNT) { - assertTrue(true, "Read ${READ_COUNT} parts") - return@handler false + val READ_COUNT = 1000 + var count = 0 + ThetaApi.callGetLivePreviewCommand(endpoint) handler@ { byteReadPacket -> + byteReadPacket.release() + if(++count == READ_COUNT) { + assertTrue(true, "Read ${READ_COUNT} parts") + return@handler false + } + return@handler true } - return@handler true } - } -*/ + */ } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/ThetaApiTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/ThetaApiTest.kt index 014d9d48fc..294bdb8f4b 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/ThetaApiTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/ThetaApiTest.kt @@ -14,80 +14,80 @@ class ThetaApiTest { fun teardown() { } -/* - @Test - fun infoApiTest() = runTest { - val info = ThetaApi.callInfoApi(endpoint) - println("oscInfo: $info") - assertTrue(info.manufacturer.startsWith("ricoh", ignoreCase = true), "manufacturer") - assertTrue(info.model.startsWith("RICOH THETA "), "model") - assertNotNull(info.serialNumber.toIntOrNull(), "serialNumber") - assertNotNull(info._wlanMacAddress?.isEmpty(), "_wlanMacAddress") - assertFalse(info._wlanMacAddress!!.isEmpty(), "_wlanMacAddress") - assertNotNull(info._bluetoothMacAddress?.isEmpty(), "_bluetoothMacAddress") - assertFalse(info.firmwareVersion.isEmpty(), "firmwareVersion") - assertFalse(info.supportUrl.isEmpty(), "supportUrl") - assertTrue(info.api.isNotEmpty(), "api") - assertTrue(info.apiLevel.isNotEmpty(), "apiLevel") - } + /* + @Test + fun infoApiTest() = runTest { + val info = ThetaApi.callInfoApi(endpoint) + println("oscInfo: $info") + assertTrue(info.manufacturer.startsWith("ricoh", ignoreCase = true), "manufacturer") + assertTrue(info.model.startsWith("RICOH THETA "), "model") + assertNotNull(info.serialNumber.toIntOrNull(), "serialNumber") + assertNotNull(info._wlanMacAddress?.isEmpty(), "_wlanMacAddress") + assertFalse(info._wlanMacAddress!!.isEmpty(), "_wlanMacAddress") + assertNotNull(info._bluetoothMacAddress?.isEmpty(), "_bluetoothMacAddress") + assertFalse(info.firmwareVersion.isEmpty(), "firmwareVersion") + assertFalse(info.supportUrl.isEmpty(), "supportUrl") + assertTrue(info.api.isNotEmpty(), "api") + assertTrue(info.apiLevel.isNotEmpty(), "apiLevel") + } - @Test - fun listFilesTest() = runTest { - val params = ListFilesParams(entryCount = 3) - val fileList: ListFilesResponse = ThetaApi.callListFilesCommand(endpoint, params) - println("listFiles: $fileList") - assertFalse(fileList.name.isEmpty(), "name") - assertTrue(fileList.state == "done", "state") - assertNotNull(fileList.results, "results") - assertNull(fileList.progress, "progress") - assertNull(fileList.error, "error") + @Test + fun listFilesTest() = runTest { + val params = ListFilesParams(entryCount = 3) + val fileList: ListFilesResponse = ThetaApi.callListFilesCommand(endpoint, params) + println("listFiles: $fileList") + assertFalse(fileList.name.isEmpty(), "name") + assertTrue(fileList.state == "done", "state") + assertNotNull(fileList.results, "results") + assertNull(fileList.progress, "progress") + assertNull(fileList.error, "error") - // Test the recult of command [camera.listFiles](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md) - assertTrue(fileList.results!!.totalEntries >= 1, "totalEntries") - assertFalse(fileList.results!!.entries.isEmpty(), "entries") - fileList.results!!.entries.forEach { - assertFalse(it.name.isEmpty(), "name") - assertFalse(it.fileUrl.isEmpty(), "fileUrl") - assertNotNull(it.dateTimeZone, "dateTimeZone") - assertFalse(it.dateTimeZone!!.isEmpty(), "dateTimeZone") - assertNull(it.dateTime, "dateTime") - //assertNotNull(it.lat, "lat") - //assertNotNull(it.lat, "lng") - assertNotNull(it.width, "width") - assertNotNull(it.height, "height") - //assertNotNull(it.thumbnail, "thumbnail") - //assertFalse(it.thumbnail!!.isEmpty(), "thumbnail") - //assertNotNull(it._thumbSize, "_thumbSize") - assertTrue(it.previewUrl.isEmpty(), "previewUrl") - assertNotNull(it._projectionType, "_projectionType") - assertTrue(it._projectionType == "Equirectangular" || it._projectionType == "Dual-Fisheye") + // Test the recult of command [camera.listFiles](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.list_files.md) + assertTrue(fileList.results!!.totalEntries >= 1, "totalEntries") + assertFalse(fileList.results!!.entries.isEmpty(), "entries") + fileList.results!!.entries.forEach { + assertFalse(it.name.isEmpty(), "name") + assertFalse(it.fileUrl.isEmpty(), "fileUrl") + assertNotNull(it.dateTimeZone, "dateTimeZone") + assertFalse(it.dateTimeZone!!.isEmpty(), "dateTimeZone") + assertNull(it.dateTime, "dateTime") + //assertNotNull(it.lat, "lat") + //assertNotNull(it.lat, "lng") + assertNotNull(it.width, "width") + assertNotNull(it.height, "height") + //assertNotNull(it.thumbnail, "thumbnail") + //assertFalse(it.thumbnail!!.isEmpty(), "thumbnail") + //assertNotNull(it._thumbSize, "_thumbSize") + assertTrue(it.previewUrl.isEmpty(), "previewUrl") + assertNotNull(it._projectionType, "_projectionType") + assertTrue(it._projectionType == "Equirectangular" || it._projectionType == "Dual-Fisheye") - // Test to get a photo - assertEquals(HttpStatusCode.OK, ThetaApi.httpClient.get(it.fileUrl).status) - assertEquals(HttpStatusCode.OK, ThetaApi.httpClient.get(it.getThumnailUrl()).status) + // Test to get a photo + assertEquals(HttpStatusCode.OK, ThetaApi.httpClient.get(it.fileUrl).status) + assertEquals(HttpStatusCode.OK, ThetaApi.httpClient.get(it.getThumnailUrl()).status) + } } - } - @Test - fun getLivePreviewTest() = runTest { - var count = 3 // read three frames - ThetaApi.callGetLivePreview(endpoint) { - if(!isActive) { - println("CoroutineScope.isActive is false") - return@callGetLivePreview false + @Test + fun getLivePreviewTest() = runTest { + var count = 3 // read three frames + ThetaApi.callGetLivePreview(endpoint) { + if(!isActive) { + println("CoroutineScope.isActive is false") + return@callGetLivePreview false + } + val byteArray = it.readBytes() + println("Got a preview frame") + it.release() + assertTrue(true, "Preview callback") + return@callGetLivePreview if (--count > 0) true else false } - val byteArray = it.readBytes() - println("Got a preview frame") - it.release() - assertTrue(true, "Preview callback") - return@callGetLivePreview if (--count > 0) true else false } - } - @Test - fun takePictureTest() = runTest { - ThetaApi.callTakePictureCommand(endpoint) - assertTrue(true, "take picture") - } -*/ + @Test + fun takePictureTest() = runTest { + ThetaApi.callTakePictureCommand(endpoint) + assertTrue(true, "take picture") + } + */ } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/PhotoCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/PhotoCaptureTest.kt index b15aeb49b7..b342673a81 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/PhotoCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/PhotoCaptureTest.kt @@ -57,6 +57,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkCommandName(request, "camera.takePicture") } @@ -127,6 +128,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -134,6 +136,7 @@ class PhotoCaptureTest { fileFormat = fileFormat.fileFormat.toMediaFileFormat() ) } + 2 -> { CheckRequest.checkCommandName(request, "camera.takePicture") } @@ -199,6 +202,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -253,6 +257,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -307,6 +312,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -361,6 +367,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -415,6 +422,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -469,6 +477,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -523,6 +532,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -581,6 +591,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -635,6 +646,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -689,6 +701,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -743,6 +756,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -797,6 +811,7 @@ class PhotoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkSetOptions( request = request, diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCaptureTest.kt index 3c37dbcfa3..4e8d6cb79e 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftCaptureTest.kt @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CaptureMode import com.ricoh360.thetaclient.transferred.FirstShootingEnum -import com.ricoh360.thetaclient.transferred.TimeShift import com.ricoh360.thetaclient.transferred.Preset +import com.ricoh360.thetaclient.transferred.TimeShift import io.ktor.client.network.sockets.* import io.ktor.client.request.* import io.ktor.http.* @@ -62,6 +62,7 @@ class TimeShiftCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.IMAGE) } + 1 -> { CheckRequest.checkCommandName(request, "camera.startCapture") } @@ -135,6 +136,7 @@ class TimeShiftCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.PRESET, preset = Preset.ROOM) } + 1 -> { CheckRequest.checkCommandName(request, "camera.startCapture") } @@ -208,6 +210,7 @@ class TimeShiftCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.PRESET, preset = Preset.ROOM) } + 1 -> { CheckRequest.checkCommandName(request, "camera.startCapture") } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/VideoCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/VideoCaptureTest.kt index 277f7bc4a4..7798c8853b 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/VideoCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/VideoCaptureTest.kt @@ -37,30 +37,35 @@ class VideoCaptureTest { Resource("src/commonTest/resources/VideoCapture/start_capture_done.json").readText(), Resource("src/commonTest/resources/VideoCapture/stop_capture_done.json").readText() ) - val requestPathArray = arrayOf( - "/osc/commands/execute", - "/osc/commands/execute", - "/osc/commands/execute" - ) var counter = 0 MockApiClient.onRequest = { request -> val index = counter++ + var response = "" // check request - assertEquals(request.url.encodedPath, requestPathArray[index], "start capture request") when (index) { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.VIDEO) + response = responseArray[0] } + 1 -> { CheckRequest.checkCommandName(request, "camera.startCapture") + response = responseArray[1] } - 2 -> { - CheckRequest.checkCommandName(request, "camera.stopCapture") + + else -> { + if (CheckRequest.getCommandName(request) == "camera.stopCapture") { + CheckRequest.checkCommandName(request, "camera.stopCapture") + response = responseArray[2] + } else if (request.url.encodedPath == "/osc/state") { + response = + Resource("src/commonTest/resources/VideoCapture/state_shooting.json").readText() + } } } - ByteReadChannel(responseArray[index]) + ByteReadChannel(response) } val deferred = CompletableDeferred() @@ -74,18 +79,23 @@ class VideoCaptureTest { var file: String? = null val capturing = videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { - file = fileUrl + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "error capture video") deferred.complete(Unit) } + + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } }) runBlocking { - delay(10) + delay(100) } capturing.stopCapture() @@ -103,87 +113,138 @@ class VideoCaptureTest { * call startCapture. */ @Test - fun startCaptureWithMaxRecordableTimeAndFileFormatTest() = runTest { + fun cancelStartCaptureTest() = runTest { // setup - val fileFormat = ThetaRepository.VideoFileFormatEnum.VIDEO_5_7K_30F - val maxRecordableTime = ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_300 - val responseArray = arrayOf( - Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), Resource("src/commonTest/resources/VideoCapture/start_capture_done.json").readText(), - Resource("src/commonTest/resources/VideoCapture/stop_capture_done.json").readText() - ) - val requestPathArray = arrayOf( - "/osc/commands/execute", - "/osc/commands/execute", - "/osc/commands/execute", - "/osc/commands/execute" ) var counter = 0 + var idleCount = 0 MockApiClient.onRequest = { request -> val index = counter++ + var response = "" // check request - assertEquals(request.url.encodedPath, requestPathArray[index], "start capture request") when (index) { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.VIDEO) + response = responseArray[0] } + 1 -> { - CheckRequest.checkSetOptions( - request = request, - fileFormat = fileFormat.fileFormat.toMediaFileFormat(), - maxRecordableTime = maxRecordableTime.sec - ) - } - 2 -> { CheckRequest.checkCommandName(request, "camera.startCapture") + response = responseArray[1] } - 3 -> { - CheckRequest.checkCommandName(request, "camera.stopCapture") + + else -> { + if (request.url.encodedPath == "/osc/state") { + idleCount += 1 + response = + Resource("src/commonTest/resources/VideoCapture/state_idle.json").readText() + } else { + assertTrue(false, "error capture video") + } } } - ByteReadChannel(responseArray[index]) + ByteReadChannel(response) } val deferred = CompletableDeferred() // execute val thetaRepository = ThetaRepository(endpoint) val videoCapture = thetaRepository.getVideoCaptureBuilder() - .setMaxRecordableTime(maxRecordableTime) - .setFileFormat(fileFormat) .build() - assertEquals(videoCapture.getMaxRecordableTime(), maxRecordableTime, "set option maxRecordableTime") - assertEquals(videoCapture.getFileFormat(), fileFormat, "set option fileFormat") + assertNull(videoCapture.getMaxRecordableTime(), "set option maxRecordableTime") + assertNull(videoCapture.getFileFormat(), "set option fileFormat") - var file: String? = null - val capturing = videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { - file = fileUrl + var file: String? = "error" + videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "error capture video") deferred.complete(Unit) } + + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } }) runBlocking { - delay(10) + delay(100) } - capturing.stopCapture() runBlocking { - withTimeout(1000) { + withTimeout(2000) { deferred.await() } } // check result - assertTrue(file?.startsWith("http://") ?: false, "start capture video") + assertNull(file, "cancel capture video") + assertEquals(idleCount, 2, "cancel capture video") + } + + /** + * call startCapture. + */ + @Test + fun startCaptureWithMaxRecordableTimeAndFileFormatTest() = runTest { + // setup + val fileFormat = ThetaRepository.VideoFileFormatEnum.VIDEO_5_7K_30F + val maxRecordableTime = ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_300 + + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + ) + val requestPathArray = arrayOf( + "/osc/commands/execute", + "/osc/commands/execute", + ) + var counter = 0 + MockApiClient.onRequest = { request -> + val index = counter++ + + // check request + assertEquals(request.url.encodedPath, requestPathArray[index], "start capture request") + when (index) { + 0 -> { + CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.VIDEO) + } + + 1 -> { + CheckRequest.checkSetOptions( + request = request, + fileFormat = fileFormat.fileFormat.toMediaFileFormat(), + maxRecordableTime = maxRecordableTime.sec + ) + } + } + + ByteReadChannel(responseArray[index]) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + val videoCapture = thetaRepository.getVideoCaptureBuilder() + .setMaxRecordableTime(maxRecordableTime) + .setFileFormat(fileFormat) + .build() + + assertEquals( + videoCapture.getMaxRecordableTime(), + maxRecordableTime, + "set option maxRecordableTime" + ) + assertEquals(videoCapture.getFileFormat(), fileFormat, "set option fileFormat") } /** @@ -209,6 +270,7 @@ class VideoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.VIDEO) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -262,6 +324,7 @@ class VideoCaptureTest { 0 -> { CheckRequest.checkSetOptions(request = request, captureMode = CaptureMode.VIDEO) } + 1 -> { CheckRequest.checkSetOptions( request = request, @@ -343,7 +406,11 @@ class VideoCaptureTest { .build() } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( - e.message!!.indexOf("json", 0, true) >= 0 || e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("json", 0, true) >= 0 || e.message!!.indexOf( + "Illegal", + 0, + true + ) >= 0, "setOptions option not json error response" ) exceptionNotJson = true @@ -383,7 +450,10 @@ class VideoCaptureTest { thetaRepository.getVideoCaptureBuilder() .build() } catch (e: ThetaRepository.ThetaWebApiException) { - assertTrue(e.message!!.indexOf("UnitTest", 0, true) >= 0, "status error and json response") + assertTrue( + e.message!!.indexOf("UnitTest", 0, true) >= 0, + "status error and json response" + ) exceptionStatusJson = true } assertTrue(exceptionStatusJson, "status error and json response") @@ -436,13 +506,21 @@ class VideoCaptureTest { // execute error response var deferred = CompletableDeferred() videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "capture video") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("UnitTest", 0, true) >= 0, "capture video error response") + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + exception.message!!.indexOf("UnitTest", 0, true) >= 0, + "capture video error response" + ) + deferred.complete(Unit) + } + + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture video") deferred.complete(Unit) } }) @@ -456,15 +534,20 @@ class VideoCaptureTest { // execute json error response deferred = CompletableDeferred() videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "capture video") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(exception.message!!.length >= 0, "capture video json error response") deferred.complete(Unit) } + + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture video") + deferred.complete(Unit) + } }) runBlocking { @@ -505,13 +588,21 @@ class VideoCaptureTest { // execute status error and json response var deferred = CompletableDeferred() videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "capture video") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("UnitTest", 0, true) >= 0, "status error and json response") + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + exception.message!!.indexOf("UnitTest", 0, true) >= 0, + "status error and json response" + ) + deferred.complete(Unit) + } + + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture video") deferred.complete(Unit) } }) @@ -525,15 +616,20 @@ class VideoCaptureTest { // execute status error and not json response deferred = CompletableDeferred() videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "capture video") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(exception.message!!.indexOf("503", 0, true) >= 0, "status error") deferred.complete(Unit) } + + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture video") + deferred.complete(Unit) + } }) runBlocking { @@ -545,15 +641,20 @@ class VideoCaptureTest { // execute timeout exception deferred = CompletableDeferred() videoCapture.startCapture(object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "capture video") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(exception.message!!.indexOf("time", 0, true) >= 0, "timeout exception") deferred.complete(Unit) } + + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture video") + deferred.complete(Unit) + } }) runBlocking { @@ -584,13 +685,21 @@ class VideoCaptureTest { var videoCapturing = VideoCapturing( endpoint, object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + exception.message!!.indexOf("UnitTest", 0, true) >= 0, + "stop capture error response" + ) + deferred.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("UnitTest", 0, true) >= 0, "stop capture error response") + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "stop capture") deferred.complete(Unit) } } @@ -608,13 +717,18 @@ class VideoCaptureTest { videoCapturing = VideoCapturing( endpoint, object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(exception.message!!.length >= 0, "stop capture json error response") + deferred.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.length >= 0, "stop capture json error response") + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "stop capture") deferred.complete(Unit) } } @@ -655,13 +769,21 @@ class VideoCaptureTest { var videoCapturing = VideoCapturing( endpoint, object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + exception.message!!.indexOf("UnitTest", 0, true) >= 0, + "status error and json response" + ) + deferred.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("UnitTest", 0, true) >= 0, "status error and json response") + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "stop capture") deferred.complete(Unit) } } @@ -679,13 +801,18 @@ class VideoCaptureTest { videoCapturing = VideoCapturing( endpoint, object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(exception.message!!.indexOf("503", 0, true) >= 0, "status error") + deferred.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("503", 0, true) >= 0, "status error") + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "stop capture") deferred.complete(Unit) } } @@ -703,13 +830,21 @@ class VideoCaptureTest { videoCapturing = VideoCapturing( endpoint, object : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + exception.message!!.indexOf("time", 0, true) >= 0, + "timeout exception" + ) + deferred.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { assertTrue(false, "stop capture") deferred.complete(Unit) } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { - assertTrue(exception.message!!.indexOf("time", 0, true) >= 0, "timeout exception") + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "stop capture") deferred.complete(Unit) } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/CancelVideoConvertTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/CancelVideoConvertTest.kt index 4fd548893f..c6c377f8a3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/CancelVideoConvertTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/CancelVideoConvertTest.kt @@ -60,7 +60,7 @@ class CancelVideoConvertTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ConvertVideoFormatsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ConvertVideoFormatsTest.kt index 47b52ee4c9..9228a38ba3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ConvertVideoFormatsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ConvertVideoFormatsTest.kt @@ -7,17 +7,21 @@ import com.ricoh360.thetaclient.transferred.ConvertVideoFormatsRequest import com.ricoh360.thetaclient.transferred.TopBottomCorrection import com.ricoh360.thetaclient.transferred.VideoFormat import com.ricoh360.thetaclient.transferred._ProjectionType -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class ConvertVideoFormatsTest { @@ -302,7 +306,7 @@ class ConvertVideoFormatsTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteAccessPointTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteAccessPointTest.kt index a0f535ca69..523d8512e3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteAccessPointTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteAccessPointTest.kt @@ -81,7 +81,7 @@ class DeleteAccessPointTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteFilesTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteFilesTest.kt index 1b52f68605..8b8051633c 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteFilesTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteFilesTest.kt @@ -4,17 +4,21 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.DeleteRequest -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class DeleteFilesTest { @@ -89,7 +93,7 @@ class DeleteFilesTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteMySettingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteMySettingTest.kt index 0e9bca6805..dce6c8c8f6 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteMySettingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/DeleteMySettingTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class DeleteMySettingTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/FinishWlanTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/FinishWlanTest.kt index 0a0678ebd4..12d4bd1c38 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/FinishWlanTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/FinishWlanTest.kt @@ -60,7 +60,7 @@ class FinishWlanTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMetadataTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMetadataTest.kt index 82ad3b1f8a..4690310862 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMetadataTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMetadataTest.kt @@ -4,17 +4,22 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.GetMetadataRequest -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class GetMetadataTest { @@ -120,7 +125,7 @@ class GetMetadataTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMySettingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMySettingTest.kt index 56c9b8aa5a..c5a06ff738 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMySettingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetMySettingTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class GetMySettingTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetOptionsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetOptionsTest.kt index 2136f2e004..b05e49989a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetOptionsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetOptionsTest.kt @@ -4,17 +4,23 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.GetOptionsRequest -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class GetOptionsTest { @@ -135,7 +141,7 @@ class GetOptionsTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginLicenseTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginLicenseTest.kt index 8c8cbb50e8..422390a2a3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginLicenseTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginLicenseTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class GetPluginLicenseTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginOrdersTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginOrdersTest.kt index 93db7d04c8..1bf63d546c 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginOrdersTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetPluginOrdersTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class GetPluginOrdersTest { @@ -35,7 +39,7 @@ class GetPluginOrdersTest { val thetaRepository = ThetaRepository(endpoint) var pluginList: List = listOf() kotlin.runCatching { - pluginList = thetaRepository.getPluginOrders() + pluginList = thetaRepository.getPluginOrders() }.onSuccess { assertTrue(pluginList.size == 3, "size of list") }.onFailure { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaInfoTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaInfoTest.kt index 13a52ef988..bb9faa77aa 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaInfoTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaInfoTest.kt @@ -54,8 +54,12 @@ class GetThetaInfoTest { assertTrue(!thetaInfo.hasGps, "info hasGps") assertTrue(thetaInfo.hasGyro, "info hasGyro") assertTrue(thetaInfo.uptime > 0, "info uptime") - assertTrue(thetaInfo.api == listOf("/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status"), "info api") + assertTrue( + thetaInfo.api == listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), "info api" + ) assertTrue(thetaInfo.endpoints == EndPoint(80, 80), "info endpoints") assertTrue(thetaInfo.apiLevel == listOf(2), "info apiLevel") } @@ -88,8 +92,12 @@ class GetThetaInfoTest { assertTrue(!thetaInfo.hasGps, "info hasGps") assertTrue(!thetaInfo.hasGyro, "info hasGyro") assertTrue(thetaInfo.uptime > 0, "info uptime") - assertTrue(thetaInfo.api == listOf("/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status"), "info api") + assertTrue( + thetaInfo.api == listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), "info api" + ) assertTrue(thetaInfo.endpoints == EndPoint(80, 80), "info endpoints") assertTrue(thetaInfo.apiLevel == listOf(1, 2), "info apiLevel") } @@ -122,8 +130,12 @@ class GetThetaInfoTest { assertTrue(thetaInfo.hasGps, "info hasGps") assertTrue(thetaInfo.hasGyro, "info hasGyro") assertTrue(thetaInfo.uptime > 0, "info uptime") - assertTrue(thetaInfo.api == listOf("/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status"), "info api") + assertTrue( + thetaInfo.api == listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), "info api" + ) assertTrue(thetaInfo.endpoints == EndPoint(80, 80), "info endpoints") assertTrue(thetaInfo.apiLevel == listOf(2), "info apiLevel") } @@ -154,13 +166,17 @@ class GetThetaInfoTest { assertTrue(thetaInfo.firmwareVersion.length > 1, "info firmwareVersion") val result = Regex("^\\d+").find(thetaInfo.firmwareVersion) assertNotNull(result) - result.value.toIntOrNull()?.let { assertTrue(it <= 5)} ?: assertTrue(false, "firmware version") + result.value.toIntOrNull()?.let { assertTrue(it <= 5) } ?: assertTrue(false, "firmware version") assertTrue(thetaInfo.supportUrl == "https://theta360.com/en/support/", "info supportUrl") assertTrue(!thetaInfo.hasGps, "info hasGps") assertTrue(thetaInfo.hasGyro, "info hasGyro") assertTrue(thetaInfo.uptime > 0, "info uptime") - assertTrue(thetaInfo.api == listOf("/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status"), "info api") + assertTrue( + thetaInfo.api == listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), "info api" + ) assertTrue(thetaInfo.endpoints == EndPoint(80, 80), "info endpoints") assertTrue(thetaInfo.apiLevel == listOf(2), "info apiLevel") } @@ -191,13 +207,17 @@ class GetThetaInfoTest { assertTrue(thetaInfo.firmwareVersion.length > 1, "info firmwareVersion") val result = Regex("^\\d+").find(thetaInfo.firmwareVersion) assertNotNull(result) - result.value.toIntOrNull()?.let { assertTrue(it >= 6)} ?: assertTrue(false, "firmware version") + result.value.toIntOrNull()?.let { assertTrue(it >= 6) } ?: assertTrue(false, "firmware version") assertTrue(thetaInfo.supportUrl == "https://theta360.com/en/support/", "info supportUrl") assertTrue(!thetaInfo.hasGps, "info hasGps") assertTrue(thetaInfo.hasGyro, "info hasGyro") assertTrue(thetaInfo.uptime > 0, "info uptime") - assertTrue(thetaInfo.api == listOf("/osc/info", "/osc/state", "/osc/checkForUpdates", - "/osc/commands/execute", "/osc/commands/status"), "info api") + assertTrue( + thetaInfo.api == listOf( + "/osc/info", "/osc/state", "/osc/checkForUpdates", + "/osc/commands/execute", "/osc/commands/status" + ), "info api" + ) assertTrue(thetaInfo.endpoints == EndPoint(80, 80), "info endpoints") assertTrue(thetaInfo.apiLevel == listOf(2), "info apiLevel") } @@ -220,7 +240,7 @@ class GetThetaInfoTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaStateTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaStateTest.kt index 745168c95b..eccc156bd6 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaStateTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaStateTest.kt @@ -54,7 +54,7 @@ class GetThetaStateTest { assertTrue(thetaState.latestFileUrl.startsWith("http://"), "state latestFileUrl") assertEquals(thetaState.chargingState, ThetaRepository.ChargingStateEnum.NOT_CHARGING, "state chargingState") assertEquals(thetaState.apiVersion, 2, "state apiVersion") - assertTrue(!thetaState.isPluginRunning!!,"state isPluginRunning") + assertTrue(!thetaState.isPluginRunning!!, "state isPluginRunning") assertTrue(thetaState.isPluginWebServer!!, "state isPluginWebServer") assertEquals(thetaState.function, ThetaRepository.ShootingFunctionEnum.SELF_TIMER, "state function") assertTrue(!thetaState.isMySettingChanged!!, "state isMySettingChanged") @@ -363,7 +363,7 @@ class GetThetaStateTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListFilesTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListFilesTest.kt index e4669428ce..56b48225d8 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListFilesTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListFilesTest.kt @@ -394,7 +394,7 @@ class ListFilesTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response", ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ResetTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ResetTest.kt index a7b0f7d6f5..7168bc4b7a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ResetTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ResetTest.kt @@ -60,7 +60,7 @@ class ResetTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RestoreSettingsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RestoreSettingsTest.kt index 54b5d8bed5..e8b41e9f0d 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RestoreSettingsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RestoreSettingsTest.kt @@ -50,6 +50,7 @@ class RestoreSettingsTest { dateTimeZone = config.dateTime, ) } + 2 -> { CheckRequest.checkSetOptions( request = request, @@ -95,6 +96,7 @@ class RestoreSettingsTest { dateTimeZone = config.dateTime, ) } + 2 -> { CheckRequest.checkSetOptions( request = request, @@ -135,6 +137,7 @@ class RestoreSettingsTest { dateTimeZone = config.dateTime, ) } + 2 -> { CheckRequest.checkSetOptions( request = request, @@ -181,7 +184,7 @@ class RestoreSettingsTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetAccessPointTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetAccessPointTest.kt index 4cc09ddccf..b15a831b49 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetAccessPointTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetAccessPointTest.kt @@ -6,17 +6,21 @@ import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.AuthenticationMode import com.ricoh360.thetaclient.transferred.IpAddressAllocation import com.ricoh360.thetaclient.transferred.SetAccessPointRequest -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class SetAccessPointTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetBluetoothDeviceTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetBluetoothDeviceTest.kt index 4d1f0a6e60..e6d992edc7 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetBluetoothDeviceTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetBluetoothDeviceTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class SetBluetoothDeviceTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetMySettingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetMySettingTest.kt index a759d1150a..3b3c0e15cd 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetMySettingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetMySettingTest.kt @@ -3,12 +3,16 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class SetMySettingTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetOptionsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetOptionsTest.kt index a70c04e191..699e128e03 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetOptionsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetOptionsTest.kt @@ -5,17 +5,21 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.SetOptionsRequest -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class SetOptionsTest { @@ -83,7 +87,7 @@ class SetOptionsTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginOrdersTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginOrdersTest.kt index fa96c535ca..6bfad56656 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginOrdersTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginOrdersTest.kt @@ -4,12 +4,16 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.SIZE_OF_SET_PLUGIN_ORDERS_ARGUMENT_LIST_FOR_Z1 import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest import kotlinx.serialization.ExperimentalSerializationApi -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) class SetPluginOrdersTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/StopSelfTimerTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/StopSelfTimerTest.kt index a78dcdd44b..081b745db2 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/StopSelfTimerTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/StopSelfTimerTest.kt @@ -60,7 +60,7 @@ class StopSelfTimerTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryClientModeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryClientModeTest.kt index c176ed0281..458922ebae 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryClientModeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryClientModeTest.kt @@ -106,13 +106,16 @@ class ThetaRepositoryClientModeTest { 0 -> { assertNull(authHeader) } + 1 -> { assertNotNull(authHeader) checkAuthHeader(authHeader, username, request.url.encodedPath, realm, qop, nonce) } + 2 -> { assertNull(authHeader) } + 3 -> { assertNotNull(authHeader) checkAuthHeader(authHeader, username, request.url.encodedPath, realm, qop, nonce) @@ -183,9 +186,11 @@ class ThetaRepositoryClientModeTest { 0 -> { assertNull(authHeader) } + 1 -> { assertNotNull(authHeader) } + 2 -> { assertTrue(false, "newInstance") } @@ -238,6 +243,7 @@ class ThetaRepositoryClientModeTest { 0 -> { assertNull(authHeader) } + 1 -> { assertTrue(false, "newInstance") } @@ -304,6 +310,7 @@ class ThetaRepositoryClientModeTest { 0 -> { assertNull(authHeader) } + 1 -> { assertTrue(false, "newInstance") } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryTest.kt index e30429be76..403aa3b09b 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ThetaRepositoryTest.kt @@ -105,9 +105,11 @@ class ThetaRepositoryTest { 2 -> { CheckRequest.checkCommandName(request, "camera.startSession") } + 3 -> { CheckRequest.checkSetOptions(request, clientVersion = 2) } + 4 -> { CheckRequest.checkGetOptions( request, @@ -161,9 +163,11 @@ class ThetaRepositoryTest { 2 -> { CheckRequest.checkCommandName(request, "camera.startSession") } + 3 -> { CheckRequest.checkSetOptions(request, clientVersion = 2) } + 4 -> { CheckRequest.checkGetOptions( request, @@ -265,6 +269,7 @@ class ThetaRepositoryTest { dateTimeZone = config.dateTime, ) } + 3 -> { CheckRequest.checkSetOptions( request = request, @@ -326,6 +331,7 @@ class ThetaRepositoryTest { dateTimeZone = config.dateTime, ) } + 6 -> { CheckRequest.checkSetOptions( request = request, @@ -387,6 +393,7 @@ class ThetaRepositoryTest { dateTimeZone = config.dateTime, ) } + 6 -> { CheckRequest.checkSetOptions( request = request, @@ -539,6 +546,7 @@ class ThetaRepositoryTest { 2 -> { CheckRequest.checkCommandName(request, "camera.startSession") } + 3 -> { CheckRequest.checkCommandName(request, "camera.setOptions") } @@ -649,7 +657,7 @@ class ThetaRepositoryTest { } catch (e: ThetaRepository.ThetaWebApiException) { assertTrue( e.message!!.indexOf("json", 0, true) >= 0 || - e.message!!.indexOf("Illegal", 0, true) >= 0, + e.message!!.indexOf("Illegal", 0, true) >= 0, "error response" ) } @@ -688,7 +696,7 @@ class ThetaRepositoryTest { arrayOf("RICOH THETA X", null, ThetaRepository.ThetaModel.THETA_X), arrayOf("RICOH THETA X", "2222222", ThetaRepository.ThetaModel.THETA_X), arrayOf("RICOH THETA Z1", null, ThetaRepository.ThetaModel.THETA_Z1), - arrayOf("RICOH THETA Z1","4444444", ThetaRepository.ThetaModel.THETA_Z1), + arrayOf("RICOH THETA Z1", "4444444", ThetaRepository.ThetaModel.THETA_Z1), arrayOf("RICOH THETA V", null, ThetaRepository.ThetaModel.THETA_V), arrayOf("RICOH THETA S", null, ThetaRepository.ThetaModel.THETA_S), arrayOf("RICOH THETA SC", null, ThetaRepository.ThetaModel.THETA_SC), diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BitrateTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BitrateTest.kt index 13335541a6..63550976f8 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BitrateTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BitrateTest.kt @@ -5,7 +5,6 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import com.ricoh360.thetaclient.transferred.WhiteBalanceAutoStrength import io.ktor.client.request.* import io.ktor.http.* import io.ktor.http.content.* diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstModeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstModeTest.kt index 5a2b69a9bf..a370678448 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstModeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstModeTest.kt @@ -4,7 +4,6 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.BluetoothPower import com.ricoh360.thetaclient.transferred.BurstMode import com.ricoh360.thetaclient.transferred.Options import io.ktor.client.request.* diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ImageStitchingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ImageStitchingTest.kt index c5065268ce..e4619a9b6e 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ImageStitchingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ImageStitchingTest.kt @@ -78,9 +78,9 @@ class ImageStitchingTest { } } - /** - * Convert ThetaRepository.Options to Options. - */ + /** + * Convert ThetaRepository.Options to Options. + */ @Test fun convertOptionImageStitchingTest() = runTest { val values = listOf( diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayTest.kt index b96eca0267..f978a4a053 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayTest.kt @@ -101,5 +101,7 @@ class OffDelayTest { val options = orgOptions.toOptions() assertEquals(options.offDelay, it.second, "offDelay ${it.second}") } + + assertEquals(ThetaRepository.OffDelayEnum.get(0), ThetaRepository.OffDelayEnum.DISABLE, "DISABLE") } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OptionsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OptionsTest.kt index 37220020a8..f901f931e1 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OptionsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OptionsTest.kt @@ -1,8 +1,45 @@ package com.ricoh360.thetaclient.repository.options import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import kotlin.test.* +import com.ricoh360.thetaclient.transferred.AiAutoThumbnail +import com.ricoh360.thetaclient.transferred.BluetoothPower +import com.ricoh360.thetaclient.transferred.BurstBracketStep +import com.ricoh360.thetaclient.transferred.BurstCaptureNum +import com.ricoh360.thetaclient.transferred.BurstCompensation +import com.ricoh360.thetaclient.transferred.BurstEnableIsoControl +import com.ricoh360.thetaclient.transferred.BurstMaxExposureTime +import com.ricoh360.thetaclient.transferred.BurstMode +import com.ricoh360.thetaclient.transferred.BurstOption +import com.ricoh360.thetaclient.transferred.BurstOrder +import com.ricoh360.thetaclient.transferred.CameraControlSource +import com.ricoh360.thetaclient.transferred.CameraMode +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.FaceDetect +import com.ricoh360.thetaclient.transferred.FirstShootingEnum +import com.ricoh360.thetaclient.transferred.Gain +import com.ricoh360.thetaclient.transferred.GpsInfo +import com.ricoh360.thetaclient.transferred.GpsTagRecording +import com.ricoh360.thetaclient.transferred.ImageFilter +import com.ricoh360.thetaclient.transferred.ImageStitching +import com.ricoh360.thetaclient.transferred.Language +import com.ricoh360.thetaclient.transferred.MediaFileFormat +import com.ricoh360.thetaclient.transferred.MediaType +import com.ricoh360.thetaclient.transferred.NetworkType +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.PowerSaving +import com.ricoh360.thetaclient.transferred.Preset +import com.ricoh360.thetaclient.transferred.PreviewFormat +import com.ricoh360.thetaclient.transferred.Proxy +import com.ricoh360.thetaclient.transferred.ShootingFunction +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.TimeShift +import com.ricoh360.thetaclient.transferred.WhiteBalance +import com.ricoh360.thetaclient.transferred.WhiteBalanceAutoStrength +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotNull class OptionsTest { @BeforeTest diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PresetTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PresetTest.kt index dca89cb427..d61f58de60 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PresetTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PresetTest.kt @@ -4,8 +4,8 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.Preset import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.Preset import io.ktor.http.* import io.ktor.utils.io.* import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PreviewFormatTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PreviewFormatTest.kt index 2e2e2063ad..27b636b74a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PreviewFormatTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PreviewFormatTest.kt @@ -4,8 +4,8 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.PreviewFormat import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.PreviewFormat import io.ktor.http.* import io.ktor.utils.io.* import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShootingMethodTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShootingMethodTest.kt index 9eddd46855..030da276e3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShootingMethodTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShootingMethodTest.kt @@ -4,9 +4,8 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.ShootingMethod import com.ricoh360.thetaclient.transferred.Options -import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.ShootingMethod import io.ktor.http.* import io.ktor.utils.io.* import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/SleepDelayTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/SleepDelayTest.kt index 4ef18c7ac6..9f9b857e6e 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/SleepDelayTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/SleepDelayTest.kt @@ -101,5 +101,7 @@ class SleepDelayTest { val options = orgOptions.toOptions() assertEquals(options.sleepDelay, it.second, "sleepDelay ${it.second}") } + + assertEquals(ThetaRepository.SleepDelayEnum.get(0), ThetaRepository.SleepDelayEnum.DISABLE, "DISABLE") } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TimeShiftTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TimeShiftTest.kt index 8e0902211f..0c9f72dbd8 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TimeShiftTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TimeShiftTest.kt @@ -5,8 +5,8 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.FirstShootingEnum -import com.ricoh360.thetaclient.transferred.TimeShift import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.TimeShift import io.ktor.http.* import io.ktor.utils.io.* import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -65,12 +65,14 @@ class TimeShiftTest { */ @Test fun setOptionTimeShiftTest() = runTest { - val value = Pair(ThetaRepository.TimeShiftSetting( - true, - ThetaRepository.TimeShiftIntervalEnum.INTERVAL_9, - ThetaRepository.TimeShiftIntervalEnum.INTERVAL_10 - ), - TimeShift(FirstShootingEnum.FRONT, 9, 10)) + val value = Pair( + ThetaRepository.TimeShiftSetting( + true, + ThetaRepository.TimeShiftIntervalEnum.INTERVAL_9, + ThetaRepository.TimeShiftIntervalEnum.INTERVAL_10 + ), + TimeShift(FirstShootingEnum.FRONT, 9, 10) + ) MockApiClient.onRequest = { request -> // check request @@ -100,12 +102,18 @@ class TimeShiftTest { fun convertOptionTimeShiftTest() = runTest { val values = listOf( Pair(ThetaRepository.TimeShiftSetting(), TimeShift()), - Pair(ThetaRepository.TimeShiftSetting(null, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_0, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_1), - TimeShift(null, 0, 1)), - Pair(ThetaRepository.TimeShiftSetting(true, null, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_5), - TimeShift(FirstShootingEnum.FRONT, null, 5)), - Pair(ThetaRepository.TimeShiftSetting(false, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_10, null), - TimeShift(FirstShootingEnum.REAR, 10, null)), + Pair( + ThetaRepository.TimeShiftSetting(null, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_0, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_1), + TimeShift(null, 0, 1) + ), + Pair( + ThetaRepository.TimeShiftSetting(true, null, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_5), + TimeShift(FirstShootingEnum.FRONT, null, 5) + ), + Pair( + ThetaRepository.TimeShiftSetting(false, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_10, null), + TimeShift(FirstShootingEnum.REAR, 10, null) + ), ) values.forEach { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyTest.kt index 3ca14800a3..2c22551bbe 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyTest.kt @@ -4,8 +4,8 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.WlanFrequency import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.WlanFrequency import io.ktor.http.* import io.ktor.utils.io.* import kotlinx.coroutines.ExperimentalCoroutinesApi diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_idle.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_idle.json new file mode 100644 index 0000000000..e9c43eff77 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_idle.json @@ -0,0 +1,22 @@ +{ + "fingerprint": "FIG_0008", + "state": { + "_apiVersion": 2, + "batteryLevel": 0.81, + "_batteryState": "disconnect", + "_cameraError": [ + "COMPASS_CALIBRATION" + ], + "_captureStatus": "idle", + "_capturedPictures": 0, + "_compositeShootingElapsedTime": 0, + "_function": "selfTimer", + "_latestFileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013331.JPG", + "_mySettingChanged": false, + "_pluginRunning": false, + "_pluginWebServer": true, + "_recordableTime": 0, + "_recordedTime": 0, + "storageUri": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_shooting.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_shooting.json new file mode 100644 index 0000000000..b6aecc5163 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_shooting.json @@ -0,0 +1,22 @@ +{ + "fingerprint": "FIG_0008", + "state": { + "_apiVersion": 2, + "batteryLevel": 0.81, + "_batteryState": "disconnect", + "_cameraError": [ + "COMPASS_CALIBRATION" + ], + "_captureStatus": "shooting", + "_capturedPictures": 0, + "_compositeShootingElapsedTime": 0, + "_function": "selfTimer", + "_latestFileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013331.JPG", + "_mySettingChanged": false, + "_pluginRunning": false, + "_pluginWebServer": true, + "_recordableTime": 0, + "_recordedTime": 0, + "storageUri": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/" + } +} diff --git a/react-native/.gitignore b/react-native/.gitignore index f41910c8df..c33bc57a9d 100644 --- a/react-native/.gitignore +++ b/react-native/.gitignore @@ -46,6 +46,10 @@ gradlew.bat # Cocoapods # ios/Pods +verification-tool/ios/Pods + +# Ruby +verification-tool/vendor/ # node.js # @@ -61,9 +65,16 @@ android/app/libs android/keystores/debug.keystore # Expo -.expo/* +.expo/ + +# Turborepo +.turbo/ # generated by bob lib/ # package package + +frameworks/ +coverage/ +android/libs/ diff --git a/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/Converter.kt b/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/Converter.kt index 52905e8f86..1239e02fb0 100644 --- a/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/Converter.kt +++ b/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/Converter.kt @@ -13,6 +13,7 @@ import com.ricoh360.thetaclient.capture.VideoCapture const val KEY_NOTIFY_NAME = "name" const val KEY_NOTIFY_PARAMS = "params" const val KEY_NOTIFY_PARAM_COMPLETION = "completion" +const val KEY_NOTIFY_PARAM_MESSAGE = "message" fun toNotify( name: String, @@ -32,6 +33,12 @@ fun toCaptureProgressNotifyParam(value: Float): WritableMap { return result } +fun toMessageNotifyParam(value: String): WritableMap { + val result = Arguments.createMap() + result.putString(KEY_NOTIFY_PARAM_MESSAGE, value) + return result +} + /** * convert option interface */ @@ -537,12 +544,12 @@ class ImageStitchingConverter : OptionConverter { } } - override fun setFromTheta(options: ThetaRepository.Options, objects: WritableMap) { + override fun setFromTheta(options: ThetaRepository.Options, objects: WritableMap) { options.imageStitching?.let { objects.putString("imageStitching", it.toString()) } } - } +} /** * IsoConverter diff --git a/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/ThetaClientSdkModule.kt b/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/ThetaClientSdkModule.kt index efcaf6d785..54f695cee3 100644 --- a/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/ThetaClientSdkModule.kt +++ b/react-native/android/src/main/java/com/ricoh360/thetaclientreactnative/ThetaClientSdkModule.kt @@ -575,11 +575,16 @@ class ThetaClientReactNativeModule( /** * getPhotoCaptureBuilder - get photo capture builder from repository + * @param promise promise to set result */ @ReactMethod - fun getPhotoCaptureBuilder() { - val theta = theta ?: throw Exception(messageNotInit) - photoCaptureBuilder = theta.getPhotoCaptureBuilder() + fun getPhotoCaptureBuilder(promise: Promise) { + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + photoCaptureBuilder = theta?.getPhotoCaptureBuilder() + promise.resolve(true) } /** @@ -591,7 +596,8 @@ class ThetaClientReactNativeModule( @ReactMethod fun buildPhotoCapture(options: ReadableMap, promise: Promise) { if (theta == null) { - throw Exception(messageNotInit) + promise.reject(Exception(messageNotInit)) + return } if (photoCaptureBuilder == null) { promise.reject(Exception("no photoCaptureBuilder")) @@ -649,19 +655,22 @@ class ThetaClientReactNativeModule( * getTimeShiftCaptureBuilder - get time-shift builder from repository */ @ReactMethod - fun getTimeShiftCaptureBuilder() { - val theta = theta ?: throw Exception(messageNotInit) - timeShiftCaptureBuilder = theta.getTimeShiftCaptureBuilder() + fun getTimeShiftCaptureBuilder(promise: Promise) { + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + timeShiftCaptureBuilder = theta?.getTimeShiftCaptureBuilder() + promise.resolve(true) } /** * buildTimeShiftCapture - build time-shift * @param options option to execute time-shift - * @param interval interval of checking time-shift status * @param promise Promise for buildTimeShiftCapture */ @ReactMethod - fun buildTimeShiftCapture(options: ReadableMap, interval: Int, promise: Promise) { + fun buildTimeShiftCapture(options: ReadableMap, promise: Promise) { if (theta == null) { promise.reject(Exception(messageNotInit)) return @@ -677,8 +686,11 @@ class ThetaClientReactNativeModule( cvt?.setTimeShiftOption(options, builder) } - if (interval >= 0) { - builder.setCheckStatusCommandInterval(interval.toLong()) + val interval = if (options.hasKey("_capture_interval")) options.getInt("_capture_interval") else null + interval?.let { + if (it >= 0) { + builder.setCheckStatusCommandInterval(it.toLong()) + } } timeShiftCapture = builder.build() @@ -713,7 +725,7 @@ class ThetaClientReactNativeModule( override fun onProgress(completion: Float) { sendNotifyEvent( - toNotify("TIME-SHIFT-PROGRESS", toCaptureProgressNotifyParam(value = completion)) + toNotify(NOTIFY_TIMESHIFT_PROGRESS, toCaptureProgressNotifyParam(value = completion)) ) } @@ -742,11 +754,16 @@ class ThetaClientReactNativeModule( /** * getVideoCaptureBuilder - get video capture builder + * @param promise promise to set result */ @ReactMethod - fun getVideoCaptureBuilder() { - val theta = theta ?: throw Exception(messageNotInit) - videoCaptureBuilder = theta.getVideoCaptureBuilder() + fun getVideoCaptureBuilder(promise: Promise) { + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + videoCaptureBuilder = theta?.getVideoCaptureBuilder() + promise.resolve(true) } /** @@ -787,7 +804,7 @@ class ThetaClientReactNativeModule( * @param promise promise to set result */ @ReactMethod - fun startCapture(promise: Promise) { + fun startVideoCapture(promise: Promise) { if (theta == null) { promise.reject(Exception(messageNotInit)) return @@ -797,17 +814,26 @@ class ThetaClientReactNativeModule( return } class StartCaptureCallback : VideoCapture.StartCaptureCallback { - override fun onSuccess(fileUrl: String) { + override fun onCaptureCompleted(fileUrl: String?) { promise.resolve(fileUrl) videoCapture = null videoCapturing = null } - override fun onError(exception: ThetaRepository.ThetaRepositoryException) { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { promise.reject(exception) videoCapture = null videoCapturing = null } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + sendNotifyEvent( + toNotify( + NOTIFY_VIDEO_CAPTURE_STOP_ERROR, + toMessageNotifyParam(exception.message ?: exception.toString()) + ) + ) + } } videoCapturing = videoCapture!!.startCapture(StartCaptureCallback()) } @@ -816,7 +842,7 @@ class ThetaClientReactNativeModule( * stopCapture - stop capturing video */ @ReactMethod - fun stopCapture() { + fun stopVideoCapture() { if (theta == null) { throw Exception(messageNotInit) } @@ -1482,5 +1508,7 @@ class ThetaClientReactNativeModule( const val NAME = "ThetaClientReactNative" const val EVENT_NAME = "ThetaFrameEvent" const val EVENT_NOTIFY = "ThetaNotify" + const val NOTIFY_TIMESHIFT_PROGRESS = "TIME-SHIFT-PROGRESS" + const val NOTIFY_VIDEO_CAPTURE_STOP_ERROR = "VIDEO-CAPTURE-STOP-ERROR" } } diff --git a/react-native/ios/ConvertUtil.swift b/react-native/ios/ConvertUtil.swift new file mode 100644 index 0000000000..97f9f2bd4a --- /dev/null +++ b/react-native/ios/ConvertUtil.swift @@ -0,0 +1,989 @@ +import THETAClient +import UIKit + +let KEY_CLIENT_MODE = "clientMode" +let KEY_NOTIFY_NAME = "name" +let KEY_NOTIFY_PARAMS = "params" +let KEY_NOTIFY_PARAM_COMPLETION = "completion" +let KEY_NOTIFY_PARAM_IMAGE = "image" +let KEY_NOTIFY_PARAM_MESSAGE = "message" +let KEY_DATETIME = "dateTime" +let KEY_LANGUAGE = "language" +let KEY_OFF_DELAY = "offDelay" +let KEY_SLEEP_DELAY = "sleepDelay" +let KEY_SHUTTER_VOLUME = "shutterVolume" +let KEY_CONNECT_TIMEOUT = "connectTimeout" +let KEY_REQUEST_TIMEOUT = "requestTimeout" +let KEY_SOCKET_TIMEOUT = "socketTimeout" +let KEY_THETA_MODEL = "thetaModel" +let KEY_TIMESHIFT = "timeShift" +let KEY_APERTURE = "aperture" +let KEY_COLOR_TEMPERATURE = "colorTemperature" +let KEY_EXPOSURE_COMPENSATION = "exposureCompensation" +let KEY_EXPOSURE_DELAY = "exposureDelay" +let KEY_EXPOSURE_PROGRAM = "exposureProgram" +let KEY_GPS_INFO = "gpsInfo" +let KEY_GPS_TAG_RECORDING = "_gpsTagRecording" +let KEY_ISO = "iso" +let KEY_ISO_AUTO_HIGH_LIMIT = "isoAutoHighLimit" +let KEY_WHITE_BALANCE = "whiteBalance" +let KEY_FILE_FORMAT = "fileFormat" +let KEY_FILTER = "filter" +let KEY_FILE_LIST = "fileList" +let KEY_PRESET = "preset" +let KEY_GPS_LATITUDE = "latitude" +let KEY_GPS_LONGITUDE = "longitude" +let KEY_GPS_ALTITUDE = "altitude" +let KEY_GPS_DATE_TIME_ZONE = "dateTimeZone" +let KEY_MAX_RECORDABLE_TIME = "maxRecordableTime" +let KEY_TOTAL_ENTRIES = "totalEntries" +let KEY_TIMESHIFT_IS_FRONT_FIRST = "isFrontFirst" +let KEY_TIMESHIFT_FIRST_INTERVAL = "firstInterval" +let KEY_TIMESHIFT_SECOND_INTERVAL = "secondInterval" +let KEY_PROXY_USE = "use" +let KEY_PROXY_URL = "url" +let KEY_PROXY_PORT = "port" +let KEY_PROXY_USER_ID = "userid" +let KEY_PROXY_PASSWORD = "password" +let KEY_BURST_CAPTURE_NUM = "burstCaptureNum" +let KEY_BURST_BRACKET_STEP = "burstBracketStep" +let KEY_BURST_COMPENSATION = "burstCompensation" +let KEY_BURST_MAX_EXPOSURE_TIME = "burstMaxExposureTime" +let KEY_BURST_ENABLE_ISO_CONTROL = "burstEnableIsoControl" +let KEY_BURST_ORDER = "burstOrder" +let KEY_CAPTURE_INTERVAL = "_capture_interval" + +public class ConvertUtil: NSObject {} + +// MARK: - Options convert + +let optionItemNameToEnum = [ + // TODO: Add items when adding options + "aiAutoThumbnail": ThetaRepository.OptionNameEnum.aiautothumbnail, + "aperture": ThetaRepository.OptionNameEnum.aperture, + "bitrate": ThetaRepository.OptionNameEnum.bitrate, + "bluetoothPower": ThetaRepository.OptionNameEnum.bluetoothpower, + "burstMode": ThetaRepository.OptionNameEnum.burstmode, + "burstOption": ThetaRepository.OptionNameEnum.burstoption, + "cameraControlSource": ThetaRepository.OptionNameEnum.cameracontrolsource, + "cameraMode": ThetaRepository.OptionNameEnum.cameramode, + "captureInterval": ThetaRepository.OptionNameEnum.captureinterval, + "captureMode": ThetaRepository.OptionNameEnum.capturemode, + "captureNumber": ThetaRepository.OptionNameEnum.capturenumber, + "colorTemperature": ThetaRepository.OptionNameEnum.colortemperature, + "compositeShootingOutputInterval": ThetaRepository.OptionNameEnum + .compositeshootingoutputinterval, + "compositeShootingTime": ThetaRepository.OptionNameEnum.compositeshootingtime, + "continuousNumber": ThetaRepository.OptionNameEnum.continuousnumber, + "dateTimeZone": ThetaRepository.OptionNameEnum.datetimezone, + "exposureCompensation": ThetaRepository.OptionNameEnum.exposurecompensation, + "exposureDelay": ThetaRepository.OptionNameEnum.exposuredelay, + "exposureProgram": ThetaRepository.OptionNameEnum.exposureprogram, + "faceDetect": ThetaRepository.OptionNameEnum.facedetect, + "fileFormat": ThetaRepository.OptionNameEnum.fileformat, + "filter": ThetaRepository.OptionNameEnum.filter, + "function": ThetaRepository.OptionNameEnum.function, + "gain": ThetaRepository.OptionNameEnum.gain, + "gpsInfo": ThetaRepository.OptionNameEnum.gpsinfo, + "imageStitching": ThetaRepository.OptionNameEnum.imagestitching, + "isGpsOn": ThetaRepository.OptionNameEnum.isgpson, + "iso": ThetaRepository.OptionNameEnum.iso, + "isoAutoHighLimit": ThetaRepository.OptionNameEnum.isoautohighlimit, + "language": ThetaRepository.OptionNameEnum.language, + "maxRecordableTime": ThetaRepository.OptionNameEnum.maxrecordabletime, + "networkType": ThetaRepository.OptionNameEnum.networktype, + "offDelay": ThetaRepository.OptionNameEnum.offdelay, + "password": ThetaRepository.OptionNameEnum.password, + "powerSaving": ThetaRepository.OptionNameEnum.powersaving, + "preset": ThetaRepository.OptionNameEnum.preset, + "previewFormat": ThetaRepository.OptionNameEnum.previewformat, + "proxy": ThetaRepository.OptionNameEnum.proxy, + "remainingPictures": ThetaRepository.OptionNameEnum.remainingpictures, + "remainingVideoSeconds": ThetaRepository.OptionNameEnum.remainingvideoseconds, + "remainingSpace": ThetaRepository.OptionNameEnum.remainingspace, + "shootingMethod": ThetaRepository.OptionNameEnum.shootingmethod, + "shutterSpeed": ThetaRepository.OptionNameEnum.shutterspeed, + "shutterVolume": ThetaRepository.OptionNameEnum.shuttervolume, + "sleepDelay": ThetaRepository.OptionNameEnum.sleepdelay, + "timeShift": ThetaRepository.OptionNameEnum.timeshift, + "totalSpace": ThetaRepository.OptionNameEnum.totalspace, + "username": ThetaRepository.OptionNameEnum.username, + "whiteBalance": ThetaRepository.OptionNameEnum.whitebalance, + "whiteBalanceAutoStrength": ThetaRepository.OptionNameEnum.whitebalanceautostrength, + "wlanFrequency": ThetaRepository.OptionNameEnum.wlanfrequency, +] + +let optionNameEnumToItemName = { + var map = [ThetaRepository.OptionNameEnum: String]() + optionItemNameToEnum.forEach { tuple in + map[tuple.value] = tuple.key + } + return map +}() + +func convertGetOptionsParam(params: [String]) -> [ThetaRepository.OptionNameEnum] { + var array: [ThetaRepository.OptionNameEnum] = [] + let values = ThetaRepository.OptionNameEnum.values() + for name in params { + if let nameEnum = getEnumValue(values: values, name: name) { + array.append(nameEnum) + } + } + return array +} + +func convertSetOptionsParam(params: [String: Any]) -> ThetaRepository.Options { + let options = ThetaRepository.Options() + params.forEach { key, value in + if let nameEnum = optionItemNameToEnum[key] { + setOptionsValue(options: options, name: nameEnum.name, value: value) + } + } + return options +} + +func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) { + // TODO: Add items when adding options + switch name { + case ThetaRepository.OptionNameEnum.aiautothumbnail.name: + options.aiAutoThumbnail = getEnumValue( + values: ThetaRepository.AiAutoThumbnailEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.aperture.name: + options.aperture = getEnumValue( + values: ThetaRepository.ApertureEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.bitrate.name: + options.bitrate = toBitrate(value: value) + case ThetaRepository.OptionNameEnum.bluetoothpower.name: + options.bluetoothPower = getEnumValue( + values: ThetaRepository.BluetoothPowerEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.burstmode.name: + options.burstMode = getEnumValue( + values: ThetaRepository.BurstModeEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.burstoption.name: + if let params = value as? [String: Any] { + options.burstOption = toBurstOption(params: params) + } + case ThetaRepository.OptionNameEnum.cameracontrolsource.name: + options.cameraControlSource = getEnumValue( + values: ThetaRepository.CameraControlSourceEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.cameramode.name: + options.cameraMode = getEnumValue( + values: ThetaRepository.CameraModeEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.captureinterval.name: + options.captureInterval = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.capturemode.name: + options.captureMode = getEnumValue( + values: ThetaRepository.CaptureModeEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.capturenumber.name: + options.captureNumber = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.colortemperature.name: + options.colorTemperature = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.compositeshootingoutputinterval.name: + options.compositeShootingOutputInterval = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.compositeshootingtime.name: + options.compositeShootingTime = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.continuousnumber.name: + options.continuousNumber = getEnumValue( + values: ThetaRepository.ContinuousNumberEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.datetimezone.name: + options.dateTimeZone = value as? String + case ThetaRepository.OptionNameEnum.exposurecompensation.name: + options.exposureCompensation = getEnumValue( + values: ThetaRepository.ExposureCompensationEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.exposuredelay.name: + options.exposureDelay = getEnumValue( + values: ThetaRepository.ExposureDelayEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.exposureprogram.name: + options.exposureProgram = getEnumValue( + values: ThetaRepository.ExposureProgramEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.facedetect.name: + options.faceDetect = getEnumValue( + values: ThetaRepository.FaceDetectEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.fileformat.name: + options.fileFormat = getEnumValue( + values: ThetaRepository.FileFormatEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.filter.name: + options.filter = getEnumValue( + values: ThetaRepository.FilterEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.function.name: + options.function = getEnumValue( + values: ThetaRepository.ShootingFunctionEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.gain.name: + options.gain = getEnumValue( + values: ThetaRepository.GainEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.gpsinfo.name: + options.gpsInfo = toGpsInfo(params: value as! [String: Any]) + case ThetaRepository.OptionNameEnum.imagestitching.name: + options.imageStitching = getEnumValue( + values: ThetaRepository.ImageStitchingEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.isgpson.name: + options.isGpsOn = (value as! Bool) ? true : false + case ThetaRepository.OptionNameEnum.iso.name: + options.iso = getEnumValue( + values: ThetaRepository.IsoEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.isoautohighlimit.name: + options.isoAutoHighLimit = getEnumValue( + values: ThetaRepository.IsoAutoHighLimitEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.language.name: + options.language = getEnumValue( + values: ThetaRepository.LanguageEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.maxrecordabletime.name: + options.maxRecordableTime = getEnumValue( + values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.networktype.name: + options.networkType = getEnumValue( + values: ThetaRepository.NetworkTypeEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.offdelay.name: + options.offDelay = getEnumValue( + values: ThetaRepository.OffDelayEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.password.name: + options.password = value as? String + case ThetaRepository.OptionNameEnum.powersaving.name: + options.powerSaving = getEnumValue( + values: ThetaRepository.PowerSavingEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.preset.name: + options.preset = getEnumValue( + values: ThetaRepository.PresetEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.previewformat.name: + options.previewFormat = getEnumValue( + values: ThetaRepository.PreviewFormatEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.proxy.name: + if let params = value as? [String: Any] { + options.proxy = toProxy(params: params) + } + case ThetaRepository.OptionNameEnum.remainingpictures.name: + options.remainingPictures = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.remainingvideoseconds.name: + options.remainingVideoSeconds = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.remainingspace.name: + options.remainingSpace = KotlinLong(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.shootingmethod.name: + options.shootingMethod = getEnumValue( + values: ThetaRepository.ShootingMethodEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.shutterspeed.name: + options.shutterSpeed = getEnumValue( + values: ThetaRepository.ShutterSpeedEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.shuttervolume.name: + options.shutterVolume = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.sleepdelay.name: + options.sleepDelay = getEnumValue( + values: ThetaRepository.SleepDelayEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.timeshift.name: + if let params = value as? [String: Any] { + options.timeShift = toTimeShift(params: params) + } + case ThetaRepository.OptionNameEnum.totalspace.name: + options.totalSpace = KotlinLong(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.username.name: + options.username = value as? String + case ThetaRepository.OptionNameEnum.whitebalance.name: + options.whiteBalance = getEnumValue( + values: ThetaRepository.WhiteBalanceEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.whitebalanceautostrength.name: + options.whiteBalanceAutoStrength = getEnumValue( + values: ThetaRepository.WhiteBalanceAutoStrengthEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.wlanfrequency.name: + options.wlanFrequency = getEnumValue( + values: ThetaRepository.WlanFrequencyEnum.values(), name: value as! String + )! + default: break + } +} + +func convertResult(options: ThetaRepository.Options) -> [String: Any] { + var result = [String: Any]() + let nameList = ThetaRepository.OptionNameEnum.values() + for i in 0 ..< nameList.size { + if let name = nameList.get(index: i), + let key = optionNameEnumToItemName[name] + { + if let value = try? options.getValue(name: name) { + if let enumValue = value as? KotlinEnum { + result[key] = enumValue.name + } else if value is KotlinBoolean { + result[key] = value as! Bool ? true : false + } else if value is NSNumber || value is String { + result[key] = value + } else if let bitrate = value as? ThetaRepository.BitrateNumber { + result[key] = bitrate.value + } else if value is ThetaRepository.BurstOption, + let burstOption = value as? ThetaRepository.BurstOption + { + result[key] = convertResult(burstOption: burstOption) + } else if value is ThetaRepository.GpsInfo { + let gpsInfo = value as! ThetaRepository.GpsInfo + result[key] = convertResult(gpsInfo: gpsInfo) + } else if value is ThetaRepository.Proxy, + let proxy = value as? ThetaRepository.Proxy + { + result[key] = convertResult(proxy: proxy) + } else if value is ThetaRepository.TimeShiftSetting, + let timeshift = value as? ThetaRepository.TimeShiftSetting + { + result[key] = convertResult(timeshift: timeshift) + } else if let offDelay = value as? ThetaRepository.OffDelaySec { + result[key] = + offDelay.sec == 0 ? ThetaRepository.OffDelayEnum.disable.name : offDelay.sec + } else if let sleepDelay = value as? ThetaRepository.SleepDelaySec { + result[key] = + sleepDelay.sec == 0 + ? ThetaRepository.SleepDelayEnum.disable.name : sleepDelay.sec + } + // TODO: Add class item when adding options + } + } + } + return result +} + +// MARK: - Notify event + +func toNotify(name: String, params: [String: Any]?) -> [String: Any] { + var result: [String: Any] = [ + KEY_NOTIFY_NAME: name, + ] + if let params = params { + result[KEY_NOTIFY_PARAMS] = params + } + return result +} + +func toCaptureProgressNotifyParam(value: Float) -> [String: Any] { + return [ + KEY_NOTIFY_PARAM_COMPLETION: value, + ] +} + +func toMessageNotifyParam(value: String) -> [String: Any] { + return [ + KEY_NOTIFY_PARAM_MESSAGE: value, + ] +} + +// MARK: - Capture builder + +func setCaptureBuilderParams(params: [String: Any], builder: CaptureBuilder) { + if let value = params[KEY_APERTURE] as? String { + if let enumValue = getEnumValue(values: ThetaRepository.ApertureEnum.values(), name: value) + { + builder.setAperture(aperture: enumValue) + } + } + if let value = params[KEY_COLOR_TEMPERATURE] as? Int32 { + builder.setColorTemperature(kelvin: value) + } + if let value = params[KEY_EXPOSURE_COMPENSATION] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.ExposureCompensationEnum.values(), name: value + ) { + builder.setExposureCompensation(value: enumValue) + } + } + if let value = params[KEY_EXPOSURE_DELAY] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.ExposureDelayEnum.values(), name: value + ) { + builder.setExposureDelay(delay: enumValue) + } + } + if let value = params[KEY_EXPOSURE_PROGRAM] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.ExposureProgramEnum.values(), name: value + ) { + builder.setExposureProgram(program: enumValue) + } + } + if let value = params[KEY_GPS_INFO] as? [String: Any] { + if let gpsInfo = toGpsInfo(params: value) { + builder.setGpsInfo(gpsInfo: gpsInfo) + } + } + if let value = params[KEY_GPS_TAG_RECORDING] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.GpsTagRecordingEnum.values(), name: value + ) { + builder.setGpsTagRecording(value: enumValue) + } + } + if let value = params[KEY_ISO] as? String { + if let enumValue = getEnumValue(values: ThetaRepository.IsoEnum.values(), name: value) { + builder.setIso(iso: enumValue) + } + } + if let value = params[KEY_ISO_AUTO_HIGH_LIMIT] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.IsoAutoHighLimitEnum.values(), name: value + ) { + builder.setIsoAutoHighLimit(iso: enumValue) + } + } + if let value = params[KEY_WHITE_BALANCE] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.WhiteBalanceEnum.values(), name: value + ) { + builder.setWhiteBalance(whiteBalance: enumValue) + } + } +} + +func setPhotoCaptureBuilderParams(params: [String: Any], builder: PhotoCapture.Builder) { + if let value = params[KEY_FILTER] as? String { + if let enumValue = getEnumValue(values: ThetaRepository.FilterEnum.values(), name: value) { + builder.setFilter(filter: enumValue) + } + } + if let value = params[KEY_FILE_FORMAT] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.PhotoFileFormatEnum.values(), name: value + ) { + builder.setFileFormat(fileFormat: enumValue) + } + } + if let value = params[KEY_PRESET] as? String { + if let enumValue = getEnumValue(values: ThetaRepository.PresetEnum.values(), name: value) { + builder.setPreset(preset: enumValue) + } + } +} + +func setTimeShiftCaptureBuilderParams(params: [String: Any], builder: TimeShiftCapture.Builder) { + if let interval = params[KEY_CAPTURE_INTERVAL] as? Int, + interval >= 0 + { + builder.setCheckStatusCommandInterval(timeMillis: Int64(interval)) + } + if let timeShiftParams = params[KEY_TIMESHIFT] as? [String: Any] { + let timeShift = toTimeShift(params: timeShiftParams) + if let isFrontFirst = timeShift.isFrontFirst { + builder.setIsFrontFirst(isFrontFirst: isFrontFirst.boolValue) + } + if let firstInterval = timeShift.firstInterval { + builder.setFirstInterval(interval: firstInterval) + } + if let secondInterval = timeShift.secondInterval { + builder.setSecondInterval(interval: secondInterval) + } + } +} + +func setVideoCaptureBuilderParams(params: [String: Any], builder: VideoCapture.Builder) { + if let value = params[KEY_MAX_RECORDABLE_TIME] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value + ) { + builder.setMaxRecordableTime(time: enumValue) + } + } + if let value = params[KEY_FILE_FORMAT] as? String { + if let enumValue = getEnumValue( + values: ThetaRepository.VideoFileFormatEnum.values(), name: value + ) { + builder.setFileFormat(fileFormat: enumValue) + } + } +} + +// MARK: - Utility + +func getEnumValue>(values: KotlinArray, name: String) -> E? { + for i in 0 ..< values.size { + let item = values.get(index: i)! + if item.name == name { + return item + } + } + return nil +} + +func convertKotlinBooleanToBool(value: Any?) -> Bool? { + guard let value = value else { return nil } + guard value is KotlinBoolean, let numVal = value as? NSNumber else { return false } + + return numVal.boolValue +} + +func toKotlinInt(value: Any?) -> KotlinInt? { + guard let value = value as? Int else { + return nil + } + return KotlinInt(integerLiteral: value) +} + +func toKotlinBoolean(value: Any?) -> KotlinBoolean? { + guard let value = value as? Bool else { + return nil + } + return KotlinBoolean(booleanLiteral: value) +} + +// MARK: - Convert to react-native object + +func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] { + var resultList = [[String: Any]]() + fileInfoList.forEach { fileInfo in + var item = [ + "name": fileInfo.name, + "fileUrl": fileInfo.fileUrl, + "size": fileInfo.size, + "dateTime": fileInfo.dateTime, + "thumbnailUrl": fileInfo.thumbnailUrl, + ] + if let dateTimeZone = fileInfo.dateTimeZone { + item["dateTimeZone"] = dateTimeZone + } + if let lat = fileInfo.lat { + item["lat"] = lat.floatValue + } + if let lng = fileInfo.lng { + item["lng"] = lng.floatValue + } + if let width = fileInfo.width { + item["width"] = width.intValue + } + if let height = fileInfo.height { + item["height"] = height.intValue + } + if let intervalCaptureGroupId = fileInfo.intervalCaptureGroupId { + item["intervalCaptureGroupId"] = intervalCaptureGroupId + } + if let compositeShootingGroupId = fileInfo.compositeShootingGroupId { + item["compositeShootingGroupId"] = compositeShootingGroupId + } + if let autoBracketGroupId = fileInfo.autoBracketGroupId { + item["autoBracketGroupId"] = autoBracketGroupId + } + if let recordTime = fileInfo.recordTime { + item["recordTime"] = recordTime.intValue + } + if let isProcessed = fileInfo.isProcessed { + item["isProcessed"] = isProcessed.boolValue + } + if let previewUrl = fileInfo.previewUrl { + item["previewUrl"] = previewUrl + } + if let codec = fileInfo.codec { + item["codec"] = codec.name + } + if let projectionType = fileInfo.projectionType { + item["projectionType"] = projectionType.name + } + if let continuousShootingGroupId = fileInfo.continuousShootingGroupId { + item["continuousShootingGroupId"] = continuousShootingGroupId + } + if let frameRate = fileInfo.frameRate { + item["frameRate"] = frameRate.intValue + } + if let favorite = fileInfo.favorite { + item["favorite"] = favorite.boolValue + } + if let imageDescription = fileInfo.imageDescription { + item["imageDescription"] = imageDescription + } + if let storageID = fileInfo.storageID { + item["storageID"] = storageID + } + resultList.append(item) + } + return resultList +} + +func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { + return [ + "manufacturer": thetaInfo.manufacturer, + "model": thetaInfo.model, + "serialNumber": thetaInfo.serialNumber, + "wlanMacAddress": thetaInfo.wlanMacAddress, + "bluetoothMacAddress": thetaInfo.bluetoothMacAddress, + "firmwareVersion": thetaInfo.firmwareVersion, + "supportUrl": thetaInfo.supportUrl, + "hasGps": thetaInfo.hasGps, + "hasGyro": thetaInfo.hasGyro, + "uptime": thetaInfo.uptime, + "api": thetaInfo.api, + "endpoints": [ + "httpPort": thetaInfo.endpoints.httpPort, + "httpUpdatesPort": thetaInfo.endpoints.httpUpdatesPort, + ], + "apiLevel": thetaInfo.apiLevel, + ] +} + +func convertResult(cameraErrorList: [ThetaRepository.CameraErrorEnum]?) -> [String]? { + guard let cameraErrorList = cameraErrorList else { + return nil + } + var result: [String] = [] + cameraErrorList.forEach { error in + result.append(error.name) + } + return result +} + +func convertResult(thetaState: ThetaRepository.ThetaState) -> [String: Any?] { + return [ + "fingerprint": thetaState.fingerprint, + "batteryLevel": thetaState.batteryLevel, + "storageUri": thetaState.storageUri, + "storageID": thetaState.storageID, + "captureStatus": thetaState.captureStatus.name, + "recordedTime": thetaState.recordedTime, + "recordableTime": thetaState.recordableTime, + "capturedPictures": thetaState.capturedPictures, + "compositeShootingElapsedTime": thetaState.compositeShootingElapsedTime, + "latestFileUrl": thetaState.latestFileUrl, + "chargingState": thetaState.chargingState.name, + "apiVersion": thetaState.apiVersion, + "isPluginRunning": convertKotlinBooleanToBool(value: thetaState.isPluginRunning), + "isPluginWebServer": convertKotlinBooleanToBool(value: thetaState.isPluginWebServer), + "function": thetaState.function?.name, + "isMySettingChanged": convertKotlinBooleanToBool(value: thetaState.isMySettingChanged), + "currentMicrophone": thetaState.currentMicrophone?.name, + "isSdCard": convertKotlinBooleanToBool(value: thetaState.isSdCard), + "cameraError": convertResult(cameraErrorList: thetaState.cameraError), + "isBatteryInsert": convertKotlinBooleanToBool(value: thetaState.isBatteryInsert), + ] +} + +func convertResult(burstOption: ThetaRepository.BurstOption) -> [String: Any] { + var result: [String: Any] = [:] + if let burstCaptureNum = burstOption.burstCaptureNum { + result[KEY_BURST_CAPTURE_NUM] = burstCaptureNum.value.name + } + if let burstBracketStep = burstOption.burstBracketStep { + result[KEY_BURST_BRACKET_STEP] = burstBracketStep.value.name + } + if let burstCompensation = burstOption.burstCompensation { + result[KEY_BURST_COMPENSATION] = burstCompensation.value.name + } + if let burstMaxExposureTime = burstOption.burstMaxExposureTime { + result[KEY_BURST_MAX_EXPOSURE_TIME] = burstMaxExposureTime.value.name + } + if let burstEnableIsoControl = burstOption.burstEnableIsoControl { + result[KEY_BURST_ENABLE_ISO_CONTROL] = burstEnableIsoControl.value.name + } + if let burstOrder = burstOption.burstOrder { + result[KEY_BURST_ORDER] = burstOrder.value.name + } + return result +} + +func convertResult(gpsInfo: ThetaRepository.GpsInfo) -> [String: Any] { + return [ + "latitude": gpsInfo.latitude, + "longitude": gpsInfo.longitude, + "altitude": gpsInfo.altitude, + "dateTimeZone": gpsInfo.dateTimeZone, + ] +} + +func convertResult(proxy: ThetaRepository.Proxy) -> [String: Any] { + var result: [String: Any] = [:] + result[KEY_PROXY_USE] = proxy.use + if let url = proxy.url { + result[KEY_PROXY_URL] = url + } + if let port = proxy.port { + result[KEY_PROXY_PORT] = port + } + if let userId = proxy.userid { + result[KEY_PROXY_USER_ID] = userId + } + if let password = proxy.password { + result[KEY_PROXY_PASSWORD] = password + } + return result +} + +func convertResult(timeshift: ThetaRepository.TimeShiftSetting) -> [String: Any] { + var result: [String: Any] = [:] + if let isFrontFirst = convertKotlinBooleanToBool(value: timeshift.isFrontFirst) { + result[KEY_TIMESHIFT_IS_FRONT_FIRST] = isFrontFirst + } + if let firstInterval = timeshift.firstInterval?.name { + result[KEY_TIMESHIFT_FIRST_INTERVAL] = firstInterval + } + if let secondInterval = timeshift.secondInterval?.name { + result[KEY_TIMESHIFT_SECOND_INTERVAL] = secondInterval + } + return result +} + +func convertResult(exif: ThetaRepository.Exif) -> [String: Any] { + var result = [String: Any]() + result["exifVersion"] = exif.exifVersion + result["dateTime"] = exif.dateTime + if let imageWidth = exif.imageWidth { + result["imageWidth"] = imageWidth + } + if let imageLength = exif.imageLength { + result["imageLength"] = imageLength + } + if let gpsLatitude = exif.gpsLatitude { + result["gpsLatitude"] = gpsLatitude + } + if let gpsLongitude = exif.gpsLongitude { + result["gpsLongitude"] = gpsLongitude + } + return result +} + +func convertResult(xmp: ThetaRepository.Xmp) -> [String: Any] { + var result = [String: Any]() + result["fullPanoWidthPixels"] = xmp.fullPanoWidthPixels + result["fullPanoHeightPixels"] = xmp.fullPanoHeightPixels + if let poseHeadingDegrees = xmp.poseHeadingDegrees { + result["poseHeadingDegrees"] = poseHeadingDegrees + } + return result +} + +func convertResult(metadata: KotlinPair) -> [String: Any] +{ + return [ + "exif": convertResult(exif: metadata.first!), + "xmp": convertResult(xmp: metadata.second!), + ] +} + +func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: Any]] { + var resultList = [[String: Any]]() + accessPointList.forEach { accessPoint in + var result = [String: Any]() + result["ssid"] = accessPoint.ssid + result["ssidStealth"] = accessPoint.ssidStealth + result["authMode"] = accessPoint.authMode.name + result["connectionPriority"] = accessPoint.connectionPriority + result["usingDhcp"] = accessPoint.usingDhcp + if let ipAddress = accessPoint.ipAddress { + result["ipAddress"] = ipAddress + } + if let subnetMask = accessPoint.subnetMask { + result["subnetMask"] = subnetMask + } + if let defaultGateway = accessPoint.defaultGateway { + result["defaultGateway"] = defaultGateway + } + if let proxy = accessPoint.proxy { + result["proxy"] = convertResult(proxy: proxy) + } + resultList.append(result) + } + return resultList +} + +func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[String: Any]] { + var resultList = [[String: Any]]() + pluginInfoList.forEach { pluginInfo in + let item = [ + "name": pluginInfo.name, + "packageName": pluginInfo.packageName, + "version": pluginInfo.version, + "isPreInstalled": pluginInfo.isPreInstalled, + "isRunning": pluginInfo.isRunning, + "isForeground": pluginInfo.isForeground, + "isBoot": pluginInfo.isBoot, + "hasWebServer": pluginInfo.hasWebServer, + "exitStatus": pluginInfo.exitStatus, + "message": pluginInfo.message, + ] + resultList.append(item) + } + return resultList +} + +// MARK: - Convert to theta-client object + +func toBitrate(value: Any) -> ThetaRepositoryBitrate? { + if value is NSNumber, let intVal = value as? Int32 { + return ThetaRepository.BitrateNumber(value: intVal) + } else if let name = value as? String, + let enumValue = getEnumValue(values: ThetaRepository.BitrateEnum.values(), name: name) + { + return enumValue + } else if let str = value as? String { + return ThetaRepository.BitrateEnum.companion.get(str: str) + } else { + return nil + } +} + +func toBurstOption(params: [String: Any]) -> ThetaRepository.BurstOption { + var burstCaptureNum: ThetaRepository.BurstCaptureNumEnum? = nil + if let name = params["burstCaptureNum"] as? String { + burstCaptureNum = getEnumValue( + values: ThetaRepository.BurstCaptureNumEnum.values(), name: name + ) + } + + var burstBracketStep: ThetaRepository.BurstBracketStepEnum? = nil + if let name = params["burstBracketStep"] as? String { + burstBracketStep = getEnumValue( + values: ThetaRepository.BurstBracketStepEnum.values(), name: name + ) + } + + var burstCompensation: ThetaRepository.BurstCompensationEnum? = nil + if let name = params["burstCompensation"] as? String { + burstCompensation = getEnumValue( + values: ThetaRepository.BurstCompensationEnum.values(), name: name + ) + } + + var burstMaxExposureTime: ThetaRepository.BurstMaxExposureTimeEnum? = nil + if let name = params["burstMaxExposureTime"] as? String { + burstMaxExposureTime = getEnumValue( + values: ThetaRepository.BurstMaxExposureTimeEnum.values(), name: name + ) + } + + var burstEnableIsoControl: ThetaRepository.BurstEnableIsoControlEnum? = nil + if let name = params["burstEnableIsoControl"] as? String { + burstEnableIsoControl = getEnumValue( + values: ThetaRepository.BurstEnableIsoControlEnum.values(), name: name + ) + } + + var burstOrder: ThetaRepository.BurstOrderEnum? = nil + if let name = params["burstOrder"] as? String { + burstOrder = getEnumValue(values: ThetaRepository.BurstOrderEnum.values(), name: name) + } + + return ThetaRepository.BurstOption( + burstCaptureNum: burstCaptureNum, + burstBracketStep: burstBracketStep, + burstCompensation: burstCompensation, + burstMaxExposureTime: burstMaxExposureTime, + burstEnableIsoControl: burstEnableIsoControl, + burstOrder: burstOrder + ) +} + +func toGpsInfo(params: [String: Any]) -> ThetaRepository.GpsInfo? { + guard let latitude = params[KEY_GPS_LATITUDE] as? Double, + let longitude = params[KEY_GPS_LONGITUDE] as? Double, + let altitude = params[KEY_GPS_ALTITUDE] as? Double, + let dateTimeZone = params[KEY_GPS_DATE_TIME_ZONE] as? String + else { return nil } + + return ThetaRepository.GpsInfo( + latitude: Float(latitude), + longitude: Float(longitude), + altitude: Float(altitude), + dateTimeZone: dateTimeZone + ) +} + +func toProxy(params: [String: Any]) -> ThetaRepository.Proxy { + return ThetaRepository.Proxy( + use: params["use"] as? Bool ?? false, + url: params["url"] as? String, + port: toKotlinInt(value: params["port"]), + userid: params["userid"] as? String, + password: params["password"] as? String + ) +} + +func toTimeShift(params: [String: Any]) -> ThetaRepository.TimeShiftSetting { + var firstInterval: ThetaRepository.TimeShiftIntervalEnum? = nil + if let name = params["firstInterval"] as? String { + firstInterval = getEnumValue( + values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name + ) + } + + var secondInterval: ThetaRepository.TimeShiftIntervalEnum? = nil + if let name = params["secondInterval"] as? String { + secondInterval = getEnumValue( + values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name + ) + } + + return ThetaRepository.TimeShiftSetting( + isFrontFirst: toKotlinBoolean(value: params["isFrontFirst"]), + firstInterval: firstInterval, + secondInterval: secondInterval + ) +} + +func toDigestAuth(params: [String: String?]?) -> DigestAuth? { + guard let params = params, + let username = params["username"] as? String + else { return nil } + + let password = params["password"] as? String + return DigestAuth(username: username, password: password) +} + +func toConfig(params: [String: Any]) -> ThetaRepository.Config { + let config = ThetaRepository.Config() + params.forEach { key, value in + switch key { + case KEY_DATETIME: + config.dateTime = value as? String + case KEY_LANGUAGE: + config.language = getEnumValue( + values: ThetaRepository.LanguageEnum.values(), name: value as? String ?? "" + ) + case KEY_OFF_DELAY: + config.offDelay = getEnumValue( + values: ThetaRepository.OffDelayEnum.values(), name: value as? String ?? "" + ) + case KEY_SLEEP_DELAY: + config.sleepDelay = getEnumValue( + values: ThetaRepository.SleepDelayEnum.values(), name: value as? String ?? "" + ) + case KEY_SHUTTER_VOLUME: + if let value = value as? Int { + config.shutterVolume = KotlinInt(integerLiteral: value) + } + case KEY_CLIENT_MODE: + config.clientMode = toDigestAuth(params: value as? [String: String?]) + default: + break + } + } + return config +} + +func toTimeout(params: [String: Any]) -> ThetaRepository.Timeout? { + guard let connectTimeout = params[KEY_CONNECT_TIMEOUT] as? Int64, + let requestTimeout = params[KEY_REQUEST_TIMEOUT] as? Int64, + let socketTimeout = params[KEY_SOCKET_TIMEOUT] as? Int64 + else { return nil } + return ThetaRepository.Timeout( + connectTimeout: connectTimeout, + requestTimeout: requestTimeout, + socketTimeout: socketTimeout + ) +} diff --git a/react-native/ios/ThetaClientReactNative-Bridging-Header.h b/react-native/ios/ThetaClientReactNative-Bridging-Header.h new file mode 100644 index 0000000000..8992b22d96 --- /dev/null +++ b/react-native/ios/ThetaClientReactNative-Bridging-Header.h @@ -0,0 +1,3 @@ +#import +#import +#import diff --git a/react-native/ios/ThetaClientReactNative.h b/react-native/ios/ThetaClientReactNative.h deleted file mode 100644 index 3a12fee205..0000000000 --- a/react-native/ios/ThetaClientReactNative.h +++ /dev/null @@ -1,34 +0,0 @@ - -#ifdef RCT_NEW_ARCH_ENABLED -#import "RNThetaClientReactNativeSpec.h" -#import - -@interface ThetaClientReactNative : RCTEventEmitter -#else -#import -#import - -@interface ThetaClientReactNative : RCTEventEmitter -#endif - -/** theta repository instance */ -@property(nonatomic, strong) THETACThetaRepository *theta; -/** photoCapture builder instance */ -@property(nonatomic, strong) THETACPhotoCaptureBuilder *photoCaptureBuilder; -/** photoCapture instance */ -@property(nonatomic, strong) THETACPhotoCapture *photoCapture; -/** timeShiftCapture builder instance */ -@property(nonatomic, strong) THETACTimeShiftCaptureBuilder *timeShiftCaptureBuilder; -/** timeShiftCapture instance */ -@property(nonatomic, strong) THETACTimeShiftCapture *timeShiftCapture; -/** timeShiftCapturing instance */ -@property(nonatomic, strong) THETACTimeShiftCapturing *timeShiftCapturing; -/** videoCapture builder instance */ -@property(nonatomic, strong) THETACVideoCaptureBuilder *videoCaptureBuilder; -/** videoCapture instance */ -@property(nonatomic, strong) THETACVideoCapture *videoCapture; -/** videoCapturing instance */ -@property(nonatomic, strong) THETACVideoCapturing *videoCapturing; -/** previewing in progress flag */ -@property(nonatomic) BOOL previewing; -@end diff --git a/react-native/ios/ThetaClientReactNative.m b/react-native/ios/ThetaClientReactNative.m new file mode 100644 index 0000000000..7412423b8b --- /dev/null +++ b/react-native/ios/ThetaClientReactNative.m @@ -0,0 +1,201 @@ +#import + +@interface RCT_EXTERN_MODULE(ThetaClientReactNative, NSObject) + +RCT_EXTERN_METHOD(multiply:(float)a withB:(float)b + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(initialize:(NSString)endPoint + withConfig:(NSDictionary*)config + withTimeout:(NSDictionary*)timeout + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(isInitialized:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getThetaModel:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getThetaInfo:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getThetaState:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(listFiles:(NSString*)fileType + withStartPosition:(int)startPosition + withEntryCount:(int)entryCount + withStorage:(NSString*)storage + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteFiles:(NSArray*)fileUrls + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteAllFiles:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteAllImageFiles:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteAllVideoFiles:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getOptions:(NSArray*)optionNames + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setOptions:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getLivePreview:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(stopLivePreview:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getPhotoCaptureBuilder:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(buildPhotoCapture:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(takePicture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getTimeShiftCaptureBuilder:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(buildTimeShiftCapture:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(startTimeShiftCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(cancelTimeShiftCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getVideoCaptureBuilder:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(buildVideoCapture:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(startVideoCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(stopVideoCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getMetadata:(NSString*)fileUrl + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(reset:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(restoreSettings:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(stopSelfTimer:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(convertVideoFormats:(NSString *)fileUrl + withToLowResolution:(BOOL)toLowResolution + withApplyTopBottomCorrection:(BOOL)applyTopBottomCorrection + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(cancelVideoConvert:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(finishWlan:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(listAccessPoints:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setAccessPointDynamically:(NSString *)ssid + withSsidStealth:(BOOL)ssidStealth + withAuthMode:(NSString *)authMode + withPassword:(NSString *)password + withConnectionPriority:(int)connectionPriority + withProxy:(NSDictionary *)proxy + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setAccessPointStatically:(NSString *)ssid + withSsidStealth:(BOOL)ssidStealth + withAuthMode:(NSString *)authMode + withPassword:(NSString *)password + withConnectionPriority:(int)connectionPriority + withIpAddress:(NSString *)ipAddress + withSubnetMask:(NSString *)subnetMask + withDefaultGateway:(NSString *)defaultGateway + withProxy:(NSDictionary *)proxy + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteAccessPoint:(NSString *)ssid + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getMySetting:(NSString *)captureMode + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getMySettingFromOldModel:(NSArray*)optionNames + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setMySetting:(NSString *)captureMode + withOptions:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(deleteMySetting:(NSString *)captureMode + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(listPlugins:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setPlugin:(NSString *)packageName + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(startPlugin:(NSString *)packageName + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(stopPlugin:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getPluginLicense:(NSString *)packageName + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(getPluginOrders:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setPluginOrders:(NSArray *) plugins + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(setBluetoothDevice:(NSString *)uuid + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + ++ (BOOL)requiresMainQueueSetup +{ + return NO; +} + +@end diff --git a/react-native/ios/ThetaClientReactNative.mm b/react-native/ios/ThetaClientReactNative.mm deleted file mode 100644 index 6a55f7310c..0000000000 --- a/react-native/ios/ThetaClientReactNative.mm +++ /dev/null @@ -1,4236 +0,0 @@ -#import "THETAClient/THETAClient.h" -#import "ThetaClientReactNative.h" -#import "RCTConvert.h" - -#define MESSAGE_NOT_INIT @"Not initialized." -#define ERROR_CODE_ERROR @"error" - -static NSDictionary* toNotify(NSString *name, NSDictionary *params) { - NSMutableDictionary *objects = [NSMutableDictionary dictionary]; - [objects setObject:name forKey:@"name"]; - if (params != nil) { - [objects setObject:params forKey:@"params"]; - } - return objects; -} - -static NSDictionary* toCaptureProgressNotifyParam(NSNumber *value) { - NSMutableDictionary *result = [NSMutableDictionary dictionary]; - [result setObject:value forKey:@"completion"]; - return toNotify(@"TIME-SHIFT-PROGRESS", result); -} - -/** - * converter for int32_t - */ -@implementation RCTConvert (int32_t) -RCT_NUMBER_CONVERTER(int32_t, intValue) -@end - -/** - * takepicture callback holder - */ -@interface PhotoCallback: NSObject -@end -@implementation PhotoCallback { - RCTPromiseResolveBlock _resolve; ///< takepicture callback resolver - RCTPromiseRejectBlock _reject; ///< takepicture callback rejecter - ThetaClientReactNative *_sdk; ///< theta client sdk instance -} -/** - * initialize PhotoCallback - * @param sdk theta client sdk instance - * @param resolve resolver for promise - * @param reject rejecter for promise - * @return initialized callback instance - */ --(id)initWith:(ThetaClientReactNative *)sdk - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject -{ - self = [super init]; - if (self) { - _sdk = sdk; - _resolve = resolve; - _reject = reject; - } - return self; -} - -/** - * call when error detect - * @param exception exception object - */ --(void)onErrorException:(THETACThetaRepositoryThetaRepositoryException *)exception -{ - _reject(ERROR_CODE_ERROR, exception.message, nil); - _sdk.photoCapture = nil; -} - -/** - * call when successfully taken picture - * @param fileUrl taken picture file url - */ --(void)onSuccessFileUrl:(NSString *)fileUrl -{ - _resolve(fileUrl); - _sdk.photoCapture = nil; -} - -- (void)onProgressCompletion:(float)completion { -} - -@end - -/** - * time-shift startCapture callback holder - */ -typedef void (^ TimeShiftOnProgressBlock)(float); -@interface TimeShiftStartCallback: NSObject -@end -@implementation TimeShiftStartCallback { - RCTPromiseResolveBlock _resolve; ///< start capture resolver - TimeShiftOnProgressBlock _onProgress; ///< start capture resolver - RCTPromiseRejectBlock _reject; ///< start capture rejecter - ThetaClientReactNative *_sdk; ///< theta client sdk instance -} - -/** - * initialize TimeShiftCallback - * @param sdk theta client sdk instance - * @param resolve resolver for promise - * @param reject rejecter for promise - * @return initialized callback instance - */ --(id)initWith:(ThetaClientReactNative *)sdk - withProgress:(TimeShiftOnProgressBlock)onProgress - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject -{ - self = [super init]; - if (self) { - _sdk = sdk; - _onProgress = onProgress; - _resolve = resolve; - _reject = reject; - } - return self; -} - -/** - * call when error detect - * @param exception exception object - */ --(void)onErrorException:(THETACThetaRepositoryThetaRepositoryException *)exception { - _reject(@"error", exception.message, nil); - _sdk.timeShiftCapture = nil; - _sdk.timeShiftCapturing = nil; -} - -/** - * call when successfully time-shift captured - * @param fileUrl captured video file url - */ -- (void)onSuccessFileUrl_:(NSString * _Nullable)fileUrl { - _resolve(fileUrl != nil ? fileUrl : [NSNull null]); - _sdk.timeShiftCapture = nil; - _sdk.timeShiftCapturing = nil; -} - -/** - * call when check time-shift progress - * @param completion captured video file url - */ -- (void)onProgressCompletion:(float)completion { - _onProgress(completion); -} -@end - -/** - * startCapture callback holder - */ -@interface VideoCallback: NSObject -@end -@implementation VideoCallback { - RCTPromiseResolveBlock _resolve; ///< start capture resolver - RCTPromiseRejectBlock _reject; ///< start capture rejecter - ThetaClientReactNative *_sdk; ///< theta client sdk instance -} - -/** - * initialize VideoCallback - * @param sdk theta client sdk instance - * @param resolve resolver for promise - * @param reject rejecter for promise - * @return initialized callback instance - */ --(id)initWith:(ThetaClientReactNative *)sdk - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject -{ - self = [super init]; - if (self) { - _sdk = sdk; - _resolve = resolve; - _reject = reject; - } - return self; -} - -/** - * call when error detect - * @param exception exception object - */ --(void)onErrorException:(THETACThetaRepositoryThetaRepositoryException *)exception -{ - _reject(ERROR_CODE_ERROR, exception.message, nil); - _sdk.videoCapture = nil; - _sdk.videoCapturing = nil; -} - -/** - * call when successfully video captured - * @param fileUrl captured video file url - */ --(void)onSuccessFileUrl:(NSString *)fileUrl -{ - _resolve(fileUrl); - _sdk.videoCapture = nil; - _sdk.videoCapturing = nil; -} -@end - -/** frame event handler with NSData */ -typedef void (^FrameEventHandler)(NSData *); -/** frame interval time */ -static CFTimeInterval FRAME_INTERVAL = (CFTimeInterval) (1.0/10.0); -/** - * live preview frame handler holder - */ -@interface FrameHandler: NSObject -@end -@implementation FrameHandler { - FrameEventHandler _handler; ///< frame event handler - ThetaClientReactNative *_sdk; ///< theta client sdk instance - CFTimeInterval _last; ///< last event time -} - -/** - * initialize frame handler - * @param sdk theta client sdk instance - * @param handler frame event handler - */ --(id)initWith:(ThetaClientReactNative *)sdk - withEvent:(FrameEventHandler)handler -{ - self = [super init]; - if (self) { - _sdk = sdk; - _handler = handler; - _last = 0; - } - return self; -} - -/** - * call when frame data arrived - * @param p1 ByteReadPacket instance - * @param completionHandler set result - */ -- (void)invokeP1:(id)p1 completionHandler:(void (^)(id _Nullable_result, NSError * _Nullable))completionHandler __attribute__((swift_name("invoke(p1:completionHandler:)"))); -{ - CFTimeInterval now = CACurrentMediaTime(); - if (now - _last > FRAME_INTERVAL) { - NSData *data = - [THETACPlatformKt frameFromPacket:(THETACKotlinPair *)p1]; - _handler(data); - completionHandler(@(_sdk.previewing), nil); - _last = now; - } else { - completionHandler(@(_sdk.previewing), nil); - } -} -@end - -/** - * check null for object - */ -static bool isNull(id obj) { - return (obj == nil || obj == [NSNull null]); -} - -/// create dictionary for converter -template -static NSDictionary* createConverterDic(THETACKotlinArray* valueArray, bool isToTheta) { - NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init]; - for (int i = 0; i < valueArray.size; i++) { - T* item = [valueArray getIndex:i]; - isToTheta ? [dictionary setObject:item forKey:item.name] : [dictionary setObject:item.name forKey:item]; - } - return dictionary; -} - -/// create dictionary for toTheta -template -static NSDictionary* createToThetaDic(THETACKotlinArray* valueArray) { - return createConverterDic(valueArray, true); -} - -/// create dictionary for fromTheta -template -static NSDictionary* createFromThetaDic(THETACKotlinArray* valueArray) { - return createConverterDic(valueArray, false); -} - -/** opiton converter from theta to react */ -typedef void (^SetFromTheta)(NSMutableDictionary *, THETACThetaRepositoryOptions *); -/** option converter from react to theta */ -typedef void (^SetToTheta)(NSDictionary *, THETACThetaRepositoryOptions *); -/** option converter to set photo option */ -typedef void (^SetPhotoOption)(NSDictionary *, THETACPhotoCaptureBuilder *); -/** option converter to set time-shift option */ -typedef void (^SetTimeShiftOption)(NSDictionary *, THETACTimeShiftCaptureBuilder *); -/** option converter to set video option */ -typedef void (^SetVideoOption)(NSDictionary *, THETACVideoCaptureBuilder *); - -typedef struct _convert_t { - NSDictionary* toTheta; ///< dictionary react to theta - NSDictionary* fromTheta; ///< dictionary theta to react - NSDictionary* photoOption; ///< dictionary photoOption - NSDictionary* videoOption; ///< dictionary videoOption - SetToTheta setToTheta; ///< option setter react to theta - SetFromTheta setFromTheta; ///< option setter theta to react - SetPhotoOption setPhotoOption; ///< photo option setter - SetTimeShiftOption setTimeShiftOption; ///< time-shift option setter - SetVideoOption setVideoOption; ///< video option setter -} convert_t; - -/** - * BitrateEnum converter - */ -static convert_t BitrateEnum = { - .toTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!BitrateEnum.toTheta) { - NSMutableDictionary* dictionary = [NSMutableDictionary dictionary]; - THETACKotlinArray* array = THETACThetaRepositoryBitrateEnum.values; - for (int i = 0; i < array.size; i++) { - THETACThetaRepositoryBitrateEnum* item = [array getIndex:i]; - [dictionary setObject:item forKey:item.name]; - } - BitrateEnum.toTheta = dictionary; - } - - id rv = [rct objectForKey:@"bitrate"]; - if (rv) { - if ([rv isKindOfClass:NSString.class]) { - opt.bitrate = [BitrateEnum.toTheta objectForKey:rv]; - } else if ([rv isKindOfClass:NSNumber.class]) { - id val = [[THETACThetaRepositoryBitrateNumber alloc] initWithValue:[rv intValue]]; - opt.bitrate = val; - } - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if ([((id)opt.bitrate) isKindOfClass:THETACThetaRepositoryBitrateEnum.class]) { - THETACThetaRepositoryBitrateEnum* bitrateEnum = (THETACThetaRepositoryBitrateEnum*)opt.bitrate; - [rct setObject:bitrateEnum.name forKey:@"bitrate"]; - } else if ([((id)opt.bitrate) isKindOfClass:THETACThetaRepositoryBitrateNumber.class]) { - THETACThetaRepositoryBitrateNumber* bitrateNumber = (THETACThetaRepositoryBitrateNumber*)opt.bitrate; - [rct setObject:[NSNumber numberWithInt:bitrateNumber.value] forKey:@"bitrate"]; - } - } -}; - -/** - * BurstModeEnum converter - */ -static convert_t BurstModeEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryBurstModeEnum.on, - @"OFF": THETACThetaRepositoryBurstModeEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryBurstModeEnum.on: @"ON", - THETACThetaRepositoryBurstModeEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [BurstModeEnum.toTheta objectForKey:[rct objectForKey:@"burstMode"]]; - if (val) { - opt.burstMode = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [BurstModeEnum.fromTheta objectForKey:opt.burstMode]; - if (val) { - [rct setObject:val forKey:@"burstMode"]; - } - } -}; - -static convert_t BurstCaptureNumEnum = { - .toTheta = @{ - @"BURST_CAPTURE_NUM_1": THETACThetaRepositoryBurstCaptureNumEnum.burstCaptureNum1, - @"BURST_CAPTURE_NUM_3": THETACThetaRepositoryBurstCaptureNumEnum.burstCaptureNum3, - @"BURST_CAPTURE_NUM_5": THETACThetaRepositoryBurstCaptureNumEnum.burstCaptureNum5, - @"BURST_CAPTURE_NUM_7": THETACThetaRepositoryBurstCaptureNumEnum.burstCaptureNum7, - @"BURST_CAPTURE_NUM_9": THETACThetaRepositoryBurstCaptureNumEnum.burstCaptureNum9 - } -}; - -static convert_t BurstBracketStepEnum = { - .toTheta = @{ - @"BRACKET_STEP_0_0": THETACThetaRepositoryBurstBracketStepEnum.bracketStep00, - @"BRACKET_STEP_0_3": THETACThetaRepositoryBurstBracketStepEnum.bracketStep03, - @"BRACKET_STEP_0_7": THETACThetaRepositoryBurstBracketStepEnum.bracketStep07, - @"BRACKET_STEP_1_0": THETACThetaRepositoryBurstBracketStepEnum.bracketStep10, - @"BRACKET_STEP_1_3": THETACThetaRepositoryBurstBracketStepEnum.bracketStep13, - @"BRACKET_STEP_1_7": THETACThetaRepositoryBurstBracketStepEnum.bracketStep17, - @"BRACKET_STEP_2_0": THETACThetaRepositoryBurstBracketStepEnum.bracketStep20, - @"BRACKET_STEP_2_3": THETACThetaRepositoryBurstBracketStepEnum.bracketStep23, - @"BRACKET_STEP_2_7": THETACThetaRepositoryBurstBracketStepEnum.bracketStep27, - @"BRACKET_STEP_3_0": THETACThetaRepositoryBurstBracketStepEnum.bracketStep30 - } -}; - -static convert_t BurstCompensationEnum = { - .toTheta = @{ - @"BURST_COMPENSATION_DOWN_5_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown50, - @"BURST_COMPENSATION_DOWN_4_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown47, - @"BURST_COMPENSATION_DOWN_4_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown43, - @"BURST_COMPENSATION_DOWN_4_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown40, - @"BURST_COMPENSATION_DOWN_3_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown37, - @"BURST_COMPENSATION_DOWN_3_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown33, - @"BURST_COMPENSATION_DOWN_3_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown30, - @"BURST_COMPENSATION_DOWN_2_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown27, - @"BURST_COMPENSATION_DOWN_2_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown23, - @"BURST_COMPENSATION_DOWN_2_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown20, - @"BURST_COMPENSATION_DOWN_1_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown17, - @"BURST_COMPENSATION_DOWN_1_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown13, - @"BURST_COMPENSATION_DOWN_1_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown10, - @"BURST_COMPENSATION_DOWN_0_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown07, - @"BURST_COMPENSATION_DOWN_0_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationDown03, - @"BURST_COMPENSATION_0_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensation00, - @"BURST_COMPENSATION_UP_0_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp03, - @"BURST_COMPENSATION_UP_0_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp07, - @"BURST_COMPENSATION_UP_1_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp10, - @"BURST_COMPENSATION_UP_1_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp13, - @"BURST_COMPENSATION_UP_1_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp17, - @"BURST_COMPENSATION_UP_2_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp20, - @"BURST_COMPENSATION_UP_2_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp23, - @"BURST_COMPENSATION_UP_2_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp27, - @"BURST_COMPENSATION_UP_3_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp30, - @"BURST_COMPENSATION_UP_3_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp33, - @"BURST_COMPENSATION_UP_3_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp37, - @"BURST_COMPENSATION_UP_4_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp40, - @"BURST_COMPENSATION_UP_4_3": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp43, - @"BURST_COMPENSATION_UP_4_7": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp47, - @"BURST_COMPENSATION_UP_5_0": THETACThetaRepositoryBurstCompensationEnum.burstCompensationUp50 - } -}; - -static convert_t BurstMaxExposureTimeEnum = { - .toTheta = @{ - @"MAX_EXPOSURE_TIME_0_5": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime05, - @"MAX_EXPOSURE_TIME_0_625": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime0625, - @"MAX_EXPOSURE_TIME_0_76923076": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime076923076, - @"MAX_EXPOSURE_TIME_1": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime1, - @"MAX_EXPOSURE_TIME_1_3": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime13, - @"MAX_EXPOSURE_TIME_1_6": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime16, - @"MAX_EXPOSURE_TIME_2": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime2, - @"MAX_EXPOSURE_TIME_2_5": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime25, - @"MAX_EXPOSURE_TIME_3_2": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime32, - @"MAX_EXPOSURE_TIME_4": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime4, - @"MAX_EXPOSURE_TIME_5": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime5, - @"MAX_EXPOSURE_TIME_6": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime6, - @"MAX_EXPOSURE_TIME_8": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime8, - @"MAX_EXPOSURE_TIME_10": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime10, - @"MAX_EXPOSURE_TIME_13": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime13_, - @"MAX_EXPOSURE_TIME_15": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime15, - @"MAX_EXPOSURE_TIME_20": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime20, - @"MAX_EXPOSURE_TIME_25": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime25_, - @"MAX_EXPOSURE_TIME_30": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime30, - @"MAX_EXPOSURE_TIME_40": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime40, - @"MAX_EXPOSURE_TIME_50": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime50, - @"MAX_EXPOSURE_TIME_60": THETACThetaRepositoryBurstMaxExposureTimeEnum.maxExposureTime60 - } -}; - -static convert_t BurstEnableIsoControlEnum = { - .toTheta = @{ - @"OFF": THETACThetaRepositoryBurstEnableIsoControlEnum.off, - @"ON": THETACThetaRepositoryBurstEnableIsoControlEnum.on - } -}; - -static convert_t BurstOrderEnum = { - .toTheta = @{ - @"BURST_BRACKET_ORDER_0": THETACThetaRepositoryBurstOrderEnum.burstBracketOrder0, - @"BURST_BRACKET_ORDER_1": THETACThetaRepositoryBurstOrderEnum.burstBracketOrder1 - } -}; - -/** - * BurstOption converter - */ -static convert_t BurstOptionCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSDictionary *dic = [rct objectForKey:@"burstOption"]; - if (dic) { - opt.burstOption = [[THETACThetaRepositoryBurstOption alloc] - initWithBurstCaptureNum:!isNull([dic objectForKey:@"burstCaptureNum"]) ? [BurstCaptureNumEnum.toTheta objectForKey:[dic objectForKey:@"burstCaptureNum"]] : nil - burstBracketStep:!isNull([dic objectForKey:@"burstBracketStep"]) ? [BurstBracketStepEnum.toTheta objectForKey:[dic objectForKey:@"burstBracketStep"]] : nil - burstCompensation:!isNull([dic objectForKey:@"burstCompensation"]) ? [BurstCompensationEnum.toTheta objectForKey:[dic objectForKey:@"burstCompensation"]] : nil - burstMaxExposureTime:!isNull([dic objectForKey:@"burstMaxExposureTime"]) ? [BurstMaxExposureTimeEnum.toTheta objectForKey:[dic objectForKey:@"burstMaxExposureTime"]] : nil - burstEnableIsoControl:!isNull([dic objectForKey:@"burstEnableIsoControl"]) ? [BurstEnableIsoControlEnum.toTheta objectForKey:[dic objectForKey:@"burstEnableIsoControl"]] : nil - burstOrder:!isNull([dic objectForKey:@"burstOrder"]) ? [BurstOrderEnum.toTheta objectForKey:[dic objectForKey:@"burstOrder"]] : nil]; - } - }, - - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.burstOption) { - NSMutableDictionary *burstOption = [NSMutableDictionary dictionary]; - - if (opt.burstOption.burstCaptureNum) { - [burstOption setObject:opt.burstOption.burstCaptureNum.name forKey:@"burstCaptureNum"]; - } - - if (opt.burstOption.burstBracketStep) { - [burstOption setObject:opt.burstOption.burstBracketStep.name forKey:@"burstBracketStep"]; - } - - if (opt.burstOption.burstCompensation) { - [burstOption setObject:opt.burstOption.burstCompensation.name forKey:@"burstCompensation"]; - } - - if (opt.burstOption.burstMaxExposureTime) { - [burstOption setObject:opt.burstOption.burstMaxExposureTime.name forKey:@"burstMaxExposureTime"]; - } - - if (opt.burstOption.burstEnableIsoControl) { - [burstOption setObject:opt.burstOption.burstEnableIsoControl.name forKey:@"burstEnableIsoControl"]; - } - - if (opt.burstOption.burstOrder) { - [burstOption setObject:opt.burstOption.burstOrder.name forKey:@"burstOrder"]; - } - - [rct setObject:burstOption forKey:@"burstOption"]; - } - } -}; - -/** - * ChargingStateEnum converter - */ -static convert_t ChargingStateEnum = { - .fromTheta = @{ - THETACThetaRepositoryChargingStateEnum.charging: @"CHARGING", - THETACThetaRepositoryChargingStateEnum.completed: @"COMPLETED", - THETACThetaRepositoryChargingStateEnum.notCharging: @"NOT_CHARGING" - } -}; - -static convert_t CaptureStatusEnum = { - .fromTheta = @{ - THETACThetaRepositoryCaptureStatusEnum.shooting: @"SHOOTING", - THETACThetaRepositoryCaptureStatusEnum.idle: @"IDLE", - THETACThetaRepositoryCaptureStatusEnum.selfTimerCountdown: @"SELF_TIMER_COUNTDOWN", - THETACThetaRepositoryCaptureStatusEnum.bracketShooting: @"BRACKET_SHOOTING", - THETACThetaRepositoryCaptureStatusEnum.converting: @"CONVERTING", - THETACThetaRepositoryCaptureStatusEnum.timeShiftShooting: @"TIME_SHIFT_SHOOTING", - THETACThetaRepositoryCaptureStatusEnum.continuousShooting: @"CONTINUOUS_SHOOTING", - THETACThetaRepositoryCaptureStatusEnum.retrospectiveImageRecording: @"RETROSPECTIVE_IMAGE_RECORDING" - } -}; - -static convert_t MicrophoneOptionEnum = { - .fromTheta = @{ - THETACThetaRepositoryMicrophoneOptionEnum.auto_: @"AUTO", - THETACThetaRepositoryMicrophoneOptionEnum.internal: @"INTERNAL", - THETACThetaRepositoryMicrophoneOptionEnum.external: @"EXTERNAL" - } -}; - -static convert_t CameraErrorEnum = { - .fromTheta = @{ - THETACThetaRepositoryCameraErrorEnum.noMemory: @"NO_MEMORY", - THETACThetaRepositoryCameraErrorEnum.fileNumberOver: @"FILE_NUMBER_OVER", - THETACThetaRepositoryCameraErrorEnum.noDateSetting: @"NO_DATE_SETTING", - THETACThetaRepositoryCameraErrorEnum.readError: @"READ_ERROR", - THETACThetaRepositoryCameraErrorEnum.notSupportedMediaType: @"NOT_SUPPORTED_MEDIA_TYPE", - THETACThetaRepositoryCameraErrorEnum.notSupportedFileSystem: @"NOT_SUPPORTED_FILE_SYSTEM", - THETACThetaRepositoryCameraErrorEnum.mediaNotReady: @"MEDIA_NOT_READY", - THETACThetaRepositoryCameraErrorEnum.notEnoughBattery: @"NOT_ENOUGH_BATTERY", - THETACThetaRepositoryCameraErrorEnum.invalidFile: @"INVALID_FILE", - THETACThetaRepositoryCameraErrorEnum.pluginBootError: @"PLUGIN_BOOT_ERROR", - THETACThetaRepositoryCameraErrorEnum.inProgressError: @"IN_PROGRESS_ERROR", - THETACThetaRepositoryCameraErrorEnum.cannotRecording: @"CANNOT_RECORDING", - THETACThetaRepositoryCameraErrorEnum.cannotRecordLowbat: @"CANNOT_RECORD_LOWBAT", - THETACThetaRepositoryCameraErrorEnum.captureHwFailed: @"CAPTURE_HW_FAILED", - THETACThetaRepositoryCameraErrorEnum.captureSwFailed: @"CAPTURE_SW_FAILED", - THETACThetaRepositoryCameraErrorEnum.internalMemAccessFail: @"INTERNAL_MEM_ACCESS_FAIL", - THETACThetaRepositoryCameraErrorEnum.unexpectedError: @"UNEXPECTED_ERROR", - THETACThetaRepositoryCameraErrorEnum.batteryChargeFail: @"BATTERY_CHARGE_FAIL", - THETACThetaRepositoryCameraErrorEnum.highTemperatureWarning: @"HIGH_TEMPERATURE_WARNING", - THETACThetaRepositoryCameraErrorEnum.highTemperature: @"HIGH_TEMPERATURE", - THETACThetaRepositoryCameraErrorEnum.batteryHighTemperature: @"BATTERY_HIGH_TEMPERATURE", - THETACThetaRepositoryCameraErrorEnum.compassCalibration: @"COMPASS_CALIBRATION" - } -}; - -/** - * FileTypeEnum converter - */ -static convert_t FileTypeEnum = { - .toTheta = @{ - @"IMAGE": THETACThetaRepositoryFileTypeEnum.image, - @"VIDEO": THETACThetaRepositoryFileTypeEnum.video, - @"ALL": THETACThetaRepositoryFileTypeEnum.all - } -}; - -/** - * StorageEnum converter - */ -static convert_t StorageEnum = { - .toTheta = @{ - @"INTERNAL": THETACThetaRepositoryStorageEnum.internal, - @"SD": THETACThetaRepositoryStorageEnum.sd, - @"CURRENT": THETACThetaRepositoryStorageEnum.current, - } -}; - -/** - * AuthModeEnum converter - */ -static convert_t AuthModeEnum = { - .toTheta = @{ - @"NONE": THETACThetaRepositoryAuthModeEnum.none, - @"WEP": THETACThetaRepositoryAuthModeEnum.wep, - @"WPA": THETACThetaRepositoryAuthModeEnum.wpa - }, - .fromTheta = @{ - THETACThetaRepositoryAuthModeEnum.none: @"NONE", - THETACThetaRepositoryAuthModeEnum.wep: @"WEP", - THETACThetaRepositoryAuthModeEnum.wpa: @"WPA" - } -}; - -/** - * AiAutoThumbnailEnum converter - */ -static convert_t AiAutoThumbnailEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryAiAutoThumbnailEnum.on, - @"OFF": THETACThetaRepositoryAiAutoThumbnailEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryAiAutoThumbnailEnum.on: @"ON", - THETACThetaRepositoryAiAutoThumbnailEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [AiAutoThumbnailEnum.toTheta objectForKey:[rct objectForKey:@"aiAutoThumbnail"]]; - if (val) { - opt.aiAutoThumbnail = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [AiAutoThumbnailEnum.fromTheta objectForKey:opt.aiAutoThumbnail]; - if (val) { - [rct setObject:val forKey:@"aiAutoThumbnail"]; - } - } -}; - -/** - * ApertureEnum converter - */ -static convert_t ApertureEnum = { - .toTheta = @{ - @"APERTURE_AUTO": THETACThetaRepositoryApertureEnum.apertureAuto, - @"APERTURE_2_0": THETACThetaRepositoryApertureEnum.aperture20, - @"APERTURE_2_1": THETACThetaRepositoryApertureEnum.aperture21, - @"APERTURE_2_4": THETACThetaRepositoryApertureEnum.aperture24, - @"APERTURE_3_5": THETACThetaRepositoryApertureEnum.aperture35, - @"APERTURE_5_6": THETACThetaRepositoryApertureEnum.aperture56 - }, - .fromTheta = @{ - THETACThetaRepositoryApertureEnum.apertureAuto: @"APERTURE_AUTO", - THETACThetaRepositoryApertureEnum.aperture20: @"APERTURE_2_0", - THETACThetaRepositoryApertureEnum.aperture21: @"APERTURE_2_1", - THETACThetaRepositoryApertureEnum.aperture24: @"APERTURE_2_4", - THETACThetaRepositoryApertureEnum.aperture35: @"APERTURE_3_5", - THETACThetaRepositoryApertureEnum.aperture56: @"APERTURE_5_6" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ApertureEnum.toTheta objectForKey:[rct objectForKey:@"aperture"]]; - if (val) { - opt.aperture = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ApertureEnum.fromTheta objectForKey:opt.aperture]; - if (val) { - [rct setObject:val forKey:@"aperture"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [ApertureEnum.toTheta objectForKey:[rct objectForKey:@"aperture"]]; - if (val) { - [builder setApertureAperture:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [ApertureEnum.toTheta objectForKey:[rct objectForKey:@"aperture"]]; - if (val) { - [builder setApertureAperture:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [ApertureEnum.toTheta objectForKey:[rct objectForKey:@"aperture"]]; - if (val) { - [builder setApertureAperture:val]; - } - } -}; - -/** - * BluetoothPowerEnum converter - */ -static convert_t BluetoothPowerEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryBluetoothPowerEnum.on, - @"OFF": THETACThetaRepositoryBluetoothPowerEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryBluetoothPowerEnum.on: @"ON", - THETACThetaRepositoryBluetoothPowerEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [BluetoothPowerEnum.toTheta objectForKey:[rct objectForKey:@"bluetoothPower"]]; - if (val) { - opt.bluetoothPower = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [BluetoothPowerEnum.fromTheta objectForKey:opt.bluetoothPower]; - if (val) { - [rct setObject:val forKey:@"bluetoothPower"]; - } - } -}; - -/** - * CameraControlSourceEnum converter - */ -static convert_t CameraControlSourceEnum = { - .toTheta = @{ - @"CAMERA": THETACThetaRepositoryCameraControlSourceEnum.camera, - @"APP": THETACThetaRepositoryCameraControlSourceEnum.app - }, - .fromTheta = @{ - THETACThetaRepositoryCameraControlSourceEnum.camera: @"CAMERA", - THETACThetaRepositoryCameraControlSourceEnum.app: @"APP" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CameraControlSourceEnum.toTheta objectForKey:[rct objectForKey:@"cameraControlSource"]]; - if (val) { - opt.cameraControlSource = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CameraControlSourceEnum.fromTheta objectForKey:opt.cameraControlSource]; - if (val) { - [rct setObject:val forKey:@"cameraControlSource"]; - } - } -}; - -/** - * CameraModeEnum converter - */ -static convert_t CameraModeEnum = { - .toTheta = @{ - @"CAPTURE": THETACThetaRepositoryCameraModeEnum.capture, - @"PLAYBACK": THETACThetaRepositoryCameraModeEnum.playback, - @"SETTING": THETACThetaRepositoryCameraModeEnum.setting, - @"PLUGIN": THETACThetaRepositoryCameraModeEnum.plugin - }, - .fromTheta = @{ - THETACThetaRepositoryCameraModeEnum.capture: @"CAPTURE", - THETACThetaRepositoryCameraModeEnum.playback: @"PLAYBACK", - THETACThetaRepositoryCameraModeEnum.setting: @"SETTING", - THETACThetaRepositoryCameraModeEnum.plugin: @"PLUGIN" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CameraModeEnum.toTheta objectForKey:[rct objectForKey:@"cameraMode"]]; - if (val) { - opt.cameraMode = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CameraModeEnum.fromTheta objectForKey:opt.cameraMode]; - if (val) { - [rct setObject:val forKey:@"cameraMode"]; - } - } -}; - -/** - * CaptureInterval converter - */ -static convert_t CaptureIntervalConverter = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"captureInterval"]; - if (val) { - opt.captureInterval = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.captureInterval) { - [rct setObject:opt.captureInterval forKey:@"captureInterval"]; - } - }, -}; - -/** - * CaptureModeEnum converter - */ -static convert_t CaptureModeEnum = { - .toTheta = @{ - @"IMAGE": THETACThetaRepositoryCaptureModeEnum.image, - @"VIDEO": THETACThetaRepositoryCaptureModeEnum.video, - @"LIVE_STREAMING": THETACThetaRepositoryCaptureModeEnum.liveStreaming, - @"INTERVAL": THETACThetaRepositoryCaptureModeEnum.interval, - @"PRESET": THETACThetaRepositoryCaptureModeEnum.preset - }, - .fromTheta = @{ - THETACThetaRepositoryCaptureModeEnum.image: @"IMAGE", - THETACThetaRepositoryCaptureModeEnum.video: @"VIDEO", - THETACThetaRepositoryCaptureModeEnum.liveStreaming: @"LIVE_STREAMING", - THETACThetaRepositoryCaptureModeEnum.interval: @"INTERVAL", - THETACThetaRepositoryCaptureModeEnum.preset: @"PRESET" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CaptureModeEnum.toTheta objectForKey:[rct objectForKey:@"captureMode"]]; - if (val) { - opt.captureMode = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [CaptureModeEnum.fromTheta objectForKey:opt.captureMode]; - if (val) { - [rct setObject:val forKey:@"captureMode"]; - } - } -}; - -/** - * CaptureNumber converter - */ -static convert_t CaptureNumberConverter = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"captureNumber"]; - if (val) { - opt.captureNumber = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.captureNumber) { - [rct setObject:opt.captureNumber forKey:@"captureNumber"]; - } - }, -}; - -/** - * ContinuousNumberEnum converter - */ -static convert_t ContinuousNumberEnum = { - .toTheta = nil, - .fromTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ContinuousNumberEnum.toTheta) { - NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init]; - THETACKotlinArray* array = THETACThetaRepositoryContinuousNumberEnum.values; - for (int i = 0; i < array.size; i++) { - THETACThetaRepositoryContinuousNumberEnum* item = [array getIndex:i]; - [dictionary setObject:item forKey:item.name]; - } - ContinuousNumberEnum.toTheta = dictionary; - } - id val = [ContinuousNumberEnum.toTheta objectForKey:[rct objectForKey:@"continuousNumber"]]; - if (val) { - opt.continuousNumber = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ContinuousNumberEnum.fromTheta) { - NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init]; - THETACKotlinArray* array = THETACThetaRepositoryContinuousNumberEnum.values; - for (int i = 0; i < array.size; i++) { - THETACThetaRepositoryContinuousNumberEnum* item = [array getIndex:i]; - [dictionary setObject:item.name forKey:item]; - } - ContinuousNumberEnum.fromTheta = dictionary; - } - id val = [ContinuousNumberEnum.fromTheta objectForKey:opt.continuousNumber]; - if (val) { - [rct setObject:val forKey:@"continuousNumber"]; - } - } -}; - -/** - * ExposureCompensationEnum converter - */ -static convert_t ExposureCompensationEnum = { - .toTheta = @{ - @"M2_0": THETACThetaRepositoryExposureCompensationEnum.m20, - @"M1_7": THETACThetaRepositoryExposureCompensationEnum.m17, - @"M1_3": THETACThetaRepositoryExposureCompensationEnum.m13, - @"M1_0": THETACThetaRepositoryExposureCompensationEnum.m10, - @"M0_7": THETACThetaRepositoryExposureCompensationEnum.m07, - @"M0_3": THETACThetaRepositoryExposureCompensationEnum.m03, - @"ZERO": THETACThetaRepositoryExposureCompensationEnum.zero, - @"P0_3": THETACThetaRepositoryExposureCompensationEnum.p03, - @"P0_7": THETACThetaRepositoryExposureCompensationEnum.p07, - @"P1_0": THETACThetaRepositoryExposureCompensationEnum.p10, - @"P1_3": THETACThetaRepositoryExposureCompensationEnum.p13, - @"P1_7": THETACThetaRepositoryExposureCompensationEnum.p17, - @"P2_0": THETACThetaRepositoryExposureCompensationEnum.p20 - }, - .fromTheta = @{ - THETACThetaRepositoryExposureCompensationEnum.m20: @"M2_0", - THETACThetaRepositoryExposureCompensationEnum.m17: @"M1_7", - THETACThetaRepositoryExposureCompensationEnum.m13: @"M1_3", - THETACThetaRepositoryExposureCompensationEnum.m10: @"M1_0", - THETACThetaRepositoryExposureCompensationEnum.m07: @"M0_7", - THETACThetaRepositoryExposureCompensationEnum.m03: @"M0_3", - THETACThetaRepositoryExposureCompensationEnum.zero: @"ZERO", - THETACThetaRepositoryExposureCompensationEnum.p03: @"P0_3", - THETACThetaRepositoryExposureCompensationEnum.p07: @"P0_7", - THETACThetaRepositoryExposureCompensationEnum.p10: @"P1_0", - THETACThetaRepositoryExposureCompensationEnum.p13: @"P1_3", - THETACThetaRepositoryExposureCompensationEnum.p17: @"P1_7", - THETACThetaRepositoryExposureCompensationEnum.p20: @"P2_0", - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureCompensationEnum.toTheta - objectForKey:[rct objectForKey:@"exposureCompensation"]]; - if (val) { - opt.exposureCompensation = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureCompensationEnum.fromTheta objectForKey:opt.exposureCompensation]; - if (val) { - [rct setObject:val forKey:@"exposureCompensation"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [ExposureCompensationEnum.toTheta - objectForKey:[rct objectForKey:@"exposureCompensation"]]; - if (val) { - [builder setExposureCompensationValue:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [ExposureCompensationEnum.toTheta objectForKey:[rct objectForKey:@"exposureCompensation"]]; - if (val) { - [builder setExposureCompensationValue:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [ExposureCompensationEnum.toTheta - objectForKey:[rct objectForKey:@"exposureCompensation"]]; - if (val) { - [builder setExposureCompensationValue:val]; - } - } -}; - -/** - * ExposureDelayEnum converter - */ -static convert_t ExposureDelayEnum = { - .toTheta = @{ - @"DELAY_OFF": THETACThetaRepositoryExposureDelayEnum.delayOff, - @"DELAY_1": THETACThetaRepositoryExposureDelayEnum.delay1, - @"DELAY_2": THETACThetaRepositoryExposureDelayEnum.delay2, - @"DELAY_3": THETACThetaRepositoryExposureDelayEnum.delay3, - @"DELAY_4": THETACThetaRepositoryExposureDelayEnum.delay4, - @"DELAY_5": THETACThetaRepositoryExposureDelayEnum.delay5, - @"DELAY_6": THETACThetaRepositoryExposureDelayEnum.delay6, - @"DELAY_7": THETACThetaRepositoryExposureDelayEnum.delay7, - @"DELAY_8": THETACThetaRepositoryExposureDelayEnum.delay8, - @"DELAY_9": THETACThetaRepositoryExposureDelayEnum.delay9, - @"DELAY_10": THETACThetaRepositoryExposureDelayEnum.delay10 - }, - .fromTheta = @{ - THETACThetaRepositoryExposureDelayEnum.delayOff: @"DELAY_OFF", - THETACThetaRepositoryExposureDelayEnum.delay1: @"DELAY_1", - THETACThetaRepositoryExposureDelayEnum.delay2: @"DELAY_2", - THETACThetaRepositoryExposureDelayEnum.delay3: @"DELAY_3", - THETACThetaRepositoryExposureDelayEnum.delay4: @"DELAY_4", - THETACThetaRepositoryExposureDelayEnum.delay5: @"DELAY_5", - THETACThetaRepositoryExposureDelayEnum.delay6: @"DELAY_6", - THETACThetaRepositoryExposureDelayEnum.delay7: @"DELAY_7", - THETACThetaRepositoryExposureDelayEnum.delay8: @"DELAY_8", - THETACThetaRepositoryExposureDelayEnum.delay9: @"DELAY_9", - THETACThetaRepositoryExposureDelayEnum.delay10: @"DELAY_10" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureDelayEnum.toTheta objectForKey:[rct objectForKey:@"exposureDelay"]]; - if (val) { - opt.exposureDelay = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureDelayEnum.fromTheta objectForKey:opt.exposureDelay]; - if (val) { - [rct setObject:val forKey:@"exposureDelay"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [ExposureDelayEnum.toTheta objectForKey:[rct objectForKey:@"exposureDelay"]]; - if (val) { - [builder setExposureDelayDelay:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [ExposureDelayEnum.toTheta objectForKey:[rct objectForKey:@"exposureDelay"]]; - if (val) { - [builder setExposureDelayDelay:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [ExposureDelayEnum.toTheta objectForKey:[rct objectForKey:@"exposureDelay"]]; - if (val) { - [builder setExposureDelayDelay:val]; - } - } -}; - -/** - * ExposureProgramEnum converter - */ -static convert_t ExposureProgramEnum = { - .toTheta = @{ - @"MANUAL": THETACThetaRepositoryExposureProgramEnum.manual, - @"NORMAL_PROGRAM": THETACThetaRepositoryExposureProgramEnum.normalProgram, - @"APERTURE_PRIORITY": THETACThetaRepositoryExposureProgramEnum.aperturePriority, - @"SHUTTER_PRIORITY": THETACThetaRepositoryExposureProgramEnum.shutterPriority, - @"ISO_PRIORITY": THETACThetaRepositoryExposureProgramEnum.isoPriority - }, - .fromTheta = @{ - THETACThetaRepositoryExposureProgramEnum.manual: @"MANUAL", - THETACThetaRepositoryExposureProgramEnum.normalProgram: @"NORMAL_PROGRAM", - THETACThetaRepositoryExposureProgramEnum.aperturePriority: @"APERTURE_PRIORITY", - THETACThetaRepositoryExposureProgramEnum.shutterPriority: @"SHUTTER_PRIORITY", - THETACThetaRepositoryExposureProgramEnum.isoPriority: @"ISO_PRIORITY" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureProgramEnum.toTheta - objectForKey:[rct objectForKey:@"exposureProgram"]]; - if (val) { - opt.exposureProgram = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ExposureProgramEnum.fromTheta objectForKey:opt.exposureProgram]; - if (val) { - [rct setObject:val forKey:@"exposureProgram"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [ExposureProgramEnum.toTheta - objectForKey:[rct objectForKey:@"exposureProgram"]]; - if (val) { - [builder setExposureProgramProgram:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [ExposureProgramEnum.toTheta objectForKey:[rct objectForKey:@"exposureProgram"]]; - if (val) { - [builder setExposureProgramProgram:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [ExposureProgramEnum.toTheta - objectForKey:[rct objectForKey:@"exposureProgram"]]; - if (val) { - [builder setExposureProgramProgram:val]; - } - } -}; - -/** - * FaceDetectEnum converter - */ -static convert_t FaceDetectEnum = { - .toTheta = nil, - .fromTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!FaceDetectEnum.toTheta) { - FaceDetectEnum.toTheta = createToThetaDic(THETACThetaRepositoryFaceDetectEnum.values); - } - id val = [FaceDetectEnum.toTheta objectForKey:[rct objectForKey:@"faceDetect"]]; - if (val) { - opt.faceDetect = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!FaceDetectEnum.fromTheta) { - FaceDetectEnum.fromTheta = createFromThetaDic(THETACThetaRepositoryFaceDetectEnum.values); - } - id val = [FaceDetectEnum.fromTheta objectForKey:opt.faceDetect]; - if (val) { - [rct setObject:val forKey:@"faceDetect"]; - } - } -}; - -/** - * FileFormatTypeEnum converter - */ -static convert_t FileFormatTypeEnum = { - .toTheta = @{ - @"JPEG": THETACThetaRepositoryFileFormatTypeEnum.jpeg, - @"MP4": THETACThetaRepositoryFileFormatTypeEnum.mp4, - @"RAW": THETACThetaRepositoryFileFormatTypeEnum.raw - }, - .fromTheta = @{ - THETACThetaRepositoryFileFormatTypeEnum.jpeg: @"JPEG", - THETACThetaRepositoryFileFormatTypeEnum.mp4: @"MP4", - THETACThetaRepositoryFileFormatTypeEnum.raw: @"RAW" - } -}; - -/** - * FileFormatEnum converter - */ -static convert_t FileFormatEnum = { - .toTheta = @{ - @"IMAGE_2K": THETACThetaRepositoryFileFormatEnum.image2k, - @"IMAGE_5K": THETACThetaRepositoryFileFormatEnum.image5k, - @"IMAGE_6_7K": THETACThetaRepositoryFileFormatEnum.image67k, - @"RAW_P_6_7K": THETACThetaRepositoryFileFormatEnum.rawP67k, - @"IMAGE_5_5K": THETACThetaRepositoryFileFormatEnum.image55k, - @"IMAGE_11K": THETACThetaRepositoryFileFormatEnum.image11k, - @"VIDEO_HD": THETACThetaRepositoryFileFormatEnum.videoHd, - @"VIDEO_FULL_HD": THETACThetaRepositoryFileFormatEnum.videoFullHd, - @"VIDEO_2K": THETACThetaRepositoryFileFormatEnum.video2k, - @"VIDEO_4K": THETACThetaRepositoryFileFormatEnum.video4k, - @"VIDEO_2K_30F": THETACThetaRepositoryFileFormatEnum.video2k30f, - @"VIDEO_2K_60F": THETACThetaRepositoryFileFormatEnum.video2k60f, - @"VIDEO_4K_30F": THETACThetaRepositoryFileFormatEnum.video4k30f, - @"VIDEO_4K_60F": THETACThetaRepositoryFileFormatEnum.video4k60f, - @"VIDEO_5_7K_2F": THETACThetaRepositoryFileFormatEnum.video57k2f, - @"VIDEO_5_7K_5F": THETACThetaRepositoryFileFormatEnum.video57k5f, - @"VIDEO_5_7K_30F": THETACThetaRepositoryFileFormatEnum.video57k30f, - @"VIDEO_7K_2F": THETACThetaRepositoryFileFormatEnum.video7k2f, - @"VIDEO_7K_5F": THETACThetaRepositoryFileFormatEnum.video7k5f, - @"VIDEO_7K_10F": THETACThetaRepositoryFileFormatEnum.video7k10f - }, - .fromTheta = @{ - THETACThetaRepositoryFileFormatEnum.image2k: @"IMAGE_2K", - THETACThetaRepositoryFileFormatEnum.image5k: @"IMAGE_5K", - THETACThetaRepositoryFileFormatEnum.image67k: @"IMAGE_6_7K", - THETACThetaRepositoryFileFormatEnum.rawP67k: @"RAW_P_6_7K", - THETACThetaRepositoryFileFormatEnum.image55k: @"IMAGE_5_5K", - THETACThetaRepositoryFileFormatEnum.image11k: @"IMAGE_11K", - THETACThetaRepositoryFileFormatEnum.videoHd: @"VIDEO_HD", - THETACThetaRepositoryFileFormatEnum.videoFullHd: @"VIDEO_FULL_HD", - THETACThetaRepositoryFileFormatEnum.video2k: @"VIDEO_2K", - THETACThetaRepositoryFileFormatEnum.video4k: @"VIDEO_4K", - THETACThetaRepositoryFileFormatEnum.video2k30f: @"VIDEO_2K_30F", - THETACThetaRepositoryFileFormatEnum.video2k60f: @"VIDEO_2K_60F", - THETACThetaRepositoryFileFormatEnum.video4k30f: @"VIDEO_4K_30F", - THETACThetaRepositoryFileFormatEnum.video4k60f: @"VIDEO_4K_60F", - THETACThetaRepositoryFileFormatEnum.video57k2f: @"VIDEO_5_7K_2F", - THETACThetaRepositoryFileFormatEnum.video57k5f: @"VIDEO_5_7K_5F", - THETACThetaRepositoryFileFormatEnum.video57k30f: @"VIDEO_5_7K_30F", - THETACThetaRepositoryFileFormatEnum.video7k2f: @"VIDEO_7K_2F", - THETACThetaRepositoryFileFormatEnum.video7k5f: @"VIDEO_7K_5F", - THETACThetaRepositoryFileFormatEnum.video7k10f: @"VIDEO_7K_10F" - }, - .photoOption = @{ - @"IMAGE_2K": THETACThetaRepositoryPhotoFileFormatEnum.image2k, - @"IMAGE_5K": THETACThetaRepositoryPhotoFileFormatEnum.image5k, - @"IMAGE_6_7K": THETACThetaRepositoryPhotoFileFormatEnum.image67k, - @"RAW_P_6_7K": THETACThetaRepositoryPhotoFileFormatEnum.rawP67k, - @"IMAGE_5_5K": THETACThetaRepositoryPhotoFileFormatEnum.image55k, - @"IMAGE_11K": THETACThetaRepositoryPhotoFileFormatEnum.image11k, - }, - .videoOption = @{ - @"VIDEO_HD": THETACThetaRepositoryVideoFileFormatEnum.videoHd, - @"VIDEO_FULL_HD": THETACThetaRepositoryVideoFileFormatEnum.videoFullHd, - @"VIDEO_2K": THETACThetaRepositoryVideoFileFormatEnum.video2k, - @"VIDEO_4K": THETACThetaRepositoryVideoFileFormatEnum.video4k, - @"VIDEO_2K_30F": THETACThetaRepositoryVideoFileFormatEnum.video2k30f, - @"VIDEO_2K_60F": THETACThetaRepositoryVideoFileFormatEnum.video2k60f, - @"VIDEO_4K_30F": THETACThetaRepositoryVideoFileFormatEnum.video4k30f, - @"VIDEO_4K_60F": THETACThetaRepositoryVideoFileFormatEnum.video4k60f, - @"VIDEO_5_7K_2F": THETACThetaRepositoryVideoFileFormatEnum.video57k2f, - @"VIDEO_5_7K_5F": THETACThetaRepositoryVideoFileFormatEnum.video57k5f, - @"VIDEO_5_7K_30F": THETACThetaRepositoryVideoFileFormatEnum.video57k30f, - @"VIDEO_7K_2F": THETACThetaRepositoryVideoFileFormatEnum.video7k2f, - @"VIDEO_7K_5F": THETACThetaRepositoryVideoFileFormatEnum.video7k5f, - @"VIDEO_7K_10F": THETACThetaRepositoryVideoFileFormatEnum.video7k10f - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [FileFormatEnum.toTheta objectForKey:[rct objectForKey:@"fileFormat"]]; - if (val) { - opt.fileFormat = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [FileFormatEnum.fromTheta objectForKey:opt.fileFormat]; - if (val) { - [rct setObject:val forKey:@"fileFormat"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [FileFormatEnum.photoOption objectForKey:[rct objectForKey:@"fileFormat"]]; - if (val) { - [builder setFileFormatFileFormat:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [FileFormatEnum.videoOption objectForKey:[rct objectForKey:@"fileFormat"]]; - if (val) { - [builder setFileFormatFileFormat:val]; - } - } -}; - -/** - * FilterEnum converter - */ -static convert_t FilterEnum = { - .toTheta = @{ - @"OFF": THETACThetaRepositoryFilterEnum.off, - @"DR_COMP": THETACThetaRepositoryFilterEnum.drComp, - @"NOISE_REDUCTION": THETACThetaRepositoryFilterEnum.noiseReduction, - @"HDR": THETACThetaRepositoryFilterEnum.hdr, - @"HH_HDR": THETACThetaRepositoryFilterEnum.hhHdr - }, - .fromTheta = @{ - THETACThetaRepositoryFilterEnum.off: @"OFF", - THETACThetaRepositoryFilterEnum.drComp: @"DR_COMP", - THETACThetaRepositoryFilterEnum.noiseReduction: @"NOISE_REDUCTION", - THETACThetaRepositoryFilterEnum.hdr: @"HDR", - THETACThetaRepositoryFilterEnum.hhHdr: @"HH_HDR" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [FilterEnum.toTheta objectForKey:[rct objectForKey:@"filter"]]; - if (val) { - opt.filter = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [FilterEnum.fromTheta objectForKey:opt.filter]; - if (val) { - [rct setObject:val forKey:@"filter"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [FilterEnum.toTheta objectForKey:[rct objectForKey:@"filter"]]; - if (val) { - [builder setFilterFilter:val]; - } - } -}; - -/** - * ShootingFunctionEnum converter - */ -static convert_t ShootingFunctionEnum = { - .toTheta = nil, - .fromTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ShootingFunctionEnum.toTheta) { - ShootingFunctionEnum.toTheta = createToThetaDic(THETACThetaRepositoryShootingFunctionEnum.values); - } - id val = [ShootingFunctionEnum.toTheta objectForKey:[rct objectForKey:@"function"]]; - if (val) { - opt.function = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ShootingFunctionEnum.fromTheta) { - ShootingFunctionEnum.fromTheta = createFromThetaDic(THETACThetaRepositoryShootingFunctionEnum.values); - } - id val = [ShootingFunctionEnum.fromTheta objectForKey:opt.function]; - if (val) { - [rct setObject:val forKey:@"function"]; - } - } -}; - -/** - * GainEnum converter - */ -static convert_t GainEnum = { - .toTheta = nil, - .fromTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!GainEnum.toTheta) { - GainEnum.toTheta = createToThetaDic(THETACThetaRepositoryGainEnum.values); - } - id val = [GainEnum.toTheta objectForKey:[rct objectForKey:@"gain"]]; - if (val) { - opt.gain = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!GainEnum.fromTheta) { - GainEnum.fromTheta = createFromThetaDic(THETACThetaRepositoryGainEnum.values); - } - id val = [GainEnum.fromTheta objectForKey:opt.gain]; - if (val) { - [rct setObject:val forKey:@"gain"]; - } - } -}; - -/** - * GpsTagRecordingEnum converter - */ -static convert_t GpsTagRecordingEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryGpsTagRecordingEnum.on, - @"OFF": THETACThetaRepositoryGpsTagRecordingEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryGpsTagRecordingEnum.on: @"ON", - THETACThetaRepositoryGpsTagRecordingEnum.off: @"OFF" - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [GpsTagRecordingEnum.toTheta - objectForKey:[rct objectForKey:@"_gpsTagRecording"]]; - if (val) { - [builder setGpsTagRecordingValue:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [GpsTagRecordingEnum.toTheta objectForKey:[rct objectForKey:@"_gpsTagRecording"]]; - if (val) { - [builder setGpsTagRecordingValue:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [GpsTagRecordingEnum.toTheta - objectForKey:[rct objectForKey:@"_gpsTagRecording"]]; - if (val) { - [builder setGpsTagRecordingValue:val]; - } - } -}; - -/** - * ImageStitching convertor - */ -static convert_t ImageStitchingEnum = { - .toTheta = @{ - @"AUTO": THETACThetaRepositoryImageStitchingEnum.auto_, - @"STATIC": THETACThetaRepositoryImageStitchingEnum.static_, - @"DYNAMIC": THETACThetaRepositoryImageStitchingEnum.dynamic, - @"DYNAMIC_AUTO": THETACThetaRepositoryImageStitchingEnum.dynamicAuto, - @"DYNAMIC_SEMI_AUTO": THETACThetaRepositoryImageStitchingEnum.dynamicSemiAuto, - @"DYNAMIC_SAVE": THETACThetaRepositoryImageStitchingEnum.dynamicSave, - @"DYNAMIC_LOAD": THETACThetaRepositoryImageStitchingEnum.dynamicLoad, - @"NONE": THETACThetaRepositoryImageStitchingEnum.none - }, - .fromTheta = @{ - THETACThetaRepositoryImageStitchingEnum.auto_: @"AUTO", - THETACThetaRepositoryImageStitchingEnum.static_: @"STATIC", - THETACThetaRepositoryImageStitchingEnum.dynamic: @"DYNAMIC", - THETACThetaRepositoryImageStitchingEnum.dynamicAuto: @"DYNAMIC_AUTO", - THETACThetaRepositoryImageStitchingEnum.dynamicSemiAuto: @"DYNAMIC_SEMI_AUTO", - THETACThetaRepositoryImageStitchingEnum.dynamicSave: @"DYNAMIC_SAVE", - THETACThetaRepositoryImageStitchingEnum.dynamicLoad: @"DYNAMIC_LOAD", - THETACThetaRepositoryImageStitchingEnum.none: @"NONE" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ImageStitchingEnum.toTheta objectForKey:[rct objectForKey:@"imageStitching"]]; - if (val) { - opt.imageStitching = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ImageStitchingEnum.fromTheta objectForKey:opt.imageStitching]; - if (val) { - [rct setObject:val forKey:@"imageStitching"]; - } - } -}; - -/** - * IsoEnum converter - */ -static convert_t IsoEnum = { - .toTheta = @{ - @"ISO_AUTO": THETACThetaRepositoryIsoEnum.isoAuto, - @"ISO_50": THETACThetaRepositoryIsoEnum.iso50, - @"ISO_64": THETACThetaRepositoryIsoEnum.iso64, - @"ISO_80": THETACThetaRepositoryIsoEnum.iso80, - @"ISO_100": THETACThetaRepositoryIsoEnum.iso100, - @"ISO_125": THETACThetaRepositoryIsoEnum.iso125, - @"ISO_160": THETACThetaRepositoryIsoEnum.iso160, - @"ISO_200": THETACThetaRepositoryIsoEnum.iso200, - @"ISO_250": THETACThetaRepositoryIsoEnum.iso250, - @"ISO_320": THETACThetaRepositoryIsoEnum.iso320, - @"ISO_400": THETACThetaRepositoryIsoEnum.iso400, - @"ISO_500": THETACThetaRepositoryIsoEnum.iso500, - @"ISO_640": THETACThetaRepositoryIsoEnum.iso640, - @"ISO_800": THETACThetaRepositoryIsoEnum.iso800, - @"ISO_1000": THETACThetaRepositoryIsoEnum.iso1000, - @"ISO_1250": THETACThetaRepositoryIsoEnum.iso1250, - @"ISO_1600": THETACThetaRepositoryIsoEnum.iso1600, - @"ISO_2000": THETACThetaRepositoryIsoEnum.iso2000, - @"ISO_2500": THETACThetaRepositoryIsoEnum.iso2500, - @"ISO_3200": THETACThetaRepositoryIsoEnum.iso3200, - @"ISO_4000": THETACThetaRepositoryIsoEnum.iso4000, - @"ISO_5000": THETACThetaRepositoryIsoEnum.iso5000, - @"ISO_6400": THETACThetaRepositoryIsoEnum.iso6400 - }, - .fromTheta = @{ - THETACThetaRepositoryIsoEnum.isoAuto: @"ISO_AUTO", - THETACThetaRepositoryIsoEnum.iso50: @"ISO_50", - THETACThetaRepositoryIsoEnum.iso64: @"ISO_64", - THETACThetaRepositoryIsoEnum.iso80: @"ISO_80", - THETACThetaRepositoryIsoEnum.iso100: @"ISO_100", - THETACThetaRepositoryIsoEnum.iso125: @"ISO_125", - THETACThetaRepositoryIsoEnum.iso160: @"ISO_160", - THETACThetaRepositoryIsoEnum.iso200: @"ISO_200", - THETACThetaRepositoryIsoEnum.iso250: @"ISO_250", - THETACThetaRepositoryIsoEnum.iso320: @"ISO_320", - THETACThetaRepositoryIsoEnum.iso400: @"ISO_400", - THETACThetaRepositoryIsoEnum.iso500: @"ISO_500", - THETACThetaRepositoryIsoEnum.iso640: @"ISO_640", - THETACThetaRepositoryIsoEnum.iso800: @"ISO_800", - THETACThetaRepositoryIsoEnum.iso1000: @"ISO_1000", - THETACThetaRepositoryIsoEnum.iso1250: @"ISO_1250", - THETACThetaRepositoryIsoEnum.iso1600: @"ISO_1600", - THETACThetaRepositoryIsoEnum.iso2000: @"ISO_2000", - THETACThetaRepositoryIsoEnum.iso2500: @"ISO_2500", - THETACThetaRepositoryIsoEnum.iso3200: @"ISO_3200", - THETACThetaRepositoryIsoEnum.iso4000: @"ISO_4000", - THETACThetaRepositoryIsoEnum.iso5000: @"ISO_5000", - THETACThetaRepositoryIsoEnum.iso6400: @"ISO_6400" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [IsoEnum.toTheta objectForKey:[rct objectForKey:@"iso"]]; - if (val) { - opt.iso = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [IsoEnum.fromTheta objectForKey:opt.iso]; - if (val) { - [rct setObject:val forKey:@"iso"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [IsoEnum.toTheta objectForKey:[rct objectForKey:@"iso"]]; - if (val) { - [builder setIsoIso:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [IsoEnum.toTheta objectForKey:[rct objectForKey:@"iso"]]; - if (val) { - [builder setIsoIso:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [IsoEnum.toTheta objectForKey:[rct objectForKey:@"iso"]]; - if (val) { - [builder setIsoIso:val]; - } - } -}; - -/** - * IsoAutoHighLimitEnum converter - */ -static convert_t IsoAutoHighLimitEnum = { - .toTheta = @{ - @"ISO_100": THETACThetaRepositoryIsoAutoHighLimitEnum.iso100, - @"ISO_125": THETACThetaRepositoryIsoAutoHighLimitEnum.iso125, - @"ISO_160": THETACThetaRepositoryIsoAutoHighLimitEnum.iso160, - @"ISO_200": THETACThetaRepositoryIsoAutoHighLimitEnum.iso200, - @"ISO_250": THETACThetaRepositoryIsoAutoHighLimitEnum.iso250, - @"ISO_320": THETACThetaRepositoryIsoAutoHighLimitEnum.iso320, - @"ISO_400": THETACThetaRepositoryIsoAutoHighLimitEnum.iso400, - @"ISO_500": THETACThetaRepositoryIsoAutoHighLimitEnum.iso500, - @"ISO_640": THETACThetaRepositoryIsoAutoHighLimitEnum.iso640, - @"ISO_800": THETACThetaRepositoryIsoAutoHighLimitEnum.iso800, - @"ISO_1000": THETACThetaRepositoryIsoAutoHighLimitEnum.iso1000, - @"ISO_1250": THETACThetaRepositoryIsoAutoHighLimitEnum.iso1250, - @"ISO_1600": THETACThetaRepositoryIsoAutoHighLimitEnum.iso1600, - @"ISO_2000": THETACThetaRepositoryIsoAutoHighLimitEnum.iso2000, - @"ISO_2500": THETACThetaRepositoryIsoAutoHighLimitEnum.iso2500, - @"ISO_3200": THETACThetaRepositoryIsoAutoHighLimitEnum.iso3200, - @"ISO_4000": THETACThetaRepositoryIsoAutoHighLimitEnum.iso4000, - @"ISO_5000": THETACThetaRepositoryIsoAutoHighLimitEnum.iso5000, - @"ISO_6400": THETACThetaRepositoryIsoAutoHighLimitEnum.iso6400 - }, - .fromTheta = @{ - THETACThetaRepositoryIsoAutoHighLimitEnum.iso100: @"ISO_100", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso125: @"ISO_125", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso160: @"ISO_160", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso200: @"ISO_200", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso250: @"ISO_250", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso320: @"ISO_320", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso400: @"ISO_400", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso500: @"ISO_500", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso640: @"ISO_640", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso800: @"ISO_800", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso1000: @"ISO_1000", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso1250: @"ISO_1250", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso1600: @"ISO_1600", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso2000: @"ISO_2000", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso2500: @"ISO_2500", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso3200: @"ISO_3200", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso4000: @"ISO_4000", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso5000: @"ISO_5000", - THETACThetaRepositoryIsoAutoHighLimitEnum.iso6400: @"ISO_6400" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [IsoAutoHighLimitEnum.toTheta - objectForKey:[rct objectForKey:@"isoAutoHighLimit"]]; - if (val) { - opt.isoAutoHighLimit = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [IsoAutoHighLimitEnum.fromTheta objectForKey:opt.isoAutoHighLimit]; - if (val) { - [rct setObject:val forKey:@"isoAutoHighLimit"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [IsoAutoHighLimitEnum.toTheta - objectForKey:[rct objectForKey:@"isoAutoHighLimit"]]; - if (val) { - [builder setIsoAutoHighLimitIso:val]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - id val = [IsoAutoHighLimitEnum.toTheta objectForKey:[rct objectForKey:@"isoAutoHighLimit"]]; - if (val) { - [builder setIsoAutoHighLimitIso:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [IsoAutoHighLimitEnum.toTheta - objectForKey:[rct objectForKey:@"isoAutoHighLimit"]]; - if (val) { - [builder setIsoAutoHighLimitIso:val]; - } - } -}; - -/** - * LanguageEnum converter - */ -static convert_t LanguageEnum = { - .toTheta = @{ - @"DE": THETACThetaRepositoryLanguageEnum.de, - @"EN_GB": THETACThetaRepositoryLanguageEnum.enGb, - @"EN_US": THETACThetaRepositoryLanguageEnum.enUs, - @"FR": THETACThetaRepositoryLanguageEnum.fr, - @"IT": THETACThetaRepositoryLanguageEnum.it, - @"JA": THETACThetaRepositoryLanguageEnum.ja, - @"KO": THETACThetaRepositoryLanguageEnum.ko, - @"ZH_CN": THETACThetaRepositoryLanguageEnum.zhCn, - @"ZH_TW": THETACThetaRepositoryLanguageEnum.zhTw - }, - .fromTheta = @{ - THETACThetaRepositoryLanguageEnum.de: @"DE", - THETACThetaRepositoryLanguageEnum.enGb: @"EN_GB", - THETACThetaRepositoryLanguageEnum.enUs: @"EN_US", - THETACThetaRepositoryLanguageEnum.fr: @"FR", - THETACThetaRepositoryLanguageEnum.it: @"IT", - THETACThetaRepositoryLanguageEnum.ja: @"JA", - THETACThetaRepositoryLanguageEnum.ko: @"KO", - THETACThetaRepositoryLanguageEnum.zhCn: @"ZH_CN", - THETACThetaRepositoryLanguageEnum.zhTw: @"ZH_TW" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [LanguageEnum.toTheta objectForKey:[rct objectForKey:@"language"]]; - if (val) { - opt.language = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [LanguageEnum.fromTheta objectForKey:opt.language]; - if (val) { - [rct setObject:val forKey:@"language"]; - } - } -}; - -/** - * MaxRecordableTimeEnum converter - */ -static convert_t MaxRecordableTimeEnum = { - .toTheta = @{ - @"RECORDABLE_TIME_180": THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime180, - @"RECORDABLE_TIME_300": THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime300, - @"RECORDABLE_TIME_1500": THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime1500, - @"RECORDABLE_TIME_7200": THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime7200, - @"DO_NOT_UPDATE_MY_SETTING_CONDITION": THETACThetaRepositoryMaxRecordableTimeEnum.doNotUpdateMySettingCondition - }, - .fromTheta = @{ - THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime180: @"RECORDABLE_TIME_180", - THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime300: @"RECORDABLE_TIME_300", - THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime1500: @"RECORDABLE_TIME_1500", - THETACThetaRepositoryMaxRecordableTimeEnum.recordableTime7200: @"RECORDABLE_TIME_7200", - THETACThetaRepositoryMaxRecordableTimeEnum.doNotUpdateMySettingCondition: @"DO_NOT_UPDATE_MY_SETTING_CONDITION" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [MaxRecordableTimeEnum.toTheta - objectForKey:[rct objectForKey:@"maxRecordableTime"]]; - if (val) { - opt.maxRecordableTime = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [MaxRecordableTimeEnum.fromTheta objectForKey:opt.maxRecordableTime]; - if (val) { - [rct setObject:val forKey:@"maxRecordableTime"]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [MaxRecordableTimeEnum.toTheta - objectForKey:[rct objectForKey:@"maxRecordableTime"]]; - if (val) { - [builder setMaxRecordableTimeTime:val]; - } - } -}; - -/** - * OffDelayEnum converter - */ -static convert_t OffDelayEnum = { - .toTheta = @{ - @"DISABLE": THETACThetaRepositoryOffDelayEnum.disable, - @"OFF_DELAY_5M": THETACThetaRepositoryOffDelayEnum.offDelay5m, - @"OFF_DELAY_10M": THETACThetaRepositoryOffDelayEnum.offDelay10m, - @"OFF_DELAY_15M": THETACThetaRepositoryOffDelayEnum.offDelay15m, - @"OFF_DELAY_30M": THETACThetaRepositoryOffDelayEnum.offDelay30m - }, - .fromTheta = @{ - @(THETACThetaRepositoryOffDelayEnum.disable.sec): @"DISABLE", - @(THETACThetaRepositoryOffDelayEnum.offDelay5m.sec): @"OFF_DELAY_5M", - @(THETACThetaRepositoryOffDelayEnum.offDelay10m.sec): @"OFF_DELAY_10M", - @(THETACThetaRepositoryOffDelayEnum.offDelay15m.sec): @"OFF_DELAY_15M", - @(THETACThetaRepositoryOffDelayEnum.offDelay30m.sec): @"OFF_DELAY_30M" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id rv = [rct objectForKey:@"offDelay"]; - if (rv) { - if ([rv isKindOfClass:NSString.class]) { - id val = [OffDelayEnum.toTheta objectForKey:rv]; - opt.offDelay = val; - } else if ([rv isKindOfClass:NSNumber.class]) { - id val = [[THETACThetaRepositoryOffDelaySec alloc] initWithSec:[rv intValue]]; - opt.offDelay = val; - } - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.offDelay) { - id val = [OffDelayEnum.fromTheta objectForKey:@(opt.offDelay.sec)]; - if (val) { - [rct setObject:val forKey:@"offDelay"]; - } else { - [rct setObject:@(opt.offDelay.sec) forKey:@"offDelay"]; - } - } - } -}; - -/** - * PowerSaving convertor - */ -static convert_t PowerSavingEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryPowerSavingEnum.on, - @"OFF": THETACThetaRepositoryPowerSavingEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryPowerSavingEnum.on: @"ON", - THETACThetaRepositoryPowerSavingEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PowerSavingEnum.toTheta objectForKey:[rct objectForKey:@"powerSaving"]]; - if (val) { - opt.powerSaving = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PowerSavingEnum.fromTheta objectForKey:opt.powerSaving]; - if (val) { - [rct setObject:val forKey:@"powerSaving"]; - } - } -}; - -/** - * PreviewFormat convertor - */ -static convert_t PreviewFormatEnum = { - .toTheta = @{ - @"W1024_H512_F30": THETACThetaRepositoryPreviewFormatEnum.w1024H512F30, - @"W1024_H512_F15": THETACThetaRepositoryPreviewFormatEnum.w1024H512F15, - @"W512_H512_F30": THETACThetaRepositoryPreviewFormatEnum.w512H512F30, - @"W1920_H960_F8": THETACThetaRepositoryPreviewFormatEnum.w1920H960F8, - @"W1024_H512_F8": THETACThetaRepositoryPreviewFormatEnum.w1024H512F8, - @"W640_H320_F30": THETACThetaRepositoryPreviewFormatEnum.w640H320F30, - @"W640_H320_F8": THETACThetaRepositoryPreviewFormatEnum.w640H320F8, - @"W640_H320_F10": THETACThetaRepositoryPreviewFormatEnum.w640H320F10 - }, - .fromTheta = @{ - THETACThetaRepositoryPreviewFormatEnum.w1024H512F30: @"W1024_H512_F30", - THETACThetaRepositoryPreviewFormatEnum.w1024H512F15: @"W1024_H512_F15", - THETACThetaRepositoryPreviewFormatEnum.w512H512F30: @"W512_H512_F30", - THETACThetaRepositoryPreviewFormatEnum.w1920H960F8: @"W1920_H960_F8", - THETACThetaRepositoryPreviewFormatEnum.w1024H512F8: @"W1024_H512_F8", - THETACThetaRepositoryPreviewFormatEnum.w640H320F30: @"W640_H320_F30", - THETACThetaRepositoryPreviewFormatEnum.w640H320F8: @"W640_H320_F8", - THETACThetaRepositoryPreviewFormatEnum.w640H320F10: @"W640_H320_F10", - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PreviewFormatEnum.toTheta objectForKey:[rct objectForKey:@"previewFormat"]]; - if (val) { - opt.previewFormat = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PreviewFormatEnum.fromTheta objectForKey:opt.previewFormat]; - if (val) { - [rct setObject:val forKey:@"previewFormat"]; - } - } -}; - - -/** - * WhiteBalanceEnum converter - */ -static convert_t WhiteBalanceEnum = { - .toTheta = @{ - @"AUTO": THETACThetaRepositoryWhiteBalanceEnum.auto_, - @"DAYLIGHT": THETACThetaRepositoryWhiteBalanceEnum.daylight, - @"SHADE": THETACThetaRepositoryWhiteBalanceEnum.shade, - @"CLOUDY_DAYLIGHT": THETACThetaRepositoryWhiteBalanceEnum.cloudyDaylight, - @"INCANDESCENT": THETACThetaRepositoryWhiteBalanceEnum.incandescent, - @"WARM_WHITE_FLUORESCENT": THETACThetaRepositoryWhiteBalanceEnum.warmWhiteFluorescent, - @"DAYLIGHT_FLUORESCENT": THETACThetaRepositoryWhiteBalanceEnum.daylightFluorescent, - @"DAYWHITE_FLUORESCENT": THETACThetaRepositoryWhiteBalanceEnum.daywhiteFluorescent, - @"FLUORESCENT": THETACThetaRepositoryWhiteBalanceEnum.fluorescent, - @"BULB_FLUORESCENT": THETACThetaRepositoryWhiteBalanceEnum.bulbFluorescent, - @"COLOR_TEMPERATURE": THETACThetaRepositoryWhiteBalanceEnum.colorTemperature, - @"UNDERWATER": THETACThetaRepositoryWhiteBalanceEnum.underwater - }, - .fromTheta = @{ - THETACThetaRepositoryWhiteBalanceEnum.auto_: @"AUTO", - THETACThetaRepositoryWhiteBalanceEnum.daylight: @"DAYLIGHT", - THETACThetaRepositoryWhiteBalanceEnum.shade: @"SHADE", - THETACThetaRepositoryWhiteBalanceEnum.cloudyDaylight: @"CLOUDY_DAYLIGHT", - THETACThetaRepositoryWhiteBalanceEnum.incandescent: @"INCANDESCENT", - THETACThetaRepositoryWhiteBalanceEnum.warmWhiteFluorescent: @"WARM_WHITE_FLUORESCENT", - THETACThetaRepositoryWhiteBalanceEnum.daylightFluorescent: @"DAYLIGHT_FLUORESCENT", - THETACThetaRepositoryWhiteBalanceEnum.daywhiteFluorescent: @"DAYWHITE_FLUORESCENT", - THETACThetaRepositoryWhiteBalanceEnum.fluorescent: @"FLUORESCENT", - THETACThetaRepositoryWhiteBalanceEnum.bulbFluorescent: @"BULB_FLUORESCENT", - THETACThetaRepositoryWhiteBalanceEnum.colorTemperature: @"COLOR_TEMPERATURE", - THETACThetaRepositoryWhiteBalanceEnum.underwater: @"UNDERWATER" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WhiteBalanceEnum.toTheta objectForKey:[rct objectForKey:@"whiteBalance"]]; - if (val) { - opt.whiteBalance = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WhiteBalanceEnum.fromTheta objectForKey:opt.whiteBalance]; - if (val) { - [rct setObject:val forKey:@"whiteBalance"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [WhiteBalanceEnum.toTheta - objectForKey:[rct objectForKey:@"whiteBalance"]]; - if (val) { - [builder setWhiteBalanceWhiteBalance:val]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - id val = [WhiteBalanceEnum.toTheta - objectForKey:[rct objectForKey:@"whiteBalance"]]; - if (val) { - [builder setWhiteBalanceWhiteBalance:val]; - } - } -}; - - -/** - * WhiteBalanceAutoStrengthEnum converter - */ -static convert_t WhiteBalanceAutoStrengthEnum = { - .toTheta = @{ - @"ON": THETACThetaRepositoryWhiteBalanceAutoStrengthEnum.on, - @"OFF": THETACThetaRepositoryWhiteBalanceAutoStrengthEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryWhiteBalanceAutoStrengthEnum.on: @"ON", - THETACThetaRepositoryWhiteBalanceAutoStrengthEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WhiteBalanceAutoStrengthEnum.toTheta objectForKey:[rct objectForKey:@"whiteBalanceAutoStrength"]]; - if (val) { - opt.whiteBalanceAutoStrength = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WhiteBalanceAutoStrengthEnum.fromTheta objectForKey:opt.whiteBalanceAutoStrength]; - if (val) { - [rct setObject:val forKey:@"whiteBalanceAutoStrength"]; - } - } -}; - -/** - * ColorTemperature converter - */ -static convert_t ColorTemperatureCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"colorTemperature"]; - if (val) { - opt.colorTemperature = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.colorTemperature) { - [rct setObject:opt.colorTemperature forKey:@"colorTemperature"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - NSNumber *val = (NSNumber*) [rct objectForKey:@"colorTemperature"]; - if (val) { - [builder setColorTemperatureKelvin:val.intValue]; - } - }, - .setVideoOption = ^(NSDictionary* rct, THETACVideoCaptureBuilder *builder) { - NSNumber *val = (NSNumber*) [rct objectForKey:@"colorTemperature"]; - if (val) { - [builder setColorTemperatureKelvin:val.intValue]; - } - } -}; - -/** - * CompositeShootingOutputInterval converter - */ -static convert_t CompositeShootingOutputIntervalCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"compositeShootingOutputInterval"]; - if (val) { - opt.compositeShootingOutputInterval = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.compositeShootingOutputInterval) { - [rct setObject:opt.compositeShootingOutputInterval forKey:@"compositeShootingOutputInterval"]; - } - }, -}; - -/** - * CompositeShootingTime converter - */ -static convert_t CompositeShootingTimeCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"compositeShootingTime"]; - if (val) { - opt.compositeShootingTime = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.compositeShootingTime) { - [rct setObject:opt.compositeShootingTime forKey:@"compositeShootingTime"]; - } - }, -}; - -/** - * DateTimeZone converter - */ -static convert_t DateTimeZoneCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSString* val = [rct objectForKey:@"dateTimeZone"]; - if (val) { - opt.dateTimeZone = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.dateTimeZone) { - [rct setObject:opt.dateTimeZone forKey:@"dateTimeZone"]; - } - } -}; - -/** - * IsGpsOn converter - */ -static convert_t IsGpsOnCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"isGpsOn"]; - if (val) { - opt.isGpsOn = [THETACBoolean numberWithBool:[val boolValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.isGpsOn) { - [rct setObject:@(((NSNumber *) opt.isGpsOn).boolValue) forKey:@"isGpsOn"]; - } - } -}; - -/** - * ShutterSpeed converter - */ -static convert_t ShutterSpeedEnum = { - .toTheta = nil, - .fromTheta = nil, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ShutterSpeedEnum.toTheta) { - NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init]; - THETACKotlinArray* array = THETACThetaRepositoryShutterSpeedEnum.values; - for (int i = 0; i < array.size; i++) { - THETACThetaRepositorySleepDelayEnum* item = [array getIndex:i]; - [dictionary setObject:item forKey:item.name]; - } - ShutterSpeedEnum.toTheta = dictionary; - } - id val = [ShutterSpeedEnum.toTheta objectForKey:[rct objectForKey:@"shutterSpeed"]]; - if (val) { - opt.shutterSpeed = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (!ShutterSpeedEnum.fromTheta) { - NSMutableDictionary* dictionary = [[NSMutableDictionary alloc] init]; - THETACKotlinArray* array = THETACThetaRepositoryShutterSpeedEnum.values; - for (int i = 0; i < array.size; i++) { - THETACThetaRepositorySleepDelayEnum* item = [array getIndex:i]; - [dictionary setObject:item.name forKey:item]; - } - ShutterSpeedEnum.fromTheta = dictionary; - } - id val = [ShutterSpeedEnum.fromTheta objectForKey:opt.shutterSpeed]; - if (val) { - [rct setObject:val forKey:@"shutterSpeed"]; - } - } -}; - -/** - * SleepDelay converter - */ -static convert_t SleepDelayEnum = { - .toTheta = @{ - @"DISABLE": THETACThetaRepositorySleepDelayEnum.disable, - @"SLEEP_DELAY_3M": THETACThetaRepositorySleepDelayEnum.sleepDelay3m, - @"SLEEP_DELAY_5M": THETACThetaRepositorySleepDelayEnum.sleepDelay5m, - @"SLEEP_DELAY_7M": THETACThetaRepositorySleepDelayEnum.sleepDelay7m, - @"SLEEP_DELAY_10M": THETACThetaRepositorySleepDelayEnum.sleepDelay10m - }, - .fromTheta = @{ - @(THETACThetaRepositorySleepDelayEnum.disable.sec): @"DISABLE", - @(THETACThetaRepositorySleepDelayEnum.sleepDelay3m.sec): @"SLEEP_DELAY_3M", - @(THETACThetaRepositorySleepDelayEnum.sleepDelay5m.sec): @"SLEEP_DELAY_5M", - @(THETACThetaRepositorySleepDelayEnum.sleepDelay7m.sec): @"SLEEP_DELAY_7M", - @(THETACThetaRepositorySleepDelayEnum.sleepDelay10m.sec): @"SLEEP_DELAY_10M" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id rv = [rct objectForKey:@"sleepDelay"]; - if (rv) { - if ([rv isKindOfClass:NSString.class]) { - id val = [SleepDelayEnum.toTheta objectForKey:rv]; - opt.sleepDelay = val; - } else if ([rv isKindOfClass:NSNumber.class]) { - id val = [[THETACThetaRepositorySleepDelaySec alloc] initWithSec:[rv intValue]]; - opt.sleepDelay = val; - } - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.sleepDelay) { - id val = [SleepDelayEnum.fromTheta objectForKey:@(opt.sleepDelay.sec)]; - if (val) { - [rct setObject:val forKey:@"sleepDelay"]; - } else { - [rct setObject:@(opt.sleepDelay.sec) forKey:@"sleepDelay"]; - } - } - } -}; - -/** - * RemainingPictures converter - */ -static convert_t RemainingPicturesCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"remainingPictures"]; - if (val) { - opt.remainingPictures = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.remainingPictures) { - [rct setObject:opt.remainingPictures forKey:@"remainingPictures"]; - } - } -}; - -/** - * RemainingVideoSeconds converter - */ -static convert_t RemainingVideoSecondsCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"remainingVideoSeconds"]; - if (val) { - opt.remainingVideoSeconds = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.remainingVideoSeconds) { - [rct setObject:opt.remainingVideoSeconds forKey:@"remainingVideoSeconds"]; - } - } -}; - -/** - * RemainingSpace converter - */ -static convert_t RemainingSpaceCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"remainingSpace"]; - if (val) { - opt.remainingSpace = [THETACLong numberWithLongLong:[val longLongValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.remainingSpace) { - [rct setObject:opt.remainingSpace forKey:@"remainingSpace"]; - } - } -}; - -/** - * TotalSpace converter - */ -static convert_t TotalSpaceCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"totalSpace"]; - if (val) { - opt.totalSpace = [THETACLong numberWithLongLong:[val longLongValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.totalSpace) { - [rct setObject:opt.totalSpace forKey:@"totalSpace"]; - } - } -}; - -/** - * ShutterVolume converter - */ -static convert_t ShutterVolumeCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSNumber* val = [rct objectForKey:@"shutterVolume"]; - if (val) { - opt.shutterVolume = [THETACInt numberWithInt:[val intValue]]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.shutterVolume) { - [rct setObject:opt.shutterVolume forKey:@"shutterVolume"]; - } - } -}; - -/** - * GpsInfo converter - */ -static convert_t GpsInfoCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSDictionary *gpsInfoDict = [rct objectForKey:@"gpsInfo"]; - if (gpsInfoDict) { - opt.gpsInfo = - [[THETACThetaRepositoryGpsInfo alloc] - initWithLatitude:((NSNumber*) [gpsInfoDict objectForKey:@"latitude"]).floatValue - longitude:((NSNumber*) [gpsInfoDict objectForKey:@"longitude"]).floatValue - altitude:((NSNumber*) [gpsInfoDict objectForKey:@"altitude"]).floatValue - dateTimeZone:[gpsInfoDict objectForKey:@"dateTimeZone"]]; - } - }, - - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.gpsInfo) { - NSDictionary *gpsInfo = @{ - @"latitude": @(opt.gpsInfo.latitude), - @"longitude": @(opt.gpsInfo.longitude), - @"altitude": @(opt.gpsInfo.altitude), - @"dateTimeZone": opt.gpsInfo.dateTimeZone - }; - [rct setObject:gpsInfo forKey:@"gpsInfo"]; - } - } -}; - -/** - * NetworkType converter - */ -static convert_t NetworkTypeEnum = { - .toTheta = @{ - @"DIRECT": THETACThetaRepositoryNetworkTypeEnum.direct, - @"CLIENT": THETACThetaRepositoryNetworkTypeEnum.client, - @"ETHERNET": THETACThetaRepositoryNetworkTypeEnum.ethernet, - @"OFF": THETACThetaRepositoryNetworkTypeEnum.off - }, - .fromTheta = @{ - THETACThetaRepositoryNetworkTypeEnum.direct: @"DIRECT", - THETACThetaRepositoryNetworkTypeEnum.client: @"CLIENT", - THETACThetaRepositoryNetworkTypeEnum.ethernet: @"ETHERNET", - THETACThetaRepositoryNetworkTypeEnum.off: @"OFF" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [NetworkTypeEnum.toTheta objectForKey:[rct objectForKey:@"networkType"]]; - if (val) { - opt.networkType = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [NetworkTypeEnum.fromTheta objectForKey:opt.networkType]; - if (val) { - [rct setObject:val forKey:@"networkType"]; - } - } -}; - -/** - * WlanFrequency converter - */ -static convert_t WlanFrequencyEnum = { - .toTheta = @{ - @"GHZ_2_4": THETACThetaRepositoryWlanFrequencyEnum.ghz24, - @"GHZ_5": THETACThetaRepositoryWlanFrequencyEnum.ghz5 - }, - .fromTheta = @{ - THETACThetaRepositoryWlanFrequencyEnum.ghz24: @"GHZ_2_4", - THETACThetaRepositoryWlanFrequencyEnum.ghz5: @"GHZ_5" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WlanFrequencyEnum.toTheta objectForKey:[rct objectForKey:@"wlanFrequency"]]; - if (val) { - opt.wlanFrequency = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [WlanFrequencyEnum.fromTheta objectForKey:opt.wlanFrequency]; - if (val) { - [rct setObject:val forKey:@"wlanFrequency"]; - } - } -}; - -/** - * Password converter - */ -static convert_t PasswordCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSString* val = [rct objectForKey:@"password"]; - if (val) { - opt.password = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.password) { - [rct setObject:opt.password forKey:@"password"]; - } - } -}; - -/** - * Preset convertor - */ -static convert_t PresetEnum = { - .toTheta = @{ - @"FACE": THETACThetaRepositoryPresetEnum.face, - @"NIGHT_VIEW": THETACThetaRepositoryPresetEnum.nightView, - @"LENS_BY_LENS_EXPOSURE": THETACThetaRepositoryPresetEnum.lensByLensExposure, - @"ROOM": THETACThetaRepositoryPresetEnum.room - }, - .fromTheta = @{ - THETACThetaRepositoryPresetEnum.face: @"FACE", - THETACThetaRepositoryPresetEnum.nightView: @"NIGHT_VIEW", - THETACThetaRepositoryPresetEnum.lensByLensExposure: @"LENS_BY_LENS_EXPOSURE", - THETACThetaRepositoryPresetEnum.room: @"ROOM" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PresetEnum.toTheta objectForKey:[rct objectForKey:@"preset"]]; - if (val) { - opt.preset = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [PresetEnum.fromTheta objectForKey:opt.preset]; - if (val) { - [rct setObject:val forKey:@"preset"]; - } - }, - .setPhotoOption = ^(NSDictionary* rct, THETACPhotoCaptureBuilder *builder) { - id val = [PresetEnum.toTheta objectForKey:[rct objectForKey:@"preset"]]; - if (val) { - [builder setPresetPreset:val]; - } - } -}; - - -/** - * Proxy converter - */ -static convert_t ProxyCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSDictionary *proxyDic = [rct objectForKey:@"proxy"]; - if (proxyDic) { - opt.proxy = [[THETACThetaRepositoryProxy alloc] - initWithUse:!isNull([proxyDic objectForKey:@"use"]) ? ((NSNumber*) [proxyDic objectForKey:@"use"]).boolValue : NO - url:!isNull([proxyDic objectForKey:@"url"]) ? [proxyDic objectForKey:@"url"] : nil - port:!isNull([proxyDic objectForKey:@"port"]) ? [THETACInt numberWithInt:((NSNumber*) [proxyDic objectForKey:@"port"]).intValue] : nil - userid:!isNull([proxyDic objectForKey:@"userid"]) ? [proxyDic objectForKey:@"userid"] : nil - password:!isNull([proxyDic objectForKey:@"password"]) ? [proxyDic objectForKey:@"password"] : nil]; - } - }, - - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.proxy) { - NSMutableDictionary *proxy = [NSMutableDictionary dictionary]; - - [proxy setObject:@(opt.proxy.use) forKey:@"use"]; - - if (opt.proxy.url) { - [proxy setObject:opt.proxy.url forKey:@"url"]; - } - - if (opt.proxy.port) { - [proxy setObject:@(opt.proxy.port.intValue) forKey:@"port"]; - } - - if (opt.proxy.userid) { - [proxy setObject:opt.proxy.userid forKey:@"userid"]; - } - - if (opt.proxy.password) { - [proxy setObject:opt.proxy.password forKey:@"password"]; - } - - [rct setObject:proxy forKey:@"proxy"]; - } - } -}; - -/** - * TimeShift converter - */ -static convert_t TimeShiftCvt = { - .toTheta = @{ - @"INTERVAL_0": THETACThetaRepositoryTimeShiftIntervalEnum.interval0, - @"INTERVAL_1": THETACThetaRepositoryTimeShiftIntervalEnum.interval1, - @"INTERVAL_2": THETACThetaRepositoryTimeShiftIntervalEnum.interval2, - @"INTERVAL_3": THETACThetaRepositoryTimeShiftIntervalEnum.interval3, - @"INTERVAL_4": THETACThetaRepositoryTimeShiftIntervalEnum.interval4, - @"INTERVAL_5": THETACThetaRepositoryTimeShiftIntervalEnum.interval5, - @"INTERVAL_6": THETACThetaRepositoryTimeShiftIntervalEnum.interval6, - @"INTERVAL_7": THETACThetaRepositoryTimeShiftIntervalEnum.interval7, - @"INTERVAL_8": THETACThetaRepositoryTimeShiftIntervalEnum.interval8, - @"INTERVAL_9": THETACThetaRepositoryTimeShiftIntervalEnum.interval9, - @"INTERVAL_10": THETACThetaRepositoryTimeShiftIntervalEnum.interval10 - }, - .fromTheta = @{ - THETACThetaRepositoryTimeShiftIntervalEnum.interval0: @"INTERVAL_0", - THETACThetaRepositoryTimeShiftIntervalEnum.interval1: @"INTERVAL_1", - THETACThetaRepositoryTimeShiftIntervalEnum.interval2: @"INTERVAL_2", - THETACThetaRepositoryTimeShiftIntervalEnum.interval3: @"INTERVAL_3", - THETACThetaRepositoryTimeShiftIntervalEnum.interval4: @"INTERVAL_4", - THETACThetaRepositoryTimeShiftIntervalEnum.interval5: @"INTERVAL_5", - THETACThetaRepositoryTimeShiftIntervalEnum.interval6: @"INTERVAL_6", - THETACThetaRepositoryTimeShiftIntervalEnum.interval7: @"INTERVAL_7", - THETACThetaRepositoryTimeShiftIntervalEnum.interval8: @"INTERVAL_8", - THETACThetaRepositoryTimeShiftIntervalEnum.interval9: @"INTERVAL_9", - THETACThetaRepositoryTimeShiftIntervalEnum.interval10: @"INTERVAL_10" - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSDictionary *timeshiftDic = [rct objectForKey:@"timeShift"]; - if (timeshiftDic) { - opt.timeShift = [[THETACThetaRepositoryTimeShiftSetting alloc] - initWithIsFrontFirst:!isNull([timeshiftDic objectForKey:@"isFrontFirst"]) ? [THETACBoolean numberWithBool:((NSNumber*) [timeshiftDic objectForKey:@"isFrontFirst"]).boolValue] : nil - firstInterval:!isNull([timeshiftDic objectForKey:@"firstInterval"]) ? [TimeShiftCvt.toTheta objectForKey:[timeshiftDic objectForKey:@"firstInterval"]] : nil - secondInterval:!isNull([timeshiftDic objectForKey:@"secondInterval"]) ? [TimeShiftCvt.toTheta objectForKey:[timeshiftDic objectForKey:@"secondInterval"]] : nil]; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.timeShift) { - NSMutableDictionary *timeshift = [NSMutableDictionary dictionary]; - - if (opt.timeShift.isFrontFirst) { - [timeshift setObject:@(((NSNumber *) opt.timeShift.isFrontFirst).boolValue) forKey:@"isFrontFirst"]; - } - - if (opt.timeShift.firstInterval) { - [timeshift setObject:[TimeShiftCvt.fromTheta objectForKey:opt.timeShift.firstInterval] forKey:@"firstInterval"]; - } - - if (opt.timeShift.secondInterval) { - [timeshift setObject:[TimeShiftCvt.fromTheta objectForKey:opt.timeShift.secondInterval] forKey:@"secondInterval"]; - } - - [rct setObject:timeshift forKey:@"timeShift"]; - } - }, - .setTimeShiftOption = ^(NSDictionary* rct, THETACTimeShiftCaptureBuilder *builder) { - NSDictionary *timeshiftDic = [rct objectForKey:@"timeShift"]; - if (timeshiftDic) { - [builder setIsFrontFirstIsFrontFirst:!isNull([timeshiftDic objectForKey:@"isFrontFirst"]) ? [THETACBoolean numberWithBool:((NSNumber*) [timeshiftDic objectForKey:@"isFrontFirst"]).boolValue] : nil]; - [builder setFirstIntervalInterval:!isNull([timeshiftDic objectForKey:@"firstInterval"]) ? [TimeShiftCvt.toTheta objectForKey:[timeshiftDic objectForKey:@"firstInterval"]] : nil]; - [builder setSecondIntervalInterval:!isNull([timeshiftDic objectForKey:@"secondInterval"]) ? [TimeShiftCvt.toTheta objectForKey:[timeshiftDic objectForKey:@"secondInterval"]] : nil]; - } - }, -}; - -/** - * ShootingMethod convertor - */ -static convert_t ShootingMethodEnum = { - .toTheta = @{ - @"NORMAL": THETACThetaRepositoryShootingMethodEnum.normal, - @"INTERVAL": THETACThetaRepositoryShootingMethodEnum.interval, - @"MOVE_INTERVAL": THETACThetaRepositoryShootingMethodEnum.moveInterval, - @"FIXED_INTERVAL": THETACThetaRepositoryShootingMethodEnum.fixedInterval, - @"BRACKET": THETACThetaRepositoryShootingMethodEnum.bracket, - @"COMPOSITE": THETACThetaRepositoryShootingMethodEnum.composite, - @"CONTINUOUS": THETACThetaRepositoryShootingMethodEnum.continuous, - @"TIME_SHIFT": THETACThetaRepositoryShootingMethodEnum.timeShift, - @"BURST": THETACThetaRepositoryShootingMethodEnum.burst, - }, - .fromTheta = @{ - THETACThetaRepositoryShootingMethodEnum.normal: @"NORMAL", - THETACThetaRepositoryShootingMethodEnum.interval: @"INTERVAL", - THETACThetaRepositoryShootingMethodEnum.moveInterval: @"MOVE_INTERVAL", - THETACThetaRepositoryShootingMethodEnum.fixedInterval: @"FIXED_INTERVAL", - THETACThetaRepositoryShootingMethodEnum.bracket: @"BRACKET", - THETACThetaRepositoryShootingMethodEnum.composite: @"COMPOSITE", - THETACThetaRepositoryShootingMethodEnum.continuous: @"CONTINUOUS", - THETACThetaRepositoryShootingMethodEnum.timeShift: @"TIME_SHIFT", - THETACThetaRepositoryShootingMethodEnum.burst: @"BURST", - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ShootingMethodEnum.toTheta objectForKey:[rct objectForKey:@"shootingMethod"]]; - if (val) { - opt.shootingMethod = val; - } - }, - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ShootingMethodEnum.toTheta objectForKey:[rct objectForKey:@"shootingMethod"]]; - if (val) { - opt.shootingMethod = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - id val = [ShootingMethodEnum.fromTheta objectForKey:opt.shootingMethod]; - if (val) { - [rct setObject:val forKey:@"shootingMethod"]; - } - } -}; - -/** - * Username converter - */ -static convert_t UsernameCvt = { - .setToTheta = ^(NSDictionary* rct, THETACThetaRepositoryOptions *opt) { - NSString* val = [rct objectForKey:@"username"]; - if (val) { - opt.username = val; - } - }, - .setFromTheta = ^(NSMutableDictionary* rct, THETACThetaRepositoryOptions *opt) { - if (opt.username) { - [rct setObject:opt.username forKey:@"username"]; - } - } -}; - -/** - * OptionNames converter - */ -static NSDictionary *NameToOptionEnum = @{ - @"AiAutoThumbnail": THETACThetaRepositoryOptionNameEnum.aiautothumbnail, - @"Aperture": THETACThetaRepositoryOptionNameEnum.aperture, - @"Bitrate": THETACThetaRepositoryOptionNameEnum.bitrate, - @"BluetoothPower": THETACThetaRepositoryOptionNameEnum.bluetoothpower, - @"BurstMode": THETACThetaRepositoryOptionNameEnum.burstmode, - @"BurstOption": THETACThetaRepositoryOptionNameEnum.burstoption, - @"CameraControlSource": THETACThetaRepositoryOptionNameEnum.cameracontrolsource, - @"CameraMode": THETACThetaRepositoryOptionNameEnum.cameramode, - @"CaptureInterval": THETACThetaRepositoryOptionNameEnum.captureinterval, - @"CaptureMode": THETACThetaRepositoryOptionNameEnum.capturemode, - @"CaptureNumber": THETACThetaRepositoryOptionNameEnum.capturenumber, - @"ColorTemperature": THETACThetaRepositoryOptionNameEnum.colortemperature, - @"CompositeShootingOutputInterval": THETACThetaRepositoryOptionNameEnum.compositeshootingoutputinterval, - @"CompositeShootingTime": THETACThetaRepositoryOptionNameEnum.compositeshootingtime, - @"ContinuousNumber": THETACThetaRepositoryOptionNameEnum.continuousnumber, - @"DateTimeZone": THETACThetaRepositoryOptionNameEnum.datetimezone, - @"ExposureCompensation": THETACThetaRepositoryOptionNameEnum.exposurecompensation, - @"ExposureDelay": THETACThetaRepositoryOptionNameEnum.exposuredelay, - @"ExposureProgram": THETACThetaRepositoryOptionNameEnum.exposureprogram, - @"FaceDetect": THETACThetaRepositoryOptionNameEnum.facedetect, - @"FileFormat": THETACThetaRepositoryOptionNameEnum.fileformat, - @"Filter": THETACThetaRepositoryOptionNameEnum.filter, - @"Function": THETACThetaRepositoryOptionNameEnum.function, - @"Gain": THETACThetaRepositoryOptionNameEnum.gain, - @"GpsInfo": THETACThetaRepositoryOptionNameEnum.gpsinfo, - @"ImageStitching": THETACThetaRepositoryOptionNameEnum.imagestitching, - @"IsGpsOn": THETACThetaRepositoryOptionNameEnum.isgpson, - @"Iso": THETACThetaRepositoryOptionNameEnum.iso, - @"IsoAutoHighLimit": THETACThetaRepositoryOptionNameEnum.isoautohighlimit, - @"Language": THETACThetaRepositoryOptionNameEnum.language, - @"MaxRecordableTime": THETACThetaRepositoryOptionNameEnum.maxrecordabletime, - @"NetworkType": THETACThetaRepositoryOptionNameEnum.networktype, - @"OffDelay": THETACThetaRepositoryOptionNameEnum.offdelay, - @"Password": THETACThetaRepositoryOptionNameEnum.password, - @"PowerSaving": THETACThetaRepositoryOptionNameEnum.powersaving, - @"Preset": THETACThetaRepositoryOptionNameEnum.preset, - @"PreviewFormat": THETACThetaRepositoryOptionNameEnum.previewformat, - @"Proxy": THETACThetaRepositoryOptionNameEnum.proxy, - @"ShootingMethod": THETACThetaRepositoryOptionNameEnum.shootingmethod, - @"ShutterSpeed": THETACThetaRepositoryOptionNameEnum.shutterspeed, - @"SleepDelay": THETACThetaRepositoryOptionNameEnum.sleepdelay, - @"RemainingPictures": THETACThetaRepositoryOptionNameEnum.remainingpictures, - @"RemainingVideoSeconds": THETACThetaRepositoryOptionNameEnum.remainingvideoseconds, - @"RemainingSpace": THETACThetaRepositoryOptionNameEnum.remainingspace, - @"TimeShift": THETACThetaRepositoryOptionNameEnum.timeshift, - @"TotalSpace": THETACThetaRepositoryOptionNameEnum.totalspace, - @"ShutterVolume": THETACThetaRepositoryOptionNameEnum.shuttervolume, - @"Username": THETACThetaRepositoryOptionNameEnum.username, - @"WhiteBalance": THETACThetaRepositoryOptionNameEnum.whitebalance, - @"WhiteBalanceAutoStrength": THETACThetaRepositoryOptionNameEnum.whitebalanceautostrength, - @"WlanFrequency": THETACThetaRepositoryOptionNameEnum.wlanfrequency -}; - -/** - * OptionNameEnum to OptionName - */ -static NSDictionary *OptionEnumToOption = @{ - @"AiAutoThumbnail": @"aiAutoThumbnail", - @"Aperture": @"aperture", - @"Bitrate": @"bitrate", - @"BluetoothPower": @"bluetoothPower", - @"BurstMode": @"burstMode", - @"BurstOption": @"burstOption", - @"CameraControlSource": @"cameraControlSource", - @"CameraMode": @"cameraMode", - @"CaptureInterval": @"captureInterval", - @"CaptureMode": @"captureMode", - @"CaptureNumber": @"captureNumber", - @"ColorTemperature": @"colorTemperature", - @"CompositeShootingOutputInterval": @"compositeShootingOutputInterval", - @"CompositeShootingTime": @"compositeShootingTime", - @"ContinuousNumber": @"continuousNumber", - @"DateTimeZone": @"dateTimeZone", - @"ExposureCompensation": @"exposureCompensation", - @"ExposureDelay": @"exposureDelay", - @"ExposureProgram": @"exposureProgram", - @"FaceDetect": @"faceDetect", - @"FileFormat": @"fileFormat", - @"Filter": @"filter", - @"Function": @"function", - @"Gain": @"gain", - @"GpsInfo": @"gpsInfo", - @"ImageStitching": @"imageStitching", - @"IsGpsOn": @"isGpsOn", - @"Iso": @"iso", - @"IsoAutoHighLimit": @"isoAutoHighLimit", - @"Language": @"language", - @"MaxRecordableTime": @"maxRecordableTime", - @"NetworkType": @"networkType", - @"OffDelay": @"offDelay", - @"Password": @"password", - @"PowerSaving": @"powerSaving", - @"Preset": @"preset", - @"PreviewFormat": @"previewFormat", - @"Proxy": @"proxy", - @"ShootingMethod": @"shootingMethod", - @"ShutterSpeed": @"shutterSpeed", - @"SleepDelay": @"sleepDelay", - @"RemainingPictures": @"remainingPictures", - @"RemainingVideoSeconds": @"remainingVideoSeconds", - @"RemainingSpace": @"remainingSpace", - @"TimeShift": @"timeShift", - @"TotalSpace": @"totalSpace", - @"ShutterVolume": @"shutterVolume", - @"Username": @"username", - @"WhiteBalance": @"whiteBalance", - @"WhiteBalanceAutoStrength": @"whiteBalanceAutoStrength", - @"WlanFrequency": @"wlanFrequency" -}; - -/** Option converter builder */ -typedef convert_t * (^OptionConverter)(); - -/** - * option converter tables - */ -static NSDictionary *NameToConverter = @{ - @"aiAutoThumbnail": ^{return &AiAutoThumbnailEnum;}, - @"aperture": ^{return &ApertureEnum;}, - @"bitrate": ^{return &BitrateEnum;}, - @"bluetoothPower": ^{return &BluetoothPowerEnum;}, - @"burstMode": ^{return &BurstModeEnum;}, - @"burstOption": ^{return &BurstOptionCvt;}, - @"cameraControlSource": ^{return &CameraControlSourceEnum;}, - @"cameraMode": ^{return &CameraModeEnum;}, - @"captureInterval": ^{return &CaptureIntervalConverter;}, - @"captureMode": ^{return &CaptureModeEnum;}, - @"captureNumber": ^{return &CaptureNumberConverter;}, - @"colorTemperature": ^{return &ColorTemperatureCvt;}, - @"compositeShootingOutputInterval": ^{return &CompositeShootingOutputIntervalCvt;}, - @"compositeShootingTime": ^{return &CompositeShootingTimeCvt;}, - @"continuousNumber": ^{return &ContinuousNumberEnum;}, - @"dateTimeZone": ^{return &DateTimeZoneCvt;}, - @"exposureCompensation": ^{return &ExposureCompensationEnum;}, - @"exposureDelay": ^{return &ExposureDelayEnum;}, - @"exposureProgram": ^{return &ExposureProgramEnum;}, - @"faceDetect": ^{return &FaceDetectEnum;}, - @"fileFormat": ^{return &FileFormatEnum;}, - @"filter": ^{return &FilterEnum;}, - @"function": ^{return &ShootingFunctionEnum;}, - @"gain": ^{return &GainEnum;}, - @"gpsInfo": ^{return &GpsInfoCvt;}, - @"imageStitching": ^{return &ImageStitchingEnum;}, - @"isGpsOn": ^{return &IsGpsOnCvt;}, - @"iso": ^{return &IsoEnum;}, - @"isoAutoHighLimit": ^{return &IsoAutoHighLimitEnum;}, - @"language": ^{return &LanguageEnum;}, - @"maxRecordableTime": ^{return &MaxRecordableTimeEnum;}, - @"networkType": ^{return &NetworkTypeEnum;}, - @"offDelay": ^{return &OffDelayEnum;}, - @"password": ^{return &PasswordCvt;}, - @"powerSaving": ^{return &PowerSavingEnum;}, - @"preset": ^{return &PresetEnum;}, - @"previewFormat": ^{return &PreviewFormatEnum;}, - @"proxy": ^{return &ProxyCvt;}, - @"shootingMethod": ^{return &ShootingMethodEnum;}, - @"shutterSpeed": ^{return &ShutterSpeedEnum;}, - @"sleepDelay": ^{return &SleepDelayEnum;}, - @"remainingPictures": ^{return &RemainingPicturesCvt;}, - @"remainingVideoSeconds": ^{return &RemainingVideoSecondsCvt;}, - @"remainingSpace": ^{return &RemainingSpaceCvt;}, - @"timeShift": ^{return &TimeShiftCvt;}, - @"totalSpace": ^{return &TotalSpaceCvt;}, - @"shutterVolume": ^{return &ShutterVolumeCvt;}, - @"username": ^{return &UsernameCvt;}, - @"whiteBalance": ^{return &WhiteBalanceEnum;}, - @"_gpsTagRecording": ^{return &GpsTagRecordingEnum;}, - @"whiteBalanceAutoStrength": ^{return &WhiteBalanceAutoStrengthEnum;}, - @"wlanFrequency": ^{return &WlanFrequencyEnum;} -}; - -static NSString *EVENT_NAME = @"ThetaFrameEvent"; -static NSString *EVENT_NOTIFY = @"ThetaNotify"; - -THETACDigestAuth* digestAuthToTheta(NSDictionary* objects) -{ - if (!objects) { - return nil; - } - NSString* username = [objects objectForKey:@"username"]; - if (!username) { - return nil; - } - NSString* password = [objects objectForKey:@"password"]; - THETACDigestAuth* digestAuth = [[THETACDigestAuth alloc] initWithUsername:username password:password]; - return digestAuth; -} - -THETACThetaRepositoryConfig* configToTheta(NSDictionary* objects) -{ - if (!objects) { - return nil; - } - THETACThetaRepositoryConfig* config = [[THETACThetaRepositoryConfig alloc] init]; - NSString* datetime = [objects objectForKey:@"dateTime"]; - if (datetime) { - config.dateTime = datetime; - } - id language = [LanguageEnum.toTheta objectForKey:[objects objectForKey:@"language"]]; - if (language) { - config.language = language; - } - id offDelay = [OffDelayEnum.toTheta objectForKey:[objects objectForKey:@"offDelay"]]; - if (offDelay) { - config.offDelay = offDelay; - } - id sleepDelay = [SleepDelayEnum.toTheta objectForKey:[objects objectForKey:@"sleepDelay"]]; - if (sleepDelay) { - config.sleepDelay = sleepDelay; - } - NSNumber* shutterVolume = [objects objectForKey:@"shutterVolume"]; - if (shutterVolume) { - config.shutterVolume = [THETACInt numberWithInt:[shutterVolume intValue]]; - } - - config.clientMode = digestAuthToTheta([objects objectForKey:@"clientMode"]); - - return config; -} - -THETACThetaRepositoryTimeout* timeoutToTheta(NSDictionary* objects) -{ - if (!objects) { - return nil; - } - NSNumber* connectTimeout = [objects objectForKey:@"connectTimeout"]; - NSNumber* requestTimeout = [objects objectForKey:@"requestTimeout"]; - NSNumber* socketTimeout = [objects objectForKey:@"socketTimeout"]; - if (!connectTimeout || !requestTimeout || !socketTimeout) { - return nil; - } - THETACThetaRepositoryTimeout* timeout = [[THETACThetaRepositoryTimeout alloc] - initWithConnectTimeout:[connectTimeout longValue] - requestTimeout:[requestTimeout longLongValue] - socketTimeout:[socketTimeout longLongValue]]; - return timeout; -} - -NSDictionary* fileInfoFromTheta(THETACThetaRepositoryFileInfo* fileInfo) { - NSMutableDictionary *fileInfoObject = [NSMutableDictionary dictionaryWithDictionary:@{ - @"name":fileInfo.name, - @"fileUrl":fileInfo.fileUrl, - @"size":@(fileInfo.size), - @"dateTime":fileInfo.dateTime, - @"thumbnailUrl":fileInfo.thumbnailUrl - }]; - if (fileInfo.dateTimeZone) { - [fileInfoObject setObject:fileInfo.dateTimeZone forKey:@"dateTimeZone"]; - } - if (fileInfo.lat) { - [fileInfoObject setObject:@(fileInfo.lat.floatValue) forKey:@"lat"]; - } - if (fileInfo.lng) { - [fileInfoObject setObject:@(fileInfo.lng.floatValue) forKey:@"lng"]; - } - if (fileInfo.width) { - [fileInfoObject setObject:@(fileInfo.width.intValue) forKey:@"width"]; - } - if (fileInfo.height) { - [fileInfoObject setObject:@(fileInfo.height.intValue) forKey:@"height"]; - } - if (fileInfo.intervalCaptureGroupId) { - [fileInfoObject setObject:fileInfo.intervalCaptureGroupId forKey:@"intervalCaptureGroupId"]; - } - if (fileInfo.compositeShootingGroupId) { - [fileInfoObject setObject:fileInfo.compositeShootingGroupId forKey:@"compositeShootingGroupId"]; - } - if (fileInfo.autoBracketGroupId) { - [fileInfoObject setObject:fileInfo.autoBracketGroupId forKey:@"autoBracketGroupId"]; - } - if (fileInfo.recordTime) { - [fileInfoObject setObject:@(fileInfo.recordTime.intValue) forKey:@"recordTime"]; - } - if (fileInfo.isProcessed) { - [fileInfoObject setObject:@(fileInfo.isProcessed.boolValue) forKey:@"isProcessed"]; - } - if (fileInfo.previewUrl) { - [fileInfoObject setObject:fileInfo.previewUrl forKey:@"previewUrl"]; - } - if (fileInfo.codec) { - [fileInfoObject setObject:fileInfo.codec.name forKey:@"codec"]; - } - if (fileInfo.projectionType) { - [fileInfoObject setObject:fileInfo.projectionType.name forKey:@"projectionType"]; - } - if (fileInfo.continuousShootingGroupId) { - [fileInfoObject setObject:fileInfo.continuousShootingGroupId forKey:@"continuousShootingGroupId"]; - } - if (fileInfo.frameRate) { - [fileInfoObject setObject:@(fileInfo.frameRate.intValue) forKey:@"frameRate"]; - } - if (fileInfo.favorite) { - [fileInfoObject setObject:@(fileInfo.favorite.boolValue) forKey:@"favorite"]; - } - if (fileInfo.imageDescription) { - [fileInfoObject setObject:fileInfo.imageDescription forKey:@"imageDescription"]; - } - if (fileInfo.storageID) { - [fileInfoObject setObject:fileInfo.storageID forKey:@"storageID"]; - } - - return fileInfoObject; -} - -/** - * ThetaClientReactNative implementation - */ -@implementation ThetaClientReactNative { - BOOL hasListeners; -} -RCT_EXPORT_MODULE(ThetaClientReactNative) - -/** - * Will be called when this module's first listener is added. - */ --(void)startObserving { - hasListeners = YES; - // Set up any upstream listeners or background tasks as necessary -} - -/** - * Will be called when this module's last listener is removed, or on dealloc. - */ --(void)stopObserving { - hasListeners = NO; - // Remove upstream listeners, stop unnecessary background tasks -} - -/** - * export constants - */ -- (NSDictionary *)constantsToExport -{ - return @{ @"DEFAULT_EVENT_NAME": EVENT_NAME}; -} - -/** - * supported event name list - */ --(NSArray *)supportedEvents -{ - return @[EVENT_NAME, EVENT_NOTIFY]; -} - -/** - * dispatch queue to send events - */ -- (dispatch_queue_t)methodQueue -{ - return dispatch_queue_create("com.ricoh360.event.queue", DISPATCH_QUEUE_SERIAL); -} - -/** - * require main queue setup - */ -+ (BOOL)requiresMainQueueSetup -{ - return YES; -} - -/** - * initialize ThetaRepository - * @param endPoint endpoint to connect theta - * @param config Configuration of initialize. If null, get from THETA - * @param timeout Timeout of HTTP call - * @param resolve resolver for initilization - * @param rejecter rejecter for initialization - */ -RCT_REMAP_METHOD(initialize, - initializeWithEndpoint:(NSString *)endPoint - withConfig:(NSDictionary*)config - withTimeout:(NSDictionary*)timeout - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - self.theta = nil; - self.photoCaptureBuilder = nil; - self.photoCapture = nil; - self.timeShiftCaptureBuilder = nil; - self.timeShiftCapture = nil; - self.timeShiftCapturing = nil; - self.videoCaptureBuilder = nil; - self.videoCapture = nil; - self.videoCapturing = nil; - self.previewing = NO; - - if (!endPoint) { - endPoint = @"http://192.168.1.1"; - } - NSError *error = nil; - THETACThetaRepositoryCompanion *companion = THETACThetaRepository.companion; - [companion doNewInstanceEndpoint:endPoint - config:configToTheta(config) - timeout:timeoutToTheta(timeout) - completionHandler:^(THETACThetaRepository *repo, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (repo) { - self.theta = repo; - resolve(@(YES)); - } else { - reject(ERROR_CODE_ERROR, @"can not create repository", nil); - } - }]; -} - -/** - * isInitialized - Returns whether it is initialized or not. - * @param resolve is initialized - * @param rejecter - */ -RCT_REMAP_METHOD(isInitialized, - isInitializedWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - resolve(_theta != nil ? @(YES) : @(NO)); -} - -/** - * getThetaModel - Returns the connected THETA model. - * @param resolve for getThetaModel - * @param rejecter - */ -RCT_REMAP_METHOD(getThetaModel, - getThetaModelWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - resolve(self.theta.cameraModel ? self.theta.cameraModel.name : nil); -} - -/** - * getThetaInfo - retrieve ThetaInfo from THETA via repository - * @param resolve resolver for getThetaInfo - * @param rejecter rejecter for getThetaInfo - */ -RCT_REMAP_METHOD(getThetaInfo, - getThetaInfoWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getThetaInfoWithCompletionHandler:^(THETACThetaRepositoryThetaInfo *info, - NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (info) { - NSMutableArray *apiLevelList = [[NSMutableArray alloc] init]; - for (THETACInt *element in info.apiLevel) { - [apiLevelList addObject:@([element intValue])]; - } - NSMutableDictionary *thetaInfoObject = [NSMutableDictionary dictionaryWithDictionary:@{ - @"manufacturer": info.manufacturer, - @"model": info.model, - @"serialNumber": info.serialNumber, - @"wlanMacAddress": info.wlanMacAddress != nil ? info.wlanMacAddress : [NSNull null], - @"bluetoothMacAddress": info.bluetoothMacAddress != nil ? info.bluetoothMacAddress : [NSNull null], - @"firmwareVersion": info.firmwareVersion, - @"supportUrl": info.supportUrl, - @"hasGps": @(info.hasGps), - @"hasGyro": @(info.hasGyro), - @"uptime": @(info.uptime), - @"api": info.api, - @"endpoints": @{ - @"httpPort": @(info.endpoints.httpPort), - @"httpUpdatesPort": @(info.endpoints.httpUpdatesPort), - }, - @"apiLevel": apiLevelList - }]; - if (self.theta.cameraModel) { - [thetaInfoObject setObject:self.theta.cameraModel.name forKey:@"thetaModel"]; - } - resolve(thetaInfoObject); - } else { - reject(ERROR_CODE_ERROR, @"no info", nil); - } - }]; -} - -/** - * getThetaState - retrieve ThetaState from THETA via repository - * @param resolve resolver for getThetaState - * @param rejecter rejecter for getThetaState - */ -RCT_REMAP_METHOD(getThetaState, - getThetaStateWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getThetaStateWithCompletionHandler:^(THETACThetaRepositoryThetaState *state, - NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (state) { - NSMutableArray *cameraErrorList = [[NSMutableArray alloc] init]; - if (state.cameraError != nil) { - for (THETACThetaRepositoryCameraErrorEnum *element in state.cameraError) { - [cameraErrorList addObject:[CameraErrorEnum.fromTheta objectForKey:element]]; - } - } - resolve(@{@"fingerprint": state.fingerprint, - @"batteryLevel": @(state.batteryLevel), - @"storageUri": state.storageUri != nil ? state.storageUri : [NSNull null], - @"storageID": state.storageID != nil ? state.storageID : [NSNull null], - @"captureStatus": [CaptureStatusEnum.fromTheta objectForKey:state.captureStatus], - @"recordedTime": @(state.recordedTime), - @"recordableTime": @(state.recordableTime), - @"capturedPictures": state.capturedPictures != nil ? @([state.capturedPictures intValue]): [NSNull null], - @"compositeShootingElapsedTime": state.compositeShootingElapsedTime != nil ? @([state.compositeShootingElapsedTime intValue]) : [NSNull null], - @"latestFileUrl": state.latestFileUrl, - @"chargingState": [ChargingStateEnum.fromTheta objectForKey:state.chargingState], - @"apiVersion": @(state.apiVersion), - @"isPluginRunning": state.isPluginRunning != nil ? @([state.isPluginRunning boolValue]) : [NSNull null], - @"isPluginWebServer": state.isPluginWebServer != nil ? @([state.isPluginWebServer boolValue]) : [NSNull null], - @"function": state.function != nil ? [ShootingFunctionEnum.fromTheta objectForKey:state.function] : [NSNull null], - @"isMySettingChanged": state.isMySettingChanged != nil? @([state.isMySettingChanged boolValue]) : [NSNull null], - @"currentMicrophone": state.currentMicrophone != nil ? [MicrophoneOptionEnum.fromTheta objectForKey:state.currentMicrophone] : [NSNull null], - @"isSdCard": @(state.isSdCard), - @"cameraError": state.cameraError != nil ? cameraErrorList : [NSNull null], - @"isBatteryInsert": state.isBatteryInsert != nil ? @([state.isBatteryInsert boolValue]) : [NSNull null]}); - } else { - reject(ERROR_CODE_ERROR, @"no state", nil); - } - }]; -} - -/** - * listFiles - retrieve File list from THETA via repository - * @param fileType file type to retrieve - * @param startPosition start position to retrieve - * @param entryCount count to retrieve - * @param storage Desired storage - * @param resolve resolver for listFiles - * @param rejecter rejecter for listFiles - */ -RCT_REMAP_METHOD(listFiles, - listFilesWithFileTypeEnum:(NSString*)fileType - withStartPosition:(int32_t)startPosition - withEntryCount:(int32_t)entryCount - withStorage:(NSString*)storage - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta listFilesFileType:[FileTypeEnum.toTheta objectForKey:fileType] - startPosition:startPosition - entryCount:entryCount - storage:[StorageEnum.toTheta objectForKey:storage] - completionHandler:^(THETACThetaRepositoryThetaFiles *items, - NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (items) { - NSMutableArray *ary = [[NSMutableArray alloc] init]; - for (int i = 0; i < items.fileList.count; i++) { - THETACThetaRepositoryFileInfo *finfo = items.fileList[i]; - [ary addObject:fileInfoFromTheta(finfo)]; - } - resolve(@{@"fileList":ary, - @"totalEntries": @(items.totalEntries)}); - } else { - reject(ERROR_CODE_ERROR, @"no items", nil); - } - }]; -} - -/** - * deleteFiles - delete specified files - * @param fileUrls file url list to delete - * @param resolve resolver for deleteFiles - * @param rejecter rejecter for deleteFiles - */ -RCT_REMAP_METHOD(deleteFiles, - deleteFilesWithFileUrls:(NSArray*)fileUrls - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteFilesFileUrls:fileUrls completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * deleteAllFiles - delete all files - * @param resolve resolver for deleteAllFiles - * @param rejecter rejecter for deleteAllFiles - */ -RCT_REMAP_METHOD(deleteAllFiles, - deleteAllFilesWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteAllFilesWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * deleteAllImageFiles - delete all image files - * @param resolve resolver for deleteAllImageFiles - * @param rejecter rejecter for deleteAllImageFiles - */ -RCT_REMAP_METHOD(deleteAllImageFiles, - deleteAllImageFilesWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteAllImageFilesWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - return; - } - resolve(@(YES)); - }]; -} - -/** - * deleteAllVideoFiles - delete all video files - * @param resolve resolver for deleteAllVideoFiles - * @param rejecter rejecter for deleteAllVideoFiles - */ -RCT_REMAP_METHOD(deleteAllVideoFiles, - deleteAllVideoFilesWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteAllVideoFilesWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * getOptions - get options from THETA via repository - * @param optionNames option name list to get - * @param resolve resolver for getOptions - * @param rejecter rejecter for getOptions - */ -RCT_REMAP_METHOD(getOptions, - getOptionsWithOptionNames:(NSArray*)optionNames - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - NSMutableArray *optionNameList = [[NSMutableArray alloc] init]; - for (id name in optionNames) { - [optionNameList addObject:[NameToOptionEnum objectForKey:name]]; - } - [_theta getOptionsOptionNames:optionNameList - completionHandler:^(THETACThetaRepositoryOptions *options, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (options) { - NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; - for (id name in optionNames) { - id option = [OptionEnumToOption objectForKey:name]; - convert_t *convert = [NameToConverter objectForKey:option](); - if (convert && convert->setFromTheta) { - convert->setFromTheta(results, options); - } - } - resolve(results); - } else { - reject(ERROR_CODE_ERROR, @"no options", nil); - } - }]; -} - -/** - * setOptions - set options to THETA via repository - * @param options options to set - * @param resolve resolver for setOptions - * @param rejecter rejecter for setOptions - */ -RCT_REMAP_METHOD(setOptions, - setOptionsWithOptions:(NSDictionary*)options - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - THETACThetaRepositoryOptions *newoptions = [[THETACThetaRepositoryOptions alloc] init]; - for (id option in [options allKeys]) { - OptionConverter converter = [NameToConverter objectForKey:option]; - if (converter) { - convert_t *convert = converter(); - if (convert && convert->setToTheta) { - convert->setToTheta(options, newoptions); - } - } - } - [_theta setOptionsOptions:newoptions completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * return created data url from [data] - */ --(NSString*)createDataURL:(NSData*) data -{ - static NSMutableString *uri = nil; - static NSUInteger prefix_length = 0; - if (!uri) { - uri = [[NSMutableString alloc] init]; - [uri appendString:@"data:image/jpeg;base64,"]; - prefix_length = [uri length]; - } - NSString *b64 = [data base64EncodedStringWithOptions:0]; - NSRange range = { - .location = prefix_length, - .length = [uri length] - prefix_length - }; - [uri replaceCharactersInRange:range withString:b64]; - b64 = nil; - return uri; -} - -/** - * getLivePreview - retrieve live preview frame from THETA via repository - * @param resolve resolver for getLivePreview - * @param rejecter rejecter for getLivePreview - */ -RCT_REMAP_METHOD(getLivePreview, - getLivePreviewWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - FrameHandler *_frameHandler = - [[FrameHandler alloc] initWith:self withEvent:^(NSData *data) { - if (self->hasListeners) { - @autoreleasepool { - NSString *dataUrl = [self createDataURL:data]; - [self sendEventWithName:EVENT_NAME body:@{@"data":dataUrl}]; - } - } - }]; - self.previewing = YES; - [_theta getLivePreviewFrameHandler:_frameHandler completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * stopLivePreview - stop live previewing - */ -RCT_REMAP_METHOD(stopLivePreview, - stopLivePreview) -{ - self.previewing = NO; -} - -/** - * getPhotoCaptureBuilder - get photo capture builder from repository - */ -RCT_REMAP_METHOD(getPhotoCaptureBuilder, - getPhotoCaptureBuilder) -{ - if (!_theta) { - [NSException raise:ERROR_CODE_ERROR format:MESSAGE_NOT_INIT]; - return; - } - self.photoCaptureBuilder = [_theta getPhotoCaptureBuilder]; -} - -/** - * buildPhotoCapture - build photo capture - * @param options option to take picture - * @param resolve resolver for buildPhotoCapture - * @param rejecter rejecter for buildPhotoCapture - */ -RCT_REMAP_METHOD(buildPhotoCapture, - buildPhotoCaptureWithOptions:(NSDictionary*)options - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.photoCaptureBuilder) { - reject(ERROR_CODE_ERROR, @"no photo capture pbuilder", nil); - return; - } - for (id photoOption in [options allKeys]) { - convert_t *convert = [NameToConverter objectForKey:photoOption](); - if (convert && convert->setPhotoOption) { - convert->setPhotoOption(options, self.photoCaptureBuilder); - } - } - [self.photoCaptureBuilder - buildWithCompletionHandler:^(THETACPhotoCapture *photoCapture, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - return; - } - if (photoCapture) { - self.photoCapture = photoCapture; - self.photoCaptureBuilder = nil; - resolve(@(YES)); - } else { - reject(ERROR_CODE_ERROR, @"no photoCapture", nil); - } - }]; -} - -/** - * takePicture - take a picture with THETA via repository - * @param resolve resolver for takePicture - * @param rejecter rejecter for takePicture - */ -RCT_REMAP_METHOD(takePicture, - takePictureWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.photoCapture) { - reject(ERROR_CODE_ERROR, @"no photoCapture", nil); - return; - } - PhotoCallback *callback = [[PhotoCallback alloc] initWith:self - withResolver:resolve - withRejecter:reject]; - [self.photoCapture takePictureCallback:callback]; -} - -/** - * getTimeShiftCaptureBuilder - get time-shift builder from repository - */ -RCT_REMAP_METHOD(getTimeShiftCaptureBuilder, - getTimeShiftCaptureBuilder) -{ - if (!_theta) { - [NSException raise:ERROR_CODE_ERROR format:MESSAGE_NOT_INIT]; - return; - } - self.timeShiftCaptureBuilder = [_theta getTimeShiftCaptureBuilder]; -} - -/** - * buildTimeShiftCapture - build time-shift capture - * @param options option to execute time-shift - * @param interval interval of checking time-shift status - * @param resolve resolver for buildTimeShiftCapture - * @param rejecter rejecter for buildTimeShiftCapture - */ -RCT_REMAP_METHOD(buildTimeShiftCapture, - buildTimeShiftCaptureWithOptions:(NSDictionary*)options - withInterval:(int)interval - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.timeShiftCaptureBuilder) { - reject(@"error", @"no time-shift capture pbuilder", nil); - return; - } - - for (id option in [options allKeys]) { - convert_t *convert = [NameToConverter objectForKey:option](); - if (convert && convert->setTimeShiftOption) { - convert->setTimeShiftOption(options, self.timeShiftCaptureBuilder); - } - } - - if (interval >= 0) { - [self.timeShiftCaptureBuilder setCheckStatusCommandIntervalTimeMillis: (int64_t)interval]; - } - - [self.timeShiftCaptureBuilder buildWithCompletionHandler:^(THETACTimeShiftCapture *timeShiftCapture, NSError *error) { - if (error) { - reject(@"error", [error localizedDescription], error); - return; - } - if (timeShiftCapture) { - self.timeShiftCapture = timeShiftCapture; - self.timeShiftCaptureBuilder = nil; - resolve(@(YES)); - } else { - reject(@"error", @"no timeShiftCapture", nil); - } - }]; -} - -/** - * startTimeShiftCapture - start time-shift - * @param resolve resolver for startTimeShiftCapture - * @param rejecter rejecter for startTimeShiftCapture - */ -RCT_REMAP_METHOD(startTimeShiftCapture, - startTimeShiftCaptureWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.timeShiftCapture) { - reject(@"error", @"no timeShiftCapture", nil); - return; - } - - TimeShiftStartCallback *callback = [[TimeShiftStartCallback alloc] initWith:self - withProgress:^(float completion) { - [self sendEventWithName:EVENT_NOTIFY - body:toCaptureProgressNotifyParam([NSNumber numberWithFloat:completion])]; - } - withResolver:resolve - withRejecter:reject]; - self.timeShiftCapturing = [self.timeShiftCapture startCaptureCallback:callback]; -} - -/** - * cancelTimeShiftCapture - stop time-shift - * @param resolve resolver for stopTimeShiftCapture - * @param rejecter rejecter for stopTimeShiftCapture - */ -RCT_REMAP_METHOD(cancelTimeShiftCapture, - cancelTimeShiftCaptureWithResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - - if (!self.timeShiftCapturing) { - reject(@"error", @"no timeShiftCapturing", nil); - return; - } - - [self.timeShiftCapturing cancelCapture]; -} - -/** - * getVideoCaptureBuilder - get video capture builder - */ -RCT_REMAP_METHOD(getVideoCaptureBuilder, - getVideoCaptureBuilder) -{ - if (!_theta) { - [NSException raise:ERROR_CODE_ERROR format:MESSAGE_NOT_INIT]; - return; - } - self.videoCaptureBuilder = [_theta getVideoCaptureBuilder]; -} - -/** - * buildVideoCapture - build video capture - * @param options option to capture video - * @param resolve resolver for buildVideoCapture - * @param rejecter rejecter for buildVideoCapture - */ -RCT_REMAP_METHOD(buildVideoCapture, - buildVideoCaptureWithOptions:(NSDictionary*)options - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.videoCaptureBuilder) { - reject(ERROR_CODE_ERROR, @"no video capture builder", nil); - return; - } - for (id videoOption in [options allKeys]) { - convert_t *convert = [NameToConverter objectForKey:videoOption](); - if (convert && convert->setVideoOption) { - convert->setVideoOption(options, self.videoCaptureBuilder); - } - } - [self.videoCaptureBuilder - buildWithCompletionHandler:^(THETACVideoCapture *videoCapture, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, @"videoCapture build failed", error); - return; - } - if (videoCapture) { - self.videoCapture = videoCapture; - self.videoCaptureBuilder = nil; - resolve(@(YES)); - } else { - reject(ERROR_CODE_ERROR, @"no videoCapture", nil); - } - }]; -} - -/** - * startCapture - start capture video - * @param resolve resolver for startCapture - * @param rejecter rejecter for startCapture - */ -RCT_REMAP_METHOD(startCapture, - startCaptureWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - if (!self.videoCapture) { - reject(ERROR_CODE_ERROR, @"no videoCapture", nil); - return; - } - VideoCallback *callback = [[VideoCallback alloc] initWith:self - withResolver:resolve - withRejecter:reject]; - self.videoCapturing = [self.videoCapture startCaptureCallback:callback]; -} - -/** - * stopCapture - stop capturing video - */ -RCT_REMAP_METHOD(stopCapture, - stopCapture) -{ - if (!_theta) { - [NSException raise:ERROR_CODE_ERROR format:MESSAGE_NOT_INIT]; - return; - } - if (!self.videoCapturing) { - // todo warning - return; - } - [self.videoCapturing stopCapture]; -} - -/** - * getMetadata - retrieve meta data from THETA via repository - * @param resolve resolver for getMetadata - * @param rejecter rejecter for getMetadata - */ -RCT_REMAP_METHOD(getMetadata, - getMetadataWithFileUrl:(NSString*)fileUrl - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getMetadataFileUrl:fileUrl - completionHandler:^(THETACKotlinPair *metaData, - NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (metaData) { - NSDictionary* exif = nil; - NSDictionary* xmp = nil; - if (metaData.first) { - exif = @{ - @"exifVersion": metaData.first.exifVersion, - @"dateTime": metaData.first.dateTime, - @"imageWidth": metaData.first.imageWidth, - @"imageLength": metaData.first.imageLength, - @"gpsLatitude": metaData.first.gpsLatitude, - @"gpsLongitude": metaData.first.gpsLongitude - }; - } - if (metaData.second) { - xmp = @{ - @"poseHeadingDegrees": metaData.second.poseHeadingDegrees, - @"fullPanoWidthPixels": @(metaData.second.fullPanoWidthPixels), - @"fullPanoHeightPixels": @(metaData.second.fullPanoHeightPixels) - }; - } - resolve(@{@"exif": exif, @"xmp": xmp}); - } else { - reject(ERROR_CODE_ERROR, @"no metaData", nil); - } - }]; -} - -/** - * reset - reset THETA via repository - * @param resolve resolver for reset - * @param rejecter rejecter for reset - */ -RCT_REMAP_METHOD(reset, - resetWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta resetWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * restoreSettings - restore THETA setting before initialization - * @param resolve resolver for restoreSettings - * @param rejecter rejecter for restoreSettings - */ -RCT_REMAP_METHOD(restoreSettings, - restoreSettingsWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta restoreSettingsWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * stopSelfTimer - stop self timer of THETA - * @param resolve resolver for stopSelfTimer - * @param rejecter rejecter for stopSelfTimer - */ -RCT_REMAP_METHOD(stopSelfTimer, - stopSelfTimerWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta stopSelfTimerWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * convertVideoFormats - convert video format in THETA - * @param fileUrl file url to convert - * @param toLowResolution to lower resolution - * @param applyTopBottomCorrection apply top bottom correction - * @param resolve resolver for convertVideoFormats - * @param rejecter rejecter for convertVideoFormats - */ -RCT_REMAP_METHOD(convertVideoFormats, - convertVideoFormatsWithFileUrl:(NSString *)fileUrl - withToLowResolution:(BOOL)toLowResolution - withApplyTopBottomCorrection:(BOOL)applyTopBottomCorrection - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta convertVideoFormatsFileUrl:fileUrl - toLowResolution:toLowResolution - applyTopBottomCorrection:applyTopBottomCorrection - completionHandler:^(NSString *convertedUrl, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (convertedUrl) { - resolve(convertedUrl); - } else { - reject(ERROR_CODE_ERROR, @"no convertVideo", nil); - } - }]; -} - -/** - * cancelVideoconvert - cancel converting video format in THETA - * @param resolve resolver for cancelVideoconvert - * @param rejecter rejecter for cancelVideoconvert - */ -RCT_REMAP_METHOD(cancelVideoConvert, - cancelVideoConvertWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta cancelVideoConvertWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * finishWlan - finish wireless lan - * @param resolve resolver for finishWlan - * @param rejecter rejecter for finishWlan - */ -RCT_REMAP_METHOD(finishWlan, - finishWlanWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta finishWlanWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * listAccessPoints - list access points - * @param resolve resolver for listAccessPoints - * @param rejecter rejecter for listAccessPoints - */ -RCT_REMAP_METHOD(listAccessPoints, - listAccessPointsWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta listAccessPointsWithCompletionHandler:^(NSArray *accessPointList, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (accessPointList) { - NSMutableArray *ary = [[NSMutableArray alloc] init]; - for (int i = 0; i < accessPointList.count; i++) { - THETACThetaRepositoryAccessPoint *apinfo = accessPointList[i]; - - NSMutableDictionary *optionsDic = [[NSMutableDictionary alloc] init]; - convert_t *convert = &ProxyCvt; - if (convert->setFromTheta) { - THETACThetaRepositoryOptions *options = [[THETACThetaRepositoryOptions alloc] init]; - [options setProxy:apinfo.proxy]; - convert->setFromTheta(optionsDic, options); - } - - NSMutableDictionary *item = [[NSMutableDictionary alloc] init]; - [item setObject:apinfo.ssid forKey:@"ssid"]; - [item setObject:@(apinfo.ssidStealth) forKey:@"ssidStealth"]; - [item setObject:[AuthModeEnum.fromTheta objectForKey:apinfo.authMode]forKey:@"authMode"]; - [item setObject:@(apinfo.connectionPriority) forKey:@"connectionPriority"]; - [item setObject:@(apinfo.usingDhcp) forKey:@"usingDhcp"]; - if (apinfo.ipAddress) { - [item setObject:apinfo.ipAddress forKey:@"ipAddress"]; - } - if (apinfo.subnetMask) { - [item setObject:apinfo.subnetMask forKey:@"subnetMask"]; - } - if (apinfo.defaultGateway) { - [item setObject:apinfo.defaultGateway forKey:@"defaultGateway"]; - } - if ([optionsDic objectForKey:@"proxy"]) { - [item setObject:[optionsDic objectForKey:@"proxy"] forKey:@"proxy"]; - } - [ary addObject:item]; - } - resolve(ary); - } else { - reject(ERROR_CODE_ERROR, @"no access point", nil); - } - }]; -} - -/** - * setAccessPointDynamically - set access point with dhcp - * @param ssid ssid to connect - * @param ssidStealth ssid is stealth or not - * @param authMode auth mode to connect - * @param password password to connect with auth - * @param connectionPriority connection priority - * @param proxy Proxy information to be used for the access point. - * @param resolve resolver for setAccessPointDynamically - * @param rejecter rejecter for setAccessPointDynamically - */ -RCT_REMAP_METHOD(setAccessPointDynamically, - setAccessPointDynamicallyWithSsid:(NSString *)ssid - withSsidStealth:(BOOL)ssidStealth - withAuthMode:(NSString *)authMode - withPassword:(NSString *)password - withConnectionPrioryty:(int32_t)connectionPriority - withProxy:(NSDictionary *)proxy - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta setAccessPointDynamicallySsid:ssid - ssidStealth:ssidStealth - authMode:[AuthModeEnum.toTheta objectForKey:authMode] - password:password - connectionPriority:connectionPriority - proxy:convertDictionaryToProxy(proxy) - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * setAccessPointStatically - set access point with static connection info - * @param ssid ssid to connect - * @param ssidStealth ssid is stealth or not - * @param authMode auth mode to connect - * @param password password to connect with auth - * @param connectionPriority connection priority - * @param ipAddress static ipaddress to connect - * @param subnetMask subnet mask for ip address - * @param defaultGateway default gateway address - * @param proxy Proxy information to be used for the access point. - * @param resolve resolver for setAccessPointStatically - * @param rejecter rejecter for setAccessPointStatically - */ -RCT_REMAP_METHOD(setAccessPointStatically, - setAccessPointStaticallyWithSsid:(NSString *)ssid - withSsidStealth:(BOOL)ssidStealth - withAuthMode:(NSString *)authMode - withPassword:(NSString *)password - withConnectionPrioryty:(int32_t)connectionPriority - withIpAddress:(NSString *)ipAddress - withSubnetMask:(NSString *)subnetMask - withDefaultGateway:(NSString *)defaultGateway - withProxy:(NSDictionary *)proxy - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta setAccessPointStaticallySsid:ssid - ssidStealth:ssidStealth - authMode:[AuthModeEnum.toTheta objectForKey:authMode] - password:password - connectionPriority:connectionPriority - ipAddress:ipAddress - subnetMask:subnetMask - defaultGateway:defaultGateway - proxy:convertDictionaryToProxy(proxy) - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * deleteAccessPoint - delete access point related ssid - * @param ssid ssid to delete - * @param resolve resolver for setAccessPointStaticcaly - * @param rejecter rejecter for setAccessPointStaticcaly - */ -RCT_REMAP_METHOD(deleteAccessPoint, - deleteAccessPointWithSsid:(NSString *)ssid - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteAccessPointSsid:ssid - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/// convert NSDictionary to THETACThetaRepositoryProxy -/// @param proxyDic NSDictionary of proxy -static THETACThetaRepositoryProxy* convertDictionaryToProxy(NSDictionary *proxyDic) { - THETACThetaRepositoryOptions *options = [[THETACThetaRepositoryOptions alloc] init]; - convert_t *convert = &ProxyCvt; - if (convert->setToTheta) { - NSMutableDictionary *optionsDic = [[NSMutableDictionary alloc] init]; - [optionsDic setObject:proxyDic forKey:@"proxy"]; - convert->setToTheta(optionsDic, options); - } - return options.proxy; -} - -/** - * convertOptionsFromTheta - convert resposnse of getMySetting - * @param results conversion results - * @param options response of getMySetting - * @return error during conversion - */ -static NSError* convertOptionsFromTheta(NSMutableDictionary* results, THETACThetaRepositoryOptions* options) -{ - THETACKotlinArray* optionNames = THETACThetaRepositoryOptionNameEnum.values; - for (int i = 0; i < optionNames.size; i++) { - THETACThetaRepositoryOptionNameEnum* optionName = [optionNames getIndex:i]; - NSError* error = nil; - id value = [options getValueName:optionName error:&error]; - if (error) { - return error; - } - if (value != nil) { - NSString* name = [OptionEnumToOption objectForKey:optionName.name]; - convert_t *convert = [NameToConverter objectForKey:name](); - if (convert && convert->setFromTheta) { - convert->setFromTheta(results, options); - } - } - } - return nil; -} - -/** - * getMySetting - acquires the shooting properties ( just for Theta V and later ) - * @param captureMode the target shooting mode - * @param resolve resolver for getMySetting - * @param rejecter rejecter for getMySetting - */ -RCT_REMAP_METHOD(getMySetting, - getMySettingWithCaptureMode:(NSString *)captureMode - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getMySettingCaptureMode:[CaptureModeEnum.toTheta objectForKey:captureMode] - completionHandler:^(THETACThetaRepositoryOptions *options, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (options) { - NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; - NSError *convertError = convertOptionsFromTheta(results, options); - if (convertError) { - reject(ERROR_CODE_ERROR, [error localizedDescription], convertError); - } - resolve(results); - } else { - reject(ERROR_CODE_ERROR, @"no options", nil); - } - }]; -} - -/** - * getMySetting - acquires the shooting properties ( just for Theta S and SC ) - * @param optionNames list of option names to acquire - * @param resolve resolver for getMySetting - * @param rejecter rejecter for getMySetting - */ -RCT_REMAP_METHOD(getMySettingFromOldModel, - getMySettingWithOptionNames:(NSArray*)optionNames - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - NSMutableArray *optionNameList = [[NSMutableArray alloc] init]; - for (id name in optionNames) { - [optionNameList addObject:[NameToOptionEnum objectForKey:name]]; - } - [_theta getMySettingOptionNames:optionNameList - completionHandler:^(THETACThetaRepositoryOptions *options, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (options) { - NSMutableDictionary *results = [[NSMutableDictionary alloc] init]; - for (id name in optionNames) { - id option = [OptionEnumToOption objectForKey:name]; - OptionConverter converter = [NameToConverter objectForKey:option]; - if (converter) { - convert_t *convert = converter(); - if (convert && convert->setFromTheta) { - convert->setFromTheta(results, options); - } - } - } - resolve(results); - } else { - reject(ERROR_CODE_ERROR, @"no options", nil); - } - }]; -} - -/** - * setMySetting - registers shooting conditions in My Settings - * @param captureMode the target shooting mode. RICOH THETA S and SC do not support My Settings in video capture mode - * @param options registered to My Settings - * @param resolve resolver for setMySetting - * @param rejecter rejecter for setMySetting - */ -RCT_REMAP_METHOD(setMySetting, - setMySettingWithCaptureMode:(NSString *)captureMode - setMySettingWithOptions:(NSDictionary*)options - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - THETACThetaRepositoryOptions *newoptions = [[THETACThetaRepositoryOptions alloc] init]; - for (id option in [options allKeys]) { - OptionConverter converter = [NameToConverter objectForKey:option]; - if (converter) { - convert_t *convert = converter(); - if (convert && convert->setToTheta) { - convert->setToTheta(options, newoptions); - } - } - } - [_theta setMySettingCaptureMode:[CaptureModeEnum.toTheta objectForKey:captureMode] - options:newoptions - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * deleteMySetting - delete shooting conditions in My Settings - * @param captureMode the target shooting mode - * @param resolve resolver for deleteMySetting - * @param rejecter rejecter for deleteMySetting - */ -RCT_REMAP_METHOD(deleteMySetting, - deleteMySettingWithCaptureMode:(NSString *)captureMode - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta deleteMySettingCaptureMode:[CaptureModeEnum.toTheta objectForKey:captureMode] - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * listPlugins - acquires a list of installed plugins - * @param resolve resolver for listPlugins - * @param rejecter rejecter for listPlugins - */ -RCT_REMAP_METHOD(listPlugins, - listPluginsWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta listPluginsWithCompletionHandler:^(NSArray *pluginList, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (pluginList) { - NSMutableArray *ary = [[NSMutableArray alloc] init]; - for (int i = 0; i < pluginList.count; i++) { - THETACThetaRepositoryPluginInfo *plugininfo = pluginList[i]; - [ary addObject: @{ - @"name": plugininfo.name, - @"packageName": plugininfo.packageName, - @"version": plugininfo.version, - @"isPreInstalled": @(plugininfo.isPreInstalled), - @"isRunning": @(plugininfo.isRunning), - @"isForeground": @(plugininfo.isForeground), - @"isBoot": @(plugininfo.isBoot), - @"hasWebServer": @(plugininfo.hasWebServer), - @"exitStatus": plugininfo.exitStatus, - @"message": plugininfo.message - }]; - } - resolve(ary); - } else { - reject(ERROR_CODE_ERROR, @"no plugin", nil); - } - }]; -} - -/** - * setPlugin - sets the installed plugin for boot - * @param packageName package name of the target plugin - * @param resolve resolver for setPlugin - * @param rejecter rejecter for setPlugin - */ -RCT_REMAP_METHOD(setPlugin, - setPluginPackageName:(NSString *)packageName - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta setPluginPackageName:packageName - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * startPlugin - start the plugin specified by the packageName - * @param packageName package name of the target plugin - * @param resolve resolver for startPlugin - * @param rejecter rejecter for startPlugin - */ -RCT_REMAP_METHOD(startPlugin, - startPluginPackageName:(NSString *)packageName - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta startPluginPackageName:packageName - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - - } else { - resolve(@(YES)); - } - }]; -} - -/** - * stopPlugin - stop the running plugin - * @param resolve resolver for stopPlugin - * @param rejecter rejecter for stopPlugin - */ -RCT_REMAP_METHOD(stopPlugin, - stopPluginWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta stopPluginWithCompletionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - - } else { - resolve(@(YES)); - } - }]; -} - -/** - * getPluginLicense - acquires the license for the installed plugin - * @param packageName package name of the target plugin - * @param resolve resolver for getPluginLicense - * @param rejecter rejecter for getPluginLicense - */ -RCT_REMAP_METHOD(getPluginLicense, - getPluginLicensePackageName:(NSString *)packageName - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getPluginLicensePackageName:packageName - completionHandler:^(NSString *pluginLicense, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - - } else if (pluginLicense) { - resolve(pluginLicense); - } else { - reject(ERROR_CODE_ERROR, @"no plugin lincense", nil); - } - }]; -} - -/** - * getPluginOrders - return the plugin orders - * @param resolve resolver for getPluginOrders - * @param rejecter rejecter for getPluginOrders - */ -RCT_REMAP_METHOD(getPluginOrders, - getPluginOrdersWithResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta getPluginOrdersWithCompletionHandler:^(NSArray *pluginOrders, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else if (pluginOrders) { - resolve(pluginOrders); - } else { - reject(ERROR_CODE_ERROR, @"no plugin orders", nil); - } - }]; -} - -/** - * setPluginOrders - sets the plugin orders - * @param list of package names of plugins - * @param resolve resolver for setPluginOrders - * @param rejecter rejecter for setPluginOrders - */ -RCT_REMAP_METHOD(setPluginOrders, - setPluginOrdersPlugins:(NSArray *) plugins - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta setPluginOrdersPlugins:plugins - completionHandler:^(NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(@(YES)); - } - }]; -} - -/** - * setBluetoothDevice - register uuid of a BLE device - * @param uuid uuid to set - * @param resolve resolver for setAccessPointStaticcaly - * @param rejecter rejecter for setAccessPointStaticcaly - */ -RCT_REMAP_METHOD(setBluetoothDevice, - setBluetoothDeviceWithUuid:(NSString *)uuid - withResolver:(RCTPromiseResolveBlock)resolve - withRejecter:(RCTPromiseRejectBlock)reject) -{ - if (!_theta) { - reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil); - return; - } - [_theta setBluetoothDeviceUuid:uuid - completionHandler:^(NSString *deviceName, NSError *error) { - if (error) { - reject(ERROR_CODE_ERROR, [error localizedDescription], error); - } else { - resolve(deviceName); - } - }]; -} - -// Don't compile this code when we build for the old architecture. -#ifdef RCT_NEW_ARCH_ENABLED -- (std::shared_ptr)getTurboModule: -(const facebook::react::ObjCTurboModule::InitParams &)params -{ - return std::make_shared(params); -} -#endif - -@end diff --git a/react-native/ios/ThetaClientReactNative.swift b/react-native/ios/ThetaClientReactNative.swift new file mode 100644 index 0000000000..525fadf6b0 --- /dev/null +++ b/react-native/ios/ThetaClientReactNative.swift @@ -0,0 +1,1337 @@ +import THETAClient + +let ERROR_CODE_ERROR = "error" +let MESSAGE_NOT_INIT = "Not initialized." +let MESSAGE_NO_RESULT = "No result." +let MESSAGE_NO_ARGUMENT = "No Argument." +let MESSAGE_NO_PHOTO_CAPTURE = "No photoCapture." +let MESSAGE_NO_PHOTO_CAPTURE_BUILDER = "no photo capture builder." +let MESSAGE_NO_TIMESHIFT_CAPTURE = "No timeShiftCapture." +let MESSAGE_NO_TIMESHIFT_CAPTURE_BUILDER = "no time-shift capture builder." +let MESSAGE_NO_TIMESHIFT_CAPTURING = "no timeShiftCapturing." +let MESSAGE_NO_VIDEO_CAPTURE = "No videoCapture." +let MESSAGE_NO_VIDEO_CAPTURE_BUILDER = "no video capture builder." +let MESSAGE_NO_VIDEO_CAPTURING = "no videoCapturing." + +@objc(ThetaClientReactNative) +class ThetaClientReactNative: RCTEventEmitter { + var thetaRepository: ThetaRepository? + var previewing = false + var photoCaptureBuilder: PhotoCapture.Builder? + var photoCapture: PhotoCapture? + var timeShiftCaptureBuilder: TimeShiftCapture.Builder? + var timeShiftCapture: TimeShiftCapture? + var timeShiftCapturing: TimeShiftCapturing? + var videoCaptureBuilder: VideoCapture.Builder? + var videoCapture: VideoCapture? + var videoCapturing: VideoCapturing? + + static let EVENT_FRAME = "ThetaFrameEvent" + static let EVENT_NOTIFY = "ThetaNotify" + + static let NOTIFY_TIMESHIFT_PROGRESS = "TIME-SHIFT-PROGRESS" + static let NOTIFY_VIDEO_CAPTURE_STOP_ERROR = "VIDEO-CAPTURE-STOP-ERROR" + + @objc + override func supportedEvents() -> [String]! { + return [ThetaClientReactNative.EVENT_FRAME, ThetaClientReactNative.EVENT_NOTIFY] + } + + @objc + override static func requiresMainQueueSetup() -> Bool { + return true + } + + @objc + override func constantsToExport() -> [AnyHashable: Any]! { + return [ + "DEFAULT_EVENT_NAME": ThetaClientReactNative.EVENT_FRAME, + ] + } + + @objc(initialize:withConfig:withTimeout:withResolver:withRejecter:) + func initialize( + endPoint: String?, + config: [AnyHashable: Any]?, + timeout: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + thetaRepository = nil + photoCaptureBuilder = nil + photoCapture = nil + timeShiftCaptureBuilder = nil + timeShiftCapture = nil + timeShiftCapturing = nil + videoCaptureBuilder = nil + videoCapture = nil + videoCapturing = nil + previewing = false + + Task { + let configParams: ThetaRepository.Config? = { + if let config = config as? [String: Any] { + return toConfig(params: config) + } + return nil + }() + let timeoutParams: ThetaRepository.Timeout? = { + if let timeout = timeout as? [String: Any] { + return toTimeout(params: timeout) + } + return nil + }() + + let endpoint = endPoint ?? "http://192.168.1.1" + ThetaRepository.Companion.shared.doNewInstance( + endpoint: endpoint, + config: configParams, + timeout: timeoutParams + ) { thetaRepository, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + self.thetaRepository = thetaRepository + resolve(true) + } + } + } + } + + @objc(isInitialized:withRejecter:) + func isInitialized( + resolve: RCTPromiseResolveBlock, + reject _: RCTPromiseRejectBlock + ) { + resolve(thetaRepository != nil) + } + + @objc(getThetaModel:withRejecter:) + func getThetaModel( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + resolve(thetaRepository.cameraModel?.name) + } + + @objc(getThetaInfo:withRejecter:) + func getThetaInfo( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + thetaRepository.getThetaInfo { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + var info = convertResult(thetaInfo: response) + if let cameraModel = thetaRepository.cameraModel { + info[KEY_THETA_MODEL] = cameraModel.name + } + resolve(info) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(getThetaState:withRejecter:) + func getThetaState( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + thetaRepository.getThetaState { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + let state = convertResult(thetaState: response) + resolve(state) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(listFiles:withStartPosition:withEntryCount:withStorage:withResolver:withRejecter:) + func listFiles( + fileType: String, + startPosition: Int, + entryCount: Int, + storage: String?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard + let fileTypeVal = getEnumValue( + values: ThetaRepository.FileTypeEnum.values(), name: fileType + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let storageVal = getEnumValue( + values: ThetaRepository.StorageEnum.values(), name: storage ?? "" + ) + + thetaRepository.listFiles( + fileType: fileTypeVal, + startPosition: Int32(startPosition), + entryCount: Int32(entryCount), + storage: storageVal + ) { files, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let files { + let resultList = convertResult(fileInfoList: files.fileList) + let resultMap = [ + KEY_FILE_LIST: resultList, + KEY_TOTAL_ENTRIES: files.totalEntries, + ] + resolve(resultMap) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(deleteFiles:withResolver:withRejecter:) + func deleteFiles( + fileUrls: [Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let urls = fileUrls as? [String] else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + thetaRepository.deleteFiles(fileUrls: urls) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(deleteAllFiles:withRejecter:) + func deleteAllFiles( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.deleteAllFiles { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(deleteAllImageFiles:withRejecter:) + func deleteAllImageFiles( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.deleteAllImageFiles { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(deleteAllVideoFiles:withRejecter:) + func deleteAllVideoFiles( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.deleteAllVideoFiles { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(getOptions:withResolver:withRejecter:) + func getOptions( + optionNames: [Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let optionNames = optionNames as? [String] else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let params = convertGetOptionsParam(params: optionNames) + thetaRepository.getOptions( + optionNames: params + ) { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + resolve(convertResult(options: response)) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(setOptions:withResolver:withRejecter:) + func setOptions( + options: [AnyHashable: Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let options = options as? [String: Any] else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + let params = convertSetOptionsParam(params: options) + + thetaRepository.setOptions( + options: params + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(getLivePreview:withRejecter:) + func getLivePreview( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + class FrameHandler: KotlinSuspendFunction1 { + let thetaClientReactNative: ThetaClientReactNative + static let FrameInterval = CFTimeInterval(1.0 / 10.0) + var last: CFTimeInterval = 0 + + init(_ thetaClientReactNative: ThetaClientReactNative) { + self.thetaClientReactNative = thetaClientReactNative + } + + func invoke(p1: Any?) async throws -> Any? { + let now = CACurrentMediaTime() + if now - last > Self.FrameInterval { + autoreleasepool { + if let frameData = p1 as? KotlinPair { + let nsData = PlatformKt.frameFrom( + packet: frameData + ) + let encodeString = nsData.base64EncodedString() + let dataUrl = "data:image/jpeg;base64," + encodeString + thetaClientReactNative.sendEvent( + withName: ThetaClientReactNative.EVENT_FRAME, + body: ["data": dataUrl] + ) + } + } + } + return thetaClientReactNative.previewing + } + } + + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + let frameHandler = FrameHandler(self) + previewing = true + thetaRepository.getLivePreview(frameHandler: frameHandler) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(stopLivePreview:withRejecter:) + func stopLivePreview( + resolve: RCTPromiseResolveBlock, + reject _: RCTPromiseRejectBlock + ) { + previewing = false + resolve(nil) + } + + @objc(getPhotoCaptureBuilder:withRejecter:) + func getPhotoCaptureBuilder( + resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + photoCaptureBuilder = thetaRepository.getPhotoCaptureBuilder() + resolve(nil) + } + + @objc(buildPhotoCapture:withResolver:withRejecter:) + func buildPhotoCapture( + options: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let photoCaptureBuilder else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_PHOTO_CAPTURE_BUILDER, nil) + return + } + + if let options = options as? [String: Any] { + setCaptureBuilderParams(params: options, builder: photoCaptureBuilder) + setPhotoCaptureBuilderParams(params: options, builder: photoCaptureBuilder) + } + photoCaptureBuilder.build { capture, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let capture { + self.photoCapture = capture + self.photoCaptureBuilder = nil + resolve(true) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_PHOTO_CAPTURE, nil) + } + } + } + + @objc(takePicture:withRejecter:) + func takePicture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let photoCapture else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_PHOTO_CAPTURE, nil) + return + } + + class Callback: PhotoCaptureTakePictureCallback { + let callback: (_ url: String?, _ error: Error?) -> Void + init(_ callback: @escaping (_ url: String?, _ error: Error?) -> Void) { + self.callback = callback + } + + func onSuccess(fileUrl: String) { + callback(fileUrl, nil) + } + + func onProgress(completion _: Float) {} + + func onError(exception: ThetaRepository.ThetaRepositoryException) { + callback(nil, exception.asError()) + } + } + + photoCapture.takePicture( + callback: Callback { url, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + self.photoCapture = nil + resolve(url) + } + }) + } + + @objc(getTimeShiftCaptureBuilder:withRejecter:) + func getTimeShiftCaptureBuilder( + resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + timeShiftCaptureBuilder = thetaRepository.getTimeShiftCaptureBuilder() + resolve(nil) + } + + @objc(buildTimeShiftCapture:withResolver:withRejecter:) + func buildTimeShiftCapture( + options: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftCaptureBuilder else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_CAPTURE_BUILDER, nil) + return + } + + if let options = options as? [String: Any] { + setCaptureBuilderParams(params: options, builder: timeShiftCaptureBuilder) + setTimeShiftCaptureBuilderParams(params: options, builder: timeShiftCaptureBuilder) + } + timeShiftCaptureBuilder.build { capture, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let capture { + self.timeShiftCapture = capture + self.timeShiftCaptureBuilder = nil + resolve(true) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_CAPTURE, nil) + } + } + } + + @objc(startTimeShiftCapture:withRejecter:) + func startTimeShiftCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftCapture else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_CAPTURE, nil) + return + } + + class Callback: TimeShiftCaptureStartCaptureCallback { + let callback: (_ url: String?, _ error: Error?) -> Void + weak var client: ThetaClientReactNative? + init( + _ callback: @escaping (_ url: String?, _ error: Error?) -> Void, + client: ThetaClientReactNative + ) { + self.callback = callback + self.client = client + } + + func onError(exception: ThetaRepository.ThetaRepositoryException) { + callback(nil, exception.asError()) + } + + func onProgress(completion: Float) { + client?.sendEvent( + withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_TIMESHIFT_PROGRESS, + params: toCaptureProgressNotifyParam(value: completion) + ) + ) + } + + func onSuccess(fileUrl_ fileUrl: String?) { + callback(fileUrl, nil) + } + } + + timeShiftCapturing = timeShiftCapture.startCapture( + callback: Callback( + { url, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(url) + } + }, client: self + )) + } + + @objc(cancelTimeShiftCapture:withRejecter:) + func cancelTimeShiftCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftCapturing else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_CAPTURING, nil) + return + } + timeShiftCapturing.cancelCapture() + resolve(nil) + } + + @objc(getVideoCaptureBuilder:withRejecter:) + func getVideoCaptureBuilder( + resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + videoCaptureBuilder = thetaRepository.getVideoCaptureBuilder() + resolve(nil) + } + + @objc(buildVideoCapture:withResolver:withRejecter:) + func buildVideoCapture( + options: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let videoCaptureBuilder else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_VIDEO_CAPTURE_BUILDER, nil) + return + } + + if let options = options as? [String: Any] { + setCaptureBuilderParams(params: options, builder: videoCaptureBuilder) + setVideoCaptureBuilderParams(params: options, builder: videoCaptureBuilder) + } + videoCaptureBuilder.build { capture, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let capture { + self.videoCapture = capture + self.videoCaptureBuilder = nil + resolve(true) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_VIDEO_CAPTURE, nil) + } + } + } + + @objc(startVideoCapture:withRejecter:) + func startVideoCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let videoCapture else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_VIDEO_CAPTURE, nil) + return + } + class Callback: VideoCaptureStartCaptureCallback { + let callback: (_ url: String?, _ error: Error?) -> Void + weak var client: ThetaClientReactNative? + init( + _ callback: @escaping (_ url: String?, _ error: Error?) -> Void, + client: ThetaClientReactNative + ) { + self.callback = callback + self.client = client + } + + func onCaptureCompleted(fileUrl: String?) { + callback(fileUrl, nil) + } + + func onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + callback(nil, exception.asError()) + } + + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + let error = exception.asError() + client?.sendEvent( + withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_VIDEO_CAPTURE_STOP_ERROR, + params: toMessageNotifyParam(value: error.localizedDescription) + ) + ) + } + } + + videoCapturing = videoCapture.startCapture( + callback: Callback( + { url, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + self.videoCapture = nil + resolve(url) + } + }, client: self + )) + } + + @objc(stopVideoCapture:withRejecter:) + func stopVideoCapture( + resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let videoCapturing else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_VIDEO_CAPTURING, nil) + return + } + videoCapturing.stopCapture() + resolve(nil) + } + + @objc(getMetadata:withResolver:withRejecter:) + func getMetadata( + fileUrl: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.getMetadata( + fileUrl: fileUrl + ) { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + resolve(convertResult(metadata: response)) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(reset:withRejecter:) + func reset( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.reset { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(restoreSettings:withRejecter:) + func restoreSettings( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.restoreSettings { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(stopSelfTimer:withRejecter:) + func stopSelfTimer( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.stopSelfTimer { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc( + convertVideoFormats:withToLowResolution:withApplyTopBottomCorrection:withResolver: + withRejecter: + ) + func convertVideoFormats( + fileUrl: String, + toLowResolution: Bool, + applyTopBottomCorrection: Bool, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.convertVideoFormats( + fileUrl: fileUrl, + toLowResolution: toLowResolution, + applyTopBottomCorrection: applyTopBottomCorrection + ) { fileUrl, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let fileUrl { + resolve(fileUrl) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(cancelVideoConvert:withRejecter:) + func cancelVideoConvert( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.cancelVideoConvert { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(finishWlan:withRejecter:) + func finishWlan( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.finishWlan { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(listAccessPoints:withRejecter:) + func listAccessPoints( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.listAccessPoints { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + var list = convertResult(accessPointList: response) + resolve(list) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc( + setAccessPointDynamically: + withSsidStealth: + withAuthMode: + withPassword: + withConnectionPriority: + withProxy: + withResolver: + withRejecter: + ) + func setAccessPointDynamically( + ssid: String, + ssidStealth: Bool, + authMode: String, + password: String, + connectionPriority: Int, + proxy: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard + let authMode = getEnumValue( + values: ThetaRepository.AuthModeEnum.values(), name: authMode + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let proxyParam: ThetaRepository.Proxy? = { + if let proxy = proxy as? [String: Any] { + return toProxy(params: proxy) + } + return nil + }() + thetaRepository.setAccessPointDynamically( + ssid: ssid, + ssidStealth: ssidStealth, + authMode: authMode, + password: password, + connectionPriority: Int32(connectionPriority), + proxy: proxyParam + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc( + setAccessPointStatically: + withSsidStealth: + withAuthMode: + withPassword: + withConnectionPriority: + withIpAddress: + withSubnetMask: + withDefaultGateway: + withProxy: + withResolver: + withRejecter: + ) + func setAccessPointStatically( + ssid: String, + ssidStealth: Bool, + authMode: String, + password: String?, + connectionPriority: Int, + ipAddress: String, + subnetMask: String, + defaultGateway: String, + proxy: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard + let authMode = getEnumValue( + values: ThetaRepository.AuthModeEnum.values(), name: authMode + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let proxyParam: ThetaRepository.Proxy? = { + if let proxy = proxy as? [String: Any] { + return toProxy(params: proxy) + } + return nil + }() + thetaRepository.setAccessPointStatically( + ssid: ssid, + ssidStealth: ssidStealth, + authMode: authMode, + password: password, + connectionPriority: Int32(connectionPriority), + ipAddress: ipAddress, + subnetMask: subnetMask, + defaultGateway: defaultGateway, + proxy: proxyParam + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(deleteAccessPoint:withResolver:withRejecter:) + func deleteAccessPoint( + ssid: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.deleteAccessPoint( + ssid: ssid + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(getMySetting:withResolver:withRejecter:) + func getMySetting( + captureMode: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard + let captureMode = getEnumValue( + values: ThetaRepository.CaptureModeEnum.values(), name: captureMode + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + thetaRepository.getMySetting( + captureMode: captureMode + ) { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + resolve(convertResult(options: response)) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(getMySettingFromOldModel:withResolver:withRejecter:) + func getMySettingFromOldModel( + optionNames: [Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let optionNames = optionNames as? [String] else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let params = convertGetOptionsParam(params: optionNames) + thetaRepository.getMySetting( + optionNames: params + ) { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + resolve(convertResult(options: response)) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(setMySetting:withOptions:withResolver:withRejecter:) + func setMySetting( + captureMode: String, + options: [AnyHashable: Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let options = options as? [String: Any], + let captureMode = getEnumValue( + values: ThetaRepository.CaptureModeEnum.values(), name: captureMode + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + let params = convertSetOptionsParam(params: options) + thetaRepository.setMySetting( + captureMode: captureMode, + options: params + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(deleteMySetting:withResolver:withRejecter:) + func deleteMySetting( + captureMode: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard + let captureMode = getEnumValue( + values: ThetaRepository.CaptureModeEnum.values(), name: captureMode + ) + else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + thetaRepository.deleteMySetting( + captureMode: captureMode + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(listPlugins:withRejecter:) + func listPlugins( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + thetaRepository.listPlugins { pluginList, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let pluginList { + resolve(toPluginInfosResult(pluginInfoList: pluginList)) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(setPlugin:withResolver:withRejecter:) + func setPlugin( + packageName: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.setPlugin(packageName: packageName) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(startPlugin:withResolver:withRejecter:) + func startPlugin( + packageName: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.startPlugin(packageName: packageName) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(stopPlugin:withRejecter:) + func stopPlugin( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.stopPlugin { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(getPluginLicense:withResolver:withRejecter:) + func getPluginLicense( + packageName: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.getPluginLicense( + packageName: packageName + ) { response, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let response { + resolve(response) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(getPluginOrders:withRejecter:) + func getPluginOrders( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + thetaRepository.getPluginOrders { plugins, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let plugins { + resolve(plugins) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } + + @objc(setPluginOrders:withResolver:withRejecter:) + func setPluginOrders( + plugins: [Any], + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let plugins = plugins as? [String] else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) + return + } + + thetaRepository.setPluginOrders( + plugins: plugins + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + + @objc(setBluetoothDevice:withResolver:withRejecter:) + func setBluetoothDevice( + uuid: String, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + thetaRepository.setBluetoothDevice(uuid: uuid) { deviceName, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let deviceName { + resolve(deviceName) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_RESULT, nil) + } + } + } +} diff --git a/react-native/ios/ThetaClientReactNative.xcodeproj/project.pbxproj b/react-native/ios/ThetaClientReactNative.xcodeproj/project.pbxproj index b07f1ebba6..56773f8595 100644 --- a/react-native/ios/ThetaClientReactNative.xcodeproj/project.pbxproj +++ b/react-native/ios/ThetaClientReactNative.xcodeproj/project.pbxproj @@ -7,7 +7,7 @@ objects = { /* Begin PBXBuildFile section */ - B77C84CC1F5427487336C198 /* Pods_ThetaClientReactNative.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F8133CD481CB10AE963C64B5 /* Pods_ThetaClientReactNative.framework */; }; + F4FF95D7245B92E800C19C63 /* ThetaClientReactNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = F4FF95D6245B92E800C19C63 /* ThetaClientReactNative.swift */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -24,11 +24,10 @@ /* Begin PBXFileReference section */ 134814201AA4EA6300B7C361 /* libThetaClientReactNative.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libThetaClientReactNative.a; sourceTree = BUILT_PRODUCTS_DIR; }; - 1CE81D83B7C8A97F7FAAFA59 /* Pods-ThetaClientReactNative.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientReactNative.debug.xcconfig"; path = "Target Support Files/Pods-ThetaClientReactNative/Pods-ThetaClientReactNative.debug.xcconfig"; sourceTree = ""; }; - 5C2D0EDC139486FC8778E75C /* Pods-ThetaClientReactNative.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientReactNative.release.xcconfig"; path = "Target Support Files/Pods-ThetaClientReactNative/Pods-ThetaClientReactNative.release.xcconfig"; sourceTree = ""; }; - 9636E7362A4C5D4200D8882D /* ThetaClientReactNative.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ThetaClientReactNative.mm; sourceTree = ""; }; - B3E7B5881CC2AC0600A0062D /* ThetaClientReactNative.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ThetaClientReactNative.h; sourceTree = ""; }; - F8133CD481CB10AE963C64B5 /* Pods_ThetaClientReactNative.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ThetaClientReactNative.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + B3E7B5891CC2AC0600A0062D /* ThetaClientReactNative.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ThetaClientReactNative.m; sourceTree = ""; }; + C94009C82A8A163900250425 /* ConvertUtil.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConvertUtil.swift; sourceTree = ""; }; + F4FF95D5245B92E700C19C63 /* ThetaClientReactNative-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ThetaClientReactNative-Bridging-Header.h"; sourceTree = ""; }; + F4FF95D6245B92E800C19C63 /* ThetaClientReactNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThetaClientReactNative.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -36,7 +35,6 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - B77C84CC1F5427487336C198 /* Pods_ThetaClientReactNative.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -54,32 +52,14 @@ 58B511D21A9E6C8500147676 = { isa = PBXGroup; children = ( - B3E7B5881CC2AC0600A0062D /* ThetaClientReactNative.h */, - 9636E7362A4C5D4200D8882D /* ThetaClientReactNative.mm */, + C94009C82A8A163900250425 /* ConvertUtil.swift */, + F4FF95D6245B92E800C19C63 /* ThetaClientReactNative.swift */, + B3E7B5891CC2AC0600A0062D /* ThetaClientReactNative.m */, + F4FF95D5245B92E700C19C63 /* ThetaClientReactNative-Bridging-Header.h */, 134814211AA4EA7D00B7C361 /* Products */, - 5CCD595E6E5DC2E4DE6A3B39 /* Pods */, - E14C170C6E7AD09CDE7ABA0D /* Frameworks */, ); sourceTree = ""; }; - 5CCD595E6E5DC2E4DE6A3B39 /* Pods */ = { - isa = PBXGroup; - children = ( - 1CE81D83B7C8A97F7FAAFA59 /* Pods-ThetaClientReactNative.debug.xcconfig */, - 5C2D0EDC139486FC8778E75C /* Pods-ThetaClientReactNative.release.xcconfig */, - ); - name = Pods; - path = Pods; - sourceTree = ""; - }; - E14C170C6E7AD09CDE7ABA0D /* Frameworks */ = { - isa = PBXGroup; - children = ( - F8133CD481CB10AE963C64B5 /* Pods_ThetaClientReactNative.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -87,7 +67,6 @@ isa = PBXNativeTarget; buildConfigurationList = 58B511EF1A9E6C8500147676 /* Build configuration list for PBXNativeTarget "ThetaClientReactNative" */; buildPhases = ( - B2BB4366CF970D90D18E56FC /* [CP] Check Pods Manifest.lock */, 58B511D71A9E6C8500147676 /* Sources */, 58B511D81A9E6C8500147676 /* Frameworks */, 58B511D91A9E6C8500147676 /* CopyFiles */, @@ -133,36 +112,12 @@ }; /* End PBXProject section */ -/* Begin PBXShellScriptBuildPhase section */ - B2BB4366CF970D90D18E56FC /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-ThetaClientReactNative-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; -/* End PBXShellScriptBuildPhase section */ - /* Begin PBXSourcesBuildPhase section */ 58B511D71A9E6C8500147676 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + F4FF95D7245B92E800C19C63 /* ThetaClientReactNative.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -266,7 +221,6 @@ }; 58B511F01A9E6C8500147676 /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 1CE81D83B7C8A97F7FAAFA59 /* Pods-ThetaClientReactNative.debug.xcconfig */; buildSettings = { HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -278,12 +232,14 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = ThetaClientReactNative; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "ThetaClientReactNative-Bridging-Header.h"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; }; name = Debug; }; 58B511F11A9E6C8500147676 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 5C2D0EDC139486FC8778E75C /* Pods-ThetaClientReactNative.release.xcconfig */; buildSettings = { HEADER_SEARCH_PATHS = ( "$(inherited)", @@ -295,6 +251,8 @@ OTHER_LDFLAGS = "-ObjC"; PRODUCT_NAME = ThetaClientReactNative; SKIP_INSTALL = YES; + SWIFT_OBJC_BRIDGING_HEADER = "ThetaClientReactNative-Bridging-Header.h"; + SWIFT_VERSION = 5.0; }; name = Release; }; diff --git a/react-native/package.json b/react-native/package.json index c60c26f9f3..1a3b4843ae 100644 --- a/react-native/package.json +++ b/react-native/package.json @@ -14,7 +14,7 @@ "ios", "cpp", "*.podspec", - "!lib/typescript/example", + "!lib/typescript/verification-tool", "!ios/build", "!android/build", "!android/gradle", @@ -28,11 +28,13 @@ ], "scripts": { "test": "jest", - "typescript": "tsc --noEmit", + "typecheck": "tsc --noEmit", "lint": "eslint \"**/*.{js,ts,tsx}\"", - "prepare": "bob build", + "prepack": "bob build", "release": "release-it", - "bootstrap": "yarn install" + "verification-tool": "yarn --cwd verification-tool", + "bootstrap": "yarn verification-tool && yarn install && yarn verification-tool pods", + "clean": "del-cli android/build verification-tool/android/build verification-tool/android/app/build verification-tool/ios/build" }, "keywords": [ "react-native", @@ -51,8 +53,7 @@ "registry": "https://registry.npmjs.org/" }, "devDependencies": { - "@arkweid/lefthook": "^0.7.7", - "@commitlint/config-conventional": "^17.3.0", + "@commitlint/config-conventional": "^17.0.3", "@react-native-community/eslint-config": "^3.0.2", "@release-it/conventional-changelog": "^5.0.0", "@types/jest": "^29.2.4", @@ -67,7 +68,7 @@ "pod-install": "^0.1.0", "prettier": "^2.8.1", "react": "18.2.0", - "react-native": "0.70.6", + "react-native": "0.71.7", "react-native-builder-bob": "^0.20.3", "release-it": "^15.5.1", "typescript": "^4.9.4" @@ -79,9 +80,14 @@ "react": "*", "react-native": "*" }, + "engines": { + "node": ">= 16.0.0" + }, + "packageManager": "^yarn@1.22.15", "jest": { "preset": "react-native", "modulePathIgnorePatterns": [ + "/verification-tool/node_modules", "/lib/" ] }, diff --git a/react-native/src/__mocks__/react-native.ts b/react-native/src/__mocks__/react-native.ts index 38198ade09..257d7b671b 100644 --- a/react-native/src/__mocks__/react-native.ts +++ b/react-native/src/__mocks__/react-native.ts @@ -10,6 +10,10 @@ export const NativeModules = { buildTimeShiftCapture: jest.fn(), startTimeShiftCapture: jest.fn(), cancelTimeShiftCapture: jest.fn(), + getVideoCaptureBuilder: jest.fn(), + buildVideoCapture: jest.fn(), + startVideoCapture: jest.fn(), + stopVideoCapture: jest.fn(), }, }; diff --git a/react-native/src/__tests__/capture/photo-capture.test.ts b/react-native/src/__tests__/capture/photo-capture.test.ts index 3ba4b8d9c5..ee24131f64 100644 --- a/react-native/src/__tests__/capture/photo-capture.test.ts +++ b/react-native/src/__tests__/capture/photo-capture.test.ts @@ -21,7 +21,7 @@ describe('photo capture', () => { test('getPhotoCaptureBuilder', async () => { const builder = getPhotoCaptureBuilder(); - expect(thetaClient.getPhotoCaptureBuilder).toHaveBeenCalled(); + // expect(thetaClient.getPhotoCaptureBuilder).toHaveBeenCalled(); expect(builder.options).toBeDefined(); builder.setFileFormat(PhotoFileFormatEnum.IMAGE_11K); diff --git a/react-native/src/__tests__/capture/time-shift-capture.test.ts b/react-native/src/__tests__/capture/time-shift-capture.test.ts index 75c14acc9f..fb8197a81a 100644 --- a/react-native/src/__tests__/capture/time-shift-capture.test.ts +++ b/react-native/src/__tests__/capture/time-shift-capture.test.ts @@ -52,8 +52,8 @@ describe('time shift capture', () => { let isCallBuild = false; jest.mocked(thetaClient.buildTimeShiftCapture).mockImplementation( - jest.fn(async (options, interval) => { - expect(interval).toBe(1); + jest.fn(async (options) => { + expect(options._capture_interval).toBe(1); expect(options.timeShift?.isFrontFirst).toBeTruthy(); expect(options.timeShift?.firstInterval).toBe( TimeShiftIntervalEnum.INTERVAL_1 @@ -83,8 +83,8 @@ describe('time shift capture', () => { expect(builder.interval).toBeUndefined(); jest.mocked(thetaClient.buildTimeShiftCapture).mockImplementation( - jest.fn(async (options, interval) => { - expect(interval).toBe(-1); + jest.fn(async (options) => { + expect(options._capture_interval).toBe(-1); expect(options.timeShift).toBeUndefined(); }) ); diff --git a/react-native/src/__tests__/capture/video-capture.test.ts b/react-native/src/__tests__/capture/video-capture.test.ts new file mode 100644 index 0000000000..988b93fb2b --- /dev/null +++ b/react-native/src/__tests__/capture/video-capture.test.ts @@ -0,0 +1,207 @@ +import { NativeModules } from 'react-native'; +import { getVideoCaptureBuilder, initialize } from '../../theta-repository'; +import { + BaseNotify, + NotifyController, +} from '../../theta-repository/notify-controller'; +import { NativeEventEmitter_addListener } from '../../__mocks__/react-native'; +import { + MaxRecordableTimeEnum, + VideoFileFormatEnum, +} from '../../theta-repository/options'; + +describe('video capture', () => { + const thetaClient = NativeModules.ThetaClientReactNative; + + beforeEach(() => { + jest.clearAllMocks(); + NotifyController.instance.release(); + }); + + afterEach(() => { + thetaClient.initialize = jest.fn(); + thetaClient.buildVideoCapture = jest.fn(); + thetaClient.startVideoCapture = jest.fn(); + thetaClient.stopVideoCapture = jest.fn(); + NotifyController.instance.release(); + }); + + test('getVideoCaptureBuilder', async () => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getVideoCaptureBuilder(); + expect(builder.options).toBeDefined(); + + builder.setFileFormat(VideoFileFormatEnum.VIDEO_4K); + builder.setMaxRecordableTime(MaxRecordableTimeEnum.RECORDABLE_TIME_1500); + + expect(builder.options.fileFormat).toBe(VideoFileFormatEnum.VIDEO_4K); + expect(builder.options.maxRecordableTime).toBe( + MaxRecordableTimeEnum.RECORDABLE_TIME_1500 + ); + + let isCallBuild = false; + jest.mocked(thetaClient.buildVideoCapture).mockImplementation( + jest.fn(async (options) => { + expect(options.fileFormat).toBe(VideoFileFormatEnum.VIDEO_4K); + expect(options.maxRecordableTime).toBe( + MaxRecordableTimeEnum.RECORDABLE_TIME_1500 + ); + isCallBuild = true; + }) + ); + + const capture = await builder.build(); + expect(capture).toBeDefined(); + expect(capture.notify).toBeDefined(); + expect(isCallBuild).toBeTruthy(); + }); + + test('startCapture', async () => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + await initialize(); + const builder = getVideoCaptureBuilder(); + jest + .mocked(thetaClient.buildVideoCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.MP4'; + jest.mocked(thetaClient.startVideoCapture).mockImplementation( + jest.fn(async () => { + return testUrl; + }) + ); + + const capture = await builder.build(); + const fileUrl = await capture.startCapture(); + expect(fileUrl).toBe(testUrl); + expect(NotifyController.instance.notifyList.size).toBe(0); + }); + + test('stopCapture', (done) => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getVideoCaptureBuilder(); + jest + .mocked(thetaClient.buildVideoCapture) + .mockImplementation(jest.fn(async () => {})); + jest.mocked(thetaClient.startVideoCapture).mockImplementation( + jest.fn(async () => { + return null; + }) + ); + + builder.build().then((capture) => { + capture.startCapture().then((value) => { + expect(value).toBeUndefined(); + done(); + }); + capture.stopCapture(); + expect(thetaClient.stopVideoCapture).toHaveBeenCalled(); + }); + }); + + test('exception startCapture', (done) => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getVideoCaptureBuilder(); + jest + .mocked(thetaClient.buildVideoCapture) + .mockImplementation(jest.fn(async () => {})); + jest.mocked(thetaClient.startVideoCapture).mockImplementation( + jest.fn(async () => { + throw 'error'; + }) + ); + + builder.build().then((capture) => { + capture + .startCapture() + .then(() => { + expect(true).toBeFalsy(); + }) + .catch((error) => { + expect(error).toBe('error'); + done(); + }); + }); + }); + + test('stop error events', async () => { + let notifyCallback: (notify: BaseNotify) => void = () => { + expect(true).toBeFalsy(); + }; + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn((_, callback) => { + notifyCallback = callback; + return { + remove: jest.fn(), + }; + }) + ); + + await initialize(); + const builder = getVideoCaptureBuilder(); + jest + .mocked(thetaClient.buildVideoCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.MP4'; + + const sendStopError = (message: string) => { + notifyCallback({ + name: 'VIDEO-CAPTURE-STOP-ERROR', + params: { + message, + }, + }); + }; + + jest.mocked(thetaClient.startVideoCapture).mockImplementation( + jest.fn(async () => { + sendStopError('stop error'); + return testUrl; + }) + ); + + const capture = await builder.build(); + let isOnStopError = false; + const fileUrl = await capture.startCapture((error) => { + expect(error.message).toBe('stop error'); + isOnStopError = true; + }); + expect(fileUrl).toBe(testUrl); + + let done: (value: unknown) => void; + const promise = new Promise((resolve) => { + done = resolve; + }); + + setTimeout(() => { + expect(NotifyController.instance.notifyList.size).toBe(0); + expect(isOnStopError).toBeTruthy(); + done(0); + }, 1); + + return promise; + }); +}); diff --git a/react-native/src/__tests__/theta-repository/list-files.test.ts b/react-native/src/__tests__/theta-repository/list-files.test.ts index 9618260b25..40b9a9adf4 100644 --- a/react-native/src/__tests__/theta-repository/list-files.test.ts +++ b/react-native/src/__tests__/theta-repository/list-files.test.ts @@ -40,6 +40,7 @@ describe('listFiles', () => { afterEach(() => { thetaClient.initialize = jest.fn(); + thetaClient.listFiles = jest.fn(); }); function createFileInfo(no: number): FileInfo { @@ -91,48 +92,70 @@ describe('listFiles', () => { ); }); - test('Call listFiles StorageEnum', async () => { + test.each([ + [StorageEnum.INTERNAL, 'INTERNAL'], + [StorageEnum.SD, 'SD'], + [StorageEnum.CURRENT, 'CURRENT'], + ])('Call listFiles StorageEnum', async (storageEnum, value) => { + jest.mocked(thetaClient.listFiles).mockImplementation( + jest.fn(async (_fileTypeEnum, _startPosition, _entryCount, _storage) => { + expect(_fileTypeEnum).toBe(fileType); + expect(_startPosition).toBe(startPosition); + expect(_entryCount).toBe(entryCount); + expect(_storage).toBe(storageEnum); + return createThetaFiles(_entryCount); + }) + ); + const fileType = FileTypeEnum.IMAGE; const startPosition = 0; const entryCount = 1; - - storageEnumArray.forEach(async (item) => { - const result = await listFiles( - fileType, - startPosition, - entryCount, - item[0] - ); - expect(result.totalEntries).toBe(entryCount); - expect(thetaClient.listFiles).toHaveBeenCalledWith( - fileType, - startPosition, - entryCount, - item[1] - ); - }); + const result = await listFiles( + fileType, + startPosition, + entryCount, + storageEnum + ); + expect(result.totalEntries).toBe(entryCount); + expect(thetaClient.listFiles).toHaveBeenCalledWith( + fileType, + startPosition, + entryCount, + value + ); }); - test('Call listFiles FileTypeEnum', async () => { + test.each([ + [FileTypeEnum.IMAGE, 'IMAGE'], + [FileTypeEnum.VIDEO, 'VIDEO'], + [FileTypeEnum.ALL, 'ALL'], + ])('Call listFiles FileTypeEnum', async (fileTypeEnum, value) => { const startPosition = 0; const entryCount = 1; const storage = StorageEnum.INTERNAL; - fileTypeEnumArray.forEach(async (item) => { - const result = await listFiles( - item[0], - startPosition, - entryCount, - storage - ); - expect(result.totalEntries).toBe(entryCount); - expect(thetaClient.listFiles).toHaveBeenCalledWith( - item[1], - startPosition, - entryCount, - storage - ); - }); + jest.mocked(thetaClient.listFiles).mockImplementation( + jest.fn(async (_fileTypeEnum, _startPosition, _entryCount, _storage) => { + expect(_fileTypeEnum).toBe(fileTypeEnum); + expect(_startPosition).toBe(startPosition); + expect(_entryCount).toBe(entryCount); + return createThetaFiles(_entryCount); + }) + ); + + const result = await listFiles( + fileTypeEnum, + startPosition, + entryCount, + storage + ); + expect(result.totalEntries).toBe(entryCount); + expect(thetaClient.listFiles).toHaveBeenCalledWith( + value, + startPosition, + entryCount, + storage + ); }); test('FileTypeEnum length', () => { diff --git a/react-native/src/capture/photo-capture.ts b/react-native/src/capture/photo-capture.ts index 2ae1f689ca..3c9a936d96 100644 --- a/react-native/src/capture/photo-capture.ts +++ b/react-native/src/capture/photo-capture.ts @@ -66,8 +66,14 @@ export class PhotoCaptureBuilder extends CaptureBuilder { * @return promise of PhotoCapture instance */ build(): Promise { - return ThetaClientReactNative.buildPhotoCapture(this.options).then( - () => new PhotoCapture() - ); + return new Promise(async (resolve, reject) => { + try { + await ThetaClientReactNative.getPhotoCaptureBuilder(); + await ThetaClientReactNative.buildPhotoCapture(this.options); + resolve(new PhotoCapture()); + } catch (error) { + reject(error); + } + }); } } diff --git a/react-native/src/capture/time-shift-capture.ts b/react-native/src/capture/time-shift-capture.ts index a2de718979..33febef0e4 100644 --- a/react-native/src/capture/time-shift-capture.ts +++ b/react-native/src/capture/time-shift-capture.ts @@ -134,9 +134,19 @@ export class TimeShiftCaptureBuilder extends CaptureBuilder { - return ThetaClientReactNative.buildTimeShiftCapture( - this.options, - this.interval ?? -1 - ).then(() => new TimeShiftCapture(NotifyController.instance)); + let params = { + ...this.options, + // Cannot pass negative values in IOS, use objects + _capture_interval: this.interval ?? -1, + }; + return new Promise(async (resolve, reject) => { + try { + await ThetaClientReactNative.getTimeShiftCaptureBuilder(); + await ThetaClientReactNative.buildTimeShiftCapture(params); + resolve(new TimeShiftCapture(NotifyController.instance)); + } catch (error) { + reject(error); + } + }); } } diff --git a/react-native/src/capture/video-capture.ts b/react-native/src/capture/video-capture.ts index 9cc3f651f2..2de3933270 100644 --- a/react-native/src/capture/video-capture.ts +++ b/react-native/src/capture/video-capture.ts @@ -5,24 +5,61 @@ import type { } from '../theta-repository/options'; import { NativeModules } from 'react-native'; +import { + BaseNotify, + NotifyController, +} from '../theta-repository/notify-controller'; const ThetaClientReactNative = NativeModules.ThetaClientReactNative; +const NOTIFY_NAME = 'VIDEO-CAPTURE-STOP-ERROR'; + +interface CaptureStopErrorNotify extends BaseNotify { + params?: { + message: string; + }; +} + /** * VideoCapture class */ export class VideoCapture { + notify: NotifyController; + constructor(notify: NotifyController) { + this.notify = notify; + } + /** * start video capture + * @param onStopFailed the block for error of stopCapture * @return promise of captured file url */ - startCapture(): Promise { - return ThetaClientReactNative.startCapture(); + startCapture( + onStopFailed?: (error: any) => void + ): Promise { + if (onStopFailed) { + this.notify.addNotify(NOTIFY_NAME, (event: CaptureStopErrorNotify) => { + onStopFailed(event.params); + }); + } + return new Promise(async (resolve, reject) => { + await ThetaClientReactNative.startVideoCapture() + .then((result?: string) => { + resolve(result ?? undefined); + }) + .catch((error: any) => { + reject(error); + }) + .finally(() => { + this.notify.removeNotify(NOTIFY_NAME); + }); + }); } + /** * stop video capture */ stopCapture() { - ThetaClientReactNative.stopCapture(); + ThetaClientReactNative.stopVideoCapture(); } } @@ -62,8 +99,14 @@ export class VideoCaptureBuilder extends CaptureBuilder { * @return promise of VideoCapture instance */ build(): Promise { - return ThetaClientReactNative.buildVideoCapture(this.options).then( - () => new VideoCapture() - ); + return new Promise(async (resolve, reject) => { + try { + await ThetaClientReactNative.getVideoCaptureBuilder(); + await ThetaClientReactNative.buildVideoCapture(this.options); + resolve(new VideoCapture(NotifyController.instance)); + } catch (error) { + reject(error); + } + }); } } diff --git a/react-native/src/theta-repository/theta-repository.ts b/react-native/src/theta-repository/theta-repository.ts index a4f32e90d6..00e330c1c2 100644 --- a/react-native/src/theta-repository/theta-repository.ts +++ b/react-native/src/theta-repository/theta-repository.ts @@ -69,7 +69,6 @@ export function reset(): Promise { * @return created PhotoCaptureBuilder */ export function getPhotoCaptureBuilder(): PhotoCaptureBuilder { - ThetaClientReactNative.getPhotoCaptureBuilder(); return new PhotoCaptureBuilder(); } @@ -80,7 +79,6 @@ export function getPhotoCaptureBuilder(): PhotoCaptureBuilder { * @return created TimeShiftCaptureBuilder */ export function getTimeShiftCaptureBuilder(): TimeShiftCaptureBuilder { - ThetaClientReactNative.getTimeShiftCaptureBuilder(); return new TimeShiftCaptureBuilder(); } @@ -91,7 +89,6 @@ export function getTimeShiftCaptureBuilder(): TimeShiftCaptureBuilder { * @return created VideoCaptureBuilder instance */ export function getVideoCaptureBuilder(): VideoCaptureBuilder { - ThetaClientReactNative.getVideoCaptureBuilder(); return new VideoCaptureBuilder(); } diff --git a/react-native/theta-client-react-native.podspec b/react-native/theta-client-react-native.podspec index 4ba2f38f0d..96b372cfc4 100644 --- a/react-native/theta-client-react-native.podspec +++ b/react-native/theta-client-react-native.podspec @@ -14,7 +14,7 @@ Pod::Spec.new do |s| s.platforms = { :ios => "15.0" } s.source = { :git => "git@github.com:ricohapi/theta-client.git", :tag => "#{s.version}" } - s.source_files = "ios/**/*.{h,m,mm}" + s.source_files = "ios/**/*.{h,m,mm,swift}" s.dependency "React-Core" s.dependency "THETAClient", "1.3.1" diff --git a/react-native/tsconfig.build.json b/react-native/tsconfig.build.json index 5e2385ab5e..4f64cecb99 100644 --- a/react-native/tsconfig.build.json +++ b/react-native/tsconfig.build.json @@ -1,4 +1,5 @@ { "extends": "./tsconfig", + "exclude": ["verification-tool"] } diff --git a/react-native/verification-tool/.bundle/config b/react-native/verification-tool/.bundle/config new file mode 100644 index 0000000000..848943bb52 --- /dev/null +++ b/react-native/verification-tool/.bundle/config @@ -0,0 +1,2 @@ +BUNDLE_PATH: "vendor/bundle" +BUNDLE_FORCE_RUBY_PLATFORM: 1 diff --git a/react-native/verification-tool/.watchmanconfig b/react-native/verification-tool/.watchmanconfig new file mode 100644 index 0000000000..9e26dfeeb6 --- /dev/null +++ b/react-native/verification-tool/.watchmanconfig @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/react-native/verification-tool/Gemfile b/react-native/verification-tool/Gemfile new file mode 100644 index 0000000000..1142b1b209 --- /dev/null +++ b/react-native/verification-tool/Gemfile @@ -0,0 +1,6 @@ +source 'https://rubygems.org' + +# You may use http://rbenv.org/ or https://rvm.io/ to install and use this version +ruby '>= 2.6.10' + +gem 'cocoapods', '>= 1.11.3' diff --git a/react-native/verification-tool/android/app/build.gradle b/react-native/verification-tool/android/app/build.gradle new file mode 100644 index 0000000000..d5316bcf8d --- /dev/null +++ b/react-native/verification-tool/android/app/build.gradle @@ -0,0 +1,170 @@ +apply plugin: "com.android.application" +apply plugin: "com.facebook.react" + +import com.android.build.OutputFile + +/** + * This is the configuration block to customize your React Native Android app. + * By default you don't need to apply any configuration, just uncomment the lines you need. + */ +react { + /* Folders */ + // The root of your project, i.e. where "package.json" lives. Default is '..' + // root = file("../") + // The folder where the react-native NPM package is. Default is ../node_modules/react-native + // reactNativeDir = file("../node_modules/react-native") + // The folder where the react-native Codegen package is. Default is ../node_modules/react-native-codegen + // codegenDir = file("../node_modules/react-native-codegen") + // The cli.js file which is the React Native CLI entrypoint. Default is ../node_modules/react-native/cli.js + // cliFile = file("../node_modules/react-native/cli.js") + + /* Variants */ + // The list of variants to that are debuggable. For those we're going to + // skip the bundling of the JS bundle and the assets. By default is just 'debug'. + // If you add flavors like lite, prod, etc. you'll have to list your debuggableVariants. + // debuggableVariants = ["liteDebug", "prodDebug"] + + /* Bundling */ + // A list containing the node command and its flags. Default is just 'node'. + // nodeExecutableAndArgs = ["node"] + // + // The command to run when bundling. By default is 'bundle' + // bundleCommand = "ram-bundle" + // + // The path to the CLI configuration file. Default is empty. + // bundleConfig = file(../rn-cli.config.js) + // + // The name of the generated asset file containing your JS bundle + // bundleAssetName = "MyApplication.android.bundle" + // + // The entry file for bundle generation. Default is 'index.android.js' or 'index.js' + // entryFile = file("../js/MyApplication.android.js") + // + // A list of extra flags to pass to the 'bundle' commands. + // See https://github.com/react-native-community/cli/blob/main/docs/commands.md#bundle + // extraPackagerArgs = [] + + /* Hermes Commands */ + // The hermes compiler command to run. By default it is 'hermesc' + // hermesCommand = "$rootDir/my-custom-hermesc/bin/hermesc" + // + // The list of flags to pass to the Hermes compiler. By default is "-O", "-output-source-map" + // hermesFlags = ["-O", "-output-source-map"] +} + +/** + * Set this to true to create four separate APKs instead of one, + * one for each native architecture. This is useful if you don't + * use App Bundles (https://developer.android.com/guide/app-bundle/) + * and want to have separate APKs to upload to the Play Store. + */ +def enableSeparateBuildPerCPUArchitecture = false + +/** + * Set this to true to Run Proguard on Release builds to minify the Java bytecode. + */ +def enableProguardInReleaseBuilds = false + +/** + * The preferred build flavor of JavaScriptCore (JSC) + * + * For example, to use the international variant, you can use: + * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` + * + * The international variant includes ICU i18n library and necessary data + * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that + * give correct results when using with locales other than en-US. Note that + * this variant is about 6MiB larger per architecture than default. + */ +def jscFlavor = 'org.webkit:android-jsc:+' + +/** + * Private function to get the list of Native Architectures you want to build. + * This reads the value from reactNativeArchitectures in your gradle.properties + * file and works together with the --active-arch-only flag of react-native run-android. + */ +def reactNativeArchitectures() { + def value = project.getProperties().get("reactNativeArchitectures") + return value ? value.split(",") : ["armeabi-v7a", "x86", "x86_64", "arm64-v8a"] +} + +android { + ndkVersion rootProject.ext.ndkVersion + + compileSdkVersion rootProject.ext.compileSdkVersion + + namespace "com.thetaclientverificationtool" + defaultConfig { + applicationId "com.thetaclientverificationtool" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + } + + splits { + abi { + reset() + enable enableSeparateBuildPerCPUArchitecture + universalApk false // If true, also generate a universal APK + include (*reactNativeArchitectures()) + } + } + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword 'android' + keyAlias 'androiddebugkey' + keyPassword 'android' + } + } + buildTypes { + debug { + signingConfig signingConfigs.debug + } + release { + // Caution! In production, you need to generate your own keystore file. + // see https://reactnative.dev/docs/signed-apk-android. + signingConfig signingConfigs.debug + minifyEnabled enableProguardInReleaseBuilds + proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" + } + } + + // applicationVariants are e.g. debug, release + applicationVariants.all { variant -> + variant.outputs.each { output -> + // For each separate APK per architecture, set a unique version code as described here: + // https://developer.android.com/studio/build/configure-apk-splits.html + // Example: versionCode 1 will generate 1001 for armeabi-v7a, 1002 for x86, etc. + def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] + def abi = output.getFilter(OutputFile.ABI) + if (abi != null) { // null for the universal-debug, universal-release variants + output.versionCodeOverride = + defaultConfig.versionCode * 1000 + versionCodes.get(abi) + } + + } + } +} + +dependencies { + // The version of react-native is set by the React Native Gradle Plugin + implementation("com.facebook.react:react-android") + + implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + + debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") + debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { + exclude group:'com.squareup.okhttp3', module:'okhttp' + } + + debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") + if (hermesEnabled.toBoolean()) { + implementation("com.facebook.react:hermes-android") + } else { + implementation jscFlavor + } +} + +apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) diff --git a/react-native/verification-tool/android/app/debug.keystore b/react-native/verification-tool/android/app/debug.keystore new file mode 100644 index 0000000000..364e105ed3 Binary files /dev/null and b/react-native/verification-tool/android/app/debug.keystore differ diff --git a/react-native/verification-tool/android/app/proguard-rules.pro b/react-native/verification-tool/android/app/proguard-rules.pro new file mode 100644 index 0000000000..11b025724a --- /dev/null +++ b/react-native/verification-tool/android/app/proguard-rules.pro @@ -0,0 +1,10 @@ +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: diff --git a/react-native/verification-tool/android/app/src/debug/AndroidManifest.xml b/react-native/verification-tool/android/app/src/debug/AndroidManifest.xml new file mode 100644 index 0000000000..4b185bc159 --- /dev/null +++ b/react-native/verification-tool/android/app/src/debug/AndroidManifest.xml @@ -0,0 +1,13 @@ + + + + + + + + + diff --git a/react-native/verification-tool/android/app/src/debug/java/com/thetaclientverificationtool/ReactNativeFlipper.java b/react-native/verification-tool/android/app/src/debug/java/com/thetaclientverificationtool/ReactNativeFlipper.java new file mode 100644 index 0000000000..564b8bf472 --- /dev/null +++ b/react-native/verification-tool/android/app/src/debug/java/com/thetaclientverificationtool/ReactNativeFlipper.java @@ -0,0 +1,75 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.thetaclientverificationtool; + +import android.content.Context; +import com.facebook.flipper.android.AndroidFlipperClient; +import com.facebook.flipper.android.utils.FlipperUtils; +import com.facebook.flipper.core.FlipperClient; +import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; +import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; +import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; +import com.facebook.flipper.plugins.inspector.DescriptorMapping; +import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; +import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; +import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; +import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; +import com.facebook.react.ReactInstanceEventListener; +import com.facebook.react.ReactInstanceManager; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.modules.network.NetworkingModule; +import okhttp3.OkHttpClient; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the debug + * flavor of it. Here you can add your own plugins and customize the Flipper setup. + */ +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + if (FlipperUtils.shouldEnableFlipper(context)) { + final FlipperClient client = AndroidFlipperClient.getInstance(context); + + client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); + client.addPlugin(new DatabasesFlipperPlugin(context)); + client.addPlugin(new SharedPreferencesFlipperPlugin(context)); + client.addPlugin(CrashReporterPlugin.getInstance()); + + NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); + NetworkingModule.setCustomClientBuilder( + new NetworkingModule.CustomClientBuilder() { + @Override + public void apply(OkHttpClient.Builder builder) { + builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); + } + }); + client.addPlugin(networkFlipperPlugin); + client.start(); + + // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized + // Hence we run if after all native modules have been initialized + ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); + if (reactContext == null) { + reactInstanceManager.addReactInstanceEventListener( + new ReactInstanceEventListener() { + @Override + public void onReactContextInitialized(ReactContext reactContext) { + reactInstanceManager.removeReactInstanceEventListener(this); + reactContext.runOnNativeModulesQueueThread( + new Runnable() { + @Override + public void run() { + client.addPlugin(new FrescoFlipperPlugin()); + } + }); + } + }); + } else { + client.addPlugin(new FrescoFlipperPlugin()); + } + } + } +} diff --git a/react-native/verification-tool/android/app/src/main/AndroidManifest.xml b/react-native/verification-tool/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..4122f36a59 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainActivity.java b/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainActivity.java new file mode 100644 index 0000000000..6ef2a44ae7 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainActivity.java @@ -0,0 +1,35 @@ +package com.thetaclientverificationtool; + +import com.facebook.react.ReactActivity; +import com.facebook.react.ReactActivityDelegate; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactActivityDelegate; + +public class MainActivity extends ReactActivity { + + /** + * Returns the name of the main component registered from JavaScript. This is used to schedule + * rendering of the component. + */ + @Override + protected String getMainComponentName() { + return "ThetaClientVerificationTool"; + } + + /** + * Returns the instance of the {@link ReactActivityDelegate}. Here we use a util class {@link + * DefaultReactActivityDelegate} which allows you to easily enable Fabric and Concurrent React + * (aka React 18) with two boolean flags. + */ + @Override + protected ReactActivityDelegate createReactActivityDelegate() { + return new DefaultReactActivityDelegate( + this, + getMainComponentName(), + // If you opted-in for the New Architecture, we enable the Fabric Renderer. + DefaultNewArchitectureEntryPoint.getFabricEnabled(), // fabricEnabled + // If you opted-in for the New Architecture, we enable Concurrent React (i.e. React 18). + DefaultNewArchitectureEntryPoint.getConcurrentReactEnabled() // concurrentRootEnabled + ); + } +} diff --git a/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainApplication.java b/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainApplication.java new file mode 100644 index 0000000000..7d938bcd45 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/java/com/thetaclientverificationtool/MainApplication.java @@ -0,0 +1,62 @@ +package com.thetaclientverificationtool; + +import android.app.Application; +import com.facebook.react.PackageList; +import com.facebook.react.ReactApplication; +import com.facebook.react.ReactNativeHost; +import com.facebook.react.ReactPackage; +import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint; +import com.facebook.react.defaults.DefaultReactNativeHost; +import com.facebook.soloader.SoLoader; +import java.util.List; + +public class MainApplication extends Application implements ReactApplication { + + private final ReactNativeHost mReactNativeHost = + new DefaultReactNativeHost(this) { + @Override + public boolean getUseDeveloperSupport() { + return BuildConfig.DEBUG; + } + + @Override + protected List getPackages() { + @SuppressWarnings("UnnecessaryLocalVariable") + List packages = new PackageList(this).getPackages(); + // Packages that cannot be autolinked yet can be added manually here, for example: + // packages.add(new MyReactNativePackage()); + return packages; + } + + @Override + protected String getJSMainModuleName() { + return "index"; + } + + @Override + protected boolean isNewArchEnabled() { + return BuildConfig.IS_NEW_ARCHITECTURE_ENABLED; + } + + @Override + protected Boolean isHermesEnabled() { + return BuildConfig.IS_HERMES_ENABLED; + } + }; + + @Override + public ReactNativeHost getReactNativeHost() { + return mReactNativeHost; + } + + @Override + public void onCreate() { + super.onCreate(); + SoLoader.init(this, /* native exopackage */ false); + if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) { + // If you opted-in for the New Architecture, we load the native entry point for this app. + DefaultNewArchitectureEntryPoint.load(); + } + ReactNativeFlipper.initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); + } +} diff --git a/react-native/verification-tool/android/app/src/main/res/drawable/rn_edit_text_material.xml b/react-native/verification-tool/android/app/src/main/res/drawable/rn_edit_text_material.xml new file mode 100644 index 0000000000..f35d996202 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/res/drawable/rn_edit_text_material.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000..a2f5908281 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000..1b52399808 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000..ff10afd6e1 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000000..115a4c768a Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000000..dcd3cd8083 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..459ca609d3 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000..8ca12fe024 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..8e19b410a1 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000000..b824ebdd48 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000000..4c19a13c23 Binary files /dev/null and b/react-native/verification-tool/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/react-native/verification-tool/android/app/src/main/res/values/strings.xml b/react-native/verification-tool/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000000..74e1365873 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + ThetaClientVerificationTool + diff --git a/react-native/verification-tool/android/app/src/main/res/values/styles.xml b/react-native/verification-tool/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000000..7ba83a2ad5 --- /dev/null +++ b/react-native/verification-tool/android/app/src/main/res/values/styles.xml @@ -0,0 +1,9 @@ + + + + + + diff --git a/react-native/verification-tool/android/app/src/release/java/com/thetaclientverificationtool/ReactNativeFlipper.java b/react-native/verification-tool/android/app/src/release/java/com/thetaclientverificationtool/ReactNativeFlipper.java new file mode 100644 index 0000000000..dc2bedb51a --- /dev/null +++ b/react-native/verification-tool/android/app/src/release/java/com/thetaclientverificationtool/ReactNativeFlipper.java @@ -0,0 +1,20 @@ +/** + * Copyright (c) Meta Platforms, Inc. and affiliates. + * + *

This source code is licensed under the MIT license found in the LICENSE file in the root + * directory of this source tree. + */ +package com.thetaclientverificationtool; + +import android.content.Context; +import com.facebook.react.ReactInstanceManager; + +/** + * Class responsible of loading Flipper inside your React Native application. This is the release + * flavor of it so it's empty as we don't want to load Flipper. + */ +public class ReactNativeFlipper { + public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { + // Do nothing as we don't want to initialize Flipper on Release. + } +} diff --git a/react-native/verification-tool/android/build.gradle b/react-native/verification-tool/android/build.gradle new file mode 100644 index 0000000000..fb37a26db3 --- /dev/null +++ b/react-native/verification-tool/android/build.gradle @@ -0,0 +1,21 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + ext { + buildToolsVersion = "33.0.0" + minSdkVersion = 26 + compileSdkVersion = 33 + targetSdkVersion = 33 + + // We use NDK 23 which has both M1 support and is the side-by-side NDK version from AGP. + ndkVersion = "23.1.7779620" + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath("com.android.tools.build:gradle:7.3.1") + classpath("com.facebook.react:react-native-gradle-plugin") + } +} diff --git a/react-native/verification-tool/android/gradle.properties b/react-native/verification-tool/android/gradle.properties new file mode 100644 index 0000000000..e4af465e8a --- /dev/null +++ b/react-native/verification-tool/android/gradle.properties @@ -0,0 +1,44 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +# Default value: -Xmx512m -XX:MaxMetaspaceSize=256m +org.gradle.jvmargs=-Xmx2048m -XX:MaxMetaspaceSize=512m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +# Automatically convert third-party libraries to use AndroidX +android.enableJetifier=true + +# Version of flipper SDK to use with React Native +FLIPPER_VERSION=0.125.0 + +# Use this property to specify which architecture you want to build. +# You can also override it from the CLI using +# ./gradlew -PreactNativeArchitectures=x86_64 +reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64 + +# Use this property to enable support to the new architecture. +# This will allow you to use TurboModules and the Fabric render in +# your application. You should enable this flag either if you want +# to write custom TurboModules/Fabric components OR use libraries that +# are providing them. +newArchEnabled=false + +# Use this property to enable or disable the Hermes JS engine. +# If set to false, you will be using JSC instead. +hermesEnabled=true diff --git a/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.jar b/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000..41d9927a4d Binary files /dev/null and b/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.properties b/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000..8fad3f5a98 --- /dev/null +++ b/react-native/verification-tool/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,5 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-all.zip +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/react-native/verification-tool/android/gradlew b/react-native/verification-tool/android/gradlew new file mode 100755 index 0000000000..1b6c787337 --- /dev/null +++ b/react-native/verification-tool/android/gradlew @@ -0,0 +1,234 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit + +APP_NAME="Gradle" +APP_BASE_NAME=${0##*/} + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + +# Collect all arguments for the java command; +# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of +# shell script including quotes and variable substitutions, so put them in +# double quotes to make sure that they get re-expanded; and +# * put everything else in single quotes, so that it's not re-expanded. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/react-native/verification-tool/android/gradlew.bat b/react-native/verification-tool/android/gradlew.bat new file mode 100644 index 0000000000..107acd32c4 --- /dev/null +++ b/react-native/verification-tool/android/gradlew.bat @@ -0,0 +1,89 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/react-native/verification-tool/android/settings.gradle b/react-native/verification-tool/android/settings.gradle new file mode 100644 index 0000000000..64d60f76bc --- /dev/null +++ b/react-native/verification-tool/android/settings.gradle @@ -0,0 +1,4 @@ +rootProject.name = 'ThetaClientVerificationTool' +apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) +include ':app' +includeBuild('../node_modules/react-native-gradle-plugin') diff --git a/react-native/verification-tool/app.json b/react-native/verification-tool/app.json new file mode 100644 index 0000000000..9c69640efc --- /dev/null +++ b/react-native/verification-tool/app.json @@ -0,0 +1,4 @@ +{ + "name": "ThetaClientVerificationTool", + "displayName": "ThetaClientVerificationTool" +} \ No newline at end of file diff --git a/react-native/verification-tool/babel.config.js b/react-native/verification-tool/babel.config.js new file mode 100644 index 0000000000..adea77bdb7 --- /dev/null +++ b/react-native/verification-tool/babel.config.js @@ -0,0 +1,17 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + presets: ['module:metro-react-native-babel-preset'], + plugins: [ + [ + 'module-resolver', + { + extensions: ['.tsx', '.ts', '.js', '.json'], + alias: { + [pak.name]: path.join(__dirname, '..', pak.source), + }, + }, + ], + ], +}; diff --git a/react-native/verification-tool/index.js b/react-native/verification-tool/index.js new file mode 100644 index 0000000000..117ddcae40 --- /dev/null +++ b/react-native/verification-tool/index.js @@ -0,0 +1,5 @@ +import { AppRegistry } from 'react-native'; +import App from './src/App'; +import { name as appName } from './app.json'; + +AppRegistry.registerComponent(appName, () => App); diff --git a/react-native/verification-tool/ios/.xcode.env b/react-native/verification-tool/ios/.xcode.env new file mode 100644 index 0000000000..3d5782c715 --- /dev/null +++ b/react-native/verification-tool/ios/.xcode.env @@ -0,0 +1,11 @@ +# This `.xcode.env` file is versioned and is used to source the environment +# used when running script phases inside Xcode. +# To customize your local environment, you can create an `.xcode.env.local` +# file that is not versioned. + +# NODE_BINARY variable contains the PATH to the node executable. +# +# Customize the NODE_BINARY variable here. +# For example, to use nvm with brew, add the following line +# . "$(brew --prefix nvm)/nvm.sh" --no-use +export NODE_BINARY=$(command -v node) diff --git a/react-native/verification-tool/ios/File.swift b/react-native/verification-tool/ios/File.swift new file mode 100644 index 0000000000..f2cb9ec721 --- /dev/null +++ b/react-native/verification-tool/ios/File.swift @@ -0,0 +1,6 @@ +// +// File.swift +// ThetaClientVerificationTool +// + +import Foundation diff --git a/react-native/verification-tool/ios/Podfile b/react-native/verification-tool/ios/Podfile new file mode 100644 index 0000000000..db6950aca1 --- /dev/null +++ b/react-native/verification-tool/ios/Podfile @@ -0,0 +1,60 @@ +require_relative '../node_modules/react-native/scripts/react_native_pods' +require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' + +platform :ios, 15 +prepare_react_native_project! + +# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set. +# because `react-native-flipper` depends on (FlipperKit,...) that will be excluded +# +# To fix this you can also exclude `react-native-flipper` using a `react-native.config.js` +# ```js +# module.exports = { +# dependencies: { +# ...(process.env.NO_FLIPPER ? { 'react-native-flipper': { platforms: { ios: null } } } : {}), +# ``` +flipper_config = ENV['NO_FLIPPER'] == "1" ? FlipperConfiguration.disabled : FlipperConfiguration.enabled + +linkage = ENV['USE_FRAMEWORKS'] +if linkage != nil + Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green + use_frameworks! :linkage => linkage.to_sym +end + +target 'ThetaClientVerificationTool' do + config = use_native_modules! + + # Flags change depending on the env values. + flags = get_default_flags() + + use_react_native!( + :path => config[:reactNativePath], + # Hermes is now enabled by default. Disable by setting this flag to false. + # Upcoming versions of React Native may rely on get_default_flags(), but + # we make it explicit here to aid in the React Native upgrade process. + :hermes_enabled => flags[:hermes_enabled], + :fabric_enabled => flags[:fabric_enabled], + # Enables Flipper. + # + # Note that if you have use_frameworks! enabled, Flipper will not work and + # you should disable the next line. + :flipper_configuration => flipper_config, + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/.." + ) + + target 'ThetaClientVerificationToolTest' do + inherit! :complete + # Pods for testing + end + + post_install do |installer| + react_native_post_install( + installer, + # Set `mac_catalyst_enabled` to `true` in order to apply patches + # necessary for Mac Catalyst builds + :mac_catalyst_enabled => false + ) + __apply_Xcode_12_5_M1_post_install_workaround(installer) + end +end diff --git a/react-native/verification-tool/ios/Podfile.lock b/react-native/verification-tool/ios/Podfile.lock new file mode 100644 index 0000000000..68c25e9aef --- /dev/null +++ b/react-native/verification-tool/ios/Podfile.lock @@ -0,0 +1,642 @@ +PODS: + - boost (1.76.0) + - CocoaAsyncSocket (7.6.5) + - DoubleConversion (1.1.6) + - FBLazyVector (0.71.7) + - FBReactNativeSpec (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTRequired (= 0.71.7) + - RCTTypeSafety (= 0.71.7) + - React-Core (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - Flipper (0.125.0): + - Flipper-Folly (~> 2.6) + - Flipper-RSocket (~> 1.4) + - Flipper-Boost-iOSX (1.76.0.1.11) + - Flipper-DoubleConversion (3.2.0.1) + - Flipper-Fmt (7.1.7) + - Flipper-Folly (2.6.10): + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt (= 7.1.7) + - Flipper-Glog + - libevent (~> 2.1.12) + - OpenSSL-Universal (= 1.1.1100) + - Flipper-Glog (0.5.0.5) + - Flipper-PeerTalk (0.0.4) + - Flipper-RSocket (1.4.3): + - Flipper-Folly (~> 2.6) + - FlipperKit (0.125.0): + - FlipperKit/Core (= 0.125.0) + - FlipperKit/Core (0.125.0): + - Flipper (~> 0.125.0) + - FlipperKit/CppBridge + - FlipperKit/FBCxxFollyDynamicConvert + - FlipperKit/FBDefines + - FlipperKit/FKPortForwarding + - SocketRocket (~> 0.6.0) + - FlipperKit/CppBridge (0.125.0): + - Flipper (~> 0.125.0) + - FlipperKit/FBCxxFollyDynamicConvert (0.125.0): + - Flipper-Folly (~> 2.6) + - FlipperKit/FBDefines (0.125.0) + - FlipperKit/FKPortForwarding (0.125.0): + - CocoaAsyncSocket (~> 7.6) + - Flipper-PeerTalk (~> 0.0.4) + - FlipperKit/FlipperKitHighlightOverlay (0.125.0) + - FlipperKit/FlipperKitLayoutHelpers (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutTextSearchable + - FlipperKit/FlipperKitLayoutIOSDescriptors (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutHelpers + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitHighlightOverlay + - FlipperKit/FlipperKitLayoutHelpers + - FlipperKit/FlipperKitLayoutIOSDescriptors + - FlipperKit/FlipperKitLayoutTextSearchable + - YogaKit (~> 1.18) + - FlipperKit/FlipperKitLayoutTextSearchable (0.125.0) + - FlipperKit/FlipperKitNetworkPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitReactPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitUserDefaultsPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/SKIOSNetworkPlugin (0.125.0): + - FlipperKit/Core + - FlipperKit/FlipperKitNetworkPlugin + - fmt (6.2.1) + - glog (0.3.5) + - hermes-engine (0.71.7): + - hermes-engine/Pre-built (= 0.71.7) + - hermes-engine/Pre-built (0.71.7) + - libevent (2.1.12) + - OpenSSL-Universal (1.1.1100) + - RCT-Folly (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - RCT-Folly/Default (= 2021.07.22.00) + - RCT-Folly/Default (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - RCT-Folly/Futures (2021.07.22.00): + - boost + - DoubleConversion + - fmt (~> 6.2.1) + - glog + - libevent + - RCTRequired (0.71.7) + - RCTTypeSafety (0.71.7): + - FBLazyVector (= 0.71.7) + - RCTRequired (= 0.71.7) + - React-Core (= 0.71.7) + - React (0.71.7): + - React-Core (= 0.71.7) + - React-Core/DevSupport (= 0.71.7) + - React-Core/RCTWebSocket (= 0.71.7) + - React-RCTActionSheet (= 0.71.7) + - React-RCTAnimation (= 0.71.7) + - React-RCTBlob (= 0.71.7) + - React-RCTImage (= 0.71.7) + - React-RCTLinking (= 0.71.7) + - React-RCTNetwork (= 0.71.7) + - React-RCTSettings (= 0.71.7) + - React-RCTText (= 0.71.7) + - React-RCTVibration (= 0.71.7) + - React-callinvoker (0.71.7) + - React-Codegen (0.71.7): + - FBReactNativeSpec + - hermes-engine + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - React-jsi + - React-jsiexecutor + - ReactCommon/turbomodule/bridging + - ReactCommon/turbomodule/core + - React-Core (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.7) + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/CoreModulesHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/Default (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/DevSupport (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.7) + - React-Core/RCTWebSocket (= 0.71.7) + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-jsinspector (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTActionSheetHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTAnimationHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTBlobHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTImageHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTLinkingHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTNetworkHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTSettingsHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTTextHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTVibrationHeaders (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-Core/RCTWebSocket (0.71.7): + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Core/Default (= 0.71.7) + - React-cxxreact (= 0.71.7) + - React-hermes + - React-jsi (= 0.71.7) + - React-jsiexecutor (= 0.71.7) + - React-perflogger (= 0.71.7) + - Yoga + - React-CoreModules (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.7) + - React-Codegen (= 0.71.7) + - React-Core/CoreModulesHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - React-RCTBlob + - React-RCTImage (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-cxxreact (0.71.7): + - boost (= 1.76.0) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.7) + - React-jsi (= 0.71.7) + - React-jsinspector (= 0.71.7) + - React-logger (= 0.71.7) + - React-perflogger (= 0.71.7) + - React-runtimeexecutor (= 0.71.7) + - React-hermes (0.71.7): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - RCT-Folly/Futures (= 2021.07.22.00) + - React-cxxreact (= 0.71.7) + - React-jsi + - React-jsiexecutor (= 0.71.7) + - React-jsinspector (= 0.71.7) + - React-perflogger (= 0.71.7) + - React-jsi (0.71.7): + - boost (= 1.76.0) + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-jsiexecutor (0.71.7): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-cxxreact (= 0.71.7) + - React-jsi (= 0.71.7) + - React-perflogger (= 0.71.7) + - React-jsinspector (0.71.7) + - React-logger (0.71.7): + - glog + - react-native-safe-area-context (4.7.1): + - React-Core + - React-perflogger (0.71.7) + - React-RCTActionSheet (0.71.7): + - React-Core/RCTActionSheetHeaders (= 0.71.7) + - React-RCTAnimation (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.7) + - React-Codegen (= 0.71.7) + - React-Core/RCTAnimationHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTAppDelegate (0.71.7): + - RCT-Folly + - RCTRequired + - RCTTypeSafety + - React-Core + - ReactCommon/turbomodule/core + - React-RCTBlob (0.71.7): + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.7) + - React-Core/RCTBlobHeaders (= 0.71.7) + - React-Core/RCTWebSocket (= 0.71.7) + - React-jsi (= 0.71.7) + - React-RCTNetwork (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTImage (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.7) + - React-Codegen (= 0.71.7) + - React-Core/RCTImageHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - React-RCTNetwork (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTLinking (0.71.7): + - React-Codegen (= 0.71.7) + - React-Core/RCTLinkingHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTNetwork (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.7) + - React-Codegen (= 0.71.7) + - React-Core/RCTNetworkHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTSettings (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - RCTTypeSafety (= 0.71.7) + - React-Codegen (= 0.71.7) + - React-Core/RCTSettingsHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-RCTText (0.71.7): + - React-Core/RCTTextHeaders (= 0.71.7) + - React-RCTVibration (0.71.7): + - RCT-Folly (= 2021.07.22.00) + - React-Codegen (= 0.71.7) + - React-Core/RCTVibrationHeaders (= 0.71.7) + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/core (= 0.71.7) + - React-runtimeexecutor (0.71.7): + - React-jsi (= 0.71.7) + - ReactCommon/turbomodule/bridging (0.71.7): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.7) + - React-Core (= 0.71.7) + - React-cxxreact (= 0.71.7) + - React-jsi (= 0.71.7) + - React-logger (= 0.71.7) + - React-perflogger (= 0.71.7) + - ReactCommon/turbomodule/core (0.71.7): + - DoubleConversion + - glog + - hermes-engine + - RCT-Folly (= 2021.07.22.00) + - React-callinvoker (= 0.71.7) + - React-Core (= 0.71.7) + - React-cxxreact (= 0.71.7) + - React-jsi (= 0.71.7) + - React-logger (= 0.71.7) + - React-perflogger (= 0.71.7) + - RNScreens (3.24.0): + - React-Core + - React-RCTImage + - SocketRocket (0.6.1) + - theta-client-react-native (1.3.1): + - React-Core + - Yoga (1.14.0) + - YogaKit (1.18.1): + - Yoga (~> 1.14) + +DEPENDENCIES: + - boost (from `../node_modules/react-native/third-party-podspecs/boost.podspec`) + - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) + - FBLazyVector (from `../node_modules/react-native/Libraries/FBLazyVector`) + - FBReactNativeSpec (from `../node_modules/react-native/React/FBReactNativeSpec`) + - Flipper (= 0.125.0) + - Flipper-Boost-iOSX (= 1.76.0.1.11) + - Flipper-DoubleConversion (= 3.2.0.1) + - Flipper-Fmt (= 7.1.7) + - Flipper-Folly (= 2.6.10) + - Flipper-Glog (= 0.5.0.5) + - Flipper-PeerTalk (= 0.0.4) + - Flipper-RSocket (= 1.4.3) + - FlipperKit (= 0.125.0) + - FlipperKit/Core (= 0.125.0) + - FlipperKit/CppBridge (= 0.125.0) + - FlipperKit/FBCxxFollyDynamicConvert (= 0.125.0) + - FlipperKit/FBDefines (= 0.125.0) + - FlipperKit/FKPortForwarding (= 0.125.0) + - FlipperKit/FlipperKitHighlightOverlay (= 0.125.0) + - FlipperKit/FlipperKitLayoutPlugin (= 0.125.0) + - FlipperKit/FlipperKitLayoutTextSearchable (= 0.125.0) + - FlipperKit/FlipperKitNetworkPlugin (= 0.125.0) + - FlipperKit/FlipperKitReactPlugin (= 0.125.0) + - FlipperKit/FlipperKitUserDefaultsPlugin (= 0.125.0) + - FlipperKit/SKIOSNetworkPlugin (= 0.125.0) + - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) + - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) + - libevent (~> 2.1.12) + - OpenSSL-Universal (= 1.1.1100) + - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) + - RCTRequired (from `../node_modules/react-native/Libraries/RCTRequired`) + - RCTTypeSafety (from `../node_modules/react-native/Libraries/TypeSafety`) + - React (from `../node_modules/react-native/`) + - React-callinvoker (from `../node_modules/react-native/ReactCommon/callinvoker`) + - React-Codegen (from `build/generated/ios`) + - React-Core (from `../node_modules/react-native/`) + - React-Core/DevSupport (from `../node_modules/react-native/`) + - React-Core/RCTWebSocket (from `../node_modules/react-native/`) + - React-CoreModules (from `../node_modules/react-native/React/CoreModules`) + - React-cxxreact (from `../node_modules/react-native/ReactCommon/cxxreact`) + - React-hermes (from `../node_modules/react-native/ReactCommon/hermes`) + - React-jsi (from `../node_modules/react-native/ReactCommon/jsi`) + - React-jsiexecutor (from `../node_modules/react-native/ReactCommon/jsiexecutor`) + - React-jsinspector (from `../node_modules/react-native/ReactCommon/jsinspector`) + - React-logger (from `../node_modules/react-native/ReactCommon/logger`) + - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) + - React-perflogger (from `../node_modules/react-native/ReactCommon/reactperflogger`) + - React-RCTActionSheet (from `../node_modules/react-native/Libraries/ActionSheetIOS`) + - React-RCTAnimation (from `../node_modules/react-native/Libraries/NativeAnimation`) + - React-RCTAppDelegate (from `../node_modules/react-native/Libraries/AppDelegate`) + - React-RCTBlob (from `../node_modules/react-native/Libraries/Blob`) + - React-RCTImage (from `../node_modules/react-native/Libraries/Image`) + - React-RCTLinking (from `../node_modules/react-native/Libraries/LinkingIOS`) + - React-RCTNetwork (from `../node_modules/react-native/Libraries/Network`) + - React-RCTSettings (from `../node_modules/react-native/Libraries/Settings`) + - React-RCTText (from `../node_modules/react-native/Libraries/Text`) + - React-RCTVibration (from `../node_modules/react-native/Libraries/Vibration`) + - React-runtimeexecutor (from `../node_modules/react-native/ReactCommon/runtimeexecutor`) + - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - RNScreens (from `../node_modules/react-native-screens`) + - theta-client-react-native (from `../..`) + - Yoga (from `../node_modules/react-native/ReactCommon/yoga`) + +SPEC REPOS: + trunk: + - CocoaAsyncSocket + - Flipper + - Flipper-Boost-iOSX + - Flipper-DoubleConversion + - Flipper-Fmt + - Flipper-Folly + - Flipper-Glog + - Flipper-PeerTalk + - Flipper-RSocket + - FlipperKit + - fmt + - libevent + - OpenSSL-Universal + - SocketRocket + - YogaKit + +EXTERNAL SOURCES: + boost: + :podspec: "../node_modules/react-native/third-party-podspecs/boost.podspec" + DoubleConversion: + :podspec: "../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec" + FBLazyVector: + :path: "../node_modules/react-native/Libraries/FBLazyVector" + FBReactNativeSpec: + :path: "../node_modules/react-native/React/FBReactNativeSpec" + glog: + :podspec: "../node_modules/react-native/third-party-podspecs/glog.podspec" + hermes-engine: + :podspec: "../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec" + RCT-Folly: + :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" + RCTRequired: + :path: "../node_modules/react-native/Libraries/RCTRequired" + RCTTypeSafety: + :path: "../node_modules/react-native/Libraries/TypeSafety" + React: + :path: "../node_modules/react-native/" + React-callinvoker: + :path: "../node_modules/react-native/ReactCommon/callinvoker" + React-Codegen: + :path: build/generated/ios + React-Core: + :path: "../node_modules/react-native/" + React-CoreModules: + :path: "../node_modules/react-native/React/CoreModules" + React-cxxreact: + :path: "../node_modules/react-native/ReactCommon/cxxreact" + React-hermes: + :path: "../node_modules/react-native/ReactCommon/hermes" + React-jsi: + :path: "../node_modules/react-native/ReactCommon/jsi" + React-jsiexecutor: + :path: "../node_modules/react-native/ReactCommon/jsiexecutor" + React-jsinspector: + :path: "../node_modules/react-native/ReactCommon/jsinspector" + React-logger: + :path: "../node_modules/react-native/ReactCommon/logger" + react-native-safe-area-context: + :path: "../node_modules/react-native-safe-area-context" + React-perflogger: + :path: "../node_modules/react-native/ReactCommon/reactperflogger" + React-RCTActionSheet: + :path: "../node_modules/react-native/Libraries/ActionSheetIOS" + React-RCTAnimation: + :path: "../node_modules/react-native/Libraries/NativeAnimation" + React-RCTAppDelegate: + :path: "../node_modules/react-native/Libraries/AppDelegate" + React-RCTBlob: + :path: "../node_modules/react-native/Libraries/Blob" + React-RCTImage: + :path: "../node_modules/react-native/Libraries/Image" + React-RCTLinking: + :path: "../node_modules/react-native/Libraries/LinkingIOS" + React-RCTNetwork: + :path: "../node_modules/react-native/Libraries/Network" + React-RCTSettings: + :path: "../node_modules/react-native/Libraries/Settings" + React-RCTText: + :path: "../node_modules/react-native/Libraries/Text" + React-RCTVibration: + :path: "../node_modules/react-native/Libraries/Vibration" + React-runtimeexecutor: + :path: "../node_modules/react-native/ReactCommon/runtimeexecutor" + ReactCommon: + :path: "../node_modules/react-native/ReactCommon" + RNScreens: + :path: "../node_modules/react-native-screens" + theta-client-react-native: + :path: "../.." + Yoga: + :path: "../node_modules/react-native/ReactCommon/yoga" + +SPEC CHECKSUMS: + boost: 57d2868c099736d80fcd648bf211b4431e51a558 + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 + DoubleConversion: 5189b271737e1565bdce30deb4a08d647e3f5f54 + FBLazyVector: a89a0525bc7ca174675045c2b492b5280d5a2470 + FBReactNativeSpec: 7714e6bc1e9ea23df6c4cb42f0b2fd9c6a3a559c + Flipper: 26fc4b7382499f1281eb8cb921e5c3ad6de91fe0 + Flipper-Boost-iOSX: fd1e2b8cbef7e662a122412d7ac5f5bea715403c + Flipper-DoubleConversion: 2dc99b02f658daf147069aad9dbd29d8feb06d30 + Flipper-Fmt: 60cbdd92fc254826e61d669a5d87ef7015396a9b + Flipper-Folly: 584845625005ff068a6ebf41f857f468decd26b3 + Flipper-Glog: 70c50ce58ddaf67dc35180db05f191692570f446 + Flipper-PeerTalk: 116d8f857dc6ef55c7a5a75ea3ceaafe878aadc9 + Flipper-RSocket: d9d9ade67cbecf6ac10730304bf5607266dd2541 + FlipperKit: cbdee19bdd4e7f05472a66ce290f1b729ba3cb86 + fmt: ff9d55029c625d3757ed641535fd4a75fedc7ce9 + glog: 04b94705f318337d7ead9e6d17c019bd9b1f6b1b + hermes-engine: 4438d2b8bf8bebaba1b1ac0451160bab59e491f8 + libevent: 4049cae6c81cdb3654a443be001fb9bdceff7913 + OpenSSL-Universal: ebc357f1e6bc71fa463ccb2fe676756aff50e88c + RCT-Folly: 424b8c9a7a0b9ab2886ffe9c3b041ef628fd4fb1 + RCTRequired: 5a4a30ac20c86eeadd6844a9328f78d4168cf9b2 + RCTTypeSafety: 279fc5861a89f0f37db3a585f27f971485b4b734 + React: 88307a9be3bd0e71a6822271cf28b84a587fb97f + React-callinvoker: 35fb980c454104ebe82f0afb9826830089248e08 + React-Codegen: a8dbde3b7476d5c19437d2adb9e8ea1b426b9595 + React-Core: 385cb6fa78762c6409ff39faeb0dd9ad664b6e84 + React-CoreModules: c2b7db313b04d9b71954ffd55d0c2e46bc40e9fb + React-cxxreact: 845fefb889132e5d004ff818f7a599e32c52e7d6 + React-hermes: 86135f35e1dd2dfccfb97afe96d0c06f6a3970c4 + React-jsi: 39c116aa6c3d6f3d9874eff6998a670b47882a28 + React-jsiexecutor: eaa5f71eb8f6861cf0e57f1a0f52aeb020d9e18e + React-jsinspector: 9885f6f94d231b95a739ef7bb50536fb87ce7539 + React-logger: 3f8ebad1be1bf3299d1ab6d7f971802d7395c7ef + react-native-safe-area-context: 9697629f7b2cda43cf52169bb7e0767d330648c2 + React-perflogger: 2d505bbe298e3b7bacdd9e542b15535be07220f6 + React-RCTActionSheet: 0e96e4560bd733c9b37efbf68f5b1a47615892fb + React-RCTAnimation: fd138e26f120371c87e406745a27535e2c8a04ef + React-RCTAppDelegate: 4a9fd1230a98dc3d4382f8a934dc9f50834d8335 + React-RCTBlob: 38a7185f06a0ce8153a023e63b406a28d67b955d + React-RCTImage: 92b0966e7c1cadda889e961c474397ad5180e194 + React-RCTLinking: b80f8d0c6e94c54294b0048def51f57eaa9a27af + React-RCTNetwork: 491b0c65ac22edbd6695d12d084b4943103b009b + React-RCTSettings: 97af3e8abe0023349ec015910df3bda1a0380117 + React-RCTText: 33c85753bd714d527d2ae538dc56ec24c6783d84 + React-RCTVibration: 08f132cad9896458776f37c112e71d60aef1c6ae + React-runtimeexecutor: c5c89f8f543842dd864b63ded1b0bbb9c9445328 + ReactCommon: dbfbe2f7f3c5ce4ce44f43f2fd0d5950d1eb67c5 + RNScreens: b21dc57dfa2b710c30ec600786a3fc223b1b92e7 + SocketRocket: f32cd54efbe0f095c4d7594881e52619cfe80b17 + theta-client-react-native: ce3758ee471f46db143fbcd62bdaaae67e13e330 + Yoga: d56980c8914db0b51692f55533409e844b66133c + YogaKit: f782866e155069a2cca2517aafea43200b01fd5a + +PODFILE CHECKSUM: 9f18c6378e34edb93be82f2d2cca253260003120 + +COCOAPODS: 1.12.1 diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool-Bridging-Header.h b/react-native/verification-tool/ios/ThetaClientVerificationTool-Bridging-Header.h new file mode 100644 index 0000000000..e11d920b12 --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool-Bridging-Header.h @@ -0,0 +1,3 @@ +// +// Use this file to import your target's public headers that you would like to expose to Swift. +// diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/project.pbxproj b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/project.pbxproj new file mode 100644 index 0000000000..25c890835c --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/project.pbxproj @@ -0,0 +1,708 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 54; + objects = { + +/* Begin PBXBuildFile section */ + 00E356F31AD99517003FC87E /* ThetaClientVerificationToolTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 00E356F21AD99517003FC87E /* ThetaClientVerificationToolTest.m */; }; + 0C80B921A6F3F58F76C31292 /* libPods-ThetaClientVerificationTool.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DCACB8F33CDC322A6C60F78 /* libPods-ThetaClientVerificationTool.a */; }; + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB01A68108700A75B9A /* AppDelegate.mm */; }; + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 13B07FB51A68108700A75B9A /* Images.xcassets */; }; + 13B07FC11A68108700A75B9A /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 13B07FB71A68108700A75B9A /* main.m */; }; + 7699B88040F8A987B510C191 /* libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 19F6CBCC0A4E27FBF8BF4A61 /* libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a */; }; + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 00E356F41AD99517003FC87E /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 83CBB9F71A601CBA00E9B192 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 13B07F861A680F5B00A75B9A; + remoteInfo = ThetaClientVerificationTool; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 00E356EE1AD99517003FC87E /* ThetaClientVerificationToolTest.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ThetaClientVerificationToolTest.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 00E356F11AD99517003FC87E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 00E356F21AD99517003FC87E /* ThetaClientVerificationToolTest.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ThetaClientVerificationToolTest.m; sourceTree = ""; }; + 13B07F961A680F5B00A75B9A /* ThetaClientVerificationTool.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ThetaClientVerificationTool.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = AppDelegate.h; path = ThetaClientVerificationTool/AppDelegate.h; sourceTree = ""; }; + 13B07FB01A68108700A75B9A /* AppDelegate.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = AppDelegate.mm; path = ThetaClientVerificationTool/AppDelegate.mm; sourceTree = ""; }; + 13B07FB51A68108700A75B9A /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Images.xcassets; path = ThetaClientVerificationTool/Images.xcassets; sourceTree = ""; }; + 13B07FB61A68108700A75B9A /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = Info.plist; path = ThetaClientVerificationTool/Info.plist; sourceTree = ""; }; + 13B07FB71A68108700A75B9A /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = main.m; path = ThetaClientVerificationTool/main.m; sourceTree = ""; }; + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 3B4392A12AC88292D35C810B /* Pods-ThetaClientVerificationTool.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientVerificationTool.debug.xcconfig"; path = "Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool.debug.xcconfig"; sourceTree = ""; }; + 5709B34CF0A7D63546082F79 /* Pods-ThetaClientVerificationTool.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientVerificationTool.release.xcconfig"; path = "Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool.release.xcconfig"; sourceTree = ""; }; + 5B7EB9410499542E8C5724F5 /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.debug.xcconfig"; path = "Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.debug.xcconfig"; sourceTree = ""; }; + 5DCACB8F33CDC322A6C60F78 /* libPods-ThetaClientVerificationTool.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ThetaClientVerificationTool.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = LaunchScreen.storyboard; path = ThetaClientVerificationTool/LaunchScreen.storyboard; sourceTree = ""; }; + 89C6BE57DB24E9ADA2F236DE /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.release.xcconfig"; path = "Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.release.xcconfig"; sourceTree = ""; }; + ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 00E356EB1AD99517003FC87E /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 7699B88040F8A987B510C191 /* libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8C1A680F5B00A75B9A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 0C80B921A6F3F58F76C31292 /* libPods-ThetaClientVerificationTool.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 00E356EF1AD99517003FC87E /* ThetaClientVerificationToolTest */ = { + isa = PBXGroup; + children = ( + 00E356F21AD99517003FC87E /* ThetaClientVerificationToolTest.m */, + 00E356F01AD99517003FC87E /* Supporting Files */, + ); + path = ThetaClientVerificationToolTest; + sourceTree = ""; + }; + 00E356F01AD99517003FC87E /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 00E356F11AD99517003FC87E /* Info.plist */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 13B07FAE1A68108700A75B9A /* ThetaClientVerificationTool */ = { + isa = PBXGroup; + children = ( + 13B07FAF1A68108700A75B9A /* AppDelegate.h */, + 13B07FB01A68108700A75B9A /* AppDelegate.mm */, + 13B07FB51A68108700A75B9A /* Images.xcassets */, + 13B07FB61A68108700A75B9A /* Info.plist */, + 81AB9BB72411601600AC10FF /* LaunchScreen.storyboard */, + 13B07FB71A68108700A75B9A /* main.m */, + ); + name = ThetaClientVerificationTool; + sourceTree = ""; + }; + 2D16E6871FA4F8E400B85C8A /* Frameworks */ = { + isa = PBXGroup; + children = ( + ED297162215061F000B7C4FE /* JavaScriptCore.framework */, + 5DCACB8F33CDC322A6C60F78 /* libPods-ThetaClientVerificationTool.a */, + 19F6CBCC0A4E27FBF8BF4A61 /* libPods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + 832341AE1AAA6A7D00B99B32 /* Libraries */ = { + isa = PBXGroup; + children = ( + ); + name = Libraries; + sourceTree = ""; + }; + 83CBB9F61A601CBA00E9B192 = { + isa = PBXGroup; + children = ( + 13B07FAE1A68108700A75B9A /* ThetaClientVerificationTool */, + 832341AE1AAA6A7D00B99B32 /* Libraries */, + 00E356EF1AD99517003FC87E /* ThetaClientVerificationToolTest */, + 83CBBA001A601CBA00E9B192 /* Products */, + 2D16E6871FA4F8E400B85C8A /* Frameworks */, + BBD78D7AC51CEA395F1C20DB /* Pods */, + ); + indentWidth = 2; + sourceTree = ""; + tabWidth = 2; + usesTabs = 0; + }; + 83CBBA001A601CBA00E9B192 /* Products */ = { + isa = PBXGroup; + children = ( + 13B07F961A680F5B00A75B9A /* ThetaClientVerificationTool.app */, + 00E356EE1AD99517003FC87E /* ThetaClientVerificationToolTest.xctest */, + ); + name = Products; + sourceTree = ""; + }; + BBD78D7AC51CEA395F1C20DB /* Pods */ = { + isa = PBXGroup; + children = ( + 3B4392A12AC88292D35C810B /* Pods-ThetaClientVerificationTool.debug.xcconfig */, + 5709B34CF0A7D63546082F79 /* Pods-ThetaClientVerificationTool.release.xcconfig */, + 5B7EB9410499542E8C5724F5 /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.debug.xcconfig */, + 89C6BE57DB24E9ADA2F236DE /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 00E356ED1AD99517003FC87E /* ThetaClientVerificationToolTest */ = { + isa = PBXNativeTarget; + buildConfigurationList = 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ThetaClientVerificationToolTest" */; + buildPhases = ( + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */, + 00E356EA1AD99517003FC87E /* Sources */, + 00E356EB1AD99517003FC87E /* Frameworks */, + 00E356EC1AD99517003FC87E /* Resources */, + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */, + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + 00E356F51AD99517003FC87E /* PBXTargetDependency */, + ); + name = ThetaClientVerificationToolTest; + productName = ThetaClientVerificationToolTest; + productReference = 00E356EE1AD99517003FC87E /* ThetaClientVerificationToolTest.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 13B07F861A680F5B00A75B9A /* ThetaClientVerificationTool */ = { + isa = PBXNativeTarget; + buildConfigurationList = 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ThetaClientVerificationTool" */; + buildPhases = ( + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */, + FD10A7F022414F080027D42C /* Start Packager */, + 13B07F871A680F5B00A75B9A /* Sources */, + 13B07F8C1A680F5B00A75B9A /* Frameworks */, + 13B07F8E1A680F5B00A75B9A /* Resources */, + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */, + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */, + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ThetaClientVerificationTool; + productName = ThetaClientVerificationTool; + productReference = 13B07F961A680F5B00A75B9A /* ThetaClientVerificationTool.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 83CBB9F71A601CBA00E9B192 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1210; + TargetAttributes = { + 00E356ED1AD99517003FC87E = { + CreatedOnToolsVersion = 6.2; + TestTargetID = 13B07F861A680F5B00A75B9A; + }; + 13B07F861A680F5B00A75B9A = { + LastSwiftMigration = 1120; + }; + }; + }; + buildConfigurationList = 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ThetaClientVerificationTool" */; + compatibilityVersion = "Xcode 12.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 83CBB9F61A601CBA00E9B192; + productRefGroup = 83CBBA001A601CBA00E9B192 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 13B07F861A680F5B00A75B9A /* ThetaClientVerificationTool */, + 00E356ED1AD99517003FC87E /* ThetaClientVerificationToolTest */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 00E356EC1AD99517003FC87E /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F8E1A680F5B00A75B9A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */, + 13B07FBF1A68108700A75B9A /* Images.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/.xcode.env.local", + "$(SRCROOT)/.xcode.env", + ); + name = "Bundle React Native code and images"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "set -e\n\nWITH_ENVIRONMENT=\"../node_modules/react-native/scripts/xcode/with-environment.sh\"\nREACT_NATIVE_XCODE=\"../node_modules/react-native/scripts/react-native-xcode.sh\"\n\n/bin/sh -c \"$WITH_ENVIRONMENT $REACT_NATIVE_XCODE\"\n"; + }; + 00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + A55EABD7B0C7F3A422A6CC61 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C38B50BA6285516D6DCD4F65 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ThetaClientVerificationTool-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + C59DA0FBD6956966B86A3779 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool/Pods-ThetaClientVerificationTool-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + F6A41C54EA430FDDC6A6ED99 /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-resources-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Copy Pods Resources"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-resources-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest/Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + FD10A7F022414F080027D42C /* Start Packager */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Start Packager"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "export RCT_METRO_PORT=\"${RCT_METRO_PORT:=8081}\"\necho \"export RCT_METRO_PORT=${RCT_METRO_PORT}\" > \"${SRCROOT}/../node_modules/react-native/scripts/.packager.env\"\nif [ -z \"${RCT_NO_LAUNCH_PACKAGER+xxx}\" ] ; then\n if nc -w 5 -z localhost ${RCT_METRO_PORT} ; then\n if ! curl -s \"http://localhost:${RCT_METRO_PORT}/status\" | grep -q \"packager-status:running\" ; then\n echo \"Port ${RCT_METRO_PORT} already in use, packager is either not running or not running correctly\"\n exit 2\n fi\n else\n open \"$SRCROOT/../node_modules/react-native/scripts/launchPackager.command\" || echo \"Can't start packager automatically\"\n fi\nfi\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 00E356EA1AD99517003FC87E /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 00E356F31AD99517003FC87E /* ThetaClientVerificationToolTest.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 13B07F871A680F5B00A75B9A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 13B07FBC1A68108700A75B9A /* AppDelegate.mm in Sources */, + 13B07FC11A68108700A75B9A /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 00E356F51AD99517003FC87E /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 13B07F861A680F5B00A75B9A /* ThetaClientVerificationTool */; + targetProxy = 00E356F41AD99517003FC87E /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 00E356F61AD99517003FC87E /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5B7EB9410499542E8C5724F5 /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.debug.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + INFOPLIST_FILE = ThetaClientVerificationToolTest/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ThetaClientVerificationTool.app/ThetaClientVerificationTool"; + }; + name = Debug; + }; + 00E356F71AD99517003FC87E /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 89C6BE57DB24E9ADA2F236DE /* Pods-ThetaClientVerificationTool-ThetaClientVerificationToolTest.release.xcconfig */; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + COPY_PHASE_STRIP = NO; + INFOPLIST_FILE = ThetaClientVerificationToolTest/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "-ObjC", + "-lc++", + "$(inherited)", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ThetaClientVerificationTool.app/ThetaClientVerificationTool"; + }; + name = Release; + }; + 13B07F941A680F5B00A75B9A /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 3B4392A12AC88292D35C810B /* Pods-ThetaClientVerificationTool.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + ENABLE_BITCODE = NO; + INFOPLIST_FILE = ThetaClientVerificationTool/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ThetaClientVerificationTool; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Debug; + }; + 13B07F951A680F5B00A75B9A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 5709B34CF0A7D63546082F79 /* Pods-ThetaClientVerificationTool.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ENABLE_MODULES = YES; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = ""; + INFOPLIST_FILE = ThetaClientVerificationTool/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + OTHER_LDFLAGS = ( + "$(inherited)", + "-ObjC", + "-lc++", + ); + PRODUCT_BUNDLE_IDENTIFIER = "org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier)"; + PRODUCT_NAME = ThetaClientVerificationTool; + SWIFT_VERSION = 5.0; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + 83CBBA201A601CBA00E9B192 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_SYMBOLS_PRIVATE_EXTERN = NO; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 83CBBA211A601CBA00E9B192 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_CXX_LANGUAGE_STANDARD = "c++17"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + COPY_PHASE_STRIP = YES; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; + LD_RUNPATH_SEARCH_PATHS = ( + /usr/lib/swift, + "$(inherited)", + ); + LIBRARY_SEARCH_PATHS = ( + "\"$(SDKROOT)/usr/lib/swift\"", + "\"$(TOOLCHAIN_DIR)/usr/lib/swift/$(PLATFORM_NAME)\"", + "\"$(inherited)\"", + ); + MTL_ENABLE_DEBUG_INFO = NO; + OTHER_CPLUSPLUSFLAGS = ( + "$(OTHER_CFLAGS)", + "-DFOLLY_NO_CONFIG", + "-DFOLLY_MOBILE=1", + "-DFOLLY_USE_LIBCPP=1", + ); + REACT_NATIVE_PATH = "${PODS_ROOT}/../../node_modules/react-native"; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 00E357021AD99517003FC87E /* Build configuration list for PBXNativeTarget "ThetaClientVerificationToolTest" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 00E356F61AD99517003FC87E /* Debug */, + 00E356F71AD99517003FC87E /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "ThetaClientVerificationTool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 13B07F941A680F5B00A75B9A /* Debug */, + 13B07F951A680F5B00A75B9A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "ThetaClientVerificationTool" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 83CBBA201A601CBA00E9B192 /* Debug */, + 83CBBA211A601CBA00E9B192 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 83CBB9F71A601CBA00E9B192 /* Project object */; +} diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/xcshareddata/xcschemes/ThetaClientVerificationTool.xcscheme b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/xcshareddata/xcschemes/ThetaClientVerificationTool.xcscheme new file mode 100644 index 0000000000..912425c62e --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcodeproj/xcshareddata/xcschemes/ThetaClientVerificationTool.xcscheme @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/contents.xcworkspacedata b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000..fe004ba00c --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000..18d981003d --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.h b/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.h new file mode 100644 index 0000000000..5d2808256c --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.h @@ -0,0 +1,6 @@ +#import +#import + +@interface AppDelegate : RCTAppDelegate + +@end diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.mm b/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.mm new file mode 100644 index 0000000000..7860ed4a6b --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/AppDelegate.mm @@ -0,0 +1,36 @@ +#import "AppDelegate.h" + +#import + +@implementation AppDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + self.moduleName = @"ThetaClientVerificationTool"; + // You can add your custom initial props in the dictionary below. + // They will be passed down to the ViewController used by React Native. + self.initialProps = @{}; + + return [super application:application didFinishLaunchingWithOptions:launchOptions]; +} + +- (NSURL *)sourceURLForBridge:(RCTBridge *)bridge +{ +#if DEBUG + return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index"]; +#else + return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; +#endif +} + +/// This method controls whether the `concurrentRoot`feature of React18 is turned on or off. +/// +/// @see: https://reactjs.org/blog/2022/03/29/react-v18.html +/// @note: This requires to be rendering on Fabric (i.e. on the New Architecture). +/// @return: `true` if the `concurrentRoot` feature is enabled. Otherwise, it returns `false`. +- (BOOL)concurrentRootEnabled +{ + return true; +} + +@end diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/AppIcon.appiconset/Contents.json b/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000000..81213230de --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,53 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/Contents.json b/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/Contents.json new file mode 100644 index 0000000000..2d92bd53fd --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/Images.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/Info.plist b/react-native/verification-tool/ios/ThetaClientVerificationTool/Info.plist new file mode 100644 index 0000000000..7baf263e0f --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/Info.plist @@ -0,0 +1,55 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + ThetaClientVerificationTool + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + $(MARKETING_VERSION) + CFBundleSignature + ???? + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + LSRequiresIPhoneOS + + NSAppTransportSecurity + + NSExceptionDomains + + localhost + + NSExceptionAllowsInsecureHTTPLoads + + + + + NSLocationWhenInUseUsageDescription + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UIViewControllerBasedStatusBarAppearance + + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/LaunchScreen.storyboard b/react-native/verification-tool/ios/ThetaClientVerificationTool/LaunchScreen.storyboard new file mode 100644 index 0000000000..177cb8fb25 --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/LaunchScreen.storyboard @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationTool/main.m b/react-native/verification-tool/ios/ThetaClientVerificationTool/main.m new file mode 100644 index 0000000000..d645c7246c --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationTool/main.m @@ -0,0 +1,10 @@ +#import + +#import "AppDelegate.h" + +int main(int argc, char *argv[]) +{ + @autoreleasepool { + return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } +} diff --git a/react-native/verification-tool/ios/ThetaClientVerificationToolTests/Info.plist b/react-native/verification-tool/ios/ThetaClientVerificationToolTests/Info.plist new file mode 100644 index 0000000000..ba72822e87 --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationToolTests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/react-native/verification-tool/ios/ThetaClientVerificationToolTests/ThetaClientReactNativeExampleTests.m b/react-native/verification-tool/ios/ThetaClientVerificationToolTests/ThetaClientReactNativeExampleTests.m new file mode 100644 index 0000000000..c1d86dc8ef --- /dev/null +++ b/react-native/verification-tool/ios/ThetaClientVerificationToolTests/ThetaClientReactNativeExampleTests.m @@ -0,0 +1,66 @@ +#import +#import + +#import +#import + +#define TIMEOUT_SECONDS 600 +#define TEXT_TO_LOOK_FOR @"Welcome to React" + +@interface ThetaClientVerificationToolTest : XCTestCase + +@end + +@implementation ThetaClientVerificationToolTest + +- (BOOL)findSubviewInView:(UIView *)view matching:(BOOL (^)(UIView *view))test +{ + if (test(view)) { + return YES; + } + for (UIView *subview in [view subviews]) { + if ([self findSubviewInView:subview matching:test]) { + return YES; + } + } + return NO; +} + +- (void)testRendersWelcomeScreen +{ + UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; + NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; + BOOL foundElement = NO; + + __block NSString *redboxError = nil; +#ifdef DEBUG + RCTSetLogFunction( + ^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { + if (level >= RCTLogLevelError) { + redboxError = message; + } + }); +#endif + + while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { + [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; + + foundElement = [self findSubviewInView:vc.view + matching:^BOOL(UIView *view) { + if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { + return YES; + } + return NO; + }]; + } + +#ifdef DEBUG + RCTSetLogFunction(RCTDefaultLogFunction); +#endif + + XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); + XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); +} + +@end diff --git a/react-native/verification-tool/metro.config.js b/react-native/verification-tool/metro.config.js new file mode 100644 index 0000000000..b5c0064bb9 --- /dev/null +++ b/react-native/verification-tool/metro.config.js @@ -0,0 +1,40 @@ +const path = require('path'); +const escape = require('escape-string-regexp'); +const exclusionList = require('metro-config/src/defaults/exclusionList'); +const pak = require('../package.json'); + +const root = path.resolve(__dirname, '..'); + +const modules = Object.keys({ + ...pak.peerDependencies, +}); + +module.exports = { + projectRoot: __dirname, + watchFolders: [root], + + // We need to make sure that only one version is loaded for peerDependencies + // So we block them at the root, and alias them to the versions in example's node_modules + resolver: { + blacklistRE: exclusionList( + modules.map( + (m) => + new RegExp(`^${escape(path.join(root, 'node_modules', m))}\\/.*$`) + ) + ), + + extraNodeModules: modules.reduce((acc, name) => { + acc[name] = path.join(__dirname, 'node_modules', name); + return acc; + }, {}), + }, + + transformer: { + getTransformOptions: async () => ({ + transform: { + experimentalImportSupport: false, + inlineRequires: true, + }, + }), + }, +}; diff --git a/react-native/verification-tool/package.json b/react-native/verification-tool/package.json new file mode 100644 index 0000000000..b7f788146c --- /dev/null +++ b/react-native/verification-tool/package.json @@ -0,0 +1,26 @@ +{ + "name": "verification-tool", + "version": "0.0.1", + "private": true, + "scripts": { + "android": "react-native run-android", + "ios": "react-native run-ios", + "start": "react-native start", + "pods": "pod-install --quiet" + }, + "dependencies": { + "@react-navigation/native": "^6.1.7", + "@react-navigation/native-stack": "^6.9.13", + "react": "18.2.0", + "react-native": "0.71.7", + "react-native-safe-area-context": "^4.7.1", + "react-native-screens": "^3.24.0" + }, + "devDependencies": { + "@babel/core": "^7.20.0", + "@babel/preset-env": "^7.20.0", + "@babel/runtime": "^7.20.0", + "babel-plugin-module-resolver": "^4.1.0", + "metro-react-native-babel-preset": "0.73.9" + } +} diff --git a/react-native/verification-tool/react-native.config.js b/react-native/verification-tool/react-native.config.js new file mode 100644 index 0000000000..a5166956f4 --- /dev/null +++ b/react-native/verification-tool/react-native.config.js @@ -0,0 +1,10 @@ +const path = require('path'); +const pak = require('../package.json'); + +module.exports = { + dependencies: { + [pak.name]: { + root: path.join(__dirname, '..'), + }, + }, +}; diff --git a/react-native/verification-tool/src/App.tsx b/react-native/verification-tool/src/App.tsx new file mode 100644 index 0000000000..860bcaf41b --- /dev/null +++ b/react-native/verification-tool/src/App.tsx @@ -0,0 +1,108 @@ +import React from 'react'; +import { NavigationContainer } from '@react-navigation/native'; +import { + NativeStackNavigationOptions, + createNativeStackNavigator, +} from '@react-navigation/native-stack'; +import MenuScreen from './screen/menu-screen'; +import VideoConvertScreen from './screen/video-convert-screen'; +import LivePreviewScreen from './screen/live-preview-screen'; +import OptionsScreen from './screen/options-screen/options-screen'; +import CommandsScreen from './screen/commands-screen'; +import PhotoCaptureScreen from './screen/photo-capture-screen'; +import FilePreviewScreen from './screen/file-preview-screen'; +import ListFilesScreen from './screen/list-files-screen'; +import VideoCaptureScreen from './screen/video-capture-screen/video-capture-screen'; +import DeleteFilesScreen from './screen/delete-files-screen/delete-files-screen'; +import GetMetadataScreen from './screen/get-metadata-screen/get-metadata-screen'; +import GetInfoScreen from './screen/get-info-screen/get-info-screen'; +import TimeShiftCaptureScreen from './screen/time-shift-capture-screen/time-shift-capture-screen'; + +const Stack = createNativeStackNavigator(); + +const screenOptions = { + headerStyle: { + backgroundColor: '#6200ee', + }, + headerTintColor: '#fff', + headerTitleStyle: { + fontWeight: 'bold', + }, + headerBackTitle: '', +} as NativeStackNavigationOptions; + +const App = () => { + return ( + + + + + + + + + + + + + + + + + + ); +}; + +export default App; diff --git a/react-native/verification-tool/src/components/capture/capture-common-options/capture-common-options.tsx b/react-native/verification-tool/src/components/capture/capture-common-options/capture-common-options.tsx new file mode 100644 index 0000000000..90d584c46e --- /dev/null +++ b/react-native/verification-tool/src/components/capture/capture-common-options/capture-common-options.tsx @@ -0,0 +1,131 @@ +import * as React from 'react'; +import type { OptionEditProps } from '../../options'; +import { View } from 'react-native'; +import { + ApertureEdit, + ExposureCompensationEdit, + ExposureDelayEdit, + ExposureProgramEdit, + GpsInfoEdit, + GpsTagRecordingEdit, + IsoEdit, + IsoAutoHighLimitEdit, + WhiteBalanceEdit, +} from '../../../components/options'; +import { NumberEdit } from '../../../components/options/number-edit'; + +export const CaptureCommonOptionsEdit: React.FC = ({ + onChange, + options, +}) => { + return ( + + { + const newOptions = { + ...options, + aperture: option.aperture, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + colorTemperature: option.colorTemperature, + }; + newOptions && onChange(newOptions); + }} + options={options} + placeHolder="Input value" + /> + { + const newOptions = { + ...options, + exposureCompensation: option.exposureCompensation, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + exposureDelay: option.exposureDelay, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + exposureProgram: option.exposureProgram, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + gpsInfo: option.gpsInfo, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + _gpsTagRecording: option._gpsTagRecording, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + iso: option.iso, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + isoAutoHighLimit: option.isoAutoHighLimit, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + { + const newOptions = { + ...options, + whiteBalance: option.whiteBalance, + }; + newOptions && onChange(newOptions); + }} + options={options} + /> + + ); +}; + +CaptureCommonOptionsEdit.displayName = 'CaptureCommonOptionsEdit'; + +export default CaptureCommonOptionsEdit; diff --git a/react-native/verification-tool/src/components/capture/capture-common-options/index.ts b/react-native/verification-tool/src/components/capture/capture-common-options/index.ts new file mode 100644 index 0000000000..b8b49aca5d --- /dev/null +++ b/react-native/verification-tool/src/components/capture/capture-common-options/index.ts @@ -0,0 +1 @@ +export * from './capture-common-options'; diff --git a/react-native/verification-tool/src/components/list-files-view/index.ts b/react-native/verification-tool/src/components/list-files-view/index.ts new file mode 100644 index 0000000000..211991dc99 --- /dev/null +++ b/react-native/verification-tool/src/components/list-files-view/index.ts @@ -0,0 +1 @@ +export * from './list-files-view'; diff --git a/react-native/verification-tool/src/components/list-files-view/list-files-view.tsx b/react-native/verification-tool/src/components/list-files-view/list-files-view.tsx new file mode 100644 index 0000000000..614d37cb32 --- /dev/null +++ b/react-native/verification-tool/src/components/list-files-view/list-files-view.tsx @@ -0,0 +1,152 @@ +import React from 'react'; +import { + type ViewProps, + type ViewStyle, + StyleProp, + TouchableOpacity, + Text, + View, + ScrollView, + RefreshControl, +} from 'react-native'; +import styles from './styles'; +import { + FileInfo, + FileTypeEnum, + StorageEnum, + ThetaFiles, + listFiles, +} from 'theta-client-react-native'; + +interface Props extends Pick { + style?: StyleProp; + selectedFiles?: FileInfo[]; + onSelected?: (files: FileInfo[]) => void; + fileType?: FileTypeEnum; + startPosition?: number; + entryCount?: number; + storage?: StorageEnum; + multiselect?: boolean; + onError?: (error: any) => void; + refreshCounter?: number; + onRefreshed?: (thetaFiles?: ThetaFiles) => void; +} + +export const ListFilesView: React.FC = ({ + onSelected, + selectedFiles, + fileType, + startPosition, + entryCount, + storage, + multiselect, + onError, + refreshCounter, + onRefreshed, +}) => { + const [refreshing, setRefreshing] = React.useState(false); + const [thetaFiles, setThetaFiles] = React.useState(); + const [selected, setSelected] = React.useState( + selectedFiles ?? [] + ); + + React.useEffect(() => { + setSelected(selectedFiles ?? []); + }, [selectedFiles]); + + const getFileList = React.useCallback(async () => { + try { + if (entryCount != null && entryCount < 0) { + return undefined; + } + const result = await listFiles( + fileType ?? FileTypeEnum.ALL, + startPosition, + entryCount ?? 100, + storage + ); + return result; + } catch (error) { + console.log('listFiles error: ' + JSON.stringify(error)); + onError?.(error); + return undefined; + } + }, [entryCount, fileType, onError, startPosition, storage]); + + const onPressItem = (item: FileInfo) => { + let items: FileInfo[] = []; + if (multiselect) { + const findFile = selected.find((element) => { + return element.fileUrl === item.fileUrl; + }); + if (findFile == null) { + items = [...selected, item]; + } else { + items = selected.filter((element) => { + return element.fileUrl !== item.fileUrl; + }); + } + } else { + items = [item]; + } + setSelected(items); + onSelected?.(items); + }; + + const isSelectedFile = (fileInfo: FileInfo) => { + const foundItem = selected.find((item) => { + return item.fileUrl === fileInfo.fileUrl; + }); + if (foundItem == null) { + return false; + } + return true; + }; + + const onRefresh = React.useCallback(async () => { + console.log('ListFilesView onRefresh'); + setSelected([]); + setRefreshing(true); + const resultListFiles = await getFileList(); + setThetaFiles(resultListFiles); + setRefreshing(false); + onRefreshed?.(resultListFiles); + }, [getFileList, onRefreshed]); + + React.useEffect(() => { + onRefresh(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [startPosition, entryCount, fileType, storage, refreshCounter]); + + const items = + thetaFiles?.fileList.map((item) => ( + onPressItem(item)} + > + + {item.name} + + + )) ?? []; + + return ( + + + } + > + {items} + + + ); +}; + +export default ListFilesView; diff --git a/react-native/verification-tool/src/components/list-files-view/styles.tsx b/react-native/verification-tool/src/components/list-files-view/styles.tsx new file mode 100644 index 0000000000..f0e1113b16 --- /dev/null +++ b/react-native/verification-tool/src/components/list-files-view/styles.tsx @@ -0,0 +1,25 @@ +import { StyleSheet } from 'react-native'; + +const styles = StyleSheet.create({ + itemText: { + color: 'black', + fontSize: 16, + paddingHorizontal: 10, + paddingVertical: 2, + }, + container: { + flex: 1, + }, + listContentContainer: { + flex: 1, + }, + listItemBase: { + width: '100%', + }, + listItemBaseSelected: { + width: '100%', + backgroundColor: 'yellow', + }, +}); + +export default styles; diff --git a/react-native/verification-tool/src/components/options/aperture/aperture-edit.tsx b/react-native/verification-tool/src/components/options/aperture/aperture-edit.tsx new file mode 100644 index 0000000000..b05e797c69 --- /dev/null +++ b/react-native/verification-tool/src/components/options/aperture/aperture-edit.tsx @@ -0,0 +1,32 @@ +import * as React from 'react'; +import { ApertureEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const ApertureEdit: React.FC = ({ + onChange, + options, +}) => { + const apertureList = [ + { name: '[undefined]', value: undefined }, + { name: 'APERTURE_AUTO', value: ApertureEnum.APERTURE_AUTO }, + { name: 'APERTURE_2_0', value: ApertureEnum.APERTURE_2_0 }, + { name: 'APERTURE_2_1', value: ApertureEnum.APERTURE_2_1 }, + { name: 'APERTURE_2_4', value: ApertureEnum.APERTURE_2_4 }, + { name: 'APERTURE_3_5', value: ApertureEnum.APERTURE_3_5 }, + { name: 'APERTURE_5_6', value: ApertureEnum.APERTURE_5_6 }, + ]; + + return ( + + ); +}; + +ApertureEdit.displayName = 'ApertureEdit'; + +export default ApertureEdit; diff --git a/react-native/verification-tool/src/components/options/aperture/index.ts b/react-native/verification-tool/src/components/options/aperture/index.ts new file mode 100644 index 0000000000..6e4233734b --- /dev/null +++ b/react-native/verification-tool/src/components/options/aperture/index.ts @@ -0,0 +1 @@ +export * from './aperture-edit'; diff --git a/react-native/verification-tool/src/components/options/capture-mode/capture-mode-edit.tsx b/react-native/verification-tool/src/components/options/capture-mode/capture-mode-edit.tsx new file mode 100644 index 0000000000..cd61a65655 --- /dev/null +++ b/react-native/verification-tool/src/components/options/capture-mode/capture-mode-edit.tsx @@ -0,0 +1,30 @@ +import * as React from 'react'; +import { CaptureModeEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const CaptureModeEdit: React.FC = ({ + onChange, + options, +}) => { + const captureModeList = [ + { name: 'IMAGE', value: CaptureModeEnum.IMAGE }, + { name: 'VIDEO', value: CaptureModeEnum.VIDEO }, + { name: 'INTERVAL', value: CaptureModeEnum.INTERVAL }, + { name: 'LIVE_STREAMING', value: CaptureModeEnum.LIVE_STREAMING }, + { name: 'PRESET', value: CaptureModeEnum.PRESET }, + ]; + + return ( + + ); +}; + +CaptureModeEdit.displayName = 'CaptureModeEdit'; + +export default CaptureModeEdit; diff --git a/react-native/verification-tool/src/components/options/capture-mode/index.ts b/react-native/verification-tool/src/components/options/capture-mode/index.ts new file mode 100644 index 0000000000..d401610d6b --- /dev/null +++ b/react-native/verification-tool/src/components/options/capture-mode/index.ts @@ -0,0 +1 @@ +export * from './capture-mode-edit'; diff --git a/react-native/verification-tool/src/components/options/enum-edit/enum-edit.tsx b/react-native/verification-tool/src/components/options/enum-edit/enum-edit.tsx new file mode 100644 index 0000000000..79a59de181 --- /dev/null +++ b/react-native/verification-tool/src/components/options/enum-edit/enum-edit.tsx @@ -0,0 +1,41 @@ +import * as React from 'react'; +import type { OptionEditProps } from '..'; +import { Item, ItemSelectorView } from '../../ui/item-list'; + +interface Props extends OptionEditProps { + enumList: Item[]; + propName: string; +} + +export const EnumEdit: React.FC = ({ + propName, + enumList, + onChange, + options, +}) => { + return ( + { + let option = { [propName]: item.value }; + onChange(option); + }} + selectedItem={enumList.find((item) => { + if (options != null) { + const option = Object.entries(options).find( + (element) => element[0] === propName + ); + if (option != null) { + return item.value === option[1]; + } + } + return false; + })} + /> + ); +}; + +EnumEdit.displayName = 'EnumEdit'; + +export default EnumEdit; diff --git a/react-native/verification-tool/src/components/options/enum-edit/index.ts b/react-native/verification-tool/src/components/options/enum-edit/index.ts new file mode 100644 index 0000000000..3549e2e4e5 --- /dev/null +++ b/react-native/verification-tool/src/components/options/enum-edit/index.ts @@ -0,0 +1 @@ +export * from './enum-edit'; diff --git a/react-native/verification-tool/src/components/options/exposure-compensation/exposure-compensation-edit.tsx b/react-native/verification-tool/src/components/options/exposure-compensation/exposure-compensation-edit.tsx new file mode 100644 index 0000000000..8504314067 --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-compensation/exposure-compensation-edit.tsx @@ -0,0 +1,39 @@ +import * as React from 'react'; +import { ExposureCompensationEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const ExposureCompensationEdit: React.FC = ({ + onChange, + options, +}) => { + const exposureCompensationList = [ + { name: '[undefined]', value: undefined }, + { name: 'M2_0', value: ExposureCompensationEnum.M_2_0 }, + { name: 'M1_7', value: ExposureCompensationEnum.M_1_7 }, + { name: 'M1_3', value: ExposureCompensationEnum.M_1_3 }, + { name: 'M1_0', value: ExposureCompensationEnum.M_1_0 }, + { name: 'M0_7', value: ExposureCompensationEnum.M_0_7 }, + { name: 'M0_3', value: ExposureCompensationEnum.M_0_3 }, + { name: 'ZERO', value: ExposureCompensationEnum.ZERO }, + { name: 'P0_3', value: ExposureCompensationEnum.P_0_3 }, + { name: 'P0_7', value: ExposureCompensationEnum.P_0_7 }, + { name: 'P1_0', value: ExposureCompensationEnum.P_1_0 }, + { name: 'P1_3', value: ExposureCompensationEnum.P_1_3 }, + { name: 'P1_7', value: ExposureCompensationEnum.P_1_7 }, + { name: 'P2_0', value: ExposureCompensationEnum.P_2_0 }, + ]; + + return ( + + ); +}; + +ExposureCompensationEdit.displayName = 'ExposureCompensationEdit'; + +export default ExposureCompensationEdit; diff --git a/react-native/verification-tool/src/components/options/exposure-compensation/index.ts b/react-native/verification-tool/src/components/options/exposure-compensation/index.ts new file mode 100644 index 0000000000..49d3211d8f --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-compensation/index.ts @@ -0,0 +1 @@ +export * from './exposure-compensation-edit'; diff --git a/react-native/verification-tool/src/components/options/exposure-delay/exposure-delay-edit.tsx b/react-native/verification-tool/src/components/options/exposure-delay/exposure-delay-edit.tsx new file mode 100644 index 0000000000..b7692b6bef --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-delay/exposure-delay-edit.tsx @@ -0,0 +1,37 @@ +import * as React from 'react'; +import { ExposureDelayEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const ExposureDelayEdit: React.FC = ({ + onChange, + options, +}) => { + const exposureDelayList = [ + { name: '[undefined]', value: undefined }, + { name: 'DELAY_OFF', value: ExposureDelayEnum.DELAY_OFF }, + { name: 'DELAY_1', value: ExposureDelayEnum.DELAY_1 }, + { name: 'DELAY_2', value: ExposureDelayEnum.DELAY_2 }, + { name: 'DELAY_3', value: ExposureDelayEnum.DELAY_3 }, + { name: 'DELAY_4', value: ExposureDelayEnum.DELAY_4 }, + { name: 'DELAY_5', value: ExposureDelayEnum.DELAY_5 }, + { name: 'DELAY_6', value: ExposureDelayEnum.DELAY_6 }, + { name: 'DELAY_7', value: ExposureDelayEnum.DELAY_7 }, + { name: 'DELAY_8', value: ExposureDelayEnum.DELAY_8 }, + { name: 'DELAY_9', value: ExposureDelayEnum.DELAY_9 }, + { name: 'DELAY_10', value: ExposureDelayEnum.DELAY_10 }, + ]; + + return ( + + ); +}; + +ExposureDelayEdit.displayName = 'ExposureDelayEdit'; + +export default ExposureDelayEdit; diff --git a/react-native/verification-tool/src/components/options/exposure-delay/index.ts b/react-native/verification-tool/src/components/options/exposure-delay/index.ts new file mode 100644 index 0000000000..ea90a60bac --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-delay/index.ts @@ -0,0 +1 @@ +export * from './exposure-delay-edit'; diff --git a/react-native/verification-tool/src/components/options/exposure-program/exposure-program-edit.tsx b/react-native/verification-tool/src/components/options/exposure-program/exposure-program-edit.tsx new file mode 100644 index 0000000000..b398ed910b --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-program/exposure-program-edit.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { ExposureProgramEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const ExposureProgramEdit: React.FC = ({ + onChange, + options, +}) => { + const exposureProgramList = [ + { name: '[undefined]', value: undefined }, + { name: 'MANUAL', value: ExposureProgramEnum.MANUAL }, + { name: 'NORMAL_PROGRAM', value: ExposureProgramEnum.NORMAL_PROGRAM }, + { name: 'APERTURE_PRIORITY', value: ExposureProgramEnum.APERTURE_PRIORITY }, + { name: 'SHUTTER_PRIORITY', value: ExposureProgramEnum.SHUTTER_PRIORITY }, + { name: 'ISO_PRIORITY', value: ExposureProgramEnum.ISO_PRIORITY }, + ]; + + return ( + + ); +}; + +ExposureProgramEdit.displayName = 'ExposureProgramEdit'; + +export default ExposureProgramEdit; diff --git a/react-native/verification-tool/src/components/options/exposure-program/index.ts b/react-native/verification-tool/src/components/options/exposure-program/index.ts new file mode 100644 index 0000000000..9840591d36 --- /dev/null +++ b/react-native/verification-tool/src/components/options/exposure-program/index.ts @@ -0,0 +1 @@ +export * from './exposure-program-edit'; diff --git a/react-native/verification-tool/src/components/options/filter/filter-edit.tsx b/react-native/verification-tool/src/components/options/filter/filter-edit.tsx new file mode 100644 index 0000000000..b195c6f077 --- /dev/null +++ b/react-native/verification-tool/src/components/options/filter/filter-edit.tsx @@ -0,0 +1,31 @@ +import * as React from 'react'; +import { FilterEnum } from 'theta-client-react-native'; +import type { OptionEditProps } from '..'; +import { EnumEdit } from '../enum-edit'; + +export const FilterEdit: React.FC = ({ + onChange, + options, +}) => { + const filterList = [ + { name: '[undefined]', value: undefined }, + { name: 'OFF', value: FilterEnum.OFF }, + { name: 'DR_COMP', value: FilterEnum.DR_COMP }, + { name: 'NOISE_REDUCTION', value: FilterEnum.NOISE_REDUCTION }, + { name: 'HDR', value: FilterEnum.HDR }, + { name: 'HH_HDR', value: FilterEnum.HH_HDR }, + ]; + + return ( + + ); +}; + +FilterEdit.displayName = 'FilterEdit'; + +export default FilterEdit; diff --git a/react-native/verification-tool/src/components/options/filter/index.ts b/react-native/verification-tool/src/components/options/filter/index.ts new file mode 100644 index 0000000000..a735a7b313 --- /dev/null +++ b/react-native/verification-tool/src/components/options/filter/index.ts @@ -0,0 +1 @@ +export * from './filter-edit'; diff --git a/react-native/verification-tool/src/components/options/gps-info/gps-info-edit.tsx b/react-native/verification-tool/src/components/options/gps-info/gps-info-edit.tsx new file mode 100644 index 0000000000..a417989628 --- /dev/null +++ b/react-native/verification-tool/src/components/options/gps-info/gps-info-edit.tsx @@ -0,0 +1,209 @@ +import * as React from 'react'; +import type { OptionEditProps } from '..'; +import { View, Text, Switch } from 'react-native'; +import type { GpsInfo } from 'theta-client-react-native'; +import { InputNumber } from '../../ui/input-number'; +import { InputString } from '../../ui/input-string'; +import styles from './styles'; +import Button from '../../ui/button'; + +interface Props extends OptionEditProps { + hideLabel?: boolean; +} + +export const GpsInfoEdit: React.FC = ({ + onChange, + options, + hideLabel = false, +}) => { + const [editGpsInfo, setEditGpsInfo] = React.useState(); + const [useGpsInfo, setUseGpsInfo] = React.useState(false); + + const getDateTimeZone = () => { + const dt = new Date(); + const y = dt.getFullYear(); + const mm = ('00' + (dt.getMonth() + 1)).slice(-2); + const dd = ('00' + dt.getDate()).slice(-2); + + const HH = ('00' + dt.getHours()).slice(-2); + const MM = ('00' + dt.getMinutes()).slice(-2); + const SS = ('00' + dt.getSeconds()).slice(-2); + + const offset = dt.getTimezoneOffset(); + const offsetHH = ('00' + Math.floor(Math.abs(offset) / 60)).slice(-2); + const offsetMM = ('00' + (Math.abs(offset) % 60)).slice(-2); + const offsetSign = offset < 0 ? '+' : '-'; + + const result = `${y}:${mm}:${dd} ${HH}:${MM}:${SS}${offsetSign}${offsetHH}:${offsetMM}`; + return result; + }; + + const defaultInfo = React.useMemo(() => { + return { + latitude: 35.485577, + longitude: 134.24005, + altitude: 47.1, + dateTimeZone: getDateTimeZone(), + }; + }, []); + + const infoDisable: GpsInfo = { + latitude: 65535, + longitude: 65535, + altitude: 0, + dateTimeZone: '', + }; + + const isUseGpsInfo = () => { + return hideLabel || useGpsInfo; + }; + + React.useEffect(() => { + const gpsInfo = options?.gpsInfo || { ...defaultInfo }; + setEditGpsInfo(gpsInfo); + }, [options, defaultInfo]); + + const onChangeUseGpsInfo = (newValue: boolean) => { + setUseGpsInfo(newValue); + if (!newValue) { + // keep values of editGpsInfo + const newOptions = options && { + ...options, + gpsInfo: undefined, + }; + onChange(newOptions ?? {}); + } else { + const newGpsInfo = + editGpsInfo == null + ? { + ...defaultInfo, + } + : { ...editGpsInfo }; + + setEditGpsInfo(newGpsInfo); + const newOptions = { + ...options, + gpsInfo: newGpsInfo, + }; + onChange(newOptions); + } + }; + + return ( + + + {!hideLabel && ( + + gpsInfo + + + )} + {isUseGpsInfo() && ( + + +