diff --git a/.github/workflows/buildAndTest.yaml b/.github/workflows/buildAndTest.yaml index f9eb1ea087d..70eeb8a16f0 100644 --- a/.github/workflows/buildAndTest.yaml +++ b/.github/workflows/buildAndTest.yaml @@ -12,7 +12,7 @@ permissions: jobs: build: - runs-on: macos-12 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 diff --git a/.github/workflows/test-flutter.yaml b/.github/workflows/test-flutter.yaml index 639dafe235d..8f4091f9958 100644 --- a/.github/workflows/test-flutter.yaml +++ b/.github/workflows/test-flutter.yaml @@ -9,15 +9,17 @@ permissions: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: flutter - uses: subosito/flutter-action@v2.3.0 + uses: subosito/flutter-action@v2 with: - architecture: arm64 + channel: stable + flutter-version: 3.29.0 - name: test-flutter run: | + flutter --version cd ./flutter flutter pub get flutter test diff --git a/.github/workflows/test-kmp.yaml b/.github/workflows/test-kmp.yaml index fe48cce7a54..71bbf96e6f3 100644 --- a/.github/workflows/test-kmp.yaml +++ b/.github/workflows/test-kmp.yaml @@ -9,7 +9,7 @@ permissions: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Set up JDK 17 diff --git a/.github/workflows/test-rn.yaml b/.github/workflows/test-rn.yaml index a6eb9516ebe..07bfd1e32e6 100644 --- a/.github/workflows/test-rn.yaml +++ b/.github/workflows/test-rn.yaml @@ -9,7 +9,7 @@ permissions: jobs: build: - runs-on: macos-13 + runs-on: macos-latest steps: - uses: actions/checkout@v3 - name: Use Node.js diff --git a/build.gradle.kts b/build.gradle.kts index 0b77444c9fd..d1e3fe83dc0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,14 +1,14 @@ plugins { //trick: for the same plugin versions in all sub-modules id("com.android.library").version("8.1.2").apply(false) - kotlin("multiplatform").version("1.9.10").apply(false) - kotlin("plugin.serialization").version("1.9.10").apply(false) - id("org.jetbrains.dokka").version("1.9.10") + kotlin("multiplatform").version("1.9.20").apply(false) + kotlin("plugin.serialization").version("1.9.20").apply(false) + id("org.jetbrains.dokka").version("1.9.20") } buildscript { dependencies { - classpath("org.jetbrains.dokka:versioning-plugin:1.9.10") + classpath("org.jetbrains.dokka:versioning-plugin:1.9.20") } } diff --git a/demos/demo-android/app/build.gradle b/demos/demo-android/app/build.gradle index 252db953413..d393ca8552a 100755 --- a/demos/demo-android/app/build.gradle +++ b/demos/demo-android/app/build.gradle @@ -59,6 +59,7 @@ android { dependencies { def lifecycle_version = "2.7.0" + def ktor_version = "2.3.13" implementation 'androidx.core:core-ktx:1.12.0' implementation "androidx.compose.ui:ui:$compose_version" @@ -74,8 +75,8 @@ dependencies { implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.6.0' implementation 'com.jakewharton.timber:timber:5.0.1' implementation 'io.coil-kt:coil-compose:2.2.2' - implementation "io.ktor:ktor-client-cio:2.3.9" - implementation "com.ricoh360.thetaclient:theta-client:1.12.1" + implementation "io.ktor:ktor-client-cio:$ktor_version" + implementation "com.ricoh360.thetaclient:theta-client:1.13.0" testImplementation 'org.junit.jupiter:junit-jupiter:5.9.0' testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version" diff --git a/demos/demo-flutter/.gitignore b/demos/demo-flutter/.gitignore index e172cfd4eee..177455d342b 100644 --- a/demos/demo-flutter/.gitignore +++ b/demos/demo-flutter/.gitignore @@ -5,9 +5,11 @@ *.swp .DS_Store .atom/ +.build/ .buildlog/ .history .svn/ +.swiftpm/ migrate_working_dir/ # IntelliJ related diff --git a/demos/demo-flutter/android/app/build.gradle b/demos/demo-flutter/android/app/build.gradle deleted file mode 100644 index f1bd7c344e4..00000000000 --- a/demos/demo-flutter/android/app/build.gradle +++ /dev/null @@ -1,72 +0,0 @@ -def localProperties = new Properties() -def localPropertiesFile = rootProject.file('local.properties') -if (localPropertiesFile.exists()) { - localPropertiesFile.withReader('UTF-8') { reader -> - localProperties.load(reader) - } -} - -def flutterRoot = localProperties.getProperty('flutter.sdk') -if (flutterRoot == null) { - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") -} - -def flutterVersionCode = localProperties.getProperty('flutter.versionCode') -if (flutterVersionCode == null) { - flutterVersionCode = '1' -} - -def flutterVersionName = localProperties.getProperty('flutter.versionName') -if (flutterVersionName == null) { - flutterVersionName = '1.0' -} - -apply plugin: 'com.android.application' -apply plugin: 'kotlin-android' -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" - -android { - compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion - - compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 - } - - kotlinOptions { - jvmTarget = '1.8' - } - - sourceSets { - main.java.srcDirs += 'src/main/kotlin' - } - - defaultConfig { - // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). - applicationId "com.example.demo_flutter" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion 26 - targetSdkVersion flutter.targetSdkVersion - versionCode flutterVersionCode.toInteger() - versionName flutterVersionName - } - - buildTypes { - release { - // TODO: Add your own signing config for the release build. - // Signing with the debug keys for now, so `flutter run --release` works. - signingConfig signingConfigs.debug - proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' - } - } -} - -flutter { - source '../..' -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" -} diff --git a/demos/demo-flutter/android/app/build.gradle.kts b/demos/demo-flutter/android/app/build.gradle.kts new file mode 100644 index 00000000000..1d294e9889b --- /dev/null +++ b/demos/demo-flutter/android/app/build.gradle.kts @@ -0,0 +1,44 @@ +plugins { + id("com.android.application") + id("kotlin-android") + // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins. + id("dev.flutter.flutter-gradle-plugin") +} + +android { + namespace = "com.example.demo_flutter" + compileSdk = flutter.compileSdkVersion + ndkVersion = flutter.ndkVersion + + compileOptions { + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 + } + + kotlinOptions { + jvmTarget = JavaVersion.VERSION_11.toString() + } + + defaultConfig { + // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). + applicationId = "com.example.demo_flutter" + // You can update the following values to match your application needs. + // For more information, see: https://flutter.dev/to/review-gradle-config. + minSdk = 26 + targetSdk = flutter.targetSdkVersion + versionCode = 1 + versionName = "1.0" + } + + buildTypes { + release { + // TODO: Add your own signing config for the release build. + // Signing with the debug keys for now, so `flutter run --release` works. + signingConfig = signingConfigs.getByName("debug") + } + } +} + +flutter { + source = "../.." +} diff --git a/demos/demo-flutter/android/build.gradle b/demos/demo-flutter/android/build.gradle deleted file mode 100644 index 9bc02fa3db7..00000000000 --- a/demos/demo-flutter/android/build.gradle +++ /dev/null @@ -1,31 +0,0 @@ -buildscript { - ext.kotlin_version = '1.8.20' - repositories { - google() - mavenCentral() - } - - dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" - } -} - -allprojects { - repositories { - google() - mavenCentral() - } -} - -rootProject.buildDir = '../build' -subprojects { - project.buildDir = "${rootProject.buildDir}/${project.name}" -} -subprojects { - project.evaluationDependsOn(':app') -} - -tasks.register("clean", Delete) { - delete rootProject.buildDir -} diff --git a/demos/demo-flutter/android/build.gradle.kts b/demos/demo-flutter/android/build.gradle.kts new file mode 100644 index 00000000000..89176ef44e8 --- /dev/null +++ b/demos/demo-flutter/android/build.gradle.kts @@ -0,0 +1,21 @@ +allprojects { + repositories { + google() + mavenCentral() + } +} + +val newBuildDir: Directory = rootProject.layout.buildDirectory.dir("../../build").get() +rootProject.layout.buildDirectory.value(newBuildDir) + +subprojects { + val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name) + project.layout.buildDirectory.value(newSubprojectBuildDir) +} +subprojects { + project.evaluationDependsOn(":app") +} + +tasks.register("clean") { + delete(rootProject.layout.buildDirectory) +} diff --git a/demos/demo-flutter/android/gradle.properties b/demos/demo-flutter/android/gradle.properties index 94adc3a3f97..f018a61817f 100644 --- a/demos/demo-flutter/android/gradle.properties +++ b/demos/demo-flutter/android/gradle.properties @@ -1,3 +1,3 @@ -org.gradle.jvmargs=-Xmx1536M +org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true diff --git a/demos/demo-flutter/android/gradle/wrapper/gradle-wrapper.properties b/demos/demo-flutter/android/gradle/wrapper/gradle-wrapper.properties index cb24abda10a..afa1e8eb0a8 100644 --- a/demos/demo-flutter/android/gradle/wrapper/gradle-wrapper.properties +++ b/demos/demo-flutter/android/gradle/wrapper/gradle-wrapper.properties @@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip diff --git a/demos/demo-flutter/android/settings.gradle b/demos/demo-flutter/android/settings.gradle deleted file mode 100644 index 44e62bcf06a..00000000000 --- a/demos/demo-flutter/android/settings.gradle +++ /dev/null @@ -1,11 +0,0 @@ -include ':app' - -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") -def properties = new Properties() - -assert localPropertiesFile.exists() -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } - -def flutterSdkPath = properties.getProperty("flutter.sdk") -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" diff --git a/demos/demo-flutter/android/settings.gradle.kts b/demos/demo-flutter/android/settings.gradle.kts new file mode 100644 index 00000000000..4ad7050fc8b --- /dev/null +++ b/demos/demo-flutter/android/settings.gradle.kts @@ -0,0 +1,25 @@ +pluginManagement { + val flutterSdkPath = run { + val properties = java.util.Properties() + file("local.properties").inputStream().use { properties.load(it) } + val flutterSdkPath = properties.getProperty("flutter.sdk") + require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" } + flutterSdkPath + } + + includeBuild("$flutterSdkPath/packages/flutter_tools/gradle") + + repositories { + google() + mavenCentral() + gradlePluginPortal() + } +} + +plugins { + id("dev.flutter.flutter-plugin-loader") version "1.0.0" + id("com.android.application") version "8.5.2" apply false + id("org.jetbrains.kotlin.android") version "1.8.22" apply false +} + +include(":app") diff --git a/demos/demo-flutter/ios/Podfile b/demos/demo-flutter/ios/Podfile index 88359b225fa..f17bddc9196 100644 --- a/demos/demo-flutter/ios/Podfile +++ b/demos/demo-flutter/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +platform :ios, '15.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/demos/demo-flutter/ios/Podfile.lock b/demos/demo-flutter/ios/Podfile.lock index c073b30aac7..ceeb38fe804 100644 --- a/demos/demo-flutter/ios/Podfile.lock +++ b/demos/demo-flutter/ios/Podfile.lock @@ -27,10 +27,10 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 - theta_client_flutter: a81f4f0b3a9f371d88f18a7b0707728db76326b2 + theta_client_flutter: 4d81bcd7ae344e358aa72b1cb216afd55f322c89 THETAClient: 0fb33ddf2994806ef4dfad9296dc337d8c9013d2 - video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3 + video_player_avfoundation: 2cef49524dd1f16c5300b9cd6efd9611ce03639b -PODFILE CHECKSUM: c4c93c5f6502fe2754f48404d3594bf779584011 +PODFILE CHECKSUM: 57c8aed26fba39d3ec9424816221f294a07c58eb COCOAPODS: 1.16.2 diff --git a/demos/demo-flutter/pubspec.lock b/demos/demo-flutter/pubspec.lock index c919b1d2586..6f8a760b06d 100644 --- a/demos/demo-flutter/pubspec.lock +++ b/demos/demo-flutter/pubspec.lock @@ -5,42 +5,42 @@ packages: dependency: transitive description: name: async - sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" + sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 url: "https://pub.dev" source: hosted - version: "2.11.0" + version: "2.12.0" boolean_selector: dependency: transitive description: name: boolean_selector - sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" + sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" url: "https://pub.dev" source: hosted - version: "2.1.1" + version: "2.1.2" characters: dependency: transitive description: name: characters - sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" + sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 url: "https://pub.dev" source: hosted - version: "1.3.0" + version: "1.4.0" clock: dependency: transitive description: name: clock - sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf + sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" collection: dependency: transitive description: name: collection - sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a + sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" url: "https://pub.dev" source: hosted - version: "1.18.0" + version: "1.19.1" csslib: dependency: transitive description: @@ -61,10 +61,10 @@ packages: dependency: transitive description: name: fake_async - sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" + sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" url: "https://pub.dev" source: hosted - version: "1.3.1" + version: "1.3.2" flutter: dependency: "direct main" description: flutter @@ -100,18 +100,18 @@ packages: dependency: transitive description: name: leak_tracker - sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" + sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec url: "https://pub.dev" source: hosted - version: "10.0.4" + version: "10.0.8" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" + sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 url: "https://pub.dev" source: hosted - version: "3.0.3" + version: "3.0.9" leak_tracker_testing: dependency: transitive description: @@ -132,34 +132,34 @@ packages: dependency: transitive description: name: matcher - sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb + sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 url: "https://pub.dev" source: hosted - version: "0.12.16+1" + version: "0.12.17" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a" + sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted - version: "0.8.0" + version: "0.11.1" meta: dependency: transitive description: name: meta - sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" + sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c url: "https://pub.dev" source: hosted - version: "1.12.0" + version: "1.16.0" path: dependency: transitive description: name: path - sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" + sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" url: "https://pub.dev" source: hosted - version: "1.9.0" + version: "1.9.1" plugin_platform_interface: dependency: transitive description: @@ -172,55 +172,55 @@ packages: dependency: transitive description: flutter source: sdk - version: "0.0.99" + version: "0.0.0" source_span: dependency: transitive description: name: source_span - sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" + sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" url: "https://pub.dev" source: hosted - version: "1.10.0" + version: "1.10.1" stack_trace: dependency: transitive description: name: stack_trace - sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" + sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" url: "https://pub.dev" source: hosted - version: "1.11.1" + version: "1.12.1" stream_channel: dependency: transitive description: name: stream_channel - sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 + sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" url: "https://pub.dev" source: hosted - version: "2.1.2" + version: "2.1.4" string_scanner: dependency: transitive description: name: string_scanner - sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" + sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" url: "https://pub.dev" source: hosted - version: "1.2.0" + version: "1.4.1" term_glyph: dependency: transitive description: name: term_glyph - sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 + sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" url: "https://pub.dev" source: hosted - version: "1.2.1" + version: "1.2.2" test_api: dependency: transitive description: name: test_api - sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" + sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd url: "https://pub.dev" source: hosted - version: "0.7.0" + version: "0.7.4" theta_client_flutter: dependency: "direct main" description: @@ -240,10 +240,10 @@ packages: dependency: "direct main" description: name: video_player - sha256: "4a8c3492d734f7c39c2588a3206707a05ee80cef52e8c7f3b2078d430c84bc17" + sha256: "48941c8b05732f9582116b1c01850b74dbee1d8520cd7e34ad4609d6df666845" url: "https://pub.dev" source: hosted - version: "2.9.2" + version: "2.9.3" video_player_android: dependency: transitive description: @@ -280,18 +280,18 @@ packages: dependency: transitive description: name: vm_service - sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" + sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" url: "https://pub.dev" source: hosted - version: "14.2.1" + version: "14.3.1" web: dependency: transitive description: name: web - sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.1.1" sdks: - dart: ">=3.4.0 <4.0.0" + dart: ">=3.7.0-0 <4.0.0" flutter: ">=3.22.0" diff --git a/demos/demo-ios/Podfile b/demos/demo-ios/Podfile index 7c5c7c60d98..4ff66a17bed 100644 --- a/demos/demo-ios/Podfile +++ b/demos/demo-ios/Podfile @@ -7,5 +7,5 @@ target 'SdkSample' do use_frameworks! # Pods for SdkSample - pod 'THETAClient', '1.12.1' + pod 'THETAClient', '1.13.0' end diff --git a/demos/demo-react-native/package.json b/demos/demo-react-native/package.json index 384578c5c4b..37ce418c912 100644 --- a/demos/demo-react-native/package.json +++ b/demos/demo-react-native/package.json @@ -13,7 +13,7 @@ "dependencies": { "@react-navigation/native": "^6.1.0", "@react-navigation/native-stack": "^6.9.5", - "theta-client-react-native": "1.12.1", + "theta-client-react-native": "1.13.0", "react": "18.2.0", "react-native": "0.71.14", "react-native-safe-area-context": "^4.4.1", diff --git a/docs/tutorial-android.ja.md b/docs/tutorial-android.ja.md index f1941c3c013..a76892fd4b7 100644 --- a/docs/tutorial-android.ja.md +++ b/docs/tutorial-android.ja.md @@ -4,7 +4,7 @@ - モジュールの`build.gradle`の`dependencies`に次を追加します。 ``` - implementation "com.ricoh360.thetaclient:theta-client:1.12.1" + implementation "com.ricoh360.thetaclient:theta-client:1.13.0" ``` - 本 SDK を使用したアプリケーションが動作するスマートフォンと THETA を無線 LAN 接続しておきます。 diff --git a/docs/tutorial-android.md b/docs/tutorial-android.md index 10bed756a1f..20b672aafe5 100644 --- a/docs/tutorial-android.md +++ b/docs/tutorial-android.md @@ -5,7 +5,7 @@ - Add following descriptions to the `dependencies` of your module's `build.gradle`. ``` - implementation "com.ricoh360.thetaclient:theta-client:1.12.1" + implementation "com.ricoh360.thetaclient:theta-client:1.13.0" ``` - Connect the wireless LAN between THETA and the smartphone that runs on the application using this SDK. diff --git a/flutter/android/build.gradle b/flutter/android/build.gradle index 511eecdc7bc..c3e94695911 100644 --- a/flutter/android/build.gradle +++ b/flutter/android/build.gradle @@ -2,15 +2,15 @@ group 'com.ricoh360.thetaclient.theta_client_flutter' version '1.0.0' buildscript { - ext.kotlin_version = '1.8.20' + ext.kotlin_version = "1.8.22" repositories { google() mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.1.2' - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" + classpath("com.android.tools.build:gradle:8.7.0") + classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version") } } @@ -21,37 +21,49 @@ allprojects { } } -apply plugin: 'com.android.library' -apply plugin: 'kotlin-android' +apply plugin: "com.android.library" +apply plugin: "kotlin-android" android { - compileSdkVersion 31 + namespace = "com.ricoh360.thetaclient.theta_client_flutter" + + compileSdk = 35 compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility = JavaVersion.VERSION_11 + targetCompatibility = JavaVersion.VERSION_11 } kotlinOptions { - jvmTarget = '1.8' + jvmTarget = JavaVersion.VERSION_11 } sourceSets { - main.java.srcDirs += 'src/main/kotlin' + main.java.srcDirs += "src/main/kotlin" + test.java.srcDirs += "src/test/kotlin" } defaultConfig { - minSdkVersion 26 + minSdk = 26 } -} -dependencies { - implementation("io.ktor:ktor-client-core:2.3.9") - implementation("io.ktor:ktor-client-cio:2.3.9") - implementation("io.ktor:ktor-client-content-negotiation:2.3.9") - implementation("io.ktor:ktor-client-logging:2.3.9") - implementation("io.ktor:ktor-serialization-kotlinx-json:2.3.9") - implementation("com.soywiz.korlibs.krypto:krypto:4.0.10") + dependencies { + testImplementation("org.jetbrains.kotlin:kotlin-test") + testImplementation("org.mockito:mockito-core:5.0.0") - implementation("com.ricoh360.thetaclient:theta-client:1.12.1") + implementation("com.soywiz.korlibs.krypto:krypto:4.0.10") + implementation("com.ricoh360.thetaclient:theta-client:1.13.0") + } + + testOptions { + unitTests.all { + useJUnitPlatform() + + testLogging { + events "passed", "skipped", "failed", "standardOut", "standardError" + outputs.upToDateWhen {false} + showStandardStreams = true + } + } + } } diff --git a/flutter/android/src/main/AndroidManifest.xml b/flutter/android/src/main/AndroidManifest.xml index 12eb8f512e5..a2f47b6057d 100644 --- a/flutter/android/src/main/AndroidManifest.xml +++ b/flutter/android/src/main/AndroidManifest.xml @@ -1,2 +1,2 @@ - + + 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 9fad0955eb1..a036bfe6601 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 @@ -4,6 +4,7 @@ import com.ricoh360.thetaclient.DigestAuth import com.ricoh360.thetaclient.ThetaRepository.* import com.ricoh360.thetaclient.capture.* import io.flutter.plugin.common.MethodCall +import kotlin.reflect.KClass const val KEY_CLIENT_MODE = "clientMode" const val KEY_NOTIFY_ID = "id" @@ -16,6 +17,30 @@ const val KEY_NOTIFY_PARAM_FILE_URL = "fileUrl" const val KEY_GPS_INFO = "gpsInfo" const val KEY_STATE_EXTERNAL_GPS_INFO = "externalGpsInfo" const val KEY_STATE_INTERNAL_GPS_INFO = "internalGpsInfo" +const val KEY_WLAN_FREQUENCY_CL_MODE_2_4 = "enable2_4" +const val KEY_WLAN_FREQUENCY_CL_MODE_5_2 = "enable5_2" +const val KEY_WLAN_FREQUENCY_CL_MODE_5_8 = "enable5_8" +const val KEY_IP_ADDRESS = "ipAddress" +const val KEY_MAC_ADDRESS = "macAddress" +const val KEY_HOST_NAME = "hostName" +const val KEY_DHCP_LEASE_ADDRESS = "dhcpLeaseAddress" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH = "pitch" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL = "roll" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW = "yaw" +const val KEY_MAX = "max" +const val KEY_MIN = "min" +const val KEY_STEP_SIZE = "stepSize" +const val KEY_SSID = "ssid" +const val KEY_SSID_STEALTH = "ssidStealth" +const val KEY_AUTH_MODE = "authMode" +const val KEY_PASSWORD = "password" +const val KEY_CONNECTION_PRIORITY = "connectionPriority" +const val KEY_SUBNET_MASK = "subnetMask" +const val KEY_DEFAULT_GATEWAY = "defaultGateway" +const val KEY_PROXY = "proxy" +const val KEY_APERTURE_SUPPORT = "apertureSupport" +const val KEY_DNS1 = "dns1" +const val KEY_DNS2 = "dns2" fun toResult(thetaInfo: ThetaInfo): Map { return mapOf( @@ -155,6 +180,68 @@ fun toResult(fileInfoList: List): List> { return result } +fun toAccessInfo(map: Map): AccessInfo { + return AccessInfo( + ssid = map[KEY_SSID] as String, + ipAddress = map[KEY_IP_ADDRESS] as String, + subnetMask = map[KEY_SUBNET_MASK] as String, + defaultGateway = map[KEY_DEFAULT_GATEWAY] as String, + dns1 = map[KEY_DNS1] as? String, + dns2 = map[KEY_DNS2] as? String, + proxyURL = map["proxyURL"] as String, + frequency = (map["frequency"] as String).let { WlanFrequencyAccessInfoEnum.valueOf(it) }, + wlanSignalStrength = map["wlanSignalStrength"] as Int, + wlanSignalLevel = map["wlanSignalLevel"] as Int, + lteSignalStrength = map["lteSignalStrength"] as Int, + lteSignalLevel = map["lteSignalLevel"] as Int, + dhcpLeaseAddress = (map[KEY_DHCP_LEASE_ADDRESS] as? List>)?.mapNotNull { toDhcpLeaseAddress(it) }?.takeIf { it.isNotEmpty() } + ) +} + +fun toResult(accessInfo: AccessInfo): Map { + val result = mutableMapOf( + KEY_SSID to accessInfo.ssid, + KEY_IP_ADDRESS to accessInfo.ipAddress, + KEY_SUBNET_MASK to accessInfo.subnetMask, + KEY_DEFAULT_GATEWAY to accessInfo.defaultGateway, + "proxyURL" to accessInfo.proxyURL, + "frequency" to accessInfo.frequency.name, + "wlanSignalStrength" to accessInfo.wlanSignalStrength, + "wlanSignalLevel" to accessInfo.wlanSignalLevel, + "lteSignalStrength" to accessInfo.lteSignalStrength, + "lteSignalLevel" to accessInfo.lteSignalLevel + ) + accessInfo.dns1?.let { + result.put(KEY_DNS1, it) + } + accessInfo.dns2?.let { + result.put(KEY_DNS2, it) + } + accessInfo.dhcpLeaseAddress?.mapNotNull { toResult(dhcpLeaseAddress = it) }?.takeIf { it.isNotEmpty() }?.let { array -> + result[KEY_DHCP_LEASE_ADDRESS] = array + } + return result +} + +fun toDhcpLeaseAddress(map: Map): DhcpLeaseAddress? { + val ipAddress = map[KEY_IP_ADDRESS] as? String + val macAddress = map[KEY_MAC_ADDRESS] as? String + val hostName = map[KEY_HOST_NAME] as? String + if (ipAddress == null || macAddress == null || hostName == null) { + return null + } + return DhcpLeaseAddress(ipAddress, macAddress, hostName) +} + +fun toResult(dhcpLeaseAddress: DhcpLeaseAddress): Map { + val result = mutableMapOf( + KEY_IP_ADDRESS to dhcpLeaseAddress.ipAddress, + KEY_MAC_ADDRESS to dhcpLeaseAddress.macAddress, + KEY_HOST_NAME to dhcpLeaseAddress.hostName + ) + return result +} + fun toAutoBracket(list: List>): BracketSettingList { val autoBracket = BracketSettingList() @@ -217,22 +304,49 @@ fun toResult(burstOption: BurstOption): Map { ) } +fun toCameraLockConfig(map: Map): CameraLockConfig { + return CameraLockConfig( + isPowerKeyLocked = map["isPowerKeyLocked"] as? Boolean, + isShutterKeyLocked = map["isShutterKeyLocked"] as? Boolean, + isModeKeyLocked = map["isModeKeyLocked"] as? Boolean, + isWlanKeyLocked = map["isWlanKeyLocked"] as? Boolean, + isFnKeyLocked = map["isFnKeyLocked"] as? Boolean, + isPanelLocked = map["isPanelLocked"] as? Boolean, + ) +} + fun toEthernetConfig(map: Map): EthernetConfig { - val proxy = map["proxy"]?.let { + val proxy = map[KEY_PROXY]?.let { @Suppress("UNCHECKED_CAST") - (it as? Map)?.let{ map -> + (it as? Map)?.let { map -> toProxy(map) } } return EthernetConfig( usingDhcp = map["usingDhcp"] as? Boolean ?: true, - ipAddress = map["ipAddress"] as? String, - subnetMask = map["subnetMask"] as? String, - defaultGateway = map["defaultGateway"] as? String, + ipAddress = map[KEY_IP_ADDRESS] as? String, + subnetMask = map[KEY_SUBNET_MASK] as? String, + defaultGateway = map[KEY_DEFAULT_GATEWAY] as? String, + dns1 = map[KEY_DNS1] as? String, + dns2 = map[KEY_DNS2] as? String, proxy = proxy ) } +fun toMobileNetworkSetting(map: Map): MobileNetworkSetting { + return MobileNetworkSetting( + roaming = (map["roaming"] as? String)?.let { RoamingEnum.valueOf(it) }, + plan = (map["plan"] as? String)?.let { PlanEnum.valueOf(it) } + ) +} + +fun toResult(mobileNetworkSetting: MobileNetworkSetting): Map { + return mapOf( + "roaming" to mobileNetworkSetting.roaming?.name, + "plan" to mobileNetworkSetting.plan?.name + ) +} + fun toGpsInfo(map: Map): GpsInfo { return GpsInfo( latitude = (map["latitude"] as Double).toFloat(), @@ -253,13 +367,24 @@ fun toOffDelay(value: Any): OffDelay? { return null } +fun toOffDelayUsb(value: Any): OffDelayUsb? { + if (value is Int) { + return OffDelayUsbSec(value) + } else if (value is String) { + getOptionValueEnum(OptionNameEnum.OffDelayUsb, value)?.let { + return it as OffDelayUsbEnum + } + } + return null +} + fun toProxy(map: Map): Proxy { return Proxy( use = map["use"] as? Boolean ?: false, url = map["url"] as? String, port = map["port"] as? Int, userid = map["userid"] as? String, - password = map["password"] as? String + password = map[KEY_PASSWORD] as? String ) } @@ -290,9 +415,9 @@ fun toTimeShift(map: Map): TimeShiftSetting { fun toTopBottomCorrectionRotation(map: Map): TopBottomCorrectionRotation { return TopBottomCorrectionRotation( - pitch = (map["pitch"] as Double).toFloat(), - roll = (map["roll"] as Double).toFloat(), - yaw = (map["yaw"] as Double).toFloat() + pitch = (map[KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH] as Double).toFloat(), + roll = (map[KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL] as Double).toFloat(), + yaw = (map[KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW] as Double).toFloat() ) } @@ -388,6 +513,26 @@ fun setTimeShiftCaptureBuilderParams(call: MethodCall, builder: TimeShiftCapture } } +fun setTimeShiftManualCaptureBuilderParams(call: MethodCall, builder: TimeShiftManualCapture.Builder) { + call.argument("_capture_interval")?.let { + if (it >= 0) { + builder.setCheckStatusCommandInterval(it.toLong()) + } + } + call.argument>(OptionNameEnum.TimeShift.name)?.let { timeShiftMap -> + val timeShift = toTimeShift(timeShiftMap) + timeShift.isFrontFirst?.let { + builder.setIsFrontFirst(it) + } + timeShift.firstInterval?.let { + builder.setFirstInterval(it) + } + timeShift.secondInterval?.let { + builder.setSecondInterval(it) + } + } +} + fun setVideoCaptureBuilderParams(call: MethodCall, builder: VideoCapture.Builder) { call.argument("_capture_interval")?.let { if (it >= 0) { @@ -497,22 +642,53 @@ fun toGetOptionsParam(data: List): List { return optionNames } +fun toResult(cameraLockConfig: CameraLockConfig): Map { + val result = mutableMapOf() + + cameraLockConfig.isPowerKeyLocked?.let { value -> + result["isPowerKeyLocked"] = value + } + cameraLockConfig.isShutterKeyLocked?.let { value -> + result["isShutterKeyLocked"] = value + } + cameraLockConfig.isModeKeyLocked?.let { value -> + result["isModeKeyLocked"] = value + } + cameraLockConfig.isWlanKeyLocked?.let { value -> + result["isWlanKeyLocked"] = value + } + cameraLockConfig.isFnKeyLocked?.let { value -> + result["isFnKeyLocked"] = value + } + cameraLockConfig.isPanelLocked?.let { value -> + result["isPanelLocked"] = value + } + + return result +} + fun toResult(ethernetConfig: EthernetConfig): Map { val result = mutableMapOf() result["usingDhcp"] = ethernetConfig.usingDhcp ethernetConfig.ipAddress?.let { value -> - result["ipAddress"] = value + result[KEY_IP_ADDRESS] = value } ethernetConfig.subnetMask?.let { value -> - result["subnetMask"] = value + result[KEY_SUBNET_MASK] = value } ethernetConfig.defaultGateway?.let { value -> - result["defaultGateway"] = value + result[KEY_DEFAULT_GATEWAY] = value + } + ethernetConfig.dns1?.let { value -> + result[KEY_DNS1] = value + } + ethernetConfig.dns2?.let { value -> + result[KEY_DNS2] = value } ethernetConfig.proxy?.let { - result["proxy"] = toResult(proxy = it) + result[KEY_PROXY] = toResult(proxy = it) } return result } @@ -538,7 +714,7 @@ fun toResult(proxy: Proxy): Map { "url" to proxy.url, "port" to proxy.port, "userid" to proxy.userid, - "password" to proxy.password + KEY_PASSWORD to proxy.password ) } @@ -558,9 +734,25 @@ fun toResult(timeShift: TimeShiftSetting): Map { fun toResult(rotation: TopBottomCorrectionRotation): Map { return mapOf( - "pitch" to rotation.pitch, - "roll" to rotation.roll, - "yaw" to rotation.yaw + KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH to rotation.pitch, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL to rotation.roll, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW to rotation.yaw + ) +} + +fun toResult(topBottomCorrectionRotationSupport: TopBottomCorrectionRotationSupport): Map { + return mapOf( + KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH to toResultValueRangeString(valueRange = topBottomCorrectionRotationSupport.pitch), + KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL to toResultValueRangeString(valueRange = topBottomCorrectionRotationSupport.roll), + KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW to toResultValueRangeString(valueRange = topBottomCorrectionRotationSupport.yaw) + ) +} + +fun toResultValueRangeString(valueRange: ValueRange): Map { + return mapOf( + KEY_MAX to valueRange.max.toString(), + KEY_MIN to valueRange.min.toString(), + KEY_STEP_SIZE to valueRange.stepSize.toString() ) } @@ -583,8 +775,26 @@ fun toResult(options: Options): Map { OptionNameEnum.ShutterVolume, OptionNameEnum.Username ) + val supportOptions = mapOf>( + OptionNameEnum.AiAutoThumbnailSupport to AiAutoThumbnailEnum::class, + OptionNameEnum.ApertureSupport to ApertureEnum::class, + OptionNameEnum.CameraControlSourceSupport to CameraControlSourceEnum::class, + OptionNameEnum.CameraPowerSupport to CameraPowerEnum::class, + OptionNameEnum.ExposureDelaySupport to ExposureDelayEnum::class, + OptionNameEnum.GpsTagRecordingSupport to GpsTagRecordingEnum::class, + OptionNameEnum.WlanFrequencySupport to WlanFrequencyEnum::class + ) + val intValueRangeSupportOptions = listOf( + OptionNameEnum.ColorTemperatureSupport, + OptionNameEnum.CompositeShootingOutputIntervalSupport, + OptionNameEnum.CompositeShootingTimeSupport, + ) OptionNameEnum.values().forEach { name -> - if (name == OptionNameEnum.AutoBracket) { + if (name == OptionNameEnum.AccessInfo) { + options.getValue(OptionNameEnum.AccessInfo)?.let { accessInfo -> + result[OptionNameEnum.AccessInfo.name] = toResult(accessInfo) + } + } else if (name == OptionNameEnum.AutoBracket) { options.getValue(OptionNameEnum.AutoBracket)?.let { autoBracket -> result[OptionNameEnum.AutoBracket.name] = toResult(autoBracket) } @@ -600,6 +810,10 @@ fun toResult(options: Options): Map { options.getValue(OptionNameEnum.BurstOption)?.let { burstOption -> result[OptionNameEnum.BurstOption.name] = toResult(burstOption) } + } else if (name == OptionNameEnum.CameraLockConfig) { + options.getValue(OptionNameEnum.CameraLockConfig)?.let { cameraLockConfig -> + result[OptionNameEnum.CameraLockConfig.name] = toResult(cameraLockConfig) + } } else if (name == OptionNameEnum.EthernetConfig) { options.getValue(OptionNameEnum.EthernetConfig)?.let { ethernetConfig -> result[OptionNameEnum.EthernetConfig.name] = toResult(ethernetConfig = ethernetConfig) @@ -608,11 +822,20 @@ fun toResult(options: Options): Map { options.getValue(OptionNameEnum.GpsInfo)?.let { gpsInfo -> result[OptionNameEnum.GpsInfo.name] = toResult(gpsInfo) } + } else if (name == OptionNameEnum.MobileNetworkSetting) { + options.getValue(OptionNameEnum.MobileNetworkSetting)?.let { mobileNetworkSetting -> + result[OptionNameEnum.MobileNetworkSetting.name] = toResult(mobileNetworkSetting) + } } else if (name == OptionNameEnum.OffDelay) { options.getValue(OptionNameEnum.OffDelay)?.let { offDelay -> result[OptionNameEnum.OffDelay.name] = if (offDelay is OffDelayEnum) offDelay.name else offDelay.sec } + } else if (name == OptionNameEnum.OffDelayUsb) { + options.getValue(OptionNameEnum.OffDelayUsb)?.let { offDelayUsb -> + result[OptionNameEnum.OffDelayUsb.name] = + if (offDelayUsb is OffDelayUsbEnum) offDelayUsb.name else offDelayUsb.sec + } } else if (name == OptionNameEnum.Proxy) { options.getValue(OptionNameEnum.Proxy)?.let { proxy -> result[OptionNameEnum.Proxy.name] = toResult(proxy) @@ -630,8 +853,20 @@ fun toResult(options: Options): Map { options.getValue(OptionNameEnum.TopBottomCorrectionRotation)?.let { rotation -> result[OptionNameEnum.TopBottomCorrectionRotation.name] = toResult(rotation) } + } else if (name == OptionNameEnum.TopBottomCorrectionRotationSupport) { + options.getValue(OptionNameEnum.TopBottomCorrectionRotationSupport)?.let { support -> + result[OptionNameEnum.TopBottomCorrectionRotationSupport.name] = toResult(topBottomCorrectionRotationSupport = support) + } + } else if (name == OptionNameEnum.WlanFrequencyClMode) { + options.getValue(OptionNameEnum.WlanFrequencyClMode)?.let { + result[OptionNameEnum.WlanFrequencyClMode.name] = toResult(it) + } } else if (valueOptions.contains(name)) { addOptionsValueToMap(options, name, result) + } else if (supportOptions.keys.contains(name)) { + addSupportOptionsValueToMap(options, name, result, supportOptions[name]!!) + } else if (intValueRangeSupportOptions.contains(name)) { + addValueRangeSupportOptionsValueToMap(options, name, result) } else { addOptionsEnumToMap(options, name, result) } @@ -651,6 +886,40 @@ fun addOptionsValueToMap(options: Options, name: OptionNameEnum, map: Mutabl } } +fun addSupportOptionsValueToMap( + options: Options, + name: OptionNameEnum, + map: MutableMap, + valueClazz: KClass<*> +) { + options.getValue>(name)?.let { list -> + val array = mutableListOf() + for (item in list) { + when { + // for enum value + java.lang.Enum::class.java.isAssignableFrom(valueClazz.java) -> { + array.add(item.toString()) + } + } + } + map[name.name] = array + } +} + +fun addValueRangeSupportOptionsValueToMap( + options: Options, + name: OptionNameEnum, + map: MutableMap +) { + options.getValue>(name)?.let { value -> + map[name.name] = mapOf( + KEY_MAX to value.max, + KEY_MIN to value.min, + KEY_STEP_SIZE to value.stepSize + ) + } +} + fun toSetOptionsParam(data: Map): Options { val options = Options() data.forEach { (key, value) -> @@ -684,6 +953,9 @@ fun setOptionValue(options: Options, name: OptionNameEnum, value: Any) { optionValue = value.toLong() } options.setValue(name, optionValue) + } else if (name == OptionNameEnum.AccessInfo) { + @Suppress("UNCHECKED_CAST") + options.setValue(name, toAccessInfo(value as Map)) } else if (name == OptionNameEnum.AutoBracket) { @Suppress("UNCHECKED_CAST") options.setValue(name, toAutoBracket(value as List>)) @@ -696,16 +968,26 @@ fun setOptionValue(options: Options, name: OptionNameEnum, value: Any) { } else if (name == OptionNameEnum.BurstOption) { @Suppress("UNCHECKED_CAST") options.setValue(name, toBurstOption(value as Map)) + } else if (name == OptionNameEnum.CameraLockConfig) { + @Suppress("UNCHECKED_CAST") + options.setValue(name, toCameraLockConfig(value as Map)) } else if (name == OptionNameEnum.EthernetConfig) { @Suppress("UNCHECKED_CAST") options.setValue(name, toEthernetConfig(value as Map)) } else if (name == OptionNameEnum.GpsInfo) { @Suppress("UNCHECKED_CAST") options.setValue(name, toGpsInfo(value as Map)) + } else if (name == OptionNameEnum.MobileNetworkSetting) { + @Suppress("UNCHECKED_CAST") + options.setValue(name, toMobileNetworkSetting(value as Map)) } else if (name == OptionNameEnum.OffDelay) { toOffDelay(value)?.let { options.setValue(name, it) } + } else if (name == OptionNameEnum.OffDelayUsb) { + toOffDelayUsb(value)?.let { + options.setValue(name, it) + } } else if (name == OptionNameEnum.Proxy) { @Suppress("UNCHECKED_CAST") options.setValue(name, toProxy(value as Map)) @@ -719,6 +1001,9 @@ fun setOptionValue(options: Options, name: OptionNameEnum, value: Any) { } else if (name == OptionNameEnum.TopBottomCorrectionRotation) { @Suppress("UNCHECKED_CAST") options.setValue(name, toTopBottomCorrectionRotation(value as Map)) + } else if (name == OptionNameEnum.WlanFrequencyClMode) { + @Suppress("UNCHECKED_CAST") + options.setValue(name, toWlanFrequencyClMode(value as Map)) } else { getOptionValueEnum(name, value as String)?.let { options.setValue(name, it) @@ -734,9 +1019,11 @@ fun getOptionValueEnum(name: OptionNameEnum, valueName: String): Any? { OptionNameEnum.BluetoothRole -> BluetoothRoleEnum.values().find { it.name == valueName } OptionNameEnum.BurstMode -> BurstModeEnum.values().find { it.name == valueName } OptionNameEnum.CameraControlSource -> CameraControlSourceEnum.values().find { it.name == valueName } + OptionNameEnum.CameraLock -> CameraLockEnum.values().find { it.name == valueName } OptionNameEnum.CameraMode -> CameraModeEnum.values().find { it.name == valueName } OptionNameEnum.CameraPower -> CameraPowerEnum.values().find { it.name == valueName } OptionNameEnum.CaptureMode -> CaptureModeEnum.values().find { it.name == valueName } + OptionNameEnum.CompassDirectionRef -> CompassDirectionRefEnum.values().find { it.name == valueName } OptionNameEnum.ContinuousNumber -> ContinuousNumberEnum.values().find { it.name == valueName } OptionNameEnum.ExposureCompensation -> ExposureCompensationEnum.values().find { it.name == valueName } OptionNameEnum.ExposureDelay -> ExposureDelayEnum.values().find { it.name == valueName } @@ -752,8 +1039,10 @@ fun getOptionValueEnum(name: OptionNameEnum, valueName: String): Any? { OptionNameEnum.Language -> LanguageEnum.values().find { it.name == valueName } OptionNameEnum.LatestEnabledExposureDelayTime -> ExposureDelayEnum.values().find { it.name == valueName } OptionNameEnum.MaxRecordableTime -> MaxRecordableTimeEnum.values().find { it.name == valueName } + OptionNameEnum.MicrophoneNoiseReduction -> MicrophoneNoiseReductionEnum.values().find { it.name == valueName } OptionNameEnum.NetworkType -> NetworkTypeEnum.values().find { it.name == valueName } OptionNameEnum.OffDelay -> OffDelayEnum.values().find { it.name == valueName } + OptionNameEnum.OffDelayUsb -> OffDelayUsbEnum.values().find { it.name == valueName } OptionNameEnum.PowerSaving -> PowerSavingEnum.values().find { it.name == valueName } OptionNameEnum.Preset -> PresetEnum.values().find { it.name == valueName } OptionNameEnum.PreviewFormat -> PreviewFormatEnum.values().find { it.name == valueName } @@ -761,10 +1050,12 @@ fun getOptionValueEnum(name: OptionNameEnum, valueName: String): Any? { OptionNameEnum.ShutterSpeed -> ShutterSpeedEnum.values().find { it.name == valueName } OptionNameEnum.SleepDelay -> SleepDelayEnum.values().find { it.name == valueName } OptionNameEnum.TopBottomCorrection -> TopBottomCorrectionOptionEnum.values().find { it.name == valueName } + OptionNameEnum.UsbConnection -> UsbConnectionEnum.values().find { it.name == valueName } OptionNameEnum.VideoStitching -> VideoStitchingEnum.values().find { it.name == valueName } OptionNameEnum.VisibilityReduction -> VisibilityReductionEnum.values().find { it.name == valueName } OptionNameEnum.WhiteBalance -> WhiteBalanceEnum.values().find { it.name == valueName } OptionNameEnum.WhiteBalanceAutoStrength -> WhiteBalanceAutoStrengthEnum.values().find { it.name == valueName } + OptionNameEnum.WlanAntennaConfig -> WlanAntennaConfigEnum.values().find { it.name == valueName } OptionNameEnum.WlanFrequency -> WlanFrequencyEnum.values().find { it.name == valueName } else -> null } @@ -774,7 +1065,7 @@ fun toDigestAuthParam(data: Map<*, *>): DigestAuth? { val username = data["username"] as? String ?: run { return null } - val password = data["password"] as? String + val password = data[KEY_PASSWORD] as? String return DigestAuth(username, password) } @@ -853,22 +1144,28 @@ fun toListAccessPointsResult(accessPointList: List): List>() accessPointList.forEach { accessPoint -> val result = mutableMapOf() - result["ssid"] = accessPoint.ssid - result["ssidStealth"] = accessPoint.ssidStealth - result["authMode"] = accessPoint.authMode.name - result["connectionPriority"] = accessPoint.connectionPriority + result[KEY_SSID] = accessPoint.ssid + result[KEY_SSID_STEALTH] = accessPoint.ssidStealth + result[KEY_AUTH_MODE] = accessPoint.authMode.name + result[KEY_CONNECTION_PRIORITY] = accessPoint.connectionPriority result["usingDhcp"] = accessPoint.usingDhcp accessPoint.ipAddress?.let { - result["ipAddress"] = it + result[KEY_IP_ADDRESS] = it } accessPoint.subnetMask?.let { - result["subnetMask"] = it + result[KEY_SUBNET_MASK] = it } accessPoint.defaultGateway?.let { - result["defaultGateway"] = it + result[KEY_DEFAULT_GATEWAY] = it + } + accessPoint.dns1?.let { + result[KEY_DNS1] = it + } + accessPoint.dns2?.let { + result[KEY_DNS2] = it } accessPoint.proxy?.let { - result["proxy"] = toResult(proxy = it) + result[KEY_PROXY] = toResult(proxy = it) } resultList.add(result) } @@ -925,6 +1222,22 @@ fun toMessageNotifyParam(message: String): Map { ) } +fun toResult(wlanFrequencyClMode: WlanFrequencyClMode): Map { + return mapOf( + KEY_WLAN_FREQUENCY_CL_MODE_2_4 to wlanFrequencyClMode.enable2_4, + KEY_WLAN_FREQUENCY_CL_MODE_5_2 to wlanFrequencyClMode.enable5_2, + KEY_WLAN_FREQUENCY_CL_MODE_5_8 to wlanFrequencyClMode.enable5_8, + ) +} + +fun toWlanFrequencyClMode(map: Map): WlanFrequencyClMode { + return WlanFrequencyClMode( + map[KEY_WLAN_FREQUENCY_CL_MODE_2_4] as Boolean, + map[KEY_WLAN_FREQUENCY_CL_MODE_5_2] as Boolean, + map[KEY_WLAN_FREQUENCY_CL_MODE_5_8] as Boolean, + ) +} + fun toCapturingNotifyParam(status: CapturingStatusEnum): Map { return mapOf( KEY_NOTIFY_PARAM_STATUS to status.name @@ -936,3 +1249,56 @@ fun toStartedNotifyParam(value: String): Map { KEY_NOTIFY_PARAM_FILE_URL to value ) } + +data class SetAccessPointParams( + val ssid: String, + val ssidStealth: Boolean?, + val authMode: AuthModeEnum, + val password: String?, + val connectionPriority: Int?, + val dns1: String?, + val dns2: String?, + val proxy: Proxy?, +) + +fun toSetAccessPointParams(arguments: Map<*, *>): SetAccessPointParams { + val ssid = arguments[KEY_SSID] as? String ?: throw IllegalArgumentException(KEY_SSID) + val ssidStealth = arguments[KEY_SSID_STEALTH] as? Boolean + val authMode = (arguments[KEY_AUTH_MODE] as? String?)?.let { + AuthModeEnum.valueOf(it) + } ?: throw IllegalArgumentException(KEY_AUTH_MODE) + val password = arguments[KEY_PASSWORD] as? String + val connectionPriority = arguments[KEY_CONNECTION_PRIORITY] as? Int + val dns1 = arguments[KEY_DNS1] as? String + val dns2 = arguments[KEY_DNS2] as? String + val proxy = (arguments[KEY_PROXY] as? Map)?.let { toProxy(it) } + + return SetAccessPointParams( + ssid = ssid, + ssidStealth = ssidStealth, + authMode = authMode, + password = password, + connectionPriority = connectionPriority, + dns1 = dns1, + dns2 = dns2, + proxy = proxy, + ) +} + +data class SetAccessPointStaticallyParams( + val ipAddress: String, + val subnetMask: String, + val defaultGateway: String, +) + +fun toSetAccessPointStaticallyParams(arguments: Map<*, *>): SetAccessPointStaticallyParams { + val ipAddress = arguments[KEY_IP_ADDRESS] as? String ?: throw IllegalArgumentException(KEY_IP_ADDRESS) + val subnetMask = arguments[KEY_SUBNET_MASK] as? String ?: throw IllegalArgumentException(KEY_SUBNET_MASK) + val defaultGateway = arguments[KEY_DEFAULT_GATEWAY] as? String ?: throw IllegalArgumentException(KEY_DEFAULT_GATEWAY) + + return SetAccessPointStaticallyParams( + ipAddress = ipAddress, + subnetMask = subnetMask, + defaultGateway = defaultGateway, + ) +} 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 0f356171832..d64ddd859ab 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 @@ -34,6 +34,9 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { var timeShiftCaptureBuilder: TimeShiftCapture.Builder? = null var timeShiftCapture: TimeShiftCapture? = null var timeShiftCapturing: TimeShiftCapturing? = null + var timeShiftManualCaptureBuilder: TimeShiftManualCapture.Builder? = null + var timeShiftManualCapture: TimeShiftManualCapture? = null + var timeShiftManualCapturing: TimeShiftManualCapturing? = null var videoCaptureBuilder: VideoCapture.Builder? = null var videoCapture: VideoCapture? = null var videoCapturing: VideoCapturing? = null @@ -63,6 +66,7 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { const val messageLivePreviewRunning: String = "Live preview is running." const val eventNameNotify = "theta_client_flutter/theta_notify" + const val notifyIdApiLog = 1 const val notifyIdLivePreview = 10001 const val notifyIdTimeShiftProgress = 10011 const val notifyIdTimeShiftStopError = 10012 @@ -88,6 +92,9 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { const val notifyIdVideoCaptureCapturing = 10082 const val notifyIdVideoCaptureStarted = 10083 const val notifyIdConvertVideoFormatsProgress = 10091 + const val notifyIdTimeShiftManualProgress = 10101 + const val notifyIdTimeShiftManualStopError = 10102 + const val notifyIdTimeShiftManualCapturing = 10103 } fun sendNotifyEvent(id: Int, params: Map) { @@ -120,6 +127,12 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { result.success("Android ${android.os.Build.VERSION.RELEASE}") } + "setApiLogListener" -> { + scope.launch { + setApiLogListener(call, result) + } + } + "initialize" -> { scope.launch { initialize(call, result) @@ -230,6 +243,28 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { stopTimeShiftCapture(result) } + "getTimeShiftManualCaptureBuilder" -> { + getTimeShiftManualCaptureBuilder(result) + } + + "buildTimeShiftManualCapture" -> { + scope.launch { + buildTimeShiftManualCapture(call, result) + } + } + + "startTimeShiftManualCapture" -> { + startTimeShiftManualCapture(result) + } + + "startTimeShiftManualSecondCapture" -> { + startTimeShiftManualSecondCapture(result) + } + + "stopTimeShiftManualCapture" -> { + stopTimeShiftManualCapture(result) + } + "getVideoCaptureBuilder" -> { getVideoCaptureBuilder(result) } @@ -370,6 +405,12 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { } } + "reboot" -> { + scope.launch { + reboot(result) + } + } + "reset" -> { scope.launch { reset(result) @@ -506,6 +547,20 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { channel.setMethodCallHandler(null) } + suspend fun setApiLogListener(call: MethodCall, result: Result) { + if ((call.arguments as? Boolean) ?: false) { + com.ricoh360.thetaclient.setApiLogListener { message: String -> + sendNotifyEvent( + notifyIdApiLog, + mapOf(KEY_NOTIFY_PARAM_MESSAGE to message) + ) + } + } else { + com.ricoh360.thetaclient.setApiLogListener(null) + } + result.success(null) + } + suspend fun initialize(call: MethodCall, result: Result) { thetaRepository = null previewing = false @@ -514,6 +569,9 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { timeShiftCaptureBuilder = null timeShiftCapture = null timeShiftCapturing = null + timeShiftManualCaptureBuilder = null + timeShiftManualCapture = null + timeShiftManualCapturing = null videoCaptureBuilder = null videoCapture = null videoCapturing = null @@ -775,6 +833,99 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { result.success(null) } + fun getTimeShiftManualCaptureBuilder(result: Result) { + val theta = thetaRepository + if (theta == null) { + result.error(errorCode, messageNotInit, null) + return + } + timeShiftManualCaptureBuilder = theta.getTimeShiftManualCaptureBuilder() + result.success(null) + } + + suspend fun buildTimeShiftManualCapture(call: MethodCall, result: Result) { + val theta = thetaRepository + val timeShiftManualCaptureBuilder = timeShiftManualCaptureBuilder + if (theta == null || timeShiftManualCaptureBuilder == null) { + result.error(errorCode, messageNotInit, null) + return + } + timeShiftManualCapture = null + timeShiftManualCapturing = null + setCaptureBuilderParams(call, timeShiftManualCaptureBuilder) + setTimeShiftManualCaptureBuilderParams(call, timeShiftManualCaptureBuilder) + try { + timeShiftManualCapture = timeShiftManualCaptureBuilder.build() + result.success(null) + } catch (e: Exception) { + result.error(e.javaClass.simpleName, e.message, null) + } + } + + fun startTimeShiftManualCapture(result: Result) { + val theta = thetaRepository + val timeShiftManualCapture = timeShiftManualCapture + if (theta == null || timeShiftManualCapture == null) { + result.error(errorCode, messageNotInit, null) + return + } + timeShiftManualCapturing = + timeShiftManualCapture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + result.error(exception.javaClass.simpleName, exception.message, null) + timeShiftManualCapturing = null + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + sendNotifyEvent( + notifyIdTimeShiftManualStopError, + toMessageNotifyParam(exception.message ?: exception.toString()) + ) + } + + override fun onProgress(completion: Float) { + sendNotifyEvent( + notifyIdTimeShiftManualProgress, + toCaptureProgressNotifyParam(completion) + ) + } + + override fun onCapturing(status: CapturingStatusEnum) { + sendNotifyEvent( + notifyIdTimeShiftManualCapturing, + toCapturingNotifyParam(status) + ) + } + + override fun onCaptureCompleted(fileUrl: String?) { + result.success(fileUrl) + timeShiftManualCapturing = null + } + }) + } + + fun startTimeShiftManualSecondCapture(result: Result) { + val theta = thetaRepository + val timeShiftManualCapturing = timeShiftManualCapturing + if (theta == null || timeShiftManualCapturing == null) { + result.error(errorCode, messageNotInit, null) + return + } + timeShiftManualCapturing.startSecondCapture() + result.success(null) + } + + fun stopTimeShiftManualCapture(result: Result) { + val theta = thetaRepository + val timeShiftManualCapturing = timeShiftManualCapturing + if (theta == null || timeShiftManualCapturing == null) { + result.error(errorCode, messageNotInit, null) + return + } + timeShiftManualCapturing.stopCapture() + result.success(null) + } + fun getVideoCaptureBuilder(result: Result) { if (thetaRepository == null) { result.error(errorCode, messageNotInit, null) @@ -1452,6 +1603,19 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { } } + suspend fun reboot(result: Result) { + if (thetaRepository == null) { + result.error(errorCode, messageNotInit, null) + return + } + try { + thetaRepository?.reboot() + result.success(null) + } catch (e: Exception) { + result.error(e.javaClass.simpleName, e.message, null) + } + } + suspend fun reset(result: Result) { if (thetaRepository == null) { result.error(errorCode, messageNotInit, null) @@ -1552,20 +1716,15 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { return } try { - val ssid = call.argument("ssid")!! - val ssidStealth = call.argument("ssidStealth")!! - val authModeName = call.argument("authMode")!! - val authMode = ThetaRepository.AuthModeEnum.values().find { - it.name == authModeName - }!! - val password = call.argument("password")!! - val connectionPriority = call.argument("connectionPriority")!! - - var proxy: ThetaRepository.Proxy? = null - (call.argument("proxy") as? Map)?.let { - proxy = toProxy(map = it) - } - thetaRepository?.setAccessPointDynamically(ssid, ssidStealth, authMode, password, connectionPriority, proxy) + val params = toSetAccessPointParams(call.arguments as Map<*, *>) + thetaRepository?.setAccessPointDynamically( + params.ssid, + params.ssidStealth, + params.authMode, + params.password, + params.connectionPriority, + params.proxy + ) result.success(null) } catch (e: Exception) { result.error(e.javaClass.simpleName, e.message, null) @@ -1578,23 +1737,21 @@ class ThetaClientFlutterPlugin : FlutterPlugin, MethodCallHandler { return } try { - val ssid = call.argument("ssid")!! - val ssidStealth = call.argument("ssidStealth")!! - val authModeName = call.argument("authMode")!! - val authMode = ThetaRepository.AuthModeEnum.values().find { - it.name == authModeName - }!! - val password = call.argument("password")!! - val connectionPriority = call.argument("connectionPriority")!! - val ipAddress = call.argument("ipAddress")!! - val subnetMask = call.argument("subnetMask")!! - val defaultGateway = call.argument("defaultGateway")!! - - var proxy: ThetaRepository.Proxy? = null - (call.argument("proxy") as? Map)?.let { - proxy = toProxy(map = it) - } - thetaRepository?.setAccessPointStatically(ssid, ssidStealth, authMode, password, connectionPriority, ipAddress, subnetMask, defaultGateway, proxy) + val params = toSetAccessPointParams(call.arguments as Map<*, *>) + val staticallyParams = toSetAccessPointStaticallyParams(call.arguments as Map<*, *>) + thetaRepository?.setAccessPointStatically( + params.ssid, + params.ssidStealth, + params.authMode, + params.password, + params.connectionPriority, + staticallyParams.ipAddress, + staticallyParams.subnetMask, + staticallyParams.defaultGateway, + params.dns1, + params.dns2, + params.proxy + ) result.success(null) } catch (e: Exception) { result.error(e.javaClass.simpleName, e.message, null) diff --git a/flutter/ios/Classes/ConvertUtil.swift b/flutter/ios/Classes/ConvertUtil.swift index 169c20e0ac6..5ba6d40ebae 100644 --- a/flutter/ios/Classes/ConvertUtil.swift +++ b/flutter/ios/Classes/ConvertUtil.swift @@ -15,6 +15,46 @@ let KEY_STATE_EXTERNAL_GPS_INFO = "externalGpsInfo" let KEY_STATE_INTERNAL_GPS_INFO = "internalGpsInfo" let KEY_STATE_BOARD_TEMP = "boardTemp" let KEY_STATE_BATTERY_TEMP = "batteryTemp" +let KEY_SSID = "ssid" +let KEY_SSID_STEALTH = "ssidStealth" +let KEY_CONNECTION_PRIORITY = "connectionPriority" +let KEY_AUTH_MODE = "authMode" +let KEY_PASSWORD = "password" +let KEY_PROXY = "proxy" +let KEY_IP_ADDRESS = "ipAddress" +let KEY_SUBNET_MASK = "subnetMask" +let KEY_DEFAULT_GATEWAY = "defaultGateway" +let KEY_MAC_ADDRESS = "macAddress" +let KEY_HOST_NAME = "hostName" +let KEY_DHCP_LEASE_ADDRESS = "dhcpLeaseAddress" +let KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH = "pitch" +let KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL = "roll" +let KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW = "yaw" +let KEY_MAX = "max" +let KEY_MIN = "min" +let KEY_STEP_SIZE = "stepSize" +let KEY_APERTURE_SUPPORT = "apertureSupport" +let KEY_WLAN_FREQUENCY_CL_MODE_2_4 = "enable2_4" +let KEY_WLAN_FREQUENCY_CL_MODE_5_2 = "enable5_2" +let KEY_WLAN_FREQUENCY_CL_MODE_5_8 = "enable5_8" +let KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED = "isPowerKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED = "isShutterKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED = "isModeKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED = "isWlanKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED = "isFnKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED = "isPanelLocked" +let KEY_DNS1 = "dns1" +let KEY_DNS2 = "dns2" + +let supportOptions: [ThetaRepository.OptionNameEnum: Any.Type] = [ + ThetaRepository.OptionNameEnum.aiautothumbnailsupport: ThetaRepository.AiAutoThumbnailEnum.self, + ThetaRepository.OptionNameEnum.aperturesupport: ThetaRepository.ApertureEnum.self, + ThetaRepository.OptionNameEnum.cameracontrolsourcesupport: ThetaRepository.CameraControlSourceEnum.self, + ThetaRepository.OptionNameEnum.camerapowersupport: ThetaRepository.CameraPowerEnum.self, + ThetaRepository.OptionNameEnum.exposuredelaysupport: ThetaRepository.ExposureDelayEnum.self, + ThetaRepository.OptionNameEnum.gpstagrecordingsupport: ThetaRepository.GpsTagRecordingEnum.self, + ThetaRepository.OptionNameEnum.wlanfrequencysupport: ThetaRepository.WlanFrequencyEnum.self, +] public class ConvertUtil: NSObject {} @@ -33,7 +73,7 @@ func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] fileInfoList.forEach { fileInfo in var item = [ "name": fileInfo.name, - "fileUrl": fileInfo.fileUrl, + KEY_NOTIFY_PARAM_FILE_URL: fileInfo.fileUrl, "size": fileInfo.size, "dateTime": fileInfo.dateTime, "thumbnailUrl": fileInfo.thumbnailUrl, @@ -98,7 +138,7 @@ func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] } func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { - return [ + [ "manufacturer": thetaInfo.manufacturer, "model": thetaInfo.model, "serialNumber": thetaInfo.serialNumber, @@ -119,7 +159,7 @@ func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { } func convertResult(cameraErrorList: [ThetaRepository.CameraErrorEnum]?) -> [String]? { - guard let cameraErrorList = cameraErrorList else { + guard let cameraErrorList else { return nil } var result: [String] = [] @@ -206,7 +246,7 @@ func convertResult(thetaState: ThetaRepository.ThetaState) -> [String: Any?] { return result } -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) @@ -298,6 +338,26 @@ func setTimeShiftCaptureBuilderParams(params: [String: Any], builder: TimeShiftC } } +func setTimeShiftManualCaptureBuilderParams(params: [String: Any], builder: TimeShiftManualCapture.Builder) { + if let interval = params["_capture_interval"] as? Int, + interval >= 0 + { + builder.setCheckStatusCommandInterval(timeMillis: Int64(interval)) + } + 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) + } + 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 interval = params["_capture_interval"] as? Int, interval >= 0 @@ -600,7 +660,7 @@ func toBurstOption(params: [String: Any]) -> ThetaRepository.BurstOption { } func convertResult(burstOption: ThetaRepository.BurstOption) -> [String: Any?] { - return [ + [ "burstCaptureNum": burstOption.burstCaptureNum?.name, "burstBracketStep": burstOption.burstBracketStep?.name, "burstCompensation": burstOption.burstCompensation?.name, @@ -610,26 +670,81 @@ func convertResult(burstOption: ThetaRepository.BurstOption) -> [String: Any?] { ] } +func toAccessInfo(params: [String: Any]) -> ThetaRepository.AccessInfo { + let frequency = getEnumValue(values: ThetaRepository.WlanFrequencyAccessInfoEnum.values(), name: params["frequency"] as! String) + let dhcpLeaseAddress: [ThetaRepository.DhcpLeaseAddress]? = { + guard let list = params[KEY_DHCP_LEASE_ADDRESS] as? [[String: Any?]] else { + return nil + } + + let array = list.compactMap { toDhcpLeaseAddress(value: $0) } + return array.isEmpty ? nil : array + }() + + return ThetaRepository.AccessInfo( + ssid: params[KEY_SSID] as! String, + ipAddress: params[KEY_IP_ADDRESS] as! String, + subnetMask: params[KEY_SUBNET_MASK] as! String, + defaultGateway: params[KEY_DEFAULT_GATEWAY] as! String, + dns1: params[KEY_DNS1] as? String, + dns2: params[KEY_DNS2] as? String, + proxyURL: params["proxyURL"] as! String, + frequency: frequency as! ThetaRepository.WlanFrequencyAccessInfoEnum, + wlanSignalStrength: params["wlanSignalStrength"] as! Int32, + wlanSignalLevel: params["wlanSignalLevel"] as! Int32, + lteSignalStrength: params["lteSignalStrength"] as! Int32, + lteSignalLevel: params["lteSignalLevel"] as! Int32, + dhcpLeaseAddress: dhcpLeaseAddress + ) +} + +func toDhcpLeaseAddress(value: [String: Any?]) -> ThetaRepository.DhcpLeaseAddress? { + guard let ipAddress = value[KEY_IP_ADDRESS] as? String, + let macAddress = value[KEY_MAC_ADDRESS] as? String, + let hostName = value[KEY_HOST_NAME] as? String + else { + return nil + } + return ThetaRepository.DhcpLeaseAddress( + ipAddress: ipAddress, + macAddress: macAddress, + hostName: hostName + ) +} + +func toCameraLockConfig(params: [String: Any]) -> ThetaRepository.CameraLockConfig { + ThetaRepository.CameraLockConfig( + isPowerKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED]), + isShutterKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED]), + isModeKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED]), + isWlanKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED]), + isFnKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED]), + isPanelLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED]) + ) +} + func toEthernetConfig(params: [String: Any]) -> ThetaRepository.EthernetConfig { let proxy: ThetaRepository.Proxy? = { - if let proxyMap = params["proxy"] as? [String: Any] { + if let proxyMap = params[KEY_PROXY] as? [String: Any] { toProxy(params: proxyMap) } else { nil } }() - + return ThetaRepository.EthernetConfig( usingDhcp: params["usingDhcp"] as? Bool ?? true, - ipAddress: params["ipAddress"] as? String, - subnetMask: params["subnetMask"] as? String, - defaultGateway: params["defaultGateway"] as? String, + ipAddress: params[KEY_IP_ADDRESS] as? String, + subnetMask: params[KEY_SUBNET_MASK] as? String, + defaultGateway: params[KEY_DEFAULT_GATEWAY] as? String, + dns1: params[KEY_DNS1] as? String, + dns2: params[KEY_DNS2] as? String, proxy: proxy ) } func toGpsInfo(params: [String: Any]) -> ThetaRepository.GpsInfo { - return ThetaRepository.GpsInfo( + ThetaRepository.GpsInfo( latitude: Float(params["latitude"] as? Double ?? 0), longitude: Float(params["longitude"] as? Double ?? 0), altitude: Float(params["altitude"] as? Double ?? 0), @@ -637,6 +752,20 @@ func toGpsInfo(params: [String: Any]) -> ThetaRepository.GpsInfo { ) } +func toMobileNetworkSetting(params: [String: Any]) -> ThetaRepository.MobileNetworkSetting { + let roaming: ThetaRepository.RoamingEnum? = if let strValue = params["roaming"] as? String { + getEnumValue(values: ThetaRepository.RoamingEnum.values(), name: strValue) + } else { + nil + } + let plan: ThetaRepository.PlanEnum? = if let strValue = params["plan"] as? String { + getEnumValue(values: ThetaRepository.PlanEnum.values(), name: strValue) + } else { + nil + } + return ThetaRepository.MobileNetworkSetting(roaming: roaming, plan: plan) +} + func toOffDelay(value: Any) -> ThetaRepositoryOffDelay? { if let strValue = value as? String { return getEnumValue(values: ThetaRepository.OffDelayEnum.values(), name: strValue) @@ -646,13 +775,22 @@ func toOffDelay(value: Any) -> ThetaRepositoryOffDelay? { return nil } +func toOffDelayUsb(value: Any) -> ThetaRepositoryOffDelayUsb? { + if let strValue = value as? String { + return getEnumValue(values: ThetaRepository.OffDelayUsbEnum.values(), name: strValue) + } else if let intValue = value as? Int32 { + return ThetaRepository.OffDelayUsbSec(sec: intValue) + } + return nil +} + func toProxy(params: [String: Any]) -> ThetaRepository.Proxy { - return ThetaRepository.Proxy( + 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 + password: params[KEY_PASSWORD] as? String ) } @@ -684,10 +822,10 @@ func toTimeShift(params: [String: Any]) -> ThetaRepository.TimeShiftSetting { } func toTopBottomCorrectionRotation(params: [String: Any]) -> ThetaRepository.TopBottomCorrectionRotation { - return ThetaRepository.TopBottomCorrectionRotation( - pitch: Float(params["pitch"] as? Double ?? 0), - roll: Float(params["roll"] as? Double ?? 0), - yaw: Float(params["yaw"] as? Double ?? 0) + ThetaRepository.TopBottomCorrectionRotation( + pitch: Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH] as? Double ?? 0), + roll: Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL] as? Double ?? 0), + yaw: Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW] as? Double ?? 0) ) } @@ -700,27 +838,99 @@ func convertGetOptionsParam(params: [String]) -> [ThetaRepository.OptionNameEnum return array } +func convertValueRangeSupportResult(valueRange: ThetaRepositoryValueRange) -> [String: Any] { + [ + KEY_MAX: valueRange.max, + KEY_MIN: valueRange.min, + KEY_STEP_SIZE: valueRange.stepSize, + ] +} + +func convertResult(accessInfo: ThetaRepository.AccessInfo) -> [String: Any?] { + var result: [String: Any?] = [ + KEY_SSID: accessInfo.ssid, + KEY_IP_ADDRESS: accessInfo.ipAddress, + KEY_SUBNET_MASK: accessInfo.subnetMask, + KEY_DEFAULT_GATEWAY: accessInfo.defaultGateway, + KEY_DNS1: accessInfo.dns1, + KEY_DNS2: accessInfo.dns2, + "proxyURL": accessInfo.proxyURL, + "frequency": accessInfo.frequency.name, + "wlanSignalStrength": accessInfo.wlanSignalStrength, + "wlanSignalLevel": accessInfo.wlanSignalLevel, + "lteSignalStrength": accessInfo.lteSignalStrength, + "lteSignalLevel": accessInfo.lteSignalLevel, + ] + + if let list = accessInfo.dhcpLeaseAddress { + let array = list.map { item in convertResult(dhcpLeaseAddress: item) } + if !array.isEmpty { + result[KEY_DHCP_LEASE_ADDRESS] = array + } + } + return result +} + +func convertResult(dhcpLeaseAddress: ThetaRepository.DhcpLeaseAddress) -> [String: Any] { + [ + KEY_IP_ADDRESS: dhcpLeaseAddress.ipAddress, + KEY_MAC_ADDRESS: dhcpLeaseAddress.macAddress, + KEY_HOST_NAME: dhcpLeaseAddress.hostName, + ] +} + +func convertResult(cameraLockConfig: ThetaRepository.CameraLockConfig) -> [String: Any] { + var result: [String: Any] = [:] + + if let isPowerKeyLocked = cameraLockConfig.isPowerKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED] = isPowerKeyLocked.boolValue + } + if let isShutterKeyLocked = cameraLockConfig.isShutterKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED] = isShutterKeyLocked.boolValue + } + if let isModeKeyLocked = cameraLockConfig.isModeKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED] = isModeKeyLocked.boolValue + } + if let isWlanKeyLocked = cameraLockConfig.isWlanKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED] = isWlanKeyLocked.boolValue + } + if let isFnKeyLocked = cameraLockConfig.isFnKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED] = isFnKeyLocked.boolValue + } + if let isPanelLocked = cameraLockConfig.isPanelLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED] = isPanelLocked.boolValue + } + + return result +} + func convertResult(ethernetConfig: ThetaRepository.EthernetConfig) -> [String: Any] { var result: [String: Any] = [ - "usingDhcp": ethernetConfig.usingDhcp + "usingDhcp": ethernetConfig.usingDhcp, ] if let ipAddress = ethernetConfig.ipAddress { - result["ipAddress"] = ipAddress + result[KEY_IP_ADDRESS] = ipAddress } if let subnetMask = ethernetConfig.subnetMask { - result["subnetMask"] = subnetMask + result[KEY_SUBNET_MASK] = subnetMask } if let defaultGateway = ethernetConfig.defaultGateway { - result["defaultGateway"] = defaultGateway + result[KEY_DEFAULT_GATEWAY] = defaultGateway + } + if let dns1 = ethernetConfig.dns1 { + result[KEY_DNS1] = dns1 + } + if let dns2 = ethernetConfig.dns2 { + result[KEY_DNS2] = dns2 } if let proxy = ethernetConfig.proxy { - result["proxy"] = convertResult(proxy: proxy) + result[KEY_PROXY] = convertResult(proxy: proxy) } return result } func convertResult(gpsInfo: ThetaRepository.GpsInfo) -> [String: Any] { - return [ + [ "latitude": gpsInfo.latitude, "longitude": gpsInfo.longitude, "altitude": gpsInfo.altitude, @@ -728,6 +938,14 @@ func convertResult(gpsInfo: ThetaRepository.GpsInfo) -> [String: Any] { ] } +func convertResult(mobileNetworkSetting: ThetaRepository.MobileNetworkSetting) -> [String: Any?] { + var result: [String: Any?] = [ + "roaming": mobileNetworkSetting.roaming?.name, + "plan": mobileNetworkSetting.plan?.name, + ] + return result +} + func convertResult(stateGpsInfo: ThetaRepository.StateGpsInfo) -> [String: Any] { guard let gpsInfo = stateGpsInfo.gpsInfo else { return [:] } return [ @@ -736,17 +954,17 @@ func convertResult(stateGpsInfo: ThetaRepository.StateGpsInfo) -> [String: Any] } func convertResult(proxy: ThetaRepository.Proxy) -> [String: Any] { - return [ + [ "use": proxy.use, "url": proxy.url, "port": proxy.port, "userid": proxy.userid, - "password": proxy.password, + KEY_PASSWORD: proxy.password, ] } func convertResult(timeshift: ThetaRepository.TimeShiftSetting) -> [String: Any] { - return [ + [ "isFrontFirst": convertKotlinBooleanToBool(value: timeshift.isFrontFirst), "firstInterval": timeshift.firstInterval?.name, "secondInterval": timeshift.secondInterval?.name, @@ -754,10 +972,33 @@ func convertResult(timeshift: ThetaRepository.TimeShiftSetting) -> [String: Any] } func convertResult(rotation: ThetaRepository.TopBottomCorrectionRotation) -> [String: Any] { + [ + KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH: rotation.pitch, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL: rotation.roll, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW: rotation.yaw, + ] +} + +func convertResult(topBottomCorrectionRotationSupport: ThetaRepository.TopBottomCorrectionRotationSupport) -> [String: Any] { + let pitch = [ + KEY_MAX: String(topBottomCorrectionRotationSupport.pitch.max.floatValue), + KEY_MIN: String(topBottomCorrectionRotationSupport.pitch.min.floatValue), + KEY_STEP_SIZE: String(topBottomCorrectionRotationSupport.pitch.stepSize.floatValue), + ] + let roll = [ + KEY_MAX: String(topBottomCorrectionRotationSupport.roll.max.floatValue), + KEY_MIN: String(topBottomCorrectionRotationSupport.roll.min.floatValue), + KEY_STEP_SIZE: String(topBottomCorrectionRotationSupport.roll.stepSize.floatValue), + ] + let yaw = [ + KEY_MAX: String(topBottomCorrectionRotationSupport.yaw.max.floatValue), + KEY_MIN: String(topBottomCorrectionRotationSupport.yaw.min.floatValue), + KEY_STEP_SIZE: String(topBottomCorrectionRotationSupport.yaw.stepSize.floatValue), + ] return [ - "pitch": rotation.pitch, - "roll": rotation.roll, - "yaw": rotation.yaw, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH: pitch, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL: roll, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW: yaw, ] } @@ -774,22 +1015,36 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { result[name.name] = value as! Bool ? true : false } else if value is NSNumber || value is String { result[name.name] = value + } else if value is ThetaRepository.AccessInfo { + let accessInfo = value as! ThetaRepository.AccessInfo + result[name.name] = convertResult(accessInfo: accessInfo) } else if let bitrate = value as? ThetaRepository.BitrateNumber { result[name.name] = bitrate.value } else if value is ThetaRepository.BracketSettingList, let autoBracket = value as? ThetaRepository.BracketSettingList { result[name.name] = convertResult(autoBracket: autoBracket) } else if value is ThetaRepository.BurstOption, let burstOption = value as? ThetaRepository.BurstOption { result[name.name] = convertResult(burstOption: burstOption) + } else if value is ThetaRepository.CameraLockConfig, let cameraLockConfig = value as? ThetaRepository.CameraLockConfig { + result[name.name] = convertResult(cameraLockConfig: cameraLockConfig) } else if value is ThetaRepository.EthernetConfig, let ethernetConfig = value as? ThetaRepository.EthernetConfig { result[name.name] = convertResult(ethernetConfig: ethernetConfig) } else if value is ThetaRepository.GpsInfo { let gpsInfo = value as! ThetaRepository.GpsInfo result[name.name] = convertResult(gpsInfo: gpsInfo) + } else if value is ThetaRepository.MobileNetworkSetting { + let mobileNetworkSetting = value as! ThetaRepository.MobileNetworkSetting + result[name.name] = convertResult(mobileNetworkSetting: mobileNetworkSetting) } else if let offDelay = value as? ThetaRepositoryOffDelay { if let enumValue = offDelay as? ThetaRepository.OffDelayEnum { result[name.name] = enumValue.name } else { - result[name.name] = offDelay.sec + result[name.name] = offDelay.sec_ + } + } else if let offDelayUsb = value as? ThetaRepositoryOffDelayUsb { + if let enumValue = offDelayUsb as? ThetaRepository.OffDelayUsbEnum { + result[name.name] = enumValue.name + } else { + result[name.name] = offDelayUsb.sec_ } } else if value is ThetaRepository.Proxy, let proxy = value as? ThetaRepository.Proxy { result[name.name] = convertResult(proxy: proxy) @@ -797,12 +1052,22 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { if let enumValue = sleepDelay as? ThetaRepository.SleepDelayEnum { result[name.name] = enumValue.name } else { - result[name.name] = sleepDelay.sec + result[name.name] = sleepDelay.sec_ } } else if value is ThetaRepository.TimeShiftSetting, let timeshift = value as? ThetaRepository.TimeShiftSetting { result[name.name] = convertResult(timeshift: timeshift) } else if value is ThetaRepository.TopBottomCorrectionRotation, let rotation = value as? ThetaRepository.TopBottomCorrectionRotation { result[name.name] = convertResult(rotation: rotation) + } else if value is ThetaRepository.TopBottomCorrectionRotationSupport, let support = value as? ThetaRepository.TopBottomCorrectionRotationSupport { + result[name.name] = convertResult(topBottomCorrectionRotationSupport: support) + } else if let enumValues = value as? [AnyObject], let enumType = supportOptions[name] { + result[name.name] = convertSupportResult(supportValueList: enumValues, enumType: enumType) + } else if let valueRange = value as? ThetaRepositoryValueRange { + result[name.name] = convertValueRangeSupportResult(valueRange: valueRange) + } else if value is ThetaRepository.WlanFrequencyClMode, let wlanFrequencyClMode = value as? ThetaRepository.WlanFrequencyClMode { + result[name.name] = convertResult(wlanFrequencyClMode: wlanFrequencyClMode) + } else if let enumValues = value as? [AnyObject], let enumType = supportOptions[name] { + result[name.name] = convertSupportResult(supportValueList: enumValues, enumType: enumType) } } } @@ -810,7 +1075,7 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { } func convertKotlinBooleanToBool(value: Any?) -> Bool? { - guard let value = value else { return nil } + guard let value else { return nil } guard value is KotlinBoolean, let numVal = value as? NSNumber else { return false } return numVal.boolValue @@ -840,6 +1105,10 @@ func convertSetOptionsParam(params: [String: Any]) -> ThetaRepository.Options { func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) { switch name { + case ThetaRepository.OptionNameEnum.accessinfo.name: + if let params = value as? [String: Any?] { + options.accessInfo = toAccessInfo(params: params) + } case ThetaRepository.OptionNameEnum.aiautothumbnail.name: options.aiAutoThumbnail = getEnumValue(values: ThetaRepository.AiAutoThumbnailEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.aperture.name: @@ -862,6 +1131,12 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) } case ThetaRepository.OptionNameEnum.cameracontrolsource.name: options.cameraControlSource = getEnumValue(values: ThetaRepository.CameraControlSourceEnum.values(), name: value as! String)! + case ThetaRepository.OptionNameEnum.cameralock.name: + options.cameraLock = getEnumValue(values: ThetaRepository.CameraLockEnum.values(), name: value as! String)! + case ThetaRepository.OptionNameEnum.cameralockconfig.name: + if let params = value as? [String: Any] { + options.cameraLockConfig = toCameraLockConfig(params: params) + } case ThetaRepository.OptionNameEnum.cameramode.name: options.cameraMode = getEnumValue(values: ThetaRepository.CameraModeEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.camerapower.name: @@ -874,6 +1149,8 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.captureNumber = KotlinInt(integerLiteral: value as! Int) case ThetaRepository.OptionNameEnum.colortemperature.name: options.colorTemperature = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.compassdirectionref.name: + options.compassDirectionRef = getEnumValue(values: ThetaRepository.CompassDirectionRefEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.compositeshootingoutputinterval.name: options.compositeShootingOutputInterval = KotlinInt(integerLiteral: value as! Int) case ThetaRepository.OptionNameEnum.compositeshootingtime.name: @@ -918,10 +1195,18 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.latestEnabledExposureDelayTime = getEnumValue(values: ThetaRepository.ExposureDelayEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.maxrecordabletime.name: options.maxRecordableTime = getEnumValue(values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value as! String)! + case ThetaRepository.OptionNameEnum.microphonenoisereduction.name: + options.microphoneNoiseReduction = getEnumValue(values: ThetaRepository.MicrophoneNoiseReductionEnum.values(), name: value as! String)! + case ThetaRepository.OptionNameEnum.mobilenetworksetting.name: + if let params = value as? [String: Any] { + options.mobileNetworkSetting = toMobileNetworkSetting(params: params) + } case ThetaRepository.OptionNameEnum.networktype.name: options.networkType = getEnumValue(values: ThetaRepository.NetworkTypeEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.offdelay.name: options.offDelay = toOffDelay(value: value) + case ThetaRepository.OptionNameEnum.offdelayusb.name: + options.offDelayUsb = toOffDelayUsb(value: value) case ThetaRepository.OptionNameEnum.password.name: options.password = value as? String case ThetaRepository.OptionNameEnum.powersaving.name: @@ -958,6 +1243,8 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.topBottomCorrectionRotation = toTopBottomCorrectionRotation(params: value as! [String: Any]) case ThetaRepository.OptionNameEnum.totalspace.name: options.totalSpace = KotlinLong(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.usbconnection.name: + options.usbConnection = getEnumValue(values: ThetaRepository.UsbConnectionEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.username.name: options.username = value as? String case ThetaRepository.OptionNameEnum.videostitching.name: @@ -968,18 +1255,22 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) 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.wlanantennaconfig.name: + options.wlanAntennaConfig = getEnumValue(values: ThetaRepository.WlanAntennaConfigEnum.values(), name: value as! String)! case ThetaRepository.OptionNameEnum.wlanfrequency.name: options.wlanFrequency = getEnumValue(values: ThetaRepository.WlanFrequencyEnum.values(), name: value as! String)! + case ThetaRepository.OptionNameEnum.wlanfrequencyclmode.name: + options.wlanFrequencyClMode = toWlanFrequencyClMode(params: value as! [String: Any]) default: break } } func toDigetAuth(params: [String: String?]?) -> DigestAuth? { - guard let params = params, + guard let params, let username = params["username"] as? String else { return nil } - let password = params["password"] as? String + let password = params[KEY_PASSWORD] as? String return DigestAuth(username: username, password: password) } @@ -1007,7 +1298,7 @@ func toConfig(params: [String: Any]) -> ThetaRepository.Config { } func toTimeout(params: [String: Any]) -> ThetaRepository.Timeout { - return ThetaRepository.Timeout( + ThetaRepository.Timeout( connectTimeout: params["connectTimeout"] as! Int64, requestTimeout: params["requestTimeout"] as! Int64, socketTimeout: params["socketTimeout"] as! Int64 @@ -1044,7 +1335,7 @@ func convertResult(xmp: ThetaRepository.Xmp) -> [String: Any] { } func convertResult(metadata: KotlinPair) -> [String: Any] { - return [ + [ "exif": convertResult(exif: metadata.first!), "xmp": convertResult(xmp: metadata.second!), ] @@ -1054,22 +1345,28 @@ func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: 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[KEY_SSID] = accessPoint.ssid + result[KEY_SSID_STEALTH] = accessPoint.ssidStealth + result[KEY_AUTH_MODE] = accessPoint.authMode.name + result[KEY_CONNECTION_PRIORITY] = accessPoint.connectionPriority result["usingDhcp"] = accessPoint.usingDhcp if let ipAddress = accessPoint.ipAddress { - result["ipAddress"] = ipAddress + result[KEY_IP_ADDRESS] = ipAddress } if let subnetMask = accessPoint.subnetMask { - result["subnetMask"] = subnetMask + result[KEY_SUBNET_MASK] = subnetMask } if let defaultGateway = accessPoint.defaultGateway { - result["defaultGateway"] = defaultGateway + result[KEY_DEFAULT_GATEWAY] = defaultGateway + } + if let dns1 = accessPoint.dns1 { + result[KEY_DNS1] = dns1 + } + if let dns2 = accessPoint.dns2 { + result[KEY_DNS2] = dns2 } if let proxy = accessPoint.proxy { - result["proxy"] = convertResult(proxy: proxy) + result[KEY_PROXY] = convertResult(proxy: proxy) } resultList.append(result) } @@ -1089,7 +1386,7 @@ func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[Stri "isBoot": pluginInfo.isBoot, "hasWebServer": pluginInfo.hasWebServer, "exitStatus": pluginInfo.exitStatus, - "message": pluginInfo.message, + KEY_NOTIFY_PARAM_MESSAGE: pluginInfo.message, ] resultList.append(item) } @@ -1100,38 +1397,145 @@ func toNotify(id: Int, params: [String: Any]?) -> [String: Any] { var result: [String: Any] = [ KEY_NOTIFY_ID: id, ] - if let params = params { + if let params { result[KEY_NOTIFY_PARAMS] = params } return result } func toCaptureProgressNotifyParam(value: Float) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_COMPLETION: value, ] } func toPreviewNotifyParam(imageData: FlutterStandardTypedData) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_IMAGE: imageData, ] } func toMessageNotifyParam(message: String) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_MESSAGE: message, ] } +func convertResult(wlanFrequencyClMode: ThetaRepository.WlanFrequencyClMode) -> [String: Any] { + var result = [String: Any]() + result[KEY_WLAN_FREQUENCY_CL_MODE_2_4] = wlanFrequencyClMode.enable2_4 + result[KEY_WLAN_FREQUENCY_CL_MODE_5_2] = wlanFrequencyClMode.enable5_2 + result[KEY_WLAN_FREQUENCY_CL_MODE_5_8] = wlanFrequencyClMode.enable5_8 + return result +} + +func toWlanFrequencyClMode(params: [String: Any]) -> ThetaRepository.WlanFrequencyClMode? { + guard let enable2_4 = params[KEY_WLAN_FREQUENCY_CL_MODE_2_4] as? Bool, + let enable5_2 = params[KEY_WLAN_FREQUENCY_CL_MODE_5_2] as? Bool, + let enable5_8 = params[KEY_WLAN_FREQUENCY_CL_MODE_5_8] as? Bool + else { return nil } + return ThetaRepository.WlanFrequencyClMode( + enable2_4: enable2_4, + enable5_2: enable5_2, + enable5_8: enable5_8 + ) +} + func toCapturingNotifyParam(value: CapturingStatusEnum) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_STATUS: value.name, ] } func toStartedNotifyParam(value: String) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_FILE_URL: value, ] } + +struct SetAccessPointParams { + let ssid: String + let ssidStealth: KotlinBoolean? + let authMode: ThetaRepository.AuthModeEnum + let password: String? + let connectionPriority: KotlinInt? + let dns1: String? + let dns2: String? + let proxy: ThetaRepository.Proxy? +} + +func toSetAccessPointParams(params: [String: Any?]) throws -> SetAccessPointParams { + guard let ssid = params[KEY_SSID] as? String else { + throw ThetaClientError.invalidArgument(KEY_SSID) + } + let ssidStealth = toKotlinBoolean(value: params[KEY_SSID_STEALTH] as? Bool) + guard let authMode = params[KEY_AUTH_MODE] as? String else { + throw ThetaClientError.invalidArgument(KEY_AUTH_MODE) + } + guard + let authModeEnum = getEnumValue( + values: ThetaRepository.AuthModeEnum.values(), name: authMode + ) + else { + throw ThetaClientError.invalidArgument(KEY_AUTH_MODE) + } + let password = params[KEY_PASSWORD] as? String + let connectionPriority = toKotlinInt(value: params[KEY_CONNECTION_PRIORITY] as? Int) + let dns1 = params[KEY_DNS1] as? String + let dns2 = params[KEY_DNS2] as? String + let proxy = params[KEY_PROXY] + let proxyParam: ThetaRepository.Proxy? = { + if let proxy = proxy as? [String: Any] { + return toProxy(params: proxy) + } + return nil + }() + + return SetAccessPointParams( + ssid: ssid, + ssidStealth: ssidStealth, + authMode: authModeEnum, + password: password, + connectionPriority: connectionPriority, + dns1: dns1, + dns2: dns2, + proxy: proxyParam + ) +} + +struct SetAccessPointStaticallyParams { + let ipAddress: String + let subnetMask: String + let defaultGateway: String +} + +func toSetAccessPointStaticallyParams(params: [String: Any?]) throws -> SetAccessPointStaticallyParams { + guard let ipAddress = params[KEY_IP_ADDRESS] as? String else { + throw ThetaClientError.invalidArgument(KEY_IP_ADDRESS) + } + guard let subnetMask = params[KEY_SUBNET_MASK] as? String else { + throw ThetaClientError.invalidArgument(KEY_SUBNET_MASK) + } + guard let defaultGateway = params[KEY_DEFAULT_GATEWAY] as? String else { + throw ThetaClientError.invalidArgument(KEY_DEFAULT_GATEWAY) + } + + return SetAccessPointStaticallyParams( + ipAddress: ipAddress, + subnetMask: subnetMask, + defaultGateway: defaultGateway + ) +} + +func convertSupportResult(supportValueList: [Any], enumType: Any.Type) -> [Any] { + var result = [Any]() + for item in supportValueList { + // for enum value + if let value = item as? KotlinEnum, + enumType is KotlinEnum.Type + { + result.append(value.name) + } + } + return result +} diff --git a/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift b/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift index 61afa350479..053c28de48d 100644 --- a/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift +++ b/flutter/ios/Classes/SwiftThetaClientFlutterPlugin.swift @@ -3,6 +3,7 @@ import THETAClient import UIKit let EVENT_NOTIFY = "theta_client_flutter/theta_notify" +let NOTIFY_API_LOG = 1 let NOTIFY_LIVE_PREVIEW = 10001 let NOTIFY_TIME_SHIFT_PROGRESS = 10011 let NOTIFY_TIME_SHIFT_STOP_ERROR = 10012 @@ -28,6 +29,13 @@ let NOTIFY_VIDEO_CAPTURE_STOP_ERROR = 10081 let NOTIFY_VIDEO_CAPTURE_CAPTURING = 10082 let NOTIFY_VIDEO_CAPTURE_STARTED = 10083 let NOTIFY_CONVERT_VIDEO_FORMATS_PROGRESS = 10091 +let NOTIFY_TIME_SHIFT_MANUAL_PROGRESS = 10101 +let NOTIFY_TIME_SHIFT_MANUAL_STOP_ERROR = 10102 +let NOTIFY_TIME_SHIFT_MANUAL_CAPTURING = 10103 + +enum ThetaClientError: Error { + case invalidArgument(String) +} public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStreamHandler { public func onListen(withArguments _: Any?, eventSink events: @escaping FlutterEventSink) -> FlutterError? { @@ -55,6 +63,9 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre var timeShiftCaptureBuilder: TimeShiftCapture.Builder? = nil var timeShiftCapture: TimeShiftCapture? = nil var timeShiftCapturing: TimeShiftCapturing? = nil + var timeShiftManualCaptureBuilder: TimeShiftManualCapture.Builder? = nil + var timeShiftManualCapture: TimeShiftManualCapture? = nil + var timeShiftManualCapturing: TimeShiftManualCapturing? = nil var videoCaptureBuilder: VideoCapture.Builder? = nil var videoCapture: VideoCapture? = nil var videoCapturing: VideoCapturing? = nil @@ -86,7 +97,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func sendNotifyEvent(id: Int, params: [String: Any]?) { - if let eventSink = eventSink { + if let eventSink { DispatchQueue.main.async { eventSink(toNotify(id: id, params: params)) } @@ -97,6 +108,8 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre switch call.method { case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) + case "setApiLogListener": + setApiLogListener(call: call, result: result) case "initialize": Task.detached { try await self.initialize(call: call, result: result) @@ -141,6 +154,16 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre startTimeShiftCapture(result: result) case "stopTimeShiftCapture": stopTimeShiftCapture(result: result) + case "getTimeShiftManualCaptureBuilder": + getTimeShiftManualCaptureBuilder(result: result) + case "buildTimeShiftManualCapture": + buildTimeShiftManualCapture(call: call, result: result) + case "startTimeShiftManualCapture": + startTimeShiftManualCapture(result: result) + case "startTimeShiftManualSecondCapture": + startTimeShiftManualSecondCapture(result: result) + case "stopTimeShiftManualCapture": + stopTimeShiftManualCapture(result: result) case "getVideoCaptureBuilder": getVideoCaptureBuilder(result: result) case "buildVideoCapture": @@ -201,6 +224,8 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre setOptions(call: call, result: result) case "getMetadata": getMetadata(call: call, result: result) + case "reboot": + reboot(result: result) case "reset": reset(result: result) case "stopSelfTimer": @@ -248,6 +273,22 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } + func setApiLogListener(call: FlutterMethodCall, result: @escaping FlutterResult) { + Task { + if call.arguments as? Bool ?? false { + try UtilKt.setApiLogListener { message in + self.sendNotifyEvent( + id: NOTIFY_API_LOG, + params: [KEY_NOTIFY_PARAM_MESSAGE: message] + ) + } + } else { + try UtilKt.setApiLogListener(listener: nil) + } + result(nil) + } + } + func initialize(call: FlutterMethodCall, result: @escaping FlutterResult) async throws { thetaRepository = nil photoCaptureBuilder = nil @@ -426,14 +467,14 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } } } - + func stopLivePreview(result: @escaping FlutterResult) { previewing = false result(true) } func listFiles(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -658,11 +699,11 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre func onProgress(completion: Float) { plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) } - + func onCapturing(status: CapturingStatusEnum) { plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_CAPTURING, params: toCapturingNotifyParam(value: status)) } - + func onCaptureCompleted(fileUrl: String?) { callback(fileUrl, nil) } @@ -691,6 +732,109 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre result(nil) } + func getTimeShiftManualCaptureBuilder(result: @escaping FlutterResult) { + guard let thetaRepository else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + timeShiftManualCaptureBuilder = thetaRepository.getTimeShiftManualCaptureBuilder() + result(nil) + } + + func buildTimeShiftManualCapture(call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let _ = thetaRepository, let builder = timeShiftManualCaptureBuilder else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + timeShiftManualCapture = nil + timeShiftManualCapturing = nil + let arguments = call.arguments as! [String: Any] + setCaptureBuilderParams(params: arguments, builder: builder) + setTimeShiftManualCaptureBuilderParams(params: arguments, builder: builder) + builder.build(completionHandler: { capture, error in + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + self.timeShiftManualCapture = capture + result(nil) + } + }) + } + + func startTimeShiftManualCapture(result: @escaping FlutterResult) { + guard let _ = thetaRepository, let capture = timeShiftManualCapture else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + class Callback: TimeShiftManualCaptureStartCaptureCallback { + let callback: (_ 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 onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + callback(nil, exception.asError()) + plugin?.timeShiftManualCapturing = nil + } + + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + let error = exception.asError() + plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_MANUAL_PROGRESS, params: toMessageNotifyParam(message: error.localizedDescription)) + } + + func onProgress(completion: Float) { + plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_MANUAL_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) + } + + func onCapturing(status: CapturingStatusEnum) { + plugin?.sendNotifyEvent(id: NOTIFY_TIME_SHIFT_MANUAL_CAPTURING, params: toCapturingNotifyParam(value: status)) + } + + func onCaptureCompleted(fileUrl: String?) { + callback(fileUrl, nil) + plugin?.timeShiftManualCapturing = nil + } + } + + timeShiftManualCapturing = 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) + ) + } + + func startTimeShiftManualSecondCapture(result: @escaping FlutterResult) { + guard let _ = thetaRepository, let capturing = timeShiftManualCapturing else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + capturing.startSecondCapture() + result(nil) + } + + func stopTimeShiftManualCapture(result: @escaping FlutterResult) { + guard let _ = thetaRepository, let capturing = timeShiftManualCapturing else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + capturing.stopCapture() + result(nil) + } + func getVideoCaptureBuilder(result: @escaping FlutterResult) { if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) @@ -748,11 +892,11 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let error = exception.asError() plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_STOP_ERROR, params: toMessageNotifyParam(message: error.localizedDescription)) } - + func onCapturing(status: CapturingStatusEnum) { plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_CAPTURING, params: toCapturingNotifyParam(value: status)) } - + func onCaptureStarted(fileUrl: String?) { plugin?.sendNotifyEvent(id: NOTIFY_VIDEO_CAPTURE_STARTED, params: toStartedNotifyParam(value: fileUrl ?? "")) } @@ -925,11 +1069,11 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre func onProgress(completion: Float) { plugin?.sendNotifyEvent(id: NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_CAPTURE_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) } - + func onCapturing(status: CapturingStatusEnum) { plugin?.sendNotifyEvent(id: NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_CAPTURE_CAPTURING, params: toCapturingNotifyParam(value: status)) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1017,11 +1161,11 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre func onProgress(completion: Float) { plugin?.sendNotifyEvent(id: NOTIFY_COMPOSITE_INTERVAL_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) } - + func onCapturing(status: CapturingStatusEnum) { plugin?.sendNotifyEvent(id: NOTIFY_COMPOSITE_INTERVAL_CAPTURING, params: toCapturingNotifyParam(value: status)) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1318,11 +1462,11 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre func onProgress(completion: Float) { plugin?.sendNotifyEvent(id: NOTIFY_CONTINUOUS_PROGRESS, params: toCaptureProgressNotifyParam(value: completion)) } - + func onCapturing(status: CapturingStatusEnum) { plugin?.sendNotifyEvent(id: NOTIFY_CONTINUOUS_CAPTURING, params: toCapturingNotifyParam(value: status)) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1396,6 +1540,22 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre }) } + func reboot(result: @escaping FlutterResult) { + guard let thetaRepository else { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) + result(flutterError) + return + } + thetaRepository.reboot { error in + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + result(nil) + } + } + } + func reset(result: @escaping FlutterResult) { if thetaRepository == nil { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) @@ -1429,7 +1589,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func convertVideoFormats(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1487,7 +1647,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func listAccessPoints(result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1497,7 +1657,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let response = response { + if let response { result(convertResult(accessPointList: response)) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) @@ -1508,100 +1668,88 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func setAccessPointDynamically(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } guard - 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 + let arguments = call.arguments as? [String: Any?] 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, - authMode: authMode, - password: password, - connectionPriority: connectionPriority, - proxy: proxy, - completionHandler: { error in - if let thetaError = error { - let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) - result(flutterError) - } else { - result(nil) + do { + let accessPointParams = try toSetAccessPointParams(params: arguments) + thetaRepository.setAccessPointDynamically( + ssid: accessPointParams.ssid, + ssidStealth: accessPointParams.ssidStealth, + authMode: accessPointParams.authMode, + password: accessPointParams.password, + connectionPriority: accessPointParams.connectionPriority, + proxy: accessPointParams.proxy, + completionHandler: { error in + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + result(nil) + } } - } - ) + ) + } catch { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: error.localizedDescription, details: nil) + result(flutterError) + } } func setAccessPointStatically(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return } guard - 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 connectionPriority = arguments["connectionPriority"] as? Int32, - let ipAddress = arguments["ipAddress"] as? String, - let subnetMask = arguments["subnetMask"] as? String, - let defaultGateway = arguments["defaultGateway"] as? String + let arguments = call.arguments as? [String: Any?] 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, - authMode: authMode, - password: password, - connectionPriority: connectionPriority, - ipAddress: ipAddress, - subnetMask: subnetMask, - defaultGateway: defaultGateway, - proxy: proxy, - completionHandler: { error in - if let thetaError = error { - let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) - result(flutterError) - } else { - result(nil) + do { + let accessPointParams = try toSetAccessPointParams(params: arguments) + let staticallyParams = try toSetAccessPointStaticallyParams(params: arguments) + thetaRepository.setAccessPointStatically( + ssid: accessPointParams.ssid, + ssidStealth: accessPointParams.ssidStealth, + authMode: accessPointParams.authMode, + password: accessPointParams.password, + connectionPriority: accessPointParams.connectionPriority, + ipAddress: staticallyParams.ipAddress, + subnetMask: staticallyParams.subnetMask, + defaultGateway: staticallyParams.defaultGateway, + dns1: accessPointParams.dns1, + dns2: accessPointParams.dns2, + proxy: accessPointParams.proxy, + completionHandler: { error in + if let thetaError = error { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) + result(flutterError) + } else { + result(nil) + } } - } - ) + ) + } catch { + let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: error.localizedDescription, details: nil) + result(flutterError) + } } func deleteAccessPoint(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1622,7 +1770,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func getMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1636,18 +1784,18 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre 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) { captureMode = mode } - if let captureMode = captureMode { + if let captureMode { thetaRepository.getMySetting(captureMode: captureMode, completionHandler: { options, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let options = options { + if let options { result(convertResult(options: options)) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) @@ -1662,7 +1810,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func getMySettingFromOldModel(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1681,7 +1829,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre optionNames = names } - if let optionNames = optionNames { + if let optionNames { thetaRepository.getMySetting(optionNames: optionNames, completionHandler: { options, error in if let thetaError = error { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) @@ -1697,7 +1845,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func setMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1705,7 +1853,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre 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 captureMode = getEnumValue(values: ThetaRepository.CaptureModeEnum.values(), name: captureModeName), let optionDic = arguments["options"] as? [String: Any] else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoArgument, details: nil) @@ -1725,7 +1873,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func deleteMySetting(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1751,7 +1899,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func listPlugins(result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1762,7 +1910,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let response = response { + if let response { result(toPluginInfosResult(pluginInfoList: response)) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) @@ -1773,7 +1921,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func setPlugin(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1796,7 +1944,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func startPlugin(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1814,7 +1962,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func stopPlugin(result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1831,7 +1979,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func getPluginLicense(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1848,7 +1996,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let response = response { + if let response { result(response) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) @@ -1859,7 +2007,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func getPluginOrders(result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1870,7 +2018,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let response = response { + if let response { result(response) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) @@ -1881,7 +2029,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func setPluginOrders(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1904,7 +2052,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre } func setBluetoothDevice(call: FlutterMethodCall, result: @escaping FlutterResult) { - guard let thetaRepository = thetaRepository else { + guard let thetaRepository else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNotInit, details: nil) result(flutterError) return @@ -1921,7 +2069,7 @@ public class SwiftThetaClientFlutterPlugin: NSObject, FlutterPlugin, FlutterStre let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: thetaError.localizedDescription, details: nil) result(flutterError) } else { - if let response = response { + if let response { result(response) } else { let flutterError = FlutterError(code: SwiftThetaClientFlutterPlugin.errorCode, message: SwiftThetaClientFlutterPlugin.messageNoResult, details: nil) diff --git a/flutter/ios/theta_client_flutter.podspec b/flutter/ios/theta_client_flutter.podspec index 91b4e4e5ea5..ba9a598bf9b 100644 --- a/flutter/ios/theta_client_flutter.podspec +++ b/flutter/ios/theta_client_flutter.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'theta_client_flutter' - s.version = '1.12.1' + s.version = '1.13.0' s.summary = 'theta-client plugin project.' s.description = <<-DESC theta-client Flutter plugin project. @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.dependency 'Flutter' s.platform = :ios, '15.0' - s.dependency 'THETAClient', '1.12.1' + s.dependency 'THETAClient', '1.13.0' # Flutter.framework does not contain a i386 slice. s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } diff --git a/flutter/lib/capture/capture.dart b/flutter/lib/capture/capture.dart index 5cca4d932e9..44a8bff1cce 100644 --- a/flutter/lib/capture/capture.dart +++ b/flutter/lib/capture/capture.dart @@ -54,11 +54,24 @@ class Capture { /// Capturing status enum CapturingStatusEnum { + /// The process is starting + starting('STARTING'), + /// Capture in progress capturing('CAPTURING'), /// Self-timer in progress selfTimerCountdown('SELF_TIMER_COUNTDOWN'), + + /// Performing timeShift shooting + timeShiftShooting('TIME_SHIFT_SHOOTING'), + + /// Performing second capture of manual timeShift shooting + timeShiftShootingSecond('TIME_SHIFT_SHOOTING_SECOND'), + + /// In the case of time-lag shooting by manual lens, + /// set while waiting for the second shot to be taken after the first shot is completed. + timeShiftShootingIdle('TIME_SHIFT_SHOOTING_IDLE'), ; final String rawValue; @@ -138,7 +151,7 @@ class TimeShiftCapture extends Capture { void Function(double completion) onProgress, void Function(Exception exception) onCaptureFailed, {void Function(Exception exception)? onStopFailed, - void Function(CapturingStatusEnum status)? onCapturing}) { + void Function(CapturingStatusEnum status)? onCapturing}) { ThetaClientFlutterPlatform.instance .startTimeShiftCapture(onProgress, onStopFailed, onCapturing) .then((value) => onCaptureCompleted(value)) @@ -147,6 +160,36 @@ class TimeShiftCapture extends Capture { } } +/// Capture of Manual TimeShift +class TimeShiftManualCapture extends Capture { + final int _interval; + + TimeShiftManualCapture(super.options, this._interval); + + int getCheckStatusCommandInterval() { + return _interval; + } + + TimeShift? getTimeShiftSetting() { + return _options[OptionNameEnum.timeShift.rawValue]; + } + + /// Starts TimeShift manual capture. + /// Later, need to call TimeShiftManualCapturing.startSecondCapture() or TimeShiftManualCapturing.stopCapture() + TimeShiftManualCapturing startCapture( + void Function(String? fileUrl) onCaptureCompleted, + void Function(double completion) onProgress, + void Function(Exception exception) onCaptureFailed, + {void Function(Exception exception)? onStopFailed, + void Function(CapturingStatusEnum status)? onCapturing}) { + ThetaClientFlutterPlatform.instance + .startTimeShiftManualCapture(onProgress, onStopFailed, onCapturing) + .then((value) => onCaptureCompleted(value)) + .onError((error, stackTrace) => onCaptureFailed(error as Exception)); + return TimeShiftManualCapturing(); + } +} + /// Capture of Video class VideoCapture extends Capture { final int _interval; @@ -200,7 +243,7 @@ class LimitlessIntervalCapture extends Capture { void Function(List? fileUrls) onCaptureCompleted, void Function(Exception exception) onCaptureFailed, {void Function(Exception exception)? onStopFailed, - void Function(CapturingStatusEnum status)? onCapturing}) { + void Function(CapturingStatusEnum status)? onCapturing}) { ThetaClientFlutterPlatform.instance .startLimitlessIntervalCapture(onStopFailed, onCapturing) .then((value) => onCaptureCompleted(value)) @@ -232,10 +275,10 @@ class ShotCountSpecifiedIntervalCapture extends Capture { void Function(double completion) onProgress, void Function(Exception exception) onCaptureFailed, {void Function(Exception exception)? onStopFailed, - void Function(CapturingStatusEnum status)? onCapturing}) { + void Function(CapturingStatusEnum status)? onCapturing}) { ThetaClientFlutterPlatform.instance .startShotCountSpecifiedIntervalCapture( - onProgress, onStopFailed, onCapturing) + onProgress, onStopFailed, onCapturing) .then((value) => onSuccess(value)) .onError((error, stackTrace) => onCaptureFailed(error as Exception)); return ShotCountSpecifiedIntervalCapturing(); @@ -266,7 +309,7 @@ class CompositeIntervalCapture extends Capture { void Function(double completion) onProgress, void Function(Exception exception) onCaptureFailed, {void Function(Exception exception)? onStopFailed, - void Function(CapturingStatusEnum status)? onCapturing}) { + void Function(CapturingStatusEnum status)? onCapturing}) { ThetaClientFlutterPlatform.instance .startCompositeIntervalCapture(onProgress, onStopFailed, onCapturing) .then((value) => onSuccess(value)) @@ -356,7 +399,8 @@ class ContinuousCapture extends Capture { } /// Starts continuous shooting - void startCapture(void Function(List? fileUrls) onSuccess, + void startCapture( + void Function(List? fileUrls) onSuccess, void Function(double completion) onProgress, void Function(Exception exception) onCaptureFailed, {void Function(CapturingStatusEnum status)? onCapturing}) { diff --git a/flutter/lib/capture/capture_builder.dart b/flutter/lib/capture/capture_builder.dart index 268f2563624..0bf074de8b1 100644 --- a/flutter/lib/capture/capture_builder.dart +++ b/flutter/lib/capture/capture_builder.dart @@ -178,6 +178,61 @@ class TimeShiftCaptureBuilder extends CaptureBuilder { } } +/// Builder of TimeShiftManualCapture +class TimeShiftManualCaptureBuilder + extends CaptureBuilder { + int _interval = -1; + TimeShift? _timeShift; + + void _checkAndInitTimeShiftSetting() { + if (_timeShift == null) { + _timeShift = TimeShift(); + _options[OptionNameEnum.timeShift.rawValue] = _timeShift; + } + } + + TimeShiftManualCaptureBuilder setCheckStatusCommandInterval(int timeMillis) { + _interval = timeMillis; + return this; + } + + /// Set is front first + TimeShiftManualCaptureBuilder setIsFrontFirst(bool isFrontFirst) { + _checkAndInitTimeShiftSetting(); + _timeShift?.isFrontFirst = isFrontFirst; + return this; + } + + /// set time (sec) before 1st lens shooting + TimeShiftManualCaptureBuilder setSecondInterval( + TimeShiftIntervalEnum interval) { + _checkAndInitTimeShiftSetting(); + _timeShift?.secondInterval = interval; + return this; + } + + /// set time (sec) before 1st lens shooting + TimeShiftManualCaptureBuilder setFirstInterval( + TimeShiftIntervalEnum interval) { + _checkAndInitTimeShiftSetting(); + _timeShift?.firstInterval = interval; + return this; + } + + /// Builds an instance of a TimeShiftManualCapture that has all the combined parameters of the Options that have been added to the Builder. + Future build() async { + var completer = Completer(); + try { + await ThetaClientFlutterPlatform.instance + .buildTimeShiftManualCapture(_options, _interval); + completer.complete(TimeShiftManualCapture(_options, _interval)); + } catch (e) { + completer.completeError(e); + } + return completer.future; + } +} + /// Builder of VideoCapture class VideoCaptureBuilder extends CaptureBuilder { int _interval = -1; @@ -360,7 +415,7 @@ class MultiBracketCaptureBuilder /// | THETA X | 2 | 13 | /// /// Number of settings have to be Number of shots in multi bracket shooting. - /// + /// /// For Theta SC2, S and SC, other settings than iso, shutterSpeed and /// colorTemperature are ignored. /// For Theta X, exposureProgram and exposureCompensation are ignored @@ -516,6 +571,15 @@ enum VideoFileFormatEnum { /// For RICOH THETA SC2 or SC2 for business video_4KnoCodec(FileFormatEnum.video_4KnoCodec), + /// Video File format. + /// type: mp4 + /// size: 1920 x 960 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 15 + /// + /// For RICOH THETA A1 + video_2K_15F(FileFormatEnum.video_2K_15F), + /// Video File format. /// type: mp4 /// size: 1920 x 960 @@ -630,6 +694,24 @@ enum VideoFileFormatEnum { /// and _1 is back lens. This mode does not record audio track to MP4 file. video_3_6K_2F(FileFormatEnum.video_3_6K_2F), + /// Video File format. + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + video_4K_2F(FileFormatEnum.video_4K_2F), + + /// Video File format. + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + video_4K_5F(FileFormatEnum.video_4K_5F), + /// Video File format. /// type: mp4 /// size: 3840 x 1920 @@ -736,7 +818,127 @@ enum VideoFileFormatEnum { /// frame rate: 10 /// /// For RICOH THETA X or later - video_7K_10F(FileFormatEnum.video_7K_10F); + video_7K_10F(FileFormatEnum.video_7K_10F), + + /// Video File format. + /// type: mp4 + /// size: 1920 x 960 + /// codec: H.265/HEVC + /// frame rate: 30 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_2K_30F_h265Hevc(FileFormatEnum.video_2K_30F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_2F_h265Hevc(FileFormatEnum.video_4K_2F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_5F_h265Hevc(FileFormatEnum.video_4K_5F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_10F_h265Hevc(FileFormatEnum.video_4K_10F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 30 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_30F_h265Hevc(FileFormatEnum.video_4K_30F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_2F_h265Hevc(FileFormatEnum.video_5_7K_2F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_5F_h265Hevc(FileFormatEnum.video_5_7K_5F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_10F_h265Hevc(FileFormatEnum.video_5_7K_10F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_2F_h265Hevc(FileFormatEnum.video_7K_2F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_5F_h265Hevc(FileFormatEnum.video_7K_5F_h265Hevc), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_10F_h265Hevc(FileFormatEnum.video_7K_10F_h265Hevc); final FileFormatEnum rawValue; diff --git a/flutter/lib/capture/capturing.dart b/flutter/lib/capture/capturing.dart index 5afe44b87ad..bff5d37b553 100644 --- a/flutter/lib/capture/capturing.dart +++ b/flutter/lib/capture/capturing.dart @@ -16,6 +16,21 @@ class TimeShiftCapturing extends Capturing { } } +/// TimeShiftManualCapturing +class TimeShiftManualCapturing extends Capturing { + /// Starts TimeShift manual second capture. + void startSecondCapture() { + ThetaClientFlutterPlatform.instance.startTimeShiftManualSecondCapture(); + } + + /// Stops Manual TimeShift capture. + /// When call stopCapture() then call property callback. + @override + void stopCapture() { + ThetaClientFlutterPlatform.instance.stopTimeShiftManualCapture(); + } +} + /// VideoCapturing class VideoCapturing extends Capturing { /// Stops video capture. diff --git a/flutter/lib/options/access_info.dart b/flutter/lib/options/access_info.dart new file mode 100644 index 00000000000..e63dc7c9f14 --- /dev/null +++ b/flutter/lib/options/access_info.dart @@ -0,0 +1,167 @@ +/// Connected network information. +class AccessInfo { + /// SSID of the wireless LAN access point that THETA connects to + String ssid; + + /// IP address of access point + String ipAddress; + + /// subnet mask of access point + String subnetMask; + + /// default gateway of access point + String defaultGateway; + + /// Primary DNS server + String? dns1; + + /// Secondary DNS server + String? dns2; + + /// proxy URL of access point + String proxyURL; + + /// Radio frequency + WlanFrequencyAccessInfoEnum frequency; + + /// WLAN signal strength. + int wlanSignalStrength; + + /// WLAN signal level. + int wlanSignalLevel; + + /// LTE signal strength. + int lteSignalStrength; + + /// LTE signal level. + int lteSignalLevel; + + /// client devices information + List? dhcpLeaseAddress; + + AccessInfo( + [this.ssid = "", + this.ipAddress = "", + this.subnetMask = "", + this.defaultGateway = "", + this.dns1, + this.dns2, + this.proxyURL = "", + this.frequency = WlanFrequencyAccessInfoEnum.initialValue, + this.wlanSignalStrength = 0, + this.wlanSignalLevel = 0, + this.lteSignalStrength = 0, + this.lteSignalLevel = 0, + this.dhcpLeaseAddress]); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! AccessInfo) return false; + return ssid == other.ssid && + ipAddress == other.ipAddress && + subnetMask == other.subnetMask && + defaultGateway == other.defaultGateway && + dns1 == other.dns1 && + dns2 == other.dns2 && + proxyURL == other.proxyURL && + frequency == other.frequency && + wlanSignalStrength == other.wlanSignalStrength && + wlanSignalLevel == other.wlanSignalLevel && + lteSignalStrength == other.lteSignalStrength && + lteSignalLevel == other.lteSignalLevel && + _compareDhcpLeaseAddressLists(dhcpLeaseAddress, other.dhcpLeaseAddress); + } + + @override + int get hashCode => Object.hashAll([ + ssid, + ipAddress, + subnetMask, + defaultGateway, + dns1, + dns2, + proxyURL, + frequency, + wlanSignalStrength, + wlanSignalLevel, + lteSignalStrength, + lteSignalLevel, + dhcpLeaseAddress + ]); + + bool _compareDhcpLeaseAddressLists( + List? list1, List? list2) { + if (list1 == null && list2 == null) return true; + if (list1 == null || list2 == null) return false; + if (list1.length != list2.length) return false; + for (int i = 0; i < list1.length; i++) { + if (list1[i] != list2[i]) return false; + } + return true; + } +} + +/// WLAN frequency of the access point. +enum WlanFrequencyAccessInfoEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// 2.4GHz + ghz_2_4('GHZ_2_4'), + + /// 5.2GHz + ghz_5_2('GHZ_5_2'), + + /// 5.8GHz + ghz_5_8('GHZ_5_8'), + + /// Initial value + initialValue('INITIAL_VALUE'); + + final String rawValue; + + const WlanFrequencyAccessInfoEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static WlanFrequencyAccessInfoEnum? getValue(String rawValue) { + return WlanFrequencyAccessInfoEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} + +/// Client devices information +class DhcpLeaseAddress { + /// IP address of client device + final String ipAddress; + + /// MAC address of client device + final String macAddress; + + /// Host name of client device + final String hostName; + + DhcpLeaseAddress({ + required this.ipAddress, + required this.macAddress, + required this.hostName, + }); + + @override + bool operator ==(Object other) { + if (identical(this, other)) return true; + if (other is! DhcpLeaseAddress) return false; + return ipAddress == other.ipAddress && + macAddress == other.macAddress && + hostName == other.hostName; + } + + @override + int get hashCode => Object.hash(ipAddress, macAddress, hostName); +} diff --git a/flutter/lib/options/ai_auto_thumbnail.dart b/flutter/lib/options/ai_auto_thumbnail.dart new file mode 100644 index 00000000000..564ada165ed --- /dev/null +++ b/flutter/lib/options/ai_auto_thumbnail.dart @@ -0,0 +1,28 @@ +/// AI auto thumbnail setting. +/// +/// For RICOH THETA X +enum AiAutoThumbnailEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// AI auto setting ON. + on('ON'), + + /// AI auto setting OFF. + off('OFF'); + + final String rawValue; + + const AiAutoThumbnailEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static AiAutoThumbnailEnum? getValue(String rawValue) { + return AiAutoThumbnailEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/aperture.dart b/flutter/lib/options/aperture.dart new file mode 100644 index 00000000000..86cf13e493c --- /dev/null +++ b/flutter/lib/options/aperture.dart @@ -0,0 +1,45 @@ +/// Aperture value. +enum ApertureEnum { + /// Aperture AUTO(0). + apertureAuto('APERTURE_AUTO'), + + /// Aperture 2.0F. + /// + /// RICOH THETA V or prior + aperture_2_0('APERTURE_2_0'), + + /// Aperture 2.1F. + /// + /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority + aperture_2_1('APERTURE_2_1'), + + /// Aperture 2.4F. + /// + /// RICOH THETA X or later + aperture_2_4('APERTURE_2_4'), + + /// Aperture 3.5F. + /// + /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority + aperture_3_5('APERTURE_3_5'), + + /// Aperture 5.6F. + /// + /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority + aperture_5_6('APERTURE_5_6'); + + final String rawValue; + + const ApertureEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static ApertureEnum? getValue(String rawValue) { + return ApertureEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/camera_control_source.dart b/flutter/lib/options/camera_control_source.dart new file mode 100644 index 00000000000..cf93cd03723 --- /dev/null +++ b/flutter/lib/options/camera_control_source.dart @@ -0,0 +1,29 @@ +/// Camera control source. +enum CameraControlSourceEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Operation is possible with the camera. Locks the smartphone + /// application UI (supported app only). + camera('CAMERA'), + + /// Operation is possible with the smartphone application. Locks + /// the UI on the shooting screen on the camera. + app('APP'); + + final String rawValue; + + const CameraControlSourceEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static CameraControlSourceEnum? getValue(String rawValue) { + return CameraControlSourceEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/camera_lock.dart b/flutter/lib/options/camera_lock.dart new file mode 100644 index 00000000000..01490807363 --- /dev/null +++ b/flutter/lib/options/camera_lock.dart @@ -0,0 +1,31 @@ +/// Control camera lock/unlock +enum CameraLockEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Camera is unlocked + unlock('UNLOCK'), + + /// Camera basic lock state + /// (Mode button, WLAN button, and Fn button presses are inhibited) + basicLock('BASIC_LOCK'), + + /// Lock according to the parameters set in _cameraLockConfig. + customLock('CUSTOM_LOCK'), + ; + + final String rawValue; + + const CameraLockEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static CameraLockEnum? getValue(String rawValue) { + return CameraLockEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/camera_lock_config.dart b/flutter/lib/options/camera_lock_config.dart new file mode 100644 index 00000000000..947f68194f7 --- /dev/null +++ b/flutter/lib/options/camera_lock_config.dart @@ -0,0 +1,47 @@ +/// Camera Lock Config +/// +/// - It is possible to enable/disable the function for each HW key. +/// - It is possible to enable/disable the operation of the panel. +/// - When all supported buttons/components are in the "unlock" state, it will be the same as the normal setting. +/// - For THETA models, if there are no wlanKey, fnKey, or panel, it will return "lock". If there are no supported buttons/components, setting to "unlock" or "lock" will not return an error and will not perform any action. +class CameraLockConfig { + /// power key locked or unlocked. + bool? isPowerKeyLocked; + + /// Shutter key locked or unlocked. + bool? isShutterKeyLocked; + + /// mode key locked or unlocked. + bool? isModeKeyLocked; + + /// wlan key locked or unlocked. + bool? isWlanKeyLocked; + + /// fn key locked or unlocked. + bool? isFnKeyLocked; + + /// panel locked or unlocked. + /// Fixed to true. Does not cause an error when false, but it is not reflected either. + bool? isPanelLocked; + + CameraLockConfig( + [this.isPowerKeyLocked, + this.isShutterKeyLocked, + this.isModeKeyLocked, + this.isWlanKeyLocked, + this.isFnKeyLocked, + this.isPanelLocked]); + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([ + isPowerKeyLocked, + isShutterKeyLocked, + isModeKeyLocked, + isWlanKeyLocked, + isFnKeyLocked, + isPanelLocked + ]); +} diff --git a/flutter/lib/options/camera_power.dart b/flutter/lib/options/camera_power.dart index 4824ad6656b..339b5720fed 100644 --- a/flutter/lib/options/camera_power.dart +++ b/flutter/lib/options/camera_power.dart @@ -1,3 +1,4 @@ +/// Camera power state. /// _cameraPower is the power status of camera. /// /// For RICOH THETA X v2.61.0 or later @@ -11,6 +12,9 @@ enum CameraPowerEnum { /// Power OFF off('OFF'), + /// Sleep + sleep('SLEEP'), + /// Power on, power saving mode. Camera is closed. /// Unavailable parameter when plugin is running. In this case, invalidParameterValue error will be returned. powerSaving('POWER_SAVING'), diff --git a/flutter/lib/options/compass_direction_ref.dart b/flutter/lib/options/compass_direction_ref.dart new file mode 100644 index 00000000000..7962f7fbae4 --- /dev/null +++ b/flutter/lib/options/compass_direction_ref.dart @@ -0,0 +1,32 @@ +/// _compassDirectionRef +enum CompassDirectionRefEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// If GPS positioning is available, record in true north; + /// if GPS is off or not available, record in magnetic north. + auto('AUTO'), + + /// If the azimuth is set to true north, GPS is turned off, or positioning is not possible, + /// the azimuth information is not recorded (positioning information is required for conversion). + trueNorth('TRUE_NORTH'), + + /// Do not set azimuth to true north, set azimuth to magnetic north + magnetic('MAGNETIC'); + + final String rawValue; + + const CompassDirectionRefEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static CompassDirectionRefEnum? getValue(String rawValue) { + return CompassDirectionRefEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/ethernet_config.dart b/flutter/lib/options/ethernet_config.dart index d437bbd1f9b..66545595730 100644 --- a/flutter/lib/options/ethernet_config.dart +++ b/flutter/lib/options/ethernet_config.dart @@ -9,14 +9,25 @@ class EthernetConfig { bool usingDhcp; /// (optional) IPv4 for IP address + /// Do not specify this property when usingDhcp is true. String? ipAddress; /// (optional) IPv4 for subnet mask + /// Do not specify this property when usingDhcp is true. String? subnetMask; /// (optional) IPv4 for default gateway + /// Do not specify this property when usingDhcp is true. String? defaultGateway; + /// (optional) IPv4 for Primary DNS server + /// Do not specify this property when usingDhcp is true. + String? dns1; + + /// (optional) IPv4 for Secondary DNS server + /// Do not specify this property when usingDhcp is true. + String? dns2; + /// (optional) refer to _proxy for detail /// /// If "use" is set to true, "url" and "port" must be set. @@ -28,12 +39,17 @@ class EthernetConfig { Proxy? proxy; EthernetConfig(this.usingDhcp, - [this.ipAddress, this.subnetMask, this.defaultGateway, this.proxy]); + [this.ipAddress, + this.subnetMask, + this.defaultGateway, + this.dns1, + this.dns2, + this.proxy]); @override bool operator ==(Object other) => hashCode == other.hashCode; @override - int get hashCode => - Object.hashAll([usingDhcp, ipAddress, subnetMask, defaultGateway, proxy]); + int get hashCode => Object.hashAll( + [usingDhcp, ipAddress, subnetMask, defaultGateway, dns1, dns2, proxy]); } diff --git a/flutter/lib/options/exposure_compensation.dart b/flutter/lib/options/exposure_compensation.dart new file mode 100644 index 00000000000..b8dbd9f9fac --- /dev/null +++ b/flutter/lib/options/exposure_compensation.dart @@ -0,0 +1,96 @@ +/// Exposure compensation (EV). +enum ExposureCompensationEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Exposure compensation -4.0 + m4_0('M4_0'), + + /// Exposure compensation -3.7 + m3_7('M3_7'), + + /// Exposure compensation -3.3 + m3_3('M3_3'), + + /// Exposure compensation -3.0 + m3_0('M3_0'), + + /// Exposure compensation -2.7 + m2_7('M2_7'), + + /// Exposure compensation -2.3 + m2_3('M2_3'), + + /// Exposure compensation -2.0 + m2_0('M2_0'), + + /// Exposure compensation -1.7 + m1_7('M1_7'), + + /// Exposure compensation -1.3 + m1_3('M1_3'), + + /// Exposure compensation -1.0 + m1_0('M1_0'), + + /// Exposure compensation -0.7 + m0_7('M0_7'), + + /// Exposure compensation -0.3 + m0_3('M0_3'), + + /// Exposure compensation 0.0 + zero('ZERO'), + + /// Exposure compensation 0.3 + p0_3('P0_3'), + + /// Exposure compensation 0.7 + p0_7('P0_7'), + + /// Exposure compensation 1.0 + p1_0('P1_0'), + + /// Exposure compensation 1.3 + p1_3('P1_3'), + + /// Exposure compensation 1.7 + p1_7('P1_7'), + + /// Exposure compensation 2.0 + p2_0('P2_0'), + + /// Exposure compensation 2.3 + p2_3('P2_3'), + + /// Exposure compensation 2.7 + p2_7('P2_7'), + + /// Exposure compensation 3.0 + p3_0('P3_0'), + + /// Exposure compensation 3.3 + p3_3('P3_3'), + + /// Exposure compensation 3.7 + p3_7('P3_7'), + + /// Exposure compensation 4.0 + p4_0('P4_0'); + + final String rawValue; + + const ExposureCompensationEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static ExposureCompensationEnum? getValue(String rawValue) { + return ExposureCompensationEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/exposure_delay.dart b/flutter/lib/options/exposure_delay.dart new file mode 100644 index 00000000000..8e3d3a6120c --- /dev/null +++ b/flutter/lib/options/exposure_delay.dart @@ -0,0 +1,50 @@ +/// Operating time (sec.) of the self-timer. +enum ExposureDelayEnum { + /// Disable self-timer. + delayOff('DELAY_OFF'), + + /// Self-timer time. 1sec. + delay1('DELAY_1'), + + /// Self-timer time. 2sec. + delay2('DELAY_2'), + + /// Self-timer time. 3sec. + delay3('DELAY_3'), + + /// Self-timer time. 4sec. + delay4('DELAY_4'), + + /// Self-timer time. 5sec. + delay5('DELAY_5'), + + /// Self-timer time. 6sec. + delay6('DELAY_6'), + + /// Self-timer time. 7sec. + delay7('DELAY_7'), + + /// Self-timer time. 8sec. + delay8('DELAY_8'), + + /// Self-timer time. 9sec. + delay9('DELAY_9'), + + /// Self-timer time. 10sec. + delay10('DELAY_10'); + + final String rawValue; + + const ExposureDelayEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static ExposureDelayEnum? getValue(String rawValue) { + return ExposureDelayEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/file_format.dart b/flutter/lib/options/file_format.dart index 40e40460b81..771ec99f818 100644 --- a/flutter/lib/options/file_format.dart +++ b/flutter/lib/options/file_format.dart @@ -89,6 +89,15 @@ enum FileFormatEnum { /// For RICOH THETA SC2 or SC2 for business video_4KnoCodec('VIDEO_4K_NO_CODEC'), + /// Video File format. + /// type: mp4 + /// size: 1920 x 960 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 15 + /// + /// For RICOH THETA A1 + video_2K_15F('VIDEO_2K_15F'), + /// Video File format. /// type: mp4 /// size: 1920 x 960 @@ -203,6 +212,24 @@ enum FileFormatEnum { /// and _1 is back lens. This mode does not record audio track to MP4 file. video_3_6K_2F('VIDEO_3_6K_2F'), + /// Video File format. + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + video_4K_2F('VIDEO_4K_2F'), + + /// Video File format. + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.264/MPEG-4 AVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + video_4K_5F('VIDEO_4K_5F'), + /// Video File format. /// type: mp4 /// size: 3840 x 1920 @@ -309,7 +336,127 @@ enum FileFormatEnum { /// frame rate: 10 /// /// For RICOH THETA X or later - video_7K_10F('VIDEO_7K_10F'); + video_7K_10F('VIDEO_7K_10F'), + + /// Video File format. + /// type: mp4 + /// size: 1920 x 960 + /// codec: H.265/HEVC + /// frame rate: 30 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_2K_30F_h265Hevc('VIDEO_2K_30F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_2F_h265Hevc('VIDEO_4K_2F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_5F_h265Hevc('VIDEO_4K_5F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_10F_h265Hevc('VIDEO_4K_10F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 3840 x 1920 + /// codec: H.265/HEVC + /// frame rate: 30 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_4K_30F_h265Hevc('VIDEO_4K_30F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_2F_h265Hevc('VIDEO_5_7K_2F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_5F_h265Hevc('VIDEO_5_7K_5F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 5760 x 2880 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_5_7K_10F_h265Hevc('VIDEO_5_7K_10F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 2 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_2F_h265Hevc('VIDEO_7K_2F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 5 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_5F_h265Hevc('VIDEO_7K_5F_H265_HEVC'), + + /// Video File format. + /// + /// type: mp4 + /// size: 7680 x 3840 + /// codec: H.265/HEVC + /// frame rate: 10 + /// + /// For RICOH THETA A1 + // ignore: constant_identifier_names + video_7K_10F_h265Hevc('VIDEO_7K_10F_H265_HEVC'); final String rawValue; diff --git a/flutter/lib/options/gps_tag_recording.dart b/flutter/lib/options/gps_tag_recording.dart new file mode 100644 index 00000000000..c87f17277e5 --- /dev/null +++ b/flutter/lib/options/gps_tag_recording.dart @@ -0,0 +1,22 @@ +/// Turns position information assigning ON/OFF. +/// +/// For RICOH THETA X +enum GpsTagRecordingEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Position information assigning ON. + on('ON'), + + /// Position information assigning OFF. + off('OFF'); + + final String rawValue; + + const GpsTagRecordingEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } +} diff --git a/flutter/lib/options/importer.dart b/flutter/lib/options/importer.dart index fc0f5447f1f..120f0cda4ad 100644 --- a/flutter/lib/options/importer.dart +++ b/flutter/lib/options/importer.dart @@ -1,10 +1,31 @@ /// Options library; +export 'ai_auto_thumbnail.dart'; +export 'aperture.dart'; export 'bluetooth_role.dart'; +export 'camera_control_source.dart'; +export 'camera_lock.dart'; +export 'camera_lock_config.dart'; export 'camera_power.dart'; +export 'compass_direction_ref.dart'; export 'ethernet_config.dart'; +export 'exposure_compensation.dart'; +export 'exposure_delay.dart'; export 'file_format.dart'; +export 'gps_tag_recording.dart'; export 'max_recordable_time.dart'; +export 'microphone_noise_reduction.dart'; +export 'mobile_network_setting.dart'; +export 'network_type.dart'; export 'off_delay.dart'; +export 'off_delay_usb.dart'; +export 'preview_format.dart'; export 'sleep_delay.dart'; +export 'top_bottom_correction.dart'; +export 'top_bottom_correction_rotation.dart'; +export 'top_bottom_correction_rotation_support.dart'; +export 'usb_connection.dart'; +export 'wlan_antenna_config.dart'; +export 'wlan_frequency.dart'; +export 'wlan_frequency_cl_mode.dart'; diff --git a/flutter/lib/options/microphone_noise_reduction.dart b/flutter/lib/options/microphone_noise_reduction.dart new file mode 100644 index 00000000000..bade62c1e44 --- /dev/null +++ b/flutter/lib/options/microphone_noise_reduction.dart @@ -0,0 +1,30 @@ +/// Built-in microphone noise reduction. +/// +/// 1) Video: Ignore changes during recording +/// 2) Live Streaming: Ignore changes during delivery +enum MicrophoneNoiseReductionEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// ON + on('ON'), + + /// OFF + off('OFF'); + + final String rawValue; + + const MicrophoneNoiseReductionEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static MicrophoneNoiseReductionEnum? getValue(String rawValue) { + return MicrophoneNoiseReductionEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/mobile_network_setting.dart b/flutter/lib/options/mobile_network_setting.dart new file mode 100644 index 00000000000..25c2318aadc --- /dev/null +++ b/flutter/lib/options/mobile_network_setting.dart @@ -0,0 +1,72 @@ +/// Mobile Network Settings +class MobileNetworkSetting { + /// roaming + RoamingEnum? roaming; + + /// plan + PlanEnum? plan; + + MobileNetworkSetting(this.roaming, this.plan); + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([roaming, plan]); +} + +/// Roaming of MobileNetworkSetting +enum RoamingEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// OFF + off('OFF'), + + /// ON + on('ON'); + + final String rawValue; + + const RoamingEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static RoamingEnum? getValue(String rawValue) { + return RoamingEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} + +/// Plan of MobileNetworkSetting +enum PlanEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Communicate with APN settings for plan-D + // ignore: constant_identifier_names + SORACOM("SORACOM"), + + /// Communicate with APN settings for plan-DU + // ignore: constant_identifier_names + SORACOM_PLAN_DU('SORACOM_PLAN_DU'); + + final String rawValue; + + const PlanEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static PlanEnum? getValue(String rawValue) { + return PlanEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/network_type.dart b/flutter/lib/options/network_type.dart new file mode 100644 index 00000000000..7831ce56086 --- /dev/null +++ b/flutter/lib/options/network_type.dart @@ -0,0 +1,53 @@ +/// Network type of the camera supported by Theta X, Z1 and V. +enum NetworkTypeEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// Direct mode + direct('DIRECT'), + + /// Client mode via WLAN + client('CLIENT'), + + /// Client mode via Ethernet cable supported by Theta Z1 and V. + ethernet('ETHERNET'), + + /// Network is off. This value can be gotten only by plugin. + off('OFF'), + + /// LTE plan-D + lteD('LTE_D'), + + /// LTE plan-DU + lteDU('LTE_DU'), + + /// LTE plan01s + lte01S('LTE_01S'), + + /// LTE planX3 + lteX3('LTE_X3'), + + /// LTE planP1 + lteP1('LTE_P1'), + + /// LTE plan-K2 + lteK2('LTE_K2'), + + /// LTE plan-K + lteK('LTE_K'); + + final String rawValue; + + const NetworkTypeEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static NetworkTypeEnum? getValue(String rawValue) { + return NetworkTypeEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/off_delay_usb.dart b/flutter/lib/options/off_delay_usb.dart new file mode 100644 index 00000000000..bac03597125 --- /dev/null +++ b/flutter/lib/options/off_delay_usb.dart @@ -0,0 +1,83 @@ +/// Auto power off time with USB power supply. +/// +/// For RICOH THETA A1 +class OffDelayUsbEnum { + final dynamic rawValue; + final int sec; + + OffDelayUsbEnum._internal(String rateStr, this.sec) : rawValue = rateStr; + + OffDelayUsbEnum._internalSec(this.sec) : rawValue = sec; + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([sec]); + + @override + String toString() => rawValue.toString(); + + /// Do not turn power off. + static final disable = OffDelayUsbEnum._internal('DISABLE', 6555); + + /// Power off after 10 minutes.(600sec) + static final offDelay_10m = OffDelayUsbEnum._internal('OFF_DELAY_10M', 600); + + /// Power off after 1 hour.(3600sec) + static final offDelay_1h = OffDelayUsbEnum._internal('OFF_DELAY_1H', 3600); + + /// Power off after 2 hour.(7200sec) + static final offDelay_2h = OffDelayUsbEnum._internal('OFF_DELAY_2H', 7200); + + /// Power off after 4 hour.(14400sec) + static final offDelay_4h = OffDelayUsbEnum._internal('OFF_DELAY_4H', 14400); + + /// Power off after 8 hour.(28800sec) + static final offDelay_8h = OffDelayUsbEnum._internal('OFF_DELAY_8H', 28800); + + /// Power off after 12 hour.(43200sec) + static final offDelay_12h = OffDelayUsbEnum._internal('OFF_DELAY_12H', 43200); + + /// Power off after 18 hour.(64800sec) + static final offDelay_18h = OffDelayUsbEnum._internal('OFF_DELAY_18H', 64800); + + /// Power off after 24 hour.(86400sec) + static final offDelay_24h = OffDelayUsbEnum._internal('OFF_DELAY_24H', 86400); + + /// Power off after 2 days.(172800sec) + static final offDelay_2d = OffDelayUsbEnum._internal('OFF_DELAY_2D', 172800); + + static final values = [ + disable, + offDelay_10m, + offDelay_1h, + offDelay_2h, + offDelay_4h, + offDelay_8h, + offDelay_12h, + offDelay_18h, + offDelay_24h, + offDelay_2d, + ]; + + static OffDelayUsbEnum? getValue(dynamic rawValue) { + if (rawValue is int) { + return OffDelayUsbSec(rawValue); + } + var list = List.of(values); + list.add(null); + return list.firstWhere((element) { + return element?.rawValue == rawValue; + }, orElse: () => null); + } +} + +/// Auto power off time with USB power supply. +/// +/// For RICOH THETA A1 +/// 0, or a value that is a multiple of 60 out of 600 or more and 2592000 or less (unit: second), or 65535. +/// Return 0 when 65535 is set and obtained (Do not turn power OFF). +class OffDelayUsbSec extends OffDelayUsbEnum { + OffDelayUsbSec(super.sec) : super._internalSec(); +} diff --git a/flutter/lib/options/preview_format.dart b/flutter/lib/options/preview_format.dart new file mode 100644 index 00000000000..b18abfce58f --- /dev/null +++ b/flutter/lib/options/preview_format.dart @@ -0,0 +1,65 @@ +/// Format of live view +enum PreviewFormatEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// For Theta X firmware v2.71.1 or later + // ignore: constant_identifier_names + w1920_h960_f30('W1920_H960_F30'), + + /// For Theta Z1 and V + // ignore: constant_identifier_names + w1920_h960_f8('W1920_H960_F8'), + + /// width_height_framerate + /// For Theta X, Z1, V and SC2 + // ignore: constant_identifier_names + w1024_h512_f30('W1024_H512_F30'), + + /// For Theta X. This value can't set. + // ignore: constant_identifier_names + w1024_h512_f15('W1024_H512_F15'), + + /// For Theta A1 + // ignore: constant_identifier_names + w1024_h512_f10('W1024_H512_F10'), + + /// For Theta Z1 and V + // ignore: constant_identifier_names + w1024_h512_f8('W1024_H512_F8'), + + /// For Theta Z1 and V + // ignore: constant_identifier_names + w640_h320_f30('W640_H320_F30'), + + /// For Theta S and SC + // ignore: constant_identifier_names + w640_h320_f10('W640_H320_F10'), + + /// For Theta Z1 and V + // ignore: constant_identifier_names + w640_h320_f8('W640_H320_F8'), + + /// For Theta X + // ignore: constant_identifier_names + w512_h512_f30('W512_H512_F30'), + + /// For Theta X + // ignore: constant_identifier_names + w3840_h1920_f30('W3840_H1920_F30'); + + final String rawValue; + + const PreviewFormatEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static PreviewFormatEnum? getValue(String rawValue) { + return PreviewFormatEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/top_bottom_correction.dart b/flutter/lib/options/top_bottom_correction.dart new file mode 100644 index 00000000000..4a19ff3af2c --- /dev/null +++ b/flutter/lib/options/top_bottom_correction.dart @@ -0,0 +1,48 @@ +/// top bottom correction +/// +/// Sets the top/bottom correction. For RICOH THETA V and RICOH +/// THETA Z1, the top/bottom correction can be set only for still +/// images. For RICOH THETA X, the top/bottom correction can be +/// set for both still images and videos. +enum TopBottomCorrectionOptionEnum { + /// Top/bottom correction is performed. + apply('APPLY'), + + /// Refer to top/bottom correction when shooting with "ApplyAuto" + applyAuto('APPLY_AUTO'), + + /// Top/bottom correction is performed. The parameters used for + /// top/bottom correction for the first image are saved and used + /// for the 2nd and subsequent images.(RICOH THETA X or later) + applySemiauto('APPLY_SEMIAUTO'), + + /// Performs top/bottom correction and then saves the parameters. + applySave('APPLY_SAVE'), + + /// Performs top/bottom correction using the saved parameters. + applyLoad('APPLY_LOAD'), + + /// Does not perform top/bottom correction. + disapply('DISAPPLY'), + + /// Performs the top/bottom correction with the specified front + /// position. The front position can be specified with + /// _topBottomCorrectionRotation. + manual('MANUAL'); + + final String rawValue; + + const TopBottomCorrectionOptionEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static TopBottomCorrectionOptionEnum? getValue(String rawValue) { + return TopBottomCorrectionOptionEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/top_bottom_correction_rotation.dart b/flutter/lib/options/top_bottom_correction_rotation.dart new file mode 100644 index 00000000000..03d7a158e08 --- /dev/null +++ b/flutter/lib/options/top_bottom_correction_rotation.dart @@ -0,0 +1,23 @@ +/// Sets the front position for the top/bottom correction. +/// Enabled only for _topBottomCorrection Manual. +class TopBottomCorrectionRotation { + /// Specifies the pitch. + /// Specified range is -90.0 to +90.0, stepSize is 0.1 + double pitch; + + /// Specifies the roll. + /// Specified range is -180.0 to +180.0, stepSize is 0.1 + double roll; + + /// Specifies the yaw. + /// Specified range is -180.0 to +180.0, stepSize is 0.1 + double yaw; + + TopBottomCorrectionRotation(this.pitch, this.roll, this.yaw); + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([pitch, roll, yaw]); +} diff --git a/flutter/lib/options/top_bottom_correction_rotation_support.dart b/flutter/lib/options/top_bottom_correction_rotation_support.dart new file mode 100644 index 00000000000..4be23e580ca --- /dev/null +++ b/flutter/lib/options/top_bottom_correction_rotation_support.dart @@ -0,0 +1,31 @@ +import 'package:theta_client_flutter/theta_client_flutter.dart'; + +/// Supported TopBottomCorrectionRotation +class TopBottomCorrectionRotationSupport { + /// Supported pitch + final ValueRange pitch; + + /// Supported roll + final ValueRange roll; + + /// Supported yaw + final ValueRange yaw; + + TopBottomCorrectionRotationSupport({ + required this.pitch, + required this.roll, + required this.yaw, + }); + + @override + bool operator ==(Object other) => + identical(this, other) || + other is TopBottomCorrectionRotationSupport && + runtimeType == other.runtimeType && + pitch == other.pitch && + roll == other.roll && + yaw == other.yaw; + + @override + int get hashCode => Object.hash(pitch, roll, yaw); +} diff --git a/flutter/lib/options/usb_connection.dart b/flutter/lib/options/usb_connection.dart new file mode 100644 index 00000000000..5be662fd24d --- /dev/null +++ b/flutter/lib/options/usb_connection.dart @@ -0,0 +1,29 @@ +/// USB connection of the camera. +/// +/// Default value is "MTP". +/// After switching the setting value, reconnect the camera to USB to enable it. +enum UsbConnectionEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// MTP + mtp('MTP'), + + /// MSC + msc('MSC'); + + final String rawValue; + + const UsbConnectionEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static UsbConnectionEnum? getValue(String rawValue) { + return UsbConnectionEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/wlan_antenna_config.dart b/flutter/lib/options/wlan_antenna_config.dart new file mode 100644 index 00000000000..36bef6fa3c7 --- /dev/null +++ b/flutter/lib/options/wlan_antenna_config.dart @@ -0,0 +1,27 @@ +/// Configure SISO or MIMO for Wireless LAN. +enum WlanAntennaConfigEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// SISO + siso('SISO'), + + /// MIMO + mimo('MIMO'); + + final String rawValue; + + const WlanAntennaConfigEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static WlanAntennaConfigEnum? getValue(String rawValue) { + return WlanAntennaConfigEnum.values + .cast() + .firstWhere((element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/wlan_frequency.dart b/flutter/lib/options/wlan_frequency.dart new file mode 100644 index 00000000000..dcc5bd59ad0 --- /dev/null +++ b/flutter/lib/options/wlan_frequency.dart @@ -0,0 +1,32 @@ +/// Wireless LAN frequency of the camera supported by Theta X, Z1 and V. +enum WlanFrequencyEnum { + /// Undefined value + unknown('UNKNOWN'), + + /// 2.4GHz + ghz_2_4('GHZ_2_4'), + + /// 5GHz + ghz_5('GHZ_5'), + + /// 5.2GHz + ghz_5_2('GHZ_5_2'), + + /// 5.8GHz + ghz_5_8('GHZ_5_8'); + + final String rawValue; + + const WlanFrequencyEnum(this.rawValue); + + @override + String toString() { + return rawValue; + } + + static WlanFrequencyEnum? getValue(String rawValue) { + return WlanFrequencyEnum.values.cast().firstWhere( + (element) => element?.rawValue == rawValue, + orElse: () => null); + } +} diff --git a/flutter/lib/options/wlan_frequency_cl_mode.dart b/flutter/lib/options/wlan_frequency_cl_mode.dart new file mode 100644 index 00000000000..47fd58bb502 --- /dev/null +++ b/flutter/lib/options/wlan_frequency_cl_mode.dart @@ -0,0 +1,21 @@ +/// Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies. +/// +/// For RICOH THETA A1 +class WlanFrequencyClMode { + /// 2.4GHz + bool enable2_4; + + /// 5.2GHz + bool enable5_2; + + /// 5.8GHz + bool enable5_8; + + WlanFrequencyClMode(this.enable2_4, this.enable5_2, this.enable5_8); + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([enable2_4, enable5_2, enable5_8]); +} diff --git a/flutter/lib/state/capture_status.dart b/flutter/lib/state/capture_status.dart index 635b077de7b..10e00846ff2 100644 --- a/flutter/lib/state/capture_status.dart +++ b/flutter/lib/state/capture_status.dart @@ -26,9 +26,13 @@ enum CaptureStatusEnum { /// Capture status. Waiting for retrospective video... retrospectiveImageRecording('RETROSPECTIVE_IMAGE_RECORDING'), - + /// Capture status. Performing burst shooting burstShooting('BURST_SHOOTING'), + + /// In the case of time-lag shooting by manual lens, + /// set while waiting for the second shot to be taken after the first shot is completed. + timeShiftShootingIdle('TIME_SHIFT_SHOOTING_IDLE'), ; final String rawValue; diff --git a/flutter/lib/state/theta_state.dart b/flutter/lib/state/theta_state.dart index e61b8bd11b0..5017357e3c1 100644 --- a/flutter/lib/state/theta_state.dart +++ b/flutter/lib/state/theta_state.dart @@ -74,7 +74,8 @@ class ThetaState { /// This represents the current temperature inside the battery as an integer value, ranging from -10°C to 100°C with a precision of 1°C. int? batteryTemp; - ThetaState(this.fingerprint, + ThetaState( + this.fingerprint, this.batteryLevel, this.storageUri, this.storageID, diff --git a/flutter/lib/theta_client_flutter.dart b/flutter/lib/theta_client_flutter.dart index d875280e47a..134f7fedde9 100644 --- a/flutter/lib/theta_client_flutter.dart +++ b/flutter/lib/theta_client_flutter.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; import 'package:theta_client_flutter/digest_auth.dart'; +import 'package:theta_client_flutter/options/access_info.dart'; import 'capture/capture_builder.dart'; import 'options/importer.dart'; @@ -20,6 +21,13 @@ class ThetaClientFlutter { return ThetaClientFlutterPlatform.instance.getPlatformVersion(); } + /// Set up a log listener for THETA API calls + /// + /// @param listener Called when there is a THETA API request and response; if null, unregister. + Future setApiLogListener(void Function(String message)? listener) { + return ThetaClientFlutterPlatform.instance.setApiLogListener(listener); + } + /// Initialize object. /// /// - @param [endpoint] URL of Theta web API endpoint. @@ -146,6 +154,12 @@ class ThetaClientFlutter { return TimeShiftCaptureBuilder(); } + /// Get TimeShiftManualCapture.Builder for capture time shift. + TimeShiftManualCaptureBuilder getTimeShiftManualCaptureBuilder() { + ThetaClientFlutterPlatform.instance.getTimeShiftManualCaptureBuilder(); + return TimeShiftManualCaptureBuilder(); + } + /// Get VideoCapture.Builder for capture video. VideoCaptureBuilder getVideoCaptureBuilder() { ThetaClientFlutterPlatform.instance.getVideoCaptureBuilder(); @@ -238,6 +252,16 @@ class ThetaClientFlutter { return ThetaClientFlutterPlatform.instance.getMetadata(fileUrl); } + /// Turn off/on (reboot) the camera. + /// Supported models are THETA A1 only. + /// + /// Error when connecting in CL mode for Japan-bound models only + /// + /// - @throws If an error occurs in THETA. + Future reboot() { + return ThetaClientFlutterPlatform.instance.reboot(); + } + /// Reset all device settings and capture settings. /// After reset, the camera will be restarted. /// @@ -263,10 +287,9 @@ class ThetaClientFlutter { /// - @param onProgress the block for convertVideoFormats progress. /// - @return URL of a converted movie file. /// - @throws Command is currently disabled. - Future convertVideoFormats(String fileUrl, - bool toLowResolution, + Future convertVideoFormats(String fileUrl, bool toLowResolution, [bool applyTopBottomCorrection = true, - void Function(double)? onProgress]) { + void Function(double)? onProgress]) { return ThetaClientFlutterPlatform.instance.convertVideoFormats( fileUrl, toLowResolution, applyTopBottomCorrection, onProgress); } @@ -301,15 +324,15 @@ class ThetaClientFlutter { /// - @param ssid SSID of the access point. /// - @param ssidStealth True if SSID stealth is enabled. /// - @param authMode Authentication mode. - /// - @param password Password. If [authMode] is "[none]", pass empty String. + /// - @param password Password. Not set if [authMode] is "[none]". /// - @param connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) /// - @param proxy Proxy information to be used for the access point. /// - @throws If an error occurs in THETA. Future setAccessPointDynamically(String ssid, - {bool ssidStealth = false, + {bool? ssidStealth, AuthModeEnum authMode = AuthModeEnum.none, - String password = '', - int connectionPriority = 1, + String? password, + int? connectionPriority, Proxy? proxy}) { return ThetaClientFlutterPlatform.instance.setAccessPointDynamically( ssid, ssidStealth, authMode, password, connectionPriority, proxy); @@ -318,23 +341,27 @@ class ThetaClientFlutter { /// Set access point. IP address is set statically. /// /// - @param ssid SSID of the access point. - /// - @param ssidStealth True if SSID stealth is enabled. - /// - @param authMode Authentication mode. - /// - @param password Password. If [authMode] is "[none]", pass empty String. - /// - @param connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) + /// - @param ssidStealth True if SSID stealth is enabled. Default is false. + /// - @param authMode [AuthModeEnum] Authentication mode. + /// - @param password Password. Not set if [authMode] is "[none]". + /// - @param connectionPriority Connection priority (1 to 5). Default is 1. Theta X fixed to 1 (The access point registered later has a higher priority.) /// - @param ipAddress IP address assigns to Theta. /// - @param subnetMask Subnet mask. /// - @param defaultGateway Default gateway. + /// - @param Primary DNS server. + /// - @param Secondary DNS server. /// - @param proxy Proxy information to be used for the access point. /// - @throws If an error occurs in THETA. Future setAccessPointStatically(String ssid, - {bool ssidStealth = false, + {bool? ssidStealth, AuthModeEnum authMode = AuthModeEnum.none, - String password = '', - int connectionPriority = 1, + String? password, + int? connectionPriority, required String ipAddress, required String subnetMask, required String defaultGateway, + String? dns1, + String? dns2, Proxy? proxy}) { return ThetaClientFlutterPlatform.instance.setAccessPointStatically( ssid, @@ -345,6 +372,8 @@ class ThetaClientFlutter { ipAddress, subnetMask, defaultGateway, + dns1, + dns2, proxy); } @@ -510,7 +539,11 @@ enum ThetaModel { thetaSC2('THETA_SC2'), /// THETA SC2 for business - thetaSC2B('THETA_SC2_B'); + thetaSC2B('THETA_SC2_B'), + + /// THETA A1 + thetaA1('THETA_A1'), + ; final String rawValue; @@ -539,7 +572,12 @@ class ThetaInfo { /// Theta serial number. final String serialNumber; - /// MAC address of wireless LAN (RICOH THETA V firmware v2.11.1 or later) + /// MAC address of wireless LAN + /// (RICOH THETA V firmware v2.11.1 or later) + /// + /// For THETA X, firmware versions v2.63.0 and earlier display `the communication MAC address`, + /// while v2.71.1 and later diplay `the physical MAC address`. + /// For other than THETA X, `the physical MAC address` is displayed. final String? wlanMacAddress; /// MAC address of Bluetooth (RICOH THETA V firmware v2.11.1 or later) @@ -644,8 +682,14 @@ enum StorageEnum { /// Video codec enum CodecEnum { + /// Undefined value + unknown('UNKNOWN'), + /// codec H.264/MPEG-4 AVC - h264mp4avc('H264MP4AVC'); + h264mp4avc('H264MP4AVC'), + + /// codec H.265/HEVC + h265hevc('H265HEVC'); final String rawValue; @@ -1142,6 +1186,9 @@ class Metadata { /// Enum for authentication mode. enum AuthModeEnum { + /// Undefined value + unknown('UNKNOWN'), + /// Authentication mode. none none('NONE'), @@ -1149,7 +1196,10 @@ enum AuthModeEnum { wep('WEP'), /// Authentication mode. WPA/WPA2 PSK - wpa('WPA'); + wpa('WPA'), + + /// Authentication mode. WPA3-SAE + wpa3('WPA3'); final String rawValue; @@ -1179,7 +1229,7 @@ class AccessPoint { AuthModeEnum authMode; /// Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) - int connectionPriority = 1; + int connectionPriority; /// Using DHCP or not. This can be acquired when SSID is registered as an enable access point. bool usingDhcp; @@ -1193,34 +1243,47 @@ class AccessPoint { /// Default Gateway. This setting can be acquired when “usingDhcp” is false. String? defaultGateway; + /// Primary DNS server. + String? dns1; + + /// Secondary DNS server. + String? dns2; + /// Proxy information to be used for the access point. Proxy? proxy; - AccessPoint( - this.ssid, - this.ssidStealth, - this.authMode, - this.connectionPriority, - this.usingDhcp, + AccessPoint(this.ssid, this.ssidStealth, this.authMode, this.usingDhcp, + [this.connectionPriority = 1, this.ipAddress, this.subnetMask, this.defaultGateway, - this.proxy); + this.dns1, + this.dns2, + this.proxy]); } /// Camera setting options name. /// /// [options name](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/options.md) enum OptionNameEnum { + /// Option name _accessInfo + accessInfo('AccessInfo', AccessInfo), + /// Option name _aiAutoThumbnail aiAutoThumbnail('AiAutoThumbnail', AiAutoThumbnailEnum), + /// Option name _aiAutoThumbnailSupport + aiAutoThumbnailSupport('AiAutoThumbnailSupport', List), + /// Option name _autoBracket autoBracket('AutoBracket', List), /// Option name aperture aperture('Aperture', ApertureEnum), + /// Option name apertureSupport + apertureSupport('ApertureSupport', List), + /// Option name _bitrate bitrate('Bitrate', Bitrate), @@ -1239,12 +1302,25 @@ enum OptionNameEnum { /// Option name _cameraControlSource cameraControlSource('CameraControlSource', CameraControlSourceEnum), + /// Option name _cameraControlSourceSupport + cameraControlSourceSupport( + 'CameraControlSourceSupport', List), + + /// Option name _cameraLock + cameraLock('CameraLock', CameraLockEnum), + + /// Option name _cameraLockConfig + cameraLockConfig('CameraLockConfig', CameraLockConfig), + /// Option name _cameraMode cameraMode('CameraMode', CameraModeEnum), /// Option name _cameraPower cameraPower('CameraPower', CameraPowerEnum), + /// Option name _cameraPowerSupport + cameraPowerSupport('CameraPowerSupport', List), + /// Option name captureInterval captureInterval('CaptureInterval', int), @@ -1257,12 +1333,25 @@ enum OptionNameEnum { /// Option name _colorTemperature colorTemperature('ColorTemperature', int), + /// Option name _colorTemperatureSupport + colorTemperatureSupport('ColorTemperatureSupport', ValueRange), + + /// Option name _compassDirectionRef + compassDirectionRef('CompassDirectionRef', CompassDirectionRefEnum), + /// Option name _compositeShootingOutputInterval compositeShootingOutputInterval('CompositeShootingOutputInterval', int), + /// Option name _compositeShootingOutputIntervalSupport + compositeShootingOutputIntervalSupport( + 'CompositeShootingOutputIntervalSupport', ValueRange), + /// Option name _compositeShootingTime compositeShootingTime('CompositeShootingTime', int), + /// Option name _compositeShootingTimeSupport + compositeShootingTimeSupport('CompositeShootingTimeSupport', ValueRange), + /// Option name continuousNumber continuousNumber('ContinuousNumber', ContinuousNumberEnum), @@ -1278,6 +1367,9 @@ enum OptionNameEnum { /// Option name exposureDelay exposureDelay('ExposureDelay', ExposureDelayEnum), + /// Option name exposureDelaySupport + exposureDelaySupport('ExposureDelaySupport', List), + /// Option name exposureProgram exposureProgram('ExposureProgram', ExposureProgramEnum), @@ -1299,6 +1391,9 @@ enum OptionNameEnum { /// Option name gpsInfo gpsInfo('GpsInfo', GpsInfo), + /// Option name gpsTagRecordingSupport + gpsTagRecordingSupport('GpsTagRecordingSupport', List), + /// Option name imageStitching imageStitching('ImageStitching', ImageStitchingEnum), @@ -1323,12 +1418,22 @@ enum OptionNameEnum { /// Option name _maxRecordableTime maxRecordableTime('MaxRecordableTime', MaxRecordableTimeEnum), + /// Option name _microphoneNoiseReduction + microphoneNoiseReduction( + 'MicrophoneNoiseReduction', MicrophoneNoiseReductionEnum), + + /// Option name _mobileNetworkSetting + mobileNetworkSetting('MobileNetworkSetting', MobileNetworkSetting), + /// Option name _networkType networkType('NetworkType', NetworkTypeEnum), /// Option name offDelay offDelay('OffDelay', OffDelayEnum), + /// Option name _offDelayUSB + offDelayUsb('OffDelayUsb', OffDelayUsbEnum), + /// Option name _password password('Password', String), @@ -1375,9 +1480,16 @@ enum OptionNameEnum { topBottomCorrectionRotation( 'TopBottomCorrectionRotation', TopBottomCorrectionRotation), + /// Option name topBottomCorrectionRotationSupport + topBottomCorrectionRotationSupport( + 'TopBottomCorrectionRotationSupport', TopBottomCorrectionRotationSupport), + /// Option name totalSpace totalSpace('TotalSpace', int), + /// Option name _usbConnection + usbConnection('UsbConnection', UsbConnectionEnum), + /// Option name _username username('Username', String), @@ -1394,8 +1506,18 @@ enum OptionNameEnum { whiteBalanceAutoStrength( 'WhiteBalanceAutoStrength', WhiteBalanceAutoStrengthEnum), - // Option name wlanfrequency - wlanFrequency('WlanFrequency', WlanFrequencyEnum); + /// Option name _wlanAntennaConfig + wlanAntennaConfig('WlanAntennaConfig', WlanAntennaConfigEnum), + + /// Option name wlanfrequency + wlanFrequency('WlanFrequency', WlanFrequencyEnum), + + /// Option name wlanFrequencySupport + wlanFrequencySupport('WlanFrequencySupport', List), + + /// Option name wlanfrequencyCLmode + wlanFrequencyClMode('WlanFrequencyClMode', WlanFrequencyClMode), + ; final String rawValue; final dynamic valueType; @@ -1414,78 +1536,6 @@ enum OptionNameEnum { } } -/// AI auto thumbnail setting. -/// -/// For RICOH THETA X -enum AiAutoThumbnailEnum { - /// AI auto setting ON. - on('ON'), - - /// AI auto setting OFF. - off('OFF'); - - final String rawValue; - - const AiAutoThumbnailEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static AiAutoThumbnailEnum? getValue(String rawValue) { - return AiAutoThumbnailEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - -/// Aperture value. -enum ApertureEnum { - /// Aperture AUTO(0). - apertureAuto('APERTURE_AUTO'), - - /// Aperture 2.0F. - /// - /// RICOH THETA V or prior - aperture_2_0('APERTURE_2_0'), - - /// Aperture 2.1F. - /// - /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority - aperture_2_1('APERTURE_2_1'), - - /// Aperture 2.4F. - /// - /// RICOH THETA X or later - aperture_2_4('APERTURE_2_4'), - - /// Aperture 3.5F. - /// - /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority - aperture_3_5('APERTURE_3_5'), - - /// Aperture 5.6F. - /// - /// RICOH THETA Z1 and the exposure program [exposureProgram] is set to Manual or Aperture Priority - aperture_5_6('APERTURE_5_6'); - - final String rawValue; - - const ApertureEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static ApertureEnum? getValue(String rawValue) { - return ApertureEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// Movie bit rate. /// /// ### Support value @@ -1612,33 +1662,6 @@ class BracketSetting { ]); } -/// Camera control source. -enum CameraControlSourceEnum { - /// Operation is possible with the camera. Locks the smartphone - /// application UI (supported app only). - camera('CAMERA'), - - /// Operation is possible with the smartphone application. Locks - /// the UI on the shooting screen on the camera. - app('APP'); - - final String rawValue; - - const CameraControlSourceEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static CameraControlSourceEnum? getValue(String rawValue) { - return CameraControlSourceEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// Camera mode. enum CameraModeEnum { /// shooting screen @@ -1796,115 +1819,6 @@ enum ContinuousNumberEnum { } } -/// Exposure compensation (EV). -enum ExposureCompensationEnum { - /// Exposure compensation -2.0 - m2_0('M2_0'), - - /// Exposure compensation -1.7 - m1_7('M1_7'), - - /// Exposure compensation -1.3 - m1_3('M1_3'), - - /// Exposure compensation -1.0 - m1_0('M1_0'), - - /// Exposure compensation -0.7 - m0_7('M0_7'), - - /// Exposure compensation -0.3 - m0_3('M0_3'), - - /// Exposure compensation 0.0 - zero('ZERO'), - - /// Exposure compensation 0.3 - p0_3('P0_3'), - - /// Exposure compensation 0.7 - p0_7('P0_7'), - - /// Exposure compensation 1.0 - p1_0('P1_0'), - - /// Exposure compensation 1.3 - p1_3('P1_3'), - - /// Exposure compensation 1.7 - p1_7('P1_7'), - - /// Exposure compensation 2.0 - p2_0('P2_0'); - - final String rawValue; - - const ExposureCompensationEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static ExposureCompensationEnum? getValue(String rawValue) { - return ExposureCompensationEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - -/// Operating time (sec.) of the self-timer. -enum ExposureDelayEnum { - /// Disable self-timer. - delayOff('DELAY_OFF'), - - /// Self-timer time. 1sec. - delay1('DELAY_1'), - - /// Self-timer time. 2sec. - delay2('DELAY_2'), - - /// Self-timer time. 3sec. - delay3('DELAY_3'), - - /// Self-timer time. 4sec. - delay4('DELAY_4'), - - /// Self-timer time. 5sec. - delay5('DELAY_5'), - - /// Self-timer time. 6sec. - delay6('DELAY_6'), - - /// Self-timer time. 7sec. - delay7('DELAY_7'), - - /// Self-timer time. 8sec. - delay8('DELAY_8'), - - /// Self-timer time. 9sec. - delay9('DELAY_9'), - - /// Self-timer time. 10sec. - delay10('DELAY_10'); - - final String rawValue; - - const ExposureDelayEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static ExposureDelayEnum? getValue(String rawValue) { - return ExposureDelayEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// Exposure program. The exposure settings that take priority can be selected. /// /// It can be set for video shooting mode at RICOH THETA V firmware v3.00.1 or later. @@ -2375,36 +2289,6 @@ enum LanguageEnum { } } -/// Network type of the camera supported by Theta X, Z1 and V. -enum NetworkTypeEnum { - // Direct mode - direct('DIRECT'), - - // Client mode via WLAN - client('CLIENT'), - - // Client mode via Ethernet cable supported by Theta Z1 and V. - ethernet('ETHERNET'), - - // Network is off. This value can be gotten only by plugin. - off('OFF'); - - final String rawValue; - - const NetworkTypeEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static NetworkTypeEnum? getValue(String rawValue) { - return NetworkTypeEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// PowerSaving /// /// For Theta X only @@ -2454,61 +2338,6 @@ enum PresetEnum { } } -/// Format of live view -enum PreviewFormatEnum { - /// width_height_framerate - /// For Theta X, Z1, V and SC2 - // ignore: constant_identifier_names - w1024_h512_f30('W1024_H512_F30'), - - /// For Theta X. This value can't set. - // ignore: constant_identifier_names - w1024_h512_f15('W1024_H512_F15'), - - /// For Theta X - // ignore: constant_identifier_names - w512_h512_f30('W512_H512_F30'), - - /// For Theta Z1 and V - // ignore: constant_identifier_names - w1920_h960_f8('W1920_H960_F8'), - - /// For Theta Z1 and V - // ignore: constant_identifier_names - w1024_h512_f8('W1024_H512_F8'), - - /// For Theta Z1 and V - // ignore: constant_identifier_names - w640_h320_f30('W640_H320_F30'), - - /// For Theta Z1 and V - // ignore: constant_identifier_names - w640_h320_f8('W640_H320_F8'), - - /// For Theta S and SC - // ignore: constant_identifier_names - w640_h320_f10('W640_H320_F10'), - - /// For Theta X - // ignore: constant_identifier_names - w3840_h1920_f30('W3840_H1920_F30'); - - final String rawValue; - - const PreviewFormatEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static PreviewFormatEnum? getValue(String rawValue) { - return PreviewFormatEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// Shooting method /// /// Shooting method for My Settings mode. In RICOH THETA X, it is used outside of MySetting. @@ -2955,55 +2784,6 @@ enum TimeShiftIntervalEnum { } } -/// top bottom correction -/// -/// Sets the top/bottom correction. For RICOH THETA V and RICOH -/// THETA Z1, the top/bottom correction can be set only for still -/// images. For RICOH THETA X, the top/bottom correction can be -/// set for both still images and videos. -enum TopBottomCorrectionOptionEnum { - /// Top/bottom correction is performed. - apply('APPLY'), - - /// Refer to top/bottom correction when shooting with "ApplyAuto" - applyAuto('APPLY_AUTO'), - - /// Top/bottom correction is performed. The parameters used for - /// top/bottom correction for the first image are saved and used - /// for the 2nd and subsequent images.(RICOH THETA X or later) - applySemiauto('APPLY_SEMIAUTO'), - - /// Performs top/bottom correction and then saves the parameters. - applySave('APPLY_SAVE'), - - /// Performs top/bottom correction using the saved parameters. - applyLoad('APPLY_LOAD'), - - /// Does not perform top/bottom correction. - disapply('DISAPPLY'), - - /// Performs the top/bottom correction with the specified front - /// position. The front position can be specified with - /// _topBottomCorrectionRotation. - manual('MANUAL'); - - final String rawValue; - - const TopBottomCorrectionOptionEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static TopBottomCorrectionOptionEnum? getValue(String rawValue) { - return TopBottomCorrectionOptionEnum.values - .cast() - .firstWhere((element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - /// Video stitching during shooting. enum VideoStitchingEnum { /// Stitching is OFF @@ -3053,30 +2833,6 @@ enum VisibilityReductionEnum { } } -/// Sets the front position for the top/bottom correction. -/// Enabled only for _topBottomCorrection Manual. -class TopBottomCorrectionRotation { - /// Specifies the pitch. - /// Specified range is -90.0 to +90.0, stepSize is 0.1 - double pitch; - - /// Specifies the roll. - /// Specified range is -180.0 to +180.0, stepSize is 0.1 - double roll; - - /// Specifies the yaw. - /// Specified range is -180.0 to +180.0, stepSize is 0.1 - double yaw; - - TopBottomCorrectionRotation(this.pitch, this.roll, this.yaw); - - @override - bool operator ==(Object other) => hashCode == other.hashCode; - - @override - int get hashCode => Object.hashAll([pitch, roll, yaw]); -} - /// White balance auto strength. /// /// To set the strength of white balance auto for low color temperature scene. @@ -3108,50 +2864,6 @@ enum WhiteBalanceAutoStrengthEnum { } } -/// Wireless LAN frequency of the camera supported by Theta X, Z1 and V. -enum WlanFrequencyEnum { - /// 2.4GHz - ghz_2_4('GHZ_2_4'), - - /// 5GHz - ghz_5('GHZ_5'); - - final String rawValue; - - const WlanFrequencyEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } - - static WlanFrequencyEnum? getValue(String rawValue) { - return WlanFrequencyEnum.values.cast().firstWhere( - (element) => element?.rawValue == rawValue, - orElse: () => null); - } -} - -/// Turns position information assigning ON/OFF. -/// -/// For RICOH THETA X -enum GpsTagRecordingEnum { - /// Position information assigning ON. - on('ON'), - - /// Position information assigning OFF. - off('OFF'); - - final String rawValue; - - const GpsTagRecordingEnum(this.rawValue); - - @override - String toString() { - return rawValue; - } -} - /// GPS information. /// 65535 is set for latitude and longitude when disabling the GPS setting at /// RICOH THETA Z1 and prior. @@ -3224,12 +2936,21 @@ class Proxy { /// /// Refer to the [options category](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/options.md) class Options { + /// Connected network information. + AccessInfo? accessInfo; + /// AI auto thumbnail setting. AiAutoThumbnailEnum? aiAutoThumbnail; + /// Supported AI auto thumbnail setting. + List? aiAutoThumbnailSupport; + /// Aperture value. ApertureEnum? aperture; + /// Supported aperture value. + List? apertureSupport; + /// Multi bracket shooting setting. List? autoBracket; @@ -3257,6 +2978,15 @@ class Options { /// For RICOH THETA X CameraControlSourceEnum? cameraControlSource; + /// Supported Camera Control Source. + List? cameraControlSourceSupport; + + /// see [CameraLockEnum] + CameraLockEnum? cameraLock; + + /// see [CameraLockConfig] + CameraLockConfig? cameraLockConfig; + /// Camera mode. /// The current setting can be acquired by camera.getOptions, and it can be changed by camera.setOptions. /// @@ -3266,6 +2996,9 @@ class Options { /// see [CameraPowerEnum] CameraPowerEnum? cameraPower; + /// Supported Camera Power. + List? cameraPowerSupport; + /// Shooting interval (sec.) for interval shooting. /// /// ### Support value @@ -3313,6 +3046,12 @@ class Options { /// 2500 to 10000. In 100-Kelvin units. int? colorTemperature; + /// supported color temperature. + ValueRange? colorTemperatureSupport; + + /// see [CompassDirectionRefEnum] + CompassDirectionRefEnum? compassDirectionRef; + /// In-progress save interval for interval composite shooting (sec). /// /// 0 (no saving), 60 to 600. In 60-second units. @@ -3323,6 +3062,9 @@ class Options { /// RICOH THETA S firmware v01.82 or later int? compositeShootingOutputInterval; + /// Supported in-progress save interval for interval composite shooting (sec). + ValueRange? compositeShootingOutputIntervalSupport; + /// Shooting time for interval composite shooting (sec). /// /// 600 to 86400. In 600-second units. @@ -3333,6 +3075,9 @@ class Options { /// RICOH THETA S firmware v01.82 or later int? compositeShootingTime; + /// Supported shooting time for interval composite shooting (sec). + ValueRange? compositeShootingTimeSupport; + /// see [ContinuousNumberEnum] ContinuousNumberEnum? continuousNumber; @@ -3362,6 +3107,9 @@ class Options { /// get the operating time of the self-timer stored in the camera. ExposureDelayEnum? exposureDelay; + /// Supported operating time (sec.) of the self-timer. + List? exposureDelaySupport; + /// Exposure program. The exposure settings that take priority can be selected. /// /// It can be set for video shooting mode at RICOH THETA V firmware v3.00.1 or later. @@ -3386,9 +3134,12 @@ class Options { /// Also, when filter is enabled, the exposure program is set to the Normal program. /// /// The condition below will result in an error. - /// [fileFormat] is raw+ and _filter is Noise reduction, HDR or Handheld HDR - /// shootingMethod is except for Normal shooting and [filter] is enabled - /// Access during video capture mode + /// + /// - When attempting to set [filter] to Noise reduction, + /// HDR or Handheld HDR while [fileFormat] is set to raw+, + /// but this restriction is only for RICOH THETA Z1 firmware v1.80.1 or earlier. + /// - [shootingMethod] is except for Normal shooting and [filter] is enabled + /// - Access during video capture mode FilterEnum? filter; /// see [ShootingFunctionEnum] @@ -3402,6 +3153,10 @@ class Options { /// In order to append the location information, this property should be specified by the client. GpsInfo? gpsInfo; + /// Supported GpsTagRecording + /// For THETA X + List? gpsTagRecordingSupport; + /// Still image stitching setting during shooting. ImageStitchingEnum? imageStitching; @@ -3428,6 +3183,12 @@ class Options { /// Maximum recordable time (in seconds) of the camera. MaxRecordableTimeEnum? maxRecordableTime; + /// see [MicrophoneNoiseReductionEnum] + MicrophoneNoiseReductionEnum? microphoneNoiseReduction; + + /// see [MobileNetworkSetting] + MobileNetworkSetting? mobileNetworkSetting; + /// Network type of the camera supported by Theta X, Z1 and V. NetworkTypeEnum? networkType; @@ -3436,6 +3197,12 @@ class Options { /// Specify [OffDelayEnum] OffDelayEnum? offDelay; + /// Auto power off time with USB power supply. + /// + /// Specify [OffDelayUsbEnum] + /// For RICOH THETA A1 + OffDelayUsbEnum? offDelayUsb; + /// Password used for digest authentication when _networkType is set to client mode. String? password; @@ -3488,9 +3255,15 @@ class Options { /// see [TopBottomCorrectionRotation] TopBottomCorrectionRotation? topBottomCorrectionRotation; + /// see [TopBottomCorrectionRotationSupport] + TopBottomCorrectionRotationSupport? topBottomCorrectionRotationSupport; + /// Total storage space (byte). int? totalSpace; + /// see [UsbConnectionEnum] + UsbConnectionEnum? usbConnection; + /// User name used for digest authentication when _networkType is set to client mode. String? username; @@ -3513,16 +3286,38 @@ class Options { /// For RICOH THETA Z1 firmware v2.20.3 or later WhiteBalanceAutoStrengthEnum? whiteBalanceAutoStrength; + /// see [WlanAntennaConfigEnum] + WlanAntennaConfigEnum? wlanAntennaConfig; + /// Wireless LAN frequency of the camera supported by Theta X, Z1 and V. WlanFrequencyEnum? wlanFrequency; + /// Supported WlanFrequency + /// + /// For RICOH THETA X, Z1 and V. + List? wlanFrequencySupport; + + /// Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + /// + /// Can be set and retrieved when in AP mode. + /// Can be retrieved when in CL mode. + /// + /// For RICOH THETA A1 + WlanFrequencyClMode? wlanFrequencyClMode; + /// Get Option value. T? getValue(OptionNameEnum name) { switch (name) { + case OptionNameEnum.accessInfo: + return accessInfo as T; case OptionNameEnum.aiAutoThumbnail: return aiAutoThumbnail as T; + case OptionNameEnum.aiAutoThumbnailSupport: + return aiAutoThumbnailSupport as T; case OptionNameEnum.aperture: return aperture as T; + case OptionNameEnum.apertureSupport: + return apertureSupport as T; case OptionNameEnum.autoBracket: return autoBracket as T; case OptionNameEnum.bitrate: @@ -3537,10 +3332,18 @@ class Options { return burstOption as T; case OptionNameEnum.cameraControlSource: return cameraControlSource as T; + case OptionNameEnum.cameraControlSourceSupport: + return cameraControlSourceSupport as T; + case OptionNameEnum.cameraLock: + return cameraLock as T; + case OptionNameEnum.cameraLockConfig: + return cameraLockConfig as T; case OptionNameEnum.cameraMode: return cameraMode as T; case OptionNameEnum.cameraPower: return cameraPower as T; + case OptionNameEnum.cameraPowerSupport: + return cameraPowerSupport as T; case OptionNameEnum.captureInterval: return captureInterval as T; case OptionNameEnum.captureMode: @@ -3549,10 +3352,18 @@ class Options { return captureNumber as T; case OptionNameEnum.colorTemperature: return colorTemperature as T; + case OptionNameEnum.colorTemperatureSupport: + return colorTemperatureSupport as T; + case OptionNameEnum.compassDirectionRef: + return compassDirectionRef as T; case OptionNameEnum.compositeShootingOutputInterval: return compositeShootingOutputInterval as T; + case OptionNameEnum.compositeShootingOutputIntervalSupport: + return compositeShootingOutputIntervalSupport as T; case OptionNameEnum.compositeShootingTime: return compositeShootingTime as T; + case OptionNameEnum.compositeShootingTimeSupport: + return compositeShootingTimeSupport as T; case OptionNameEnum.continuousNumber: return continuousNumber as T; case OptionNameEnum.dateTimeZone: @@ -3563,6 +3374,8 @@ class Options { return exposureCompensation as T; case OptionNameEnum.exposureDelay: return exposureDelay as T; + case OptionNameEnum.exposureDelaySupport: + return exposureDelaySupport as T; case OptionNameEnum.exposureProgram: return exposureProgram as T; case OptionNameEnum.faceDetect: @@ -3577,6 +3390,8 @@ class Options { return gain as T; case OptionNameEnum.gpsInfo: return gpsInfo as T; + case OptionNameEnum.gpsTagRecordingSupport: + return gpsTagRecordingSupport as T; case OptionNameEnum.imageStitching: return imageStitching as T; case OptionNameEnum.isGpsOn: @@ -3591,10 +3406,16 @@ class Options { return latestEnabledExposureDelayTime as T; case OptionNameEnum.maxRecordableTime: return maxRecordableTime as T; + case OptionNameEnum.microphoneNoiseReduction: + return microphoneNoiseReduction as T; + case OptionNameEnum.mobileNetworkSetting: + return mobileNetworkSetting as T; case OptionNameEnum.networkType: return networkType as T; case OptionNameEnum.offDelay: return offDelay as T; + case OptionNameEnum.offDelayUsb: + return offDelayUsb as T; case OptionNameEnum.password: return password as T; case OptionNameEnum.powerSaving: @@ -3625,8 +3446,12 @@ class Options { return topBottomCorrection as T; case OptionNameEnum.topBottomCorrectionRotation: return topBottomCorrectionRotation as T; + case OptionNameEnum.topBottomCorrectionRotationSupport: + return topBottomCorrectionRotationSupport as T; case OptionNameEnum.totalSpace: return totalSpace as T; + case OptionNameEnum.usbConnection: + return usbConnection as T; case OptionNameEnum.username: return username as T; case OptionNameEnum.videoStitching: @@ -3637,8 +3462,14 @@ class Options { return whiteBalance as T; case OptionNameEnum.whiteBalanceAutoStrength: return whiteBalanceAutoStrength as T; + case OptionNameEnum.wlanAntennaConfig: + return wlanAntennaConfig as T; case OptionNameEnum.wlanFrequency: return wlanFrequency as T; + case OptionNameEnum.wlanFrequencySupport: + return wlanFrequencySupport as T; + case OptionNameEnum.wlanFrequencyClMode: + return wlanFrequencyClMode as T; } } @@ -3649,12 +3480,22 @@ class Options { } switch (name) { + case OptionNameEnum.wlanFrequencySupport: + throw Exception('This value cannot be set'); + case OptionNameEnum.accessInfo: + accessInfo = value; + break; case OptionNameEnum.aiAutoThumbnail: aiAutoThumbnail = value; break; + case OptionNameEnum.aiAutoThumbnailSupport: + throw Exception('This value cannot be set'); case OptionNameEnum.aperture: aperture = value; break; + case OptionNameEnum.apertureSupport: + throw Exception('This value cannot be set'); + break; case OptionNameEnum.autoBracket: autoBracket = value; break; @@ -3676,12 +3517,22 @@ class Options { case OptionNameEnum.cameraControlSource: cameraControlSource = value; break; + case OptionNameEnum.cameraControlSourceSupport: + throw Exception('This value cannot be set'); + case OptionNameEnum.cameraLock: + cameraLock = value; + break; + case OptionNameEnum.cameraLockConfig: + cameraLockConfig = value; + break; case OptionNameEnum.cameraMode: cameraMode = value; break; case OptionNameEnum.cameraPower: cameraPower = value; break; + case OptionNameEnum.cameraPowerSupport: + throw Exception('This value cannot be set'); case OptionNameEnum.captureInterval: captureInterval = value; break; @@ -3694,12 +3545,24 @@ class Options { case OptionNameEnum.colorTemperature: colorTemperature = value; break; + case OptionNameEnum.colorTemperatureSupport: + colorTemperatureSupport = value; + break; + case OptionNameEnum.compassDirectionRef: + compassDirectionRef = value; + break; case OptionNameEnum.compositeShootingOutputInterval: compositeShootingOutputInterval = value; break; + case OptionNameEnum.compositeShootingOutputIntervalSupport: + compositeShootingOutputIntervalSupport = value; + break; case OptionNameEnum.compositeShootingTime: compositeShootingTime = value; break; + case OptionNameEnum.compositeShootingTimeSupport: + compositeShootingTimeSupport = value; + break; case OptionNameEnum.continuousNumber: continuousNumber = value; break; @@ -3715,6 +3578,9 @@ class Options { case OptionNameEnum.exposureDelay: exposureDelay = value; break; + case OptionNameEnum.exposureDelaySupport: + exposureDelaySupport = value; + break; case OptionNameEnum.exposureProgram: exposureProgram = value; break; @@ -3736,6 +3602,8 @@ class Options { case OptionNameEnum.gpsInfo: gpsInfo = value; break; + case OptionNameEnum.gpsTagRecordingSupport: + throw Exception('This value cannot be set'); case OptionNameEnum.imageStitching: imageStitching = value; break; @@ -3757,12 +3625,21 @@ class Options { case OptionNameEnum.maxRecordableTime: maxRecordableTime = value; break; + case OptionNameEnum.microphoneNoiseReduction: + microphoneNoiseReduction = value; + break; + case OptionNameEnum.mobileNetworkSetting: + mobileNetworkSetting = value; + break; case OptionNameEnum.networkType: networkType = value; break; case OptionNameEnum.offDelay: offDelay = value; break; + case OptionNameEnum.offDelayUsb: + offDelayUsb = value; + break; case OptionNameEnum.password: password = value; break; @@ -3808,9 +3685,15 @@ class Options { case OptionNameEnum.topBottomCorrectionRotation: topBottomCorrectionRotation = value; break; + case OptionNameEnum.topBottomCorrectionRotationSupport: + topBottomCorrectionRotationSupport = value; + break; case OptionNameEnum.totalSpace: totalSpace = value; break; + case OptionNameEnum.usbConnection: + usbConnection = value; + break; case OptionNameEnum.username: username = value; break; @@ -3826,9 +3709,15 @@ class Options { case OptionNameEnum.whiteBalanceAutoStrength: whiteBalanceAutoStrength = value; break; + case OptionNameEnum.wlanAntennaConfig: + wlanAntennaConfig = value; + break; case OptionNameEnum.wlanFrequency: wlanFrequency = value; break; + case OptionNameEnum.wlanFrequencyClMode: + wlanFrequencyClMode = value; + break; } } } @@ -3914,3 +3803,22 @@ class PluginInfo { this.exitStatus, this.message); } + +class ValueRange { + /// maximum value + T max; + + /// minimum value + T min; + + /// step size + T stepSize; + + ValueRange(this.max, this.min, this.stepSize); + + @override + bool operator ==(Object other) => hashCode == other.hashCode; + + @override + int get hashCode => Object.hashAll([max, min, stepSize]); +} diff --git a/flutter/lib/theta_client_flutter_method_channel.dart b/flutter/lib/theta_client_flutter_method_channel.dart index 0cf9e6440a5..2b466d9f1a5 100644 --- a/flutter/lib/theta_client_flutter_method_channel.dart +++ b/flutter/lib/theta_client_flutter_method_channel.dart @@ -7,6 +7,7 @@ import 'package:theta_client_flutter/utils/convert_utils.dart'; import 'theta_client_flutter_platform_interface.dart'; +const notifyIdApiLog = 1; const notifyIdLivePreview = 10001; const notifyIdTimeShiftProgress = 10011; const notifyIdTimeShiftStopError = 10012; @@ -32,6 +33,9 @@ const notifyIdVideoCaptureStopError = 10081; const notifyIdVideoCaptureCapturing = 10082; const notifyIdVideoCaptureStarted = 10083; const notifyIdConvertVideoFormatsProgress = 10091; +const notifyIdTimeShiftManualProgress = 10101; +const notifyIdTimeShiftManualStopError = 10102; +const notifyIdTimeShiftManualCapturing = 10103; /// An implementation of [ThetaClientFlutterPlatform] that uses method channels. class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @@ -90,12 +94,40 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { return version; } + void Function(String message)? apiLogListener; + + Future setNotifyApiLogListener( + void Function(String message)? listener) { + if (listener != null) { + addNotify(notifyIdApiLog, (params) { + final message = params?['message'] as String?; + if (message != null) { + listener(message); + } + }); + } else { + removeNotify(notifyIdApiLog); + } + return methodChannel.invokeMethod( + 'setApiLogListener', listener != null); + } + + @override + Future setApiLogListener(void Function(String message)? listener) { + enableNotifyEventReceiver(); + apiLogListener = listener; + return setNotifyApiLogListener(apiLogListener); + } + @override Future initialize( String endpoint, ThetaConfig? config, ThetaTimeout? timeout) async { clearNotify(); disableNotifyEventReceiver(); enableNotifyEventReceiver(); + if (apiLogListener != null) { + setNotifyApiLogListener(apiLogListener); + } var completer = Completer(); try { @@ -253,11 +285,11 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future buildPhotoCapture(Map options, int interval) async { + Future buildPhotoCapture( + Map options, int interval) async { final params = ConvertUtils.convertCaptureParams(options); params['_capture_interval'] = interval; - return methodChannel.invokeMethod( - 'buildPhotoCapture', params); + return methodChannel.invokeMethod('buildPhotoCapture', params); } @override @@ -301,7 +333,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future startTimeShiftCapture(void Function(double)? onProgress, + Future startTimeShiftCapture( + void Function(double)? onProgress, void Function(Exception exception)? onStopFailed, void Function(CapturingStatusEnum status)? onCapturing) async { var completer = Completer(); @@ -354,6 +387,81 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { return methodChannel.invokeMethod('stopTimeShiftCapture'); } + @override + Future getTimeShiftManualCaptureBuilder() async { + return methodChannel.invokeMethod('getTimeShiftManualCaptureBuilder'); + } + + @override + Future buildTimeShiftManualCapture( + Map options, int interval) async { + final params = ConvertUtils.convertCaptureParams(options); + params['_capture_interval'] = interval; + return methodChannel.invokeMethod( + 'buildTimeShiftManualCapture', params); + } + + @override + Future startTimeShiftManualCapture( + void Function(double)? onProgress, + void Function(Exception exception)? onStopFailed, + void Function(CapturingStatusEnum status)? onCapturing) async { + var completer = Completer(); + try { + enableNotifyEventReceiver(); + if (onProgress != null) { + addNotify(notifyIdTimeShiftManualProgress, (params) { + final completion = params?['completion'] as double?; + if (completion != null) { + onProgress(completion); + } + }); + } + if (onStopFailed != null) { + addNotify(notifyIdTimeShiftManualStopError, (params) { + final message = params?['message'] as String?; + if (message != null) { + onStopFailed(Exception(message)); + } + }); + } + if (onCapturing != null) { + addNotify(notifyIdTimeShiftManualCapturing, (params) { + final strStatus = params?['status'] as String?; + if (strStatus != null) { + final status = CapturingStatusEnum.getValue(strStatus); + if (status != null) { + onCapturing(status); + } + } + }); + } + final fileUrl = await methodChannel + .invokeMethod('startTimeShiftManualCapture'); + removeNotify(notifyIdTimeShiftManualProgress); + removeNotify(notifyIdTimeShiftManualStopError); + removeNotify(notifyIdTimeShiftManualCapturing); + completer.complete(fileUrl); + } catch (e) { + removeNotify(notifyIdTimeShiftManualProgress); + removeNotify(notifyIdTimeShiftManualStopError); + removeNotify(notifyIdTimeShiftManualCapturing); + completer.completeError(e); + } + return completer.future; + } + + @override + Future startTimeShiftManualSecondCapture() async { + return methodChannel + .invokeMethod('startTimeShiftManualSecondCapture'); + } + + @override + Future stopTimeShiftManualCapture() async { + return methodChannel.invokeMethod('stopTimeShiftManualCapture'); + } + @override Future getVideoCaptureBuilder() async { return methodChannel.invokeMethod('getVideoCaptureBuilder'); @@ -425,8 +533,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { } @override - Future buildLimitlessIntervalCapture(Map options, - int interval) async { + Future buildLimitlessIntervalCapture( + Map options, int interval) async { final params = ConvertUtils.convertCaptureParams(options); params['_capture_interval'] = interval; return methodChannel.invokeMethod( @@ -884,6 +992,11 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { return completer.future; } + @override + Future reboot() async { + return methodChannel.invokeMethod('reboot'); + } + @override Future reset() async { return methodChannel.invokeMethod('reset'); @@ -956,10 +1069,10 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @override Future setAccessPointDynamically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, Proxy? proxy) async { final Map params = { 'ssid': ssid, @@ -976,13 +1089,15 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { @override Future setAccessPointStatically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, String ipAddress, String subnetMask, String defaultGateway, + String? dns1, + String? dns2, Proxy? proxy) async { final Map params = { 'ssid': ssid, @@ -993,6 +1108,8 @@ class MethodChannelThetaClientFlutter extends ThetaClientFlutterPlatform { 'ipAddress': ipAddress, 'subnetMask': subnetMask, 'defaultGateway': defaultGateway, + 'dns1': dns1, + 'dns2': dns2, 'proxy': proxy != null ? ConvertUtils.convertProxyParam(proxy) : null }; return methodChannel.invokeMethod('setAccessPointStatically', params); diff --git a/flutter/lib/theta_client_flutter_platform_interface.dart b/flutter/lib/theta_client_flutter_platform_interface.dart index 89418165f08..fc469c698e6 100644 --- a/flutter/lib/theta_client_flutter_platform_interface.dart +++ b/flutter/lib/theta_client_flutter_platform_interface.dart @@ -30,6 +30,10 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { throw UnimplementedError('platformVersion() has not been implemented.'); } + Future setApiLogListener(void Function(String message)? listener) { + throw UnimplementedError('setApiLogListener() has not been implemented.'); + } + Future initialize( String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { throw UnimplementedError('initialize() has not been implemented.'); @@ -112,7 +116,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { 'buildTimeShiftCapture() has not been implemented.'); } - Future startTimeShiftCapture(void Function(double)? onProgress, + Future startTimeShiftCapture( + void Function(double)? onProgress, void Function(Exception exception)? onStopFailed, void Function(CapturingStatusEnum status)? onCapturing) { throw UnimplementedError( @@ -124,6 +129,35 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { 'stopTimeShiftCapture() has not been implemented.'); } + Future getTimeShiftManualCaptureBuilder() { + throw UnimplementedError( + 'getTimeShiftManualCaptureBuilder() has not been implemented.'); + } + + Future buildTimeShiftManualCapture( + Map options, int interval) { + throw UnimplementedError( + 'buildTimeShiftManualCapture() has not been implemented.'); + } + + Future startTimeShiftManualCapture( + void Function(double)? onProgress, + void Function(Exception exception)? onStopFailed, + void Function(CapturingStatusEnum status)? onCapturing) { + throw UnimplementedError( + 'startTimeShiftManualCapture() has not been implemented.'); + } + + Future startTimeShiftManualSecondCapture() { + throw UnimplementedError( + 'startTimeShiftManualSecondCapture() has not been implemented.'); + } + + Future stopTimeShiftManualCapture() { + throw UnimplementedError( + 'stopTimeShiftManualCapture() has not been implemented.'); + } + Future getVideoCaptureBuilder() { throw UnimplementedError( 'getVideoCaptureBuilder() has not been implemented.'); @@ -149,8 +183,8 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { 'getLimitlessIntervalCaptureBuilder() has not been implemented.'); } - Future buildLimitlessIntervalCapture(Map options, - int interval) { + Future buildLimitlessIntervalCapture( + Map options, int interval) { throw UnimplementedError( 'buildLimitlessIntervalCapture() has not been implemented.'); } @@ -295,6 +329,10 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { throw UnimplementedError('getMetadata() has not been implemented.'); } + Future reboot() { + throw UnimplementedError('reboot() has not been implemented.'); + } + Future reset() { throw UnimplementedError('reset() has not been implemented.'); } @@ -322,10 +360,10 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { Future setAccessPointDynamically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, Proxy? proxy) { throw UnimplementedError( 'setAccessPointDynamically() has not been implemented.'); @@ -333,13 +371,15 @@ abstract class ThetaClientFlutterPlatform extends PlatformInterface { Future setAccessPointStatically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, String ipAddress, String subnetMask, String defaultGateway, + String? dns1, + String? dns2, Proxy? proxy) { throw UnimplementedError( 'setAccessPointStatically() has not been implemented.'); diff --git a/flutter/lib/utils/convert_utils.dart b/flutter/lib/utils/convert_utils.dart index 78af22e5fa7..9e80abcf752 100644 --- a/flutter/lib/utils/convert_utils.dart +++ b/flutter/lib/utils/convert_utils.dart @@ -1,7 +1,83 @@ import 'package:theta_client_flutter/digest_auth.dart'; +import 'package:theta_client_flutter/options/access_info.dart'; import 'package:theta_client_flutter/theta_client_flutter.dart'; class ConvertUtils { + static AccessInfo? convertAccessInfo(Map? data) { + if (data == null) { + return null; + } + + List>? dataList = + (data['dhcpLeaseAddress'] as List?) + ?.map((item) => item as Map) + .toList(); + List? dhcpLeaseAddressList = dataList + ?.map((item) => convertDhcpLeaseAddress(item)) + .where((address) => address != null) + .cast() + .toList(); + + var accessInfo = AccessInfo( + data['ssid'], + data['ipAddress'], + data['subnetMask'], + data['defaultGateway'], + data['dns1'], + data['dns2'], + data['proxyURL'], + WlanFrequencyAccessInfoEnum.getValue(data['frequency'] as String)!, + data['wlanSignalStrength'], + data['wlanSignalLevel'], + data['lteSignalStrength'], + data['lteSignalLevel'], + dhcpLeaseAddressList); + return accessInfo; + } + + static Map convertAccessInfoParam(AccessInfo accessInfo) { + return { + 'ssid': accessInfo.ssid, + 'ipAddress': accessInfo.ipAddress, + 'subnetMask': accessInfo.subnetMask, + 'defaultGateway': accessInfo.defaultGateway, + 'dns1': accessInfo.dns1, + 'dns2': accessInfo.dns2, + 'proxyURL': accessInfo.proxyURL, + 'frequency': accessInfo.frequency.rawValue, + 'wlanSignalStrength': accessInfo.wlanSignalStrength, + 'wlanSignalLevel': accessInfo.wlanSignalLevel, + 'lteSignalStrength': accessInfo.lteSignalStrength, + 'lteSignalLevel': accessInfo.lteSignalLevel, + 'dhcpLeaseAddress': accessInfo.dhcpLeaseAddress + ?.map((address) => convertDhcpLeaseAddressParam(address)) + .toList() + }; + } + + static DhcpLeaseAddress? convertDhcpLeaseAddress( + Map? data) { + if (data == null) { + return null; + } + + var result = DhcpLeaseAddress( + ipAddress: data['ipAddress'], + macAddress: data['macAddress'], + hostName: data['hostName'], + ); + return result; + } + + static Map convertDhcpLeaseAddressParam( + DhcpLeaseAddress dhcpLeaseAddress) { + return { + 'ipAddress': dhcpLeaseAddress.ipAddress, + 'macAddress': dhcpLeaseAddress.macAddress, + 'hostName': dhcpLeaseAddress.hostName, + }; + } + static List? convertAutoBracketOption(List? data) { if (data == null) { return null; @@ -222,6 +298,31 @@ class ConvertUtils { return optionNameList; } + static Map convertCameraLockConfigParam( + CameraLockConfig config) { + Map result = {}; + + if (config.isPowerKeyLocked != null) { + result['isPowerKeyLocked'] = config.isPowerKeyLocked; + } + if (config.isShutterKeyLocked != null) { + result['isShutterKeyLocked'] = config.isShutterKeyLocked; + } + if (config.isModeKeyLocked != null) { + result['isModeKeyLocked'] = config.isModeKeyLocked; + } + if (config.isWlanKeyLocked != null) { + result['isWlanKeyLocked'] = config.isWlanKeyLocked; + } + if (config.isFnKeyLocked != null) { + result['isFnKeyLocked'] = config.isFnKeyLocked; + } + if (config.isPanelLocked != null) { + result['isPanelLocked'] = config.isPanelLocked; + } + return result; + } + static Map convertEthernetConfigParam( EthernetConfig ethernetConfig) { Map result = {}; @@ -237,6 +338,12 @@ class ConvertUtils { if (ethernetConfig.defaultGateway != null) { result['defaultGateway'] = ethernetConfig.defaultGateway; } + if (ethernetConfig.dns1 != null) { + result['dns1'] = ethernetConfig.dns1; + } + if (ethernetConfig.dns2 != null) { + result['dns2'] = ethernetConfig.dns2; + } Proxy? proxy = ethernetConfig.proxy; if (proxy != null) { @@ -329,6 +436,55 @@ class ConvertUtils { }; } + static ValueRange? convertValueRangeSupport( + Map? data) { + if (data == null) { + return null; + } + return ValueRange( + data['max'] as T, + data['min'] as T, + data['stepSize'] as T, + ); + } + + static Map convertWlanFrequencyClModeParam( + WlanFrequencyClMode wlanFrequencyClMode) { + return { + 'enable2_4': wlanFrequencyClMode.enable2_4, + 'enable5_2': wlanFrequencyClMode.enable5_2, + 'enable5_8': wlanFrequencyClMode.enable5_8, + }; + } + + static CameraLockConfig? convertCameraLockConfig( + Map? data) { + if (data == null) { + return null; + } + + var config = CameraLockConfig(); + if (data['isPowerKeyLocked'] != null) { + config.isPowerKeyLocked = data['isPowerKeyLocked']; + } + if (data['isShutterKeyLocked'] != null) { + config.isShutterKeyLocked = data['isShutterKeyLocked']; + } + if (data['isModeKeyLocked'] != null) { + config.isModeKeyLocked = data['isModeKeyLocked']; + } + if (data['isWlanKeyLocked'] != null) { + config.isWlanKeyLocked = data['isWlanKeyLocked']; + } + if (data['isFnKeyLocked'] != null) { + config.isFnKeyLocked = data['isFnKeyLocked']; + } + if (data['isPanelLocked'] != null) { + config.isPanelLocked = data['isPanelLocked']; + } + return config; + } + static EthernetConfig? convertEthernetConfig(Map? data) { if (data == null) { return null; @@ -345,6 +501,12 @@ class ConvertUtils { if (data['defaultGateway'] != null) { ethernetConfig.defaultGateway = data['defaultGateway']; } + if (data['dns1'] != null) { + ethernetConfig.dns1 = data['dns1']; + } + if (data['dns2'] != null) { + ethernetConfig.dns2 = data['dns2']; + } if (data['proxy'] != null) { ethernetConfig.proxy = convertProxy(data['proxy']); } @@ -393,17 +555,58 @@ class ConvertUtils { return rotation; } + static TopBottomCorrectionRotationSupport? + convertTopBottomCorrectionRotationSupport(Map? data) { + if (data == null) { + return null; + } + + var pitchData = data['pitch']; + var rollData = data['roll']; + var yawData = data['yaw']; + + var support = TopBottomCorrectionRotationSupport( + pitch: ValueRange( + double.parse(pitchData['max']), + double.parse(pitchData['min']), + double.parse(pitchData['stepSize']), + ), + roll: ValueRange( + double.parse(rollData['max']), + double.parse(rollData['min']), + double.parse(rollData['stepSize']), + ), + yaw: ValueRange( + double.parse(yawData['max']), + double.parse(yawData['min']), + double.parse(yawData['stepSize']), + ), + ); + return support; + } + static Options convertOptions(Map data) { var result = Options(); for (var entry in data.entries) { final name = OptionNameEnum.getValue(entry.key)!; switch (name) { + case OptionNameEnum.accessInfo: + result.accessInfo = convertAccessInfo(entry.value); + break; case OptionNameEnum.aiAutoThumbnail: result.aiAutoThumbnail = AiAutoThumbnailEnum.getValue(entry.value); break; + case OptionNameEnum.aiAutoThumbnailSupport: + result.aiAutoThumbnailSupport = + convertSupportValueList(entry.value, AiAutoThumbnailEnum.values); + break; case OptionNameEnum.aperture: result.aperture = ApertureEnum.getValue(entry.value); break; + case OptionNameEnum.apertureSupport: + result.apertureSupport = + convertSupportValueList(entry.value, ApertureEnum.values); + break; case OptionNameEnum.autoBracket: result.autoBracket = convertAutoBracketOption(entry.value); break; @@ -428,12 +631,26 @@ class ConvertUtils { result.cameraControlSource = CameraControlSourceEnum.getValue(entry.value); break; + case OptionNameEnum.cameraControlSourceSupport: + result.cameraControlSourceSupport = convertSupportValueList( + entry.value, CameraControlSourceEnum.values); + break; + case OptionNameEnum.cameraLock: + result.cameraLock = CameraLockEnum.getValue(entry.value); + break; + case OptionNameEnum.cameraLockConfig: + result.cameraLockConfig = convertCameraLockConfig(entry.value); + break; case OptionNameEnum.cameraMode: result.cameraMode = CameraModeEnum.getValue(entry.value); break; case OptionNameEnum.cameraPower: result.cameraPower = CameraPowerEnum.getValue(entry.value); break; + case OptionNameEnum.cameraPowerSupport: + result.cameraPowerSupport = + convertSupportValueList(entry.value, CameraPowerEnum.values); + break; case OptionNameEnum.captureInterval: result.captureInterval = entry.value; break; @@ -446,12 +663,28 @@ class ConvertUtils { case OptionNameEnum.colorTemperature: result.colorTemperature = entry.value; break; + case OptionNameEnum.colorTemperatureSupport: + result.colorTemperatureSupport = + convertValueRangeSupport(entry.value); + break; + case OptionNameEnum.compassDirectionRef: + result.compassDirectionRef = + CompassDirectionRefEnum.getValue(entry.value); + break; case OptionNameEnum.compositeShootingOutputInterval: result.compositeShootingOutputInterval = entry.value; break; + case OptionNameEnum.compositeShootingOutputIntervalSupport: + result.compositeShootingOutputIntervalSupport = + convertValueRangeSupport(entry.value); + break; case OptionNameEnum.compositeShootingTime: result.compositeShootingTime = entry.value; break; + case OptionNameEnum.compositeShootingTimeSupport: + result.compositeShootingTimeSupport = + convertValueRangeSupport(entry.value); + break; case OptionNameEnum.continuousNumber: result.continuousNumber = ContinuousNumberEnum.getValue(entry.value); break; @@ -468,6 +701,10 @@ class ConvertUtils { case OptionNameEnum.exposureDelay: result.exposureDelay = ExposureDelayEnum.getValue(entry.value); break; + case OptionNameEnum.exposureDelaySupport: + result.exposureDelaySupport = + convertSupportValueList(entry.value, ExposureDelayEnum.values); + break; case OptionNameEnum.exposureProgram: result.exposureProgram = ExposureProgramEnum.getValue(entry.value); break; @@ -489,6 +726,10 @@ class ConvertUtils { case OptionNameEnum.gpsInfo: result.gpsInfo = convertGpsInfo(entry.value); break; + case OptionNameEnum.gpsTagRecordingSupport: + result.gpsTagRecordingSupport = + convertSupportValueList(entry.value, GpsTagRecordingEnum.values); + break; case OptionNameEnum.imageStitching: result.imageStitching = ImageStitchingEnum.getValue(entry.value); break; @@ -512,12 +753,23 @@ class ConvertUtils { result.maxRecordableTime = MaxRecordableTimeEnum.getValue(entry.value); break; + case OptionNameEnum.microphoneNoiseReduction: + result.microphoneNoiseReduction = + MicrophoneNoiseReductionEnum.getValue(entry.value); + break; + case OptionNameEnum.mobileNetworkSetting: + result.mobileNetworkSetting = + convertMobileNetworkSetting(entry.value); + break; case OptionNameEnum.networkType: result.networkType = NetworkTypeEnum.getValue(entry.value); break; case OptionNameEnum.offDelay: result.offDelay = OffDelayEnum.getValue(entry.value); break; + case OptionNameEnum.offDelayUsb: + result.offDelayUsb = OffDelayUsbEnum.getValue(entry.value); + break; case OptionNameEnum.password: result.password = entry.value; break; @@ -565,9 +817,16 @@ class ConvertUtils { result.topBottomCorrectionRotation = convertTopBottomCorrectionRotation(entry.value); break; + case OptionNameEnum.topBottomCorrectionRotationSupport: + result.topBottomCorrectionRotationSupport = + convertTopBottomCorrectionRotationSupport(entry.value); + break; case OptionNameEnum.totalSpace: result.totalSpace = entry.value; break; + case OptionNameEnum.usbConnection: + result.usbConnection = UsbConnectionEnum.getValue(entry.value); + break; case OptionNameEnum.username: result.username = entry.value; break; @@ -585,9 +844,33 @@ class ConvertUtils { result.whiteBalanceAutoStrength = WhiteBalanceAutoStrengthEnum.getValue(entry.value); break; + case OptionNameEnum.wlanAntennaConfig: + result.wlanAntennaConfig = + WlanAntennaConfigEnum.getValue(entry.value); + break; case OptionNameEnum.wlanFrequency: result.wlanFrequency = WlanFrequencyEnum.getValue(entry.value); break; + case OptionNameEnum.wlanFrequencySupport: + result.wlanFrequencySupport = + convertSupportValueList(entry.value, WlanFrequencyEnum.values); + break; + case OptionNameEnum.wlanFrequencyClMode: + result.wlanFrequencyClMode = convertWlanFrequencyClMode(entry.value); + break; + } + } + return result; + } + + static List convertSupportValueList( + List supportValueList, List enumValues) { + List result = []; + for (var value in supportValueList) { + var element = enumValues + .firstWhere((element) => element.toString() == value.toString()); + if (element != null) { + result.add(element); } } return result; @@ -605,7 +888,9 @@ class ConvertUtils { } static dynamic convertOptionValueToMapValue(dynamic value) { - if (value is AiAutoThumbnailEnum) { + if (value is AccessInfo) { + return convertAccessInfoParam(value); + } else if (value is AiAutoThumbnailEnum) { return value.rawValue; } else if (value is ApertureEnum) { return value.rawValue; @@ -625,12 +910,18 @@ class ConvertUtils { return convertBurstOptionParam(value); } else if (value is CameraControlSourceEnum) { return value.rawValue; + } else if (value is CameraLockEnum) { + return value.rawValue; + } else if (value is CameraLockConfig) { + return convertCameraLockConfigParam(value); } else if (value is CameraModeEnum) { return value.rawValue; } else if (value is CameraPowerEnum) { return value.rawValue; } else if (value is CaptureModeEnum) { return value.rawValue; + } else if (value is CompassDirectionRefEnum) { + return value.rawValue; } else if (value is ContinuousNumberEnum) { return value.rawValue; } else if (value is EthernetConfig) { @@ -661,10 +952,16 @@ class ConvertUtils { return value.rawValue; } else if (value is MaxRecordableTimeEnum) { return value.rawValue; + } else if (value is MicrophoneNoiseReductionEnum) { + return value.rawValue; + } else if (value is MobileNetworkSetting) { + return convertMobileNetworkSettingParam(value); } else if (value is NetworkTypeEnum) { return value.rawValue; } else if (value is OffDelayEnum) { return value.rawValue; + } else if (value is OffDelayUsbEnum) { + return value.rawValue; } else if (value is PowerSavingEnum) { return value.rawValue; } else if (value is PresetEnum) { @@ -679,6 +976,8 @@ class ConvertUtils { return value.rawValue; } else if (value is TopBottomCorrectionOptionEnum) { return value.rawValue; + } else if (value is UsbConnectionEnum) { + return value.rawValue; } else if (value is VideoStitchingEnum) { return value.rawValue; } else if (value is VisibilityReductionEnum) { @@ -687,6 +986,8 @@ class ConvertUtils { return value.rawValue; } else if (value is WhiteBalanceAutoStrengthEnum) { return value.rawValue; + } else if (value is WlanAntennaConfigEnum) { + return value.rawValue; } else if (value is WlanFrequencyEnum) { return value.rawValue; } else if (value is int || @@ -702,6 +1003,8 @@ class ConvertUtils { return convertTimeShiftParam(value); } else if (value is TopBottomCorrectionRotation) { return convertTopBottomCorrectionRotationParam(value); + } else if (value is WlanFrequencyClMode) { + return convertWlanFrequencyClModeParam(value); } return null; } @@ -774,6 +1077,37 @@ class ConvertUtils { return nameList; } + static WlanFrequencyClMode convertWlanFrequencyClMode( + Map data) { + var value = WlanFrequencyClMode(data['enable2_4'] ?? false, + data['enable5_2'] ?? false, data['enable5_8'] ?? false); + return value; + } + + static MobileNetworkSetting? convertMobileNetworkSetting( + Map? data) { + if (data == null) { + return null; + } + + var result = MobileNetworkSetting( + data['roaming'] != null + ? RoamingEnum.getValue(data['roaming'] as String) + : null, + data['plan'] != null + ? PlanEnum.getValue(data['plan'] as String) + : null); + return result; + } + + static Map convertMobileNetworkSettingParam( + MobileNetworkSetting mobileNetworkSetting) { + return { + 'roaming': mobileNetworkSetting.roaming?.rawValue, + 'plan': mobileNetworkSetting.plan?.rawValue, + }; + } + static List toAccessPointList(List> data) { var accessPointList = List.empty(growable: true); for (Map element in data) { @@ -782,11 +1116,13 @@ class ConvertUtils { element['ssid'], element['ssidStealth'], (authModeValue != null) ? authModeValue : AuthModeEnum.none, - element['connectionPriority'], element['usingDhcp'], + element['connectionPriority'], element['ipAddress'], element['subnetMask'], element['defaultGateway'], + element['dns1'], + element['dns2'], convertProxy(element['proxy'])); accessPointList.add(accessPoint); } diff --git a/flutter/pubspec.yaml b/flutter/pubspec.yaml index f09718959e9..f22786eae77 100644 --- a/flutter/pubspec.yaml +++ b/flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: theta_client_flutter description: THETA Client Flutter plugin project. -version: 1.12.1 +version: 1.13.0 homepage: environment: @@ -15,7 +15,7 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter - flutter_lints: ^3.0.0 + flutter_lints: ^3.0.2 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec diff --git a/flutter/test/capture/composite_interval_capture_method_channel_test.dart b/flutter/test/capture/composite_interval_capture_method_channel_test.dart index eca64178def..c7d6cceb17c 100644 --- a/flutter/test/capture/composite_interval_capture_method_channel_test.dart +++ b/flutter/test/capture/composite_interval_capture_method_channel_test.dart @@ -167,7 +167,7 @@ void main() { var isOnStopFailed = false; var resultCapture = - platform.startCompositeIntervalCapture(null, (exception) { + platform.startCompositeIntervalCapture(null, (exception) { isOnStopFailed = true; }, null); var result = await resultCapture.timeout(const Duration(seconds: 5)); @@ -199,7 +199,7 @@ void main() { CapturingStatusEnum? lastStatus; var resultCapture = - platform.startCompositeIntervalCapture(null, (exception) {}, (status) { + platform.startCompositeIntervalCapture(null, (exception) {}, (status) { lastStatus = status; }); var result = await resultCapture.timeout(const Duration(seconds: 5)); diff --git a/flutter/test/capture/composite_interval_capture_test.dart b/flutter/test/capture/composite_interval_capture_test.dart index d10b43259d8..b99c828f5cc 100644 --- a/flutter/test/capture/composite_interval_capture_test.dart +++ b/flutter/test/capture/composite_interval_capture_test.dart @@ -283,7 +283,7 @@ void main() { test('call onCapturing', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var completer = Completer(); @@ -298,12 +298,15 @@ void main() { const shootingTimeSec = 600; var builder = - thetaClientPlugin.getCompositeIntervalCaptureBuilder(shootingTimeSec); + thetaClientPlugin.getCompositeIntervalCaptureBuilder(shootingTimeSec); var capture = await builder.build(); var isOnCapturing = false; - capture.startCapture((fileUrl) { - expect(false, isTrue, reason: 'startCapture'); - }, (completion) {}, (exception) {}, + capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) {}, onCapturing: (status) { isOnCapturing = true; expect(status, CapturingStatusEnum.capturing); diff --git a/flutter/test/capture/continuous_capture_method_channel_test.dart b/flutter/test/capture/continuous_capture_method_channel_test.dart index 212f44576bc..2e3ae408a7f 100644 --- a/flutter/test/capture/continuous_capture_method_channel_test.dart +++ b/flutter/test/capture/continuous_capture_method_channel_test.dart @@ -164,7 +164,7 @@ void main() { CapturingStatusEnum? lastStatus; var resultCapture = - platform.startContinuousCapture((completion) {}, (status) { + platform.startContinuousCapture((completion) {}, (status) { lastStatus = status; }); var result = await resultCapture.timeout(const Duration(seconds: 5)); diff --git a/flutter/test/capture/continuous_capture_test.dart b/flutter/test/capture/continuous_capture_test.dart index f1cbf72ea79..d3fe4d4b35d 100644 --- a/flutter/test/capture/continuous_capture_test.dart +++ b/flutter/test/capture/continuous_capture_test.dart @@ -233,7 +233,7 @@ void main() { test('call onCapturing', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var completer = Completer(); @@ -248,9 +248,12 @@ void main() { final builder = thetaClientPlugin.getContinuousCaptureBuilder(); var capture = await builder.build(); var isOnCapturing = false; - capture.startCapture((fileUrl) { - expect(false, isTrue, reason: 'startCapture'); - }, (completion) {}, (exception) {}, + capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) {}, onCapturing: (status) { isOnCapturing = true; expect(status, CapturingStatusEnum.capturing); diff --git a/flutter/test/capture/limitless_interval_capture_test.dart b/flutter/test/capture/limitless_interval_capture_test.dart index a9a5d63c947..00349b420db 100644 --- a/flutter/test/capture/limitless_interval_capture_test.dart +++ b/flutter/test/capture/limitless_interval_capture_test.dart @@ -215,7 +215,7 @@ void main() { test('call onStopFailed', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const imageUrls = ['http://test.jpeg']; @@ -248,7 +248,7 @@ void main() { test('call onCapturing', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; const imageUrls = ['http://test.jpeg']; diff --git a/flutter/test/capture/shot_count_specified_interval_capture_method_channel_test.dart b/flutter/test/capture/shot_count_specified_interval_capture_method_channel_test.dart index e961389a0e0..0f890249d41 100644 --- a/flutter/test/capture/shot_count_specified_interval_capture_method_channel_test.dart +++ b/flutter/test/capture/shot_count_specified_interval_capture_method_channel_test.dart @@ -132,7 +132,7 @@ void main() { int progressCount = 0; var resultCapture = - platform.startShotCountSpecifiedIntervalCapture((completion) { + platform.startShotCountSpecifiedIntervalCapture((completion) { progressCount++; }, null, null); var result = await resultCapture.timeout(const Duration(seconds: 5)); @@ -164,7 +164,7 @@ void main() { var isOnStopFailed = false; var resultCapture = - platform.startShotCountSpecifiedIntervalCapture(null, (exception) { + platform.startShotCountSpecifiedIntervalCapture(null, (exception) { isOnStopFailed = true; }, null); var result = await resultCapture.timeout(const Duration(seconds: 5)); @@ -195,9 +195,8 @@ void main() { }); CapturingStatusEnum? lastStatus; - var resultCapture = - platform.startShotCountSpecifiedIntervalCapture( - null, (exception) {}, (status) { + var resultCapture = platform + .startShotCountSpecifiedIntervalCapture(null, (exception) {}, (status) { lastStatus = status; }); var result = await resultCapture.timeout(const Duration(seconds: 5)); diff --git a/flutter/test/capture/shot_count_specified_interval_capture_test.dart b/flutter/test/capture/shot_count_specified_interval_capture_test.dart index 2f42e5b10d6..add23b24873 100644 --- a/flutter/test/capture/shot_count_specified_interval_capture_test.dart +++ b/flutter/test/capture/shot_count_specified_interval_capture_test.dart @@ -280,7 +280,7 @@ void main() { test('call onCapturing', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var completer = Completer(); @@ -298,9 +298,12 @@ void main() { .getShotCountSpecifiedIntervalCaptureBuilder(shotCount); var capture = await builder.build(); var isOnCapturing = false; - capture.startCapture((fileUrl) { - expect(false, isTrue, reason: 'startCapture'); - }, (completion) {}, (exception) {}, + capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) {}, onCapturing: (status) { isOnCapturing = true; completer.complete(null); diff --git a/flutter/test/capture/time_shift_capture_test.dart b/flutter/test/capture/time_shift_capture_test.dart index 648d69b18ac..4248939cdd9 100644 --- a/flutter/test/capture/time_shift_capture_test.dart +++ b/flutter/test/capture/time_shift_capture_test.dart @@ -195,7 +195,7 @@ void main() { test('call onCapturing', () async { ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); MockThetaClientFlutterPlatform fakePlatform = - MockThetaClientFlutterPlatform(); + MockThetaClientFlutterPlatform(); ThetaClientFlutterPlatform.instance = fakePlatform; var completer = Completer(); @@ -215,11 +215,11 @@ void main() { var capture = await builder.build(); var isOnCapturing = false; var capturing = capture.startCapture( - (fileUrl) { + (fileUrl) { expect(false, isTrue, reason: 'startCapture'); }, - (completion) {}, - (exception) {}, + (completion) {}, + (exception) {}, onCapturing: (status) { expect(status, CapturingStatusEnum.capturing); isOnCapturing = true; diff --git a/flutter/test/capture/time_shift_manual_capture_method_channel_test.dart b/flutter/test/capture/time_shift_manual_capture_method_channel_test.dart new file mode 100644 index 00000000000..5c82278b7cb --- /dev/null +++ b/flutter/test/capture/time_shift_manual_capture_method_channel_test.dart @@ -0,0 +1,156 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + platform = MethodChannelThetaClientFlutter(); + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('buildTimeShiftManualCapture', () async { + final timeShift = TimeShift( + isFrontFirst: true, + firstInterval: TimeShiftIntervalEnum.interval_1, + secondInterval: TimeShiftIntervalEnum.interval_2); + + Map options = { + 'TimeShift': timeShift, + }; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + final timeShiftMap = arguments['TimeShift']; + expect(arguments['_capture_interval'], 1); + expect(timeShiftMap, isNotNull); + expect(timeShiftMap['isFrontFirst'], true); + expect(timeShiftMap['firstInterval'], 'INTERVAL_1'); + expect(timeShiftMap['secondInterval'], 'INTERVAL_2'); + + return Future.value(); + }); + await platform.buildTimeShiftManualCapture(options, 1); + }); + + test('startTimeShiftManualCapture', () async { + const fileUrl = + 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4'; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + return fileUrl; + }); + + await platform.startTimeShiftManualCapture((completion) { + expect(completion, fileUrl); + }, null, null); + + platform.startTimeShiftManualSecondCapture(); + }); + + test('startTimeShiftManualCapture no file', () async { + const fileUrl = null; + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + return fileUrl; + }); + expect(await platform.startTimeShiftManualCapture(null, null, null), null); + }); + + test('startTimeShiftManualCapture exception', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + throw Exception('test error'); + }); + try { + await platform.startTimeShiftManualCapture(null, null, null); + expect(true, false, reason: 'not exception'); + } catch (error) { + expect(error.toString().contains('test error'), true); + } + }); + + test('progress of capture', () async { + const fileUrl = + 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4'; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + expect(platform.notifyList.containsKey(10101), true, + reason: 'add notify progress'); + + // native event + platform.onNotify({ + 'id': 10101, + 'params': { + 'completion': 0.1, + }, + }); + await Future.delayed(const Duration(milliseconds: 10)); + platform.onNotify({ + 'id': 10101, + 'params': { + 'completion': 0.2, + }, + }); + await Future.delayed(const Duration(milliseconds: 10)); + + return fileUrl; + }); + + int progressCount = 0; + var resultCapture = platform.startTimeShiftManualCapture((completion) { + progressCount++; + }, null, null); + var result = await resultCapture.timeout(const Duration(seconds: 5)); + expect(result, fileUrl); + expect(progressCount, 2); + expect(platform.notifyList.containsKey(10101), false, + reason: 'remove notify progress'); + }); + + test('call onCapturing', () async { + const fileUrl = + 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4'; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + expect(platform.notifyList.containsKey(10103), true, + reason: 'add notify capturing'); + + // native event + platform.onNotify({ + 'id': 10103, + 'params': { + 'status': 'SELF_TIMER_COUNTDOWN', + }, + }); + await Future.delayed(const Duration(milliseconds: 10)); + + return fileUrl; + }); + + CapturingStatusEnum? lastStatus; + var resultCapture = + platform.startTimeShiftManualCapture(null, null, (status) { + lastStatus = status; + }); + var result = await resultCapture.timeout(const Duration(seconds: 5)); + expect(result, fileUrl); + expect(lastStatus, CapturingStatusEnum.selfTimerCountdown); + expect(platform.notifyList.containsKey(10103), false, + reason: 'remove notify capturing'); + }); +} diff --git a/flutter/test/capture/time_shift_manual_capture_test.dart b/flutter/test/capture/time_shift_manual_capture_test.dart new file mode 100644 index 00000000000..f6a9673fdf8 --- /dev/null +++ b/flutter/test/capture/time_shift_manual_capture_test.dart @@ -0,0 +1,238 @@ +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_platform_interface.dart'; + +import '../theta_client_flutter_test.dart'; + +void main() { + setUp(() {}); + + tearDown(() { + onCallGetTimeShiftManualCaptureBuilder = Future.value; + onCallBuildTimeShiftManualCapture = (options, interval) => Future.value(); + }); + + test('getTimeShiftManualCaptureBuilder', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + expect(builder, isNotNull); + }); + + test('buildTimeShiftManualCapture', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + final timeShift = TimeShift( + isFrontFirst: true, + firstInterval: TimeShiftIntervalEnum.interval_1, + secondInterval: TimeShiftIntervalEnum.interval_2); + + onCallBuildTimeShiftManualCapture = (options, interval) { + final optionTimeShift = options['TimeShift']; + expect(optionTimeShift, isNotNull); + expect(optionTimeShift.isFrontFirst, true); + expect(optionTimeShift.firstInterval, TimeShiftIntervalEnum.interval_1); + expect(optionTimeShift.secondInterval, TimeShiftIntervalEnum.interval_2); + return Future.value(null); + }; + + final builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + builder.setIsFrontFirst(timeShift.isFrontFirst as bool); + builder.setFirstInterval(timeShift.firstInterval as TimeShiftIntervalEnum); + builder + .setSecondInterval(timeShift.secondInterval as TimeShiftIntervalEnum); + + var capture = await builder.build(); + expect(capture, isNotNull); + final optionTimeShift = capture.getTimeShiftSetting(); + expect(optionTimeShift, isNotNull); + expect(optionTimeShift?.isFrontFirst, true); + expect(optionTimeShift?.firstInterval, TimeShiftIntervalEnum.interval_1); + expect(optionTimeShift?.secondInterval, TimeShiftIntervalEnum.interval_2); + }); + + test('startTimeShiftManualCapture', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + const imageUrl = 'http://test.JPG'; + + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) { + return Future.value(imageUrl); + }; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + var capture = await builder.build(); + String? fileUrl; + capture.startCapture( + (value) { + expect(value, imageUrl); + fileUrl = value; + }, + (completion) {}, + (exception) { + expect(false, isTrue, reason: 'Error. startCapture'); + }); + + await Future.delayed(const Duration(milliseconds: 10), () {}); + expect(fileUrl, imageUrl); + }); + + test('startTimeShiftManualCapture Exception', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + var completer = Completer(); + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) { + return completer.future; + }; + onCallStopTimeShiftManualCapture = () { + completer.completeError(Exception('Error. startTimeShiftManualCapture')); + return Future.value(); + }; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + var capture = await builder.build(); + var capturing = capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) { + expect(exception, isNotNull, reason: 'Error. startCapture'); + }); + + capturing.stopCapture(); + await Future.delayed(const Duration(milliseconds: 10), () {}); + }); + + test('stopTimeShiftManualCapture', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + const imageUrl = 'http://test.mp4'; + + var completer = Completer(); + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) { + return completer.future; + }; + onCallStopTimeShiftManualCapture = () { + completer.complete(imageUrl); + return Future.value(); + }; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + var capture = await builder.build(); + String? fileUrl; + var capturing = capture.startCapture( + (value) { + expect(value, imageUrl); + fileUrl = value; + }, + (completion) {}, + (exception) { + expect(false, isTrue, reason: 'Error. startCapture'); + }); + + capturing.stopCapture(); + await Future.delayed(const Duration(milliseconds: 10), () {}); + expect(fileUrl, imageUrl); + }); + + test('call onStopFailed', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + var completer = Completer(); + + void Function(Exception exception)? paramStopFailed; + + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) { + paramStopFailed = onStopFailed; + return Completer().future; + }; + onCallStopTimeShiftManualCapture = () { + paramStopFailed?.call(Exception("on stop error.")); + return Future.value(); + }; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + var capture = await builder.build(); + var isOnStopFailed = false; + var capturing = capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) {}, + onStopFailed: (exception) { + expect(exception, isNotNull, reason: 'Error. stopCapture'); + isOnStopFailed = true; + completer.complete(null); + }); + + capturing.stopCapture(); + await completer.future.timeout(const Duration(milliseconds: 10)); + expect(isOnStopFailed, true); + }); + + test('call onCapturing', () async { + ThetaClientFlutter thetaClientPlugin = ThetaClientFlutter(); + MockThetaClientFlutterPlatform fakePlatform = + MockThetaClientFlutterPlatform(); + ThetaClientFlutterPlatform.instance = fakePlatform; + + var completer = Completer(); + + void Function(CapturingStatusEnum status)? paramOnCapturing; + + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) { + paramOnCapturing = onCapturing; + return Completer().future; + }; + onCallStopTimeShiftManualCapture = () { + paramOnCapturing?.call(CapturingStatusEnum.capturing); + return Future.value(); + }; + + var builder = thetaClientPlugin.getTimeShiftManualCaptureBuilder(); + var capture = await builder.build(); + var isOnCapturing = false; + var capturing = capture.startCapture( + (fileUrl) { + expect(false, isTrue, reason: 'startCapture'); + }, + (completion) {}, + (exception) {}, + onCapturing: (status) { + expect(status, CapturingStatusEnum.capturing); + isOnCapturing = true; + completer.complete(null); + }); + + capturing.stopCapture(); + await completer.future.timeout(const Duration(milliseconds: 10)); + expect(isOnCapturing, true); + }); +} diff --git a/flutter/test/commands/list_files_test.dart b/flutter/test/commands/list_files_test.dart index 5f65edec745..d5e187cff6b 100644 --- a/flutter/test/commands/list_files_test.dart +++ b/flutter/test/commands/list_files_test.dart @@ -14,30 +14,29 @@ void main() { const name = 'R0013336.JPG'; var infoList = List.empty(growable: true); infoList.add(FileInfo( - name, - 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.JPG', - 100, - '2022:11:15 14:00:15+09:00', - '2022:11:15 14:00:15', - 1.1, - 2.1, - 1000, - 500, - 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.JPG?type=thumb', - 'id_intervalCaptureGroupId', - 'id_compositeShootingGroupId', - 'id_autoBracketGroupId', - 2, - true, - 'http://192.168.1.1/preview', - CodecEnum.h264mp4avc, - ProjectionTypeEnum.dualFisheye, - 'id_continuousShootingGroupId', - 30, - true, - 'imageDescription_Description', - '01234567890', - )); + name, + 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.JPG', + 100, + '2022:11:15 14:00:15+09:00', + '2022:11:15 14:00:15', + 1.1, + 2.1, + 1000, + 500, + 'http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.JPG?type=thumb', + 'id_intervalCaptureGroupId', + 'id_compositeShootingGroupId', + 'id_autoBracketGroupId', + 2, + true, + 'http://192.168.1.1/preview', + CodecEnum.h264mp4avc, + ProjectionTypeEnum.dualFisheye, + 'id_continuousShootingGroupId', + 30, + true, + 'imageDescription_Description', + '01234567890')); var input = ThetaFiles(infoList, 10); onCallListFiles = () { return Future.value(input); @@ -50,7 +49,9 @@ void main() { test('CodecEnum', () async { List> data = [ + [CodecEnum.unknown, 'UNKNOWN'], [CodecEnum.h264mp4avc, 'H264MP4AVC'], + [CodecEnum.h265hevc, 'H265HEVC'], ]; expect(data.length, CodecEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { diff --git a/flutter/test/enum_name_test.dart b/flutter/test/enum_name_test.dart index 33ab264ca61..3fa0472108f 100644 --- a/flutter/test/enum_name_test.dart +++ b/flutter/test/enum_name_test.dart @@ -359,9 +359,11 @@ void main() { test('AuthModeEnum', () async { List> data = [ + [AuthModeEnum.unknown, 'UNKNOWN'], [AuthModeEnum.none, 'NONE'], [AuthModeEnum.wep, 'WEP'], [AuthModeEnum.wpa, 'WPA'], + [AuthModeEnum.wpa3, 'WPA3'], ]; expect(data.length, AuthModeEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { @@ -369,18 +371,6 @@ void main() { } }); - test('AiAutoThumbnailEnum', () async { - List> data = [ - [AiAutoThumbnailEnum.on, 'ON'], - [AiAutoThumbnailEnum.off, 'OFF'], - ]; - 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]); - } - }); - test('ApertureEnum', () async { List> data = [ [ApertureEnum.apertureAuto, 'APERTURE_AUTO'], @@ -396,18 +386,6 @@ void main() { } }); - test('CameraControlSourceEnum', () async { - List> data = [ - [CameraControlSourceEnum.camera, 'CAMERA'], - [CameraControlSourceEnum.app, 'APP'], - ]; - 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]); - } - }); - test('CameraModeEnum', () async { List> data = [ [CameraModeEnum.capture, 'CAPTURE'], @@ -435,29 +413,6 @@ void main() { } }); - test('ExposureCompensationEnum', () async { - List> data = [ - [ExposureCompensationEnum.m2_0, 'M2_0'], - [ExposureCompensationEnum.m1_7, 'M1_7'], - [ExposureCompensationEnum.m1_3, 'M1_3'], - [ExposureCompensationEnum.m1_0, 'M1_0'], - [ExposureCompensationEnum.m0_7, 'M0_7'], - [ExposureCompensationEnum.m0_3, 'M0_3'], - [ExposureCompensationEnum.zero, 'ZERO'], - [ExposureCompensationEnum.p0_3, 'P0_3'], - [ExposureCompensationEnum.p0_7, 'P0_7'], - [ExposureCompensationEnum.p1_0, 'P1_0'], - [ExposureCompensationEnum.p1_3, 'P1_3'], - [ExposureCompensationEnum.p1_7, 'P1_7'], - [ExposureCompensationEnum.p2_0, 'P2_0'], - ]; - 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]); - } - }); - test('ExposureDelayEnum', () async { List> data = [ [ExposureDelayEnum.delayOff, 'DELAY_OFF'], @@ -557,6 +512,7 @@ void main() { [VideoFileFormatEnum.video_2KnoCodec, 'VIDEO_2K_NO_CODEC'], [VideoFileFormatEnum.video_4K, 'VIDEO_4K'], [VideoFileFormatEnum.video_4KnoCodec, 'VIDEO_4K_NO_CODEC'], + [VideoFileFormatEnum.video_2K_15F, 'VIDEO_2K_15F'], [VideoFileFormatEnum.video_2K_30F, 'VIDEO_2K_30F'], [VideoFileFormatEnum.video_2K_60F, 'VIDEO_2K_60F'], [VideoFileFormatEnum.video_2_7K_2752_2F, 'VIDEO_2_7K_2752_2F'], @@ -567,6 +523,8 @@ void main() { [VideoFileFormatEnum.video_2_7K_2F, 'VIDEO_2_7K_2F'], [VideoFileFormatEnum.video_3_6K_1F, 'VIDEO_3_6K_1F'], [VideoFileFormatEnum.video_3_6K_2F, 'VIDEO_3_6K_2F'], + [VideoFileFormatEnum.video_4K_2F, 'VIDEO_4K_2F'], + [VideoFileFormatEnum.video_4K_5F, 'VIDEO_4K_5F'], [VideoFileFormatEnum.video_4K_10F, 'VIDEO_4K_10F'], [VideoFileFormatEnum.video_4K_15F, 'VIDEO_4K_15F'], [VideoFileFormatEnum.video_4K_30F, 'VIDEO_4K_30F'], @@ -579,6 +537,17 @@ void main() { [VideoFileFormatEnum.video_7K_2F, 'VIDEO_7K_2F'], [VideoFileFormatEnum.video_7K_5F, 'VIDEO_7K_5F'], [VideoFileFormatEnum.video_7K_10F, 'VIDEO_7K_10F'], + [VideoFileFormatEnum.video_2K_30F_h265Hevc, 'VIDEO_2K_30F_H265_HEVC'], + [VideoFileFormatEnum.video_4K_2F_h265Hevc, 'VIDEO_4K_2F_H265_HEVC'], + [VideoFileFormatEnum.video_4K_5F_h265Hevc, 'VIDEO_4K_5F_H265_HEVC'], + [VideoFileFormatEnum.video_4K_10F_h265Hevc, 'VIDEO_4K_10F_H265_HEVC'], + [VideoFileFormatEnum.video_4K_30F_h265Hevc, 'VIDEO_4K_30F_H265_HEVC'], + [VideoFileFormatEnum.video_5_7K_2F_h265Hevc, 'VIDEO_5_7K_2F_H265_HEVC'], + [VideoFileFormatEnum.video_5_7K_5F_h265Hevc, 'VIDEO_5_7K_5F_H265_HEVC'], + [VideoFileFormatEnum.video_5_7K_10F_h265Hevc, 'VIDEO_5_7K_10F_H265_HEVC'], + [VideoFileFormatEnum.video_7K_2F_h265Hevc, 'VIDEO_7K_2F_H265_HEVC'], + [VideoFileFormatEnum.video_7K_5F_h265Hevc, 'VIDEO_7K_5F_H265_HEVC'], + [VideoFileFormatEnum.video_7K_10F_h265Hevc, 'VIDEO_7K_10F_H265_HEVC'], ]; expect(data.length, VideoFileFormatEnum.values.length, reason: 'enum count'); @@ -613,18 +582,6 @@ void main() { } }); - test('GpsTagRecordingEnum', () async { - List> data = [ - [GpsTagRecordingEnum.on, 'ON'], - [GpsTagRecordingEnum.off, 'OFF'], - ]; - 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]); - } - }); - test('IsoEnum', () async { List> data = [ [IsoEnum.isoAuto, 'ISO_AUTO'], @@ -704,19 +661,6 @@ void main() { } }); - test('NetworkTypeEnum', () async { - List> data = [ - [NetworkTypeEnum.client, 'CLIENT'], - [NetworkTypeEnum.direct, 'DIRECT'], - [NetworkTypeEnum.ethernet, 'ETHERNET'], - [NetworkTypeEnum.off, 'OFF'], - ]; - expect(data.length, NetworkTypeEnum.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]); - } - }); - test('PowerSavingEnum', () async { List> data = [ [PowerSavingEnum.on, 'ON'], @@ -741,24 +685,6 @@ void main() { } }); - test('PreviewFormatEnum', () async { - List> data = [ - [PreviewFormatEnum.w1024_h512_f30, 'W1024_H512_F30'], - [PreviewFormatEnum.w1024_h512_f15, 'W1024_H512_F15'], - [PreviewFormatEnum.w512_h512_f30, 'W512_H512_F30'], - [PreviewFormatEnum.w1920_h960_f8, 'W1920_H960_F8'], - [PreviewFormatEnum.w1024_h512_f8, 'W1024_H512_F8'], - [PreviewFormatEnum.w640_h320_f30, 'W640_H320_F30'], - [PreviewFormatEnum.w640_h320_f8, 'W640_H320_F8'], - [PreviewFormatEnum.w640_h320_f10, 'W640_H320_F10'], - [PreviewFormatEnum.w3840_h1920_f30, 'W3840_H1920_F30'], - ]; - expect(data.length, PreviewFormatEnum.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]); - } - }); - test('ShootingMethodEnum', () async { List> data = [ [ShootingMethodEnum.normal, 'NORMAL'], @@ -798,6 +724,7 @@ void main() { [ThetaModel.thetaX, 'THETA_X'], [ThetaModel.thetaSC2, 'THETA_SC2'], [ThetaModel.thetaSC2B, 'THETA_SC2_B'], + [ThetaModel.thetaA1, 'THETA_A1'], ]; expect(data.length, ThetaModel.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { @@ -899,21 +826,17 @@ void main() { } }); - test('WlanFrequencyEnum', () async { - List> data = [ - [WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4'], - [WlanFrequencyEnum.ghz_5, 'GHZ_5'], - ]; - expect(data.length, WlanFrequencyEnum.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]); - } - }); - test('CapturingStatusEnum', () async { List> data = [ + [CapturingStatusEnum.starting, 'STARTING'], [CapturingStatusEnum.capturing, 'CAPTURING'], [CapturingStatusEnum.selfTimerCountdown, 'SELF_TIMER_COUNTDOWN'], + [CapturingStatusEnum.timeShiftShooting, 'TIME_SHIFT_SHOOTING'], + [ + CapturingStatusEnum.timeShiftShootingSecond, + 'TIME_SHIFT_SHOOTING_SECOND' + ], + [CapturingStatusEnum.timeShiftShootingIdle, 'TIME_SHIFT_SHOOTING_IDLE'], ]; expect(data.length, CapturingStatusEnum.values.length, reason: 'enum count'); diff --git a/flutter/test/options/option_access_info_test.dart b/flutter/test/options/option_access_info_test.dart new file mode 100644 index 00000000000..b426a75f2bd --- /dev/null +++ b/flutter/test/options/option_access_info_test.dart @@ -0,0 +1,88 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/options/access_info.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('WlanFrequencyAccessInfoEnum const', () async { + List> data = [ + [WlanFrequencyAccessInfoEnum.unknown, 'UNKNOWN'], + [WlanFrequencyAccessInfoEnum.ghz_2_4, 'GHZ_2_4'], + [WlanFrequencyAccessInfoEnum.ghz_5_2, 'GHZ_5_2'], + [WlanFrequencyAccessInfoEnum.ghz_5_8, 'GHZ_5_8'], + [WlanFrequencyAccessInfoEnum.initialValue, 'INITIAL_VALUE'], + ]; + expect(data.length, WlanFrequencyAccessInfoEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['AccessInfo'] = { + "ssid": "ssid_test", + "ipAddress": "192.168.1.2", + "subnetMask": "255.255.0.0", + "defaultGateway": "192.168.1.12", + "dns1": "192.168.1.55", + "dns2": "192.168.1.66", + "proxyURL": "http://192.168.1.3", + "frequency": "GHZ_2_4", + "wlanSignalStrength": -60, + "wlanSignalLevel": 4, + "lteSignalStrength": 0, + "lteSignalLevel": 0, + "dhcpLeaseAddress": [ + { + "ipAddress": "192.168.1.5", + "macAddress": "58:38:79:12:34:56", + "hostName": "Macbook-Pro" + } + ] + }; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.accessInfo]); + expect( + options.accessInfo, + AccessInfo( + "ssid_test", + "192.168.1.2", + "255.255.0.0", + "192.168.1.12", + "192.168.1.55", + "192.168.1.66", + "http://192.168.1.3", + WlanFrequencyAccessInfoEnum.ghz_2_4, + -60, + 4, + 0, + 0, [ + DhcpLeaseAddress( + ipAddress: "192.168.1.5", + macAddress: "58:38:79:12:34:56", + hostName: "Macbook-Pro") + ]), + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_ai_auto_thumbnail.dart b/flutter/test/options/option_ai_auto_thumbnail.dart new file mode 100644 index 00000000000..567264e431c --- /dev/null +++ b/flutter/test/options/option_ai_auto_thumbnail.dart @@ -0,0 +1,32 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; + +void main() { + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('AiAutoThumbnailEnum const', () async { + List> data = [ + [AiAutoThumbnailEnum.unknown, 'UNKNOWN'], + [AiAutoThumbnailEnum.on, 'ON'], + [AiAutoThumbnailEnum.off, 'OFF'], + ]; + 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]); + } + }); +} diff --git a/flutter/test/options/option_ai_auto_thumbnail_support.dart b/flutter/test/options/option_ai_auto_thumbnail_support.dart new file mode 100644 index 00000000000..5d721f42f85 --- /dev/null +++ b/flutter/test/options/option_ai_auto_thumbnail_support.dart @@ -0,0 +1,40 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['AiAutoThumbnailSupport'] = ['ON', 'OFF', 'UNKNOWN']; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.aiAutoThumbnailSupport]); + expect( + options.aiAutoThumbnailSupport, + [ + AiAutoThumbnailEnum.on, + AiAutoThumbnailEnum.off, + AiAutoThumbnailEnum.unknown + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_camera_control_source.dart b/flutter/test/options/option_camera_control_source.dart new file mode 100644 index 00000000000..4928ce48432 --- /dev/null +++ b/flutter/test/options/option_camera_control_source.dart @@ -0,0 +1,32 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; + +void main() { + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('CameraControlSourceEnum const', () async { + List> data = [ + [CameraControlSourceEnum.unknown, 'UNKNOWN'], + [CameraControlSourceEnum.camera, 'CAMERA'], + [CameraControlSourceEnum.app, 'APP'], + ]; + 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]); + } + }); +} diff --git a/flutter/test/options/option_camera_control_source_support.dart b/flutter/test/options/option_camera_control_source_support.dart new file mode 100644 index 00000000000..48a395ede5d --- /dev/null +++ b/flutter/test/options/option_camera_control_source_support.dart @@ -0,0 +1,40 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CameraControlSourceSupport'] = ['CAMERA', 'APP', 'UNKNOWN']; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.cameraControlSourceSupport]); + expect( + options.cameraControlSourceSupport, + [ + CameraControlSourceEnum.camera, + CameraControlSourceEnum.app, + CameraControlSourceEnum.unknown + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_camera_lock_test.dart b/flutter/test/options/option_camera_lock_test.dart new file mode 100644 index 00000000000..6139ed42c6b --- /dev/null +++ b/flutter/test/options/option_camera_lock_test.dart @@ -0,0 +1,57 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('CameraLockEnum const', () async { + List> data = [ + [CameraLockEnum.unknown, 'UNKNOWN'], + [CameraLockEnum.unlock, 'UNLOCK'], + [CameraLockEnum.basicLock, 'BASIC_LOCK'], + [CameraLockEnum.customLock, 'CUSTOM_LOCK'], + ]; + expect(data.length, CameraLockEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CameraLock'] = 'UNLOCK'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.cameraLock]); + expect(options.cameraLock?.rawValue, 'UNLOCK', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['CameraLock'], 'BASIC_LOCK', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.cameraLock = CameraLockEnum.basicLock; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_camera_power_support_test.dart b/flutter/test/options/option_camera_power_support_test.dart new file mode 100644 index 00000000000..7d82a845d65 --- /dev/null +++ b/flutter/test/options/option_camera_power_support_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CameraPowerSupport'] = [ + 'ON', + 'OFF', + 'POWER_SAVING', + 'SILENT_MODE', + 'UNKNOWN' + ]; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.cameraPowerSupport]); + expect( + options.cameraPowerSupport, + [ + CameraPowerEnum.on, + CameraPowerEnum.off, + CameraPowerEnum.powerSaving, + CameraPowerEnum.silentMode, + CameraPowerEnum.unknown + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_camera_power_test.dart b/flutter/test/options/option_camera_power_test.dart index 3b46f6d040e..0ac50525522 100644 --- a/flutter/test/options/option_camera_power_test.dart +++ b/flutter/test/options/option_camera_power_test.dart @@ -24,6 +24,7 @@ void main() { [CameraPowerEnum.unknown, 'UNKNOWN'], [CameraPowerEnum.on, 'ON'], [CameraPowerEnum.off, 'OFF'], + [CameraPowerEnum.sleep, 'SLEEP'], [CameraPowerEnum.powerSaving, 'POWER_SAVING'], [CameraPowerEnum.silentMode, 'SILENT_MODE'] ]; diff --git a/flutter/test/options/option_compass_direction_ref_test.dart b/flutter/test/options/option_compass_direction_ref_test.dart new file mode 100644 index 00000000000..e0386d5155b --- /dev/null +++ b/flutter/test/options/option_compass_direction_ref_test.dart @@ -0,0 +1,60 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('CompassDirectionRefEnum const', () async { + List> data = [ + [CompassDirectionRefEnum.unknown, 'UNKNOWN'], + [CompassDirectionRefEnum.auto, 'AUTO'], + [CompassDirectionRefEnum.trueNorth, 'TRUE_NORTH'], + [CompassDirectionRefEnum.magnetic, 'MAGNETIC'] + ]; + expect(data.length, CompassDirectionRefEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CompassDirectionRef'] = 'MAGNETIC'; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.compassDirectionRef]); + expect(options.compassDirectionRef?.rawValue, 'MAGNETIC', + reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['CompassDirectionRef'], 'TRUE_NORTH', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.compassDirectionRef = CompassDirectionRefEnum.trueNorth; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_composite_shooting_output_interval_support.dart b/flutter/test/options/option_composite_shooting_output_interval_support.dart new file mode 100644 index 00000000000..6cad5638b8c --- /dev/null +++ b/flutter/test/options/option_composite_shooting_output_interval_support.dart @@ -0,0 +1,42 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CompositeShootingOutputIntervalSupport'] = { + 'max': 600, + 'min': 0, + 'stepSize': 60 + }; + return Future.value(optionMap); + }); + + Options options = await platform + .getOptions([OptionNameEnum.compositeShootingOutputIntervalSupport]); + expect( + options.compositeShootingOutputIntervalSupport, + ValueRange(600, 0, 60), + reason: 'interval values should match', + ); + }); +} diff --git a/flutter/test/options/option_composite_shooting_time_support.dart b/flutter/test/options/option_composite_shooting_time_support.dart new file mode 100644 index 00000000000..66a66eb8b94 --- /dev/null +++ b/flutter/test/options/option_composite_shooting_time_support.dart @@ -0,0 +1,42 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['CompositeShootingTimeSupport'] = { + 'max': 86400, + 'min': 600, + 'stepSize': 600 + }; + return Future.value(optionMap); + }); + + Options options = await platform + .getOptions([OptionNameEnum.compositeShootingTimeSupport]); + expect( + options.compositeShootingTimeSupport, + ValueRange(86400, 600, 600), + reason: 'interval values should match', + ); + }); +} diff --git a/flutter/test/options/option_exposure_compensation_test.dart b/flutter/test/options/option_exposure_compensation_test.dart new file mode 100644 index 00000000000..55dc8a86998 --- /dev/null +++ b/flutter/test/options/option_exposure_compensation_test.dart @@ -0,0 +1,81 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('ExposureCompensationEnum const', () async { + List> data = [ + [ExposureCompensationEnum.unknown, 'UNKNOWN'], + [ExposureCompensationEnum.m4_0, 'M4_0'], + [ExposureCompensationEnum.m3_7, 'M3_7'], + [ExposureCompensationEnum.m3_3, 'M3_3'], + [ExposureCompensationEnum.m3_0, 'M3_0'], + [ExposureCompensationEnum.m2_7, 'M2_7'], + [ExposureCompensationEnum.m2_3, 'M2_3'], + [ExposureCompensationEnum.m2_0, 'M2_0'], + [ExposureCompensationEnum.m1_7, 'M1_7'], + [ExposureCompensationEnum.m1_3, 'M1_3'], + [ExposureCompensationEnum.m1_0, 'M1_0'], + [ExposureCompensationEnum.m0_7, 'M0_7'], + [ExposureCompensationEnum.m0_3, 'M0_3'], + [ExposureCompensationEnum.zero, 'ZERO'], + [ExposureCompensationEnum.p0_3, 'P0_3'], + [ExposureCompensationEnum.p0_7, 'P0_7'], + [ExposureCompensationEnum.p1_0, 'P1_0'], + [ExposureCompensationEnum.p1_3, 'P1_3'], + [ExposureCompensationEnum.p1_7, 'P1_7'], + [ExposureCompensationEnum.p2_0, 'P2_0'], + [ExposureCompensationEnum.p2_3, 'P2_3'], + [ExposureCompensationEnum.p2_7, 'P2_7'], + [ExposureCompensationEnum.p3_0, 'P3_0'], + [ExposureCompensationEnum.p3_3, 'P3_3'], + [ExposureCompensationEnum.p3_7, 'P3_7'], + [ExposureCompensationEnum.p4_0, 'P4_0'], + ]; + 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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['ExposureCompensation'] = 'ZERO'; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.exposureCompensation]); + expect(options.exposureCompensation?.rawValue, 'ZERO', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['ExposureCompensation'], 'P4_0', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.exposureCompensation = ExposureCompensationEnum.p4_0; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_exposure_delay_support.dart b/flutter/test/options/option_exposure_delay_support.dart new file mode 100644 index 00000000000..de020054256 --- /dev/null +++ b/flutter/test/options/option_exposure_delay_support.dart @@ -0,0 +1,60 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['ExposureDelaySupport'] = [ + 'DELAY_OFF', + 'DELAY_1', + 'DELAY_2', + 'DELAY_3', + 'DELAY_4', + 'DELAY_5', + 'DELAY_6', + 'DELAY_7', + 'DELAY_8', + 'DELAY_9', + 'DELAY_10' + ]; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.exposureDelaySupport]); + expect( + options.exposureDelaySupport, + [ + ExposureDelayEnum.delayOff, + ExposureDelayEnum.delay1, + ExposureDelayEnum.delay2, + ExposureDelayEnum.delay3, + ExposureDelayEnum.delay4, + ExposureDelayEnum.delay5, + ExposureDelayEnum.delay6, + ExposureDelayEnum.delay7, + ExposureDelayEnum.delay8, + ExposureDelayEnum.delay9, + ExposureDelayEnum.delay10 + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_file_format_test.dart b/flutter/test/options/option_file_format_test.dart index 9d6268a99db..0565316a3b3 100644 --- a/flutter/test/options/option_file_format_test.dart +++ b/flutter/test/options/option_file_format_test.dart @@ -38,12 +38,15 @@ void main() { [FileFormatEnum.video_3_6K_2F, 'VIDEO_3_6K_2F'], [FileFormatEnum.video_4K, 'VIDEO_4K'], [FileFormatEnum.video_4KnoCodec, 'VIDEO_4K_NO_CODEC'], + [FileFormatEnum.video_2K_15F, 'VIDEO_2K_15F'], [FileFormatEnum.video_2K_30F, 'VIDEO_2K_30F'], [FileFormatEnum.video_2K_60F, 'VIDEO_2K_60F'], [FileFormatEnum.video_2_7K_2752_2F, 'VIDEO_2_7K_2752_2F'], [FileFormatEnum.video_2_7K_2752_5F, 'VIDEO_2_7K_2752_5F'], [FileFormatEnum.video_2_7K_2752_10F, 'VIDEO_2_7K_2752_10F'], [FileFormatEnum.video_2_7K_2752_30F, 'VIDEO_2_7K_2752_30F'], + [FileFormatEnum.video_4K_2F, 'VIDEO_4K_2F'], + [FileFormatEnum.video_4K_5F, 'VIDEO_4K_5F'], [FileFormatEnum.video_4K_10F, 'VIDEO_4K_10F'], [FileFormatEnum.video_4K_15F, 'VIDEO_4K_15F'], [FileFormatEnum.video_4K_30F, 'VIDEO_4K_30F'], @@ -56,6 +59,17 @@ void main() { [FileFormatEnum.video_7K_2F, 'VIDEO_7K_2F'], [FileFormatEnum.video_7K_5F, 'VIDEO_7K_5F'], [FileFormatEnum.video_7K_10F, 'VIDEO_7K_10F'], + [FileFormatEnum.video_2K_30F_h265Hevc, 'VIDEO_2K_30F_H265_HEVC'], + [FileFormatEnum.video_4K_2F_h265Hevc, 'VIDEO_4K_2F_H265_HEVC'], + [FileFormatEnum.video_4K_5F_h265Hevc, 'VIDEO_4K_5F_H265_HEVC'], + [FileFormatEnum.video_4K_10F_h265Hevc, 'VIDEO_4K_10F_H265_HEVC'], + [FileFormatEnum.video_4K_30F_h265Hevc, 'VIDEO_4K_30F_H265_HEVC'], + [FileFormatEnum.video_5_7K_2F_h265Hevc, 'VIDEO_5_7K_2F_H265_HEVC'], + [FileFormatEnum.video_5_7K_5F_h265Hevc, 'VIDEO_5_7K_5F_H265_HEVC'], + [FileFormatEnum.video_5_7K_10F_h265Hevc, 'VIDEO_5_7K_10F_H265_HEVC'], + [FileFormatEnum.video_7K_2F_h265Hevc, 'VIDEO_7K_2F_H265_HEVC'], + [FileFormatEnum.video_7K_5F_h265Hevc, 'VIDEO_7K_5F_H265_HEVC'], + [FileFormatEnum.video_7K_10F_h265Hevc, 'VIDEO_7K_10F_H265_HEVC'], ]; expect(data.length, FileFormatEnum.values.length, reason: 'enum count'); for (int i = 0; i < data.length; i++) { diff --git a/flutter/test/options/option_gps_tag_recording.dart b/flutter/test/options/option_gps_tag_recording.dart new file mode 100644 index 00000000000..0c599025754 --- /dev/null +++ b/flutter/test/options/option_gps_tag_recording.dart @@ -0,0 +1,32 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; + +void main() { + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('GpsTagRecordingEnum const', () async { + List> data = [ + [GpsTagRecordingEnum.unknown, 'UNKNOWN'], + [GpsTagRecordingEnum.on, 'ON'], + [GpsTagRecordingEnum.off, 'OFF'], + ]; + 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]); + } + }); +} diff --git a/flutter/test/options/option_gps_tag_recording_support.dart b/flutter/test/options/option_gps_tag_recording_support.dart new file mode 100644 index 00000000000..cf5766003ce --- /dev/null +++ b/flutter/test/options/option_gps_tag_recording_support.dart @@ -0,0 +1,40 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['GpsTagRecordingSupport'] = ['ON', 'OFF', 'UNKNOWN']; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.gpsTagRecordingSupport]); + expect( + options.gpsTagRecordingSupport, + [ + GpsTagRecordingEnum.on, + GpsTagRecordingEnum.off, + GpsTagRecordingEnum.unknown + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_max_recordable_time_test.dart b/flutter/test/options/option_max_recordable_time_test.dart index 79ba5d9f2b9..1699bbdf4b7 100644 --- a/flutter/test/options/option_max_recordable_time_test.dart +++ b/flutter/test/options/option_max_recordable_time_test.dart @@ -46,15 +46,18 @@ void main() { optionMap['MaxRecordableTime'] = 'RECORDABLE_TIME_180'; return Future.value(optionMap); }); - Options options = await platform.getOptions([OptionNameEnum.maxRecordableTime]); - expect(options.maxRecordableTime?.rawValue, 'RECORDABLE_TIME_180', reason: 'maxRecordableTime'); + Options options = + await platform.getOptions([OptionNameEnum.maxRecordableTime]); + expect(options.maxRecordableTime?.rawValue, 'RECORDABLE_TIME_180', + reason: 'maxRecordableTime'); }); test('setOptions', () async { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { var arguments = methodCall.arguments as Map; - expect(arguments['MaxRecordableTime'], 'RECORDABLE_TIME_180', reason: 'maxRecordableTime'); + expect(arguments['MaxRecordableTime'], 'RECORDABLE_TIME_180', + reason: 'maxRecordableTime'); return Future.value(); }); final options = Options(); diff --git a/flutter/test/options/option_microphone_noise_reduction_test.dart b/flutter/test/options/option_microphone_noise_reduction_test.dart new file mode 100644 index 00000000000..5652aca287f --- /dev/null +++ b/flutter/test/options/option_microphone_noise_reduction_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('MicrophoneNoiseReductionEnum const', () async { + List> data = [ + [MicrophoneNoiseReductionEnum.unknown, 'UNKNOWN'], + [MicrophoneNoiseReductionEnum.on, 'ON'], + [MicrophoneNoiseReductionEnum.off, 'OFF'] + ]; + expect(data.length, MicrophoneNoiseReductionEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['MicrophoneNoiseReduction'] = 'ON'; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.microphoneNoiseReduction]); + expect(options.microphoneNoiseReduction?.rawValue, 'ON', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['MicrophoneNoiseReduction'], 'OFF', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.microphoneNoiseReduction = MicrophoneNoiseReductionEnum.off; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_mobile_network_setting_test.dart b/flutter/test/options/option_mobile_network_setting_test.dart new file mode 100644 index 00000000000..6c24bb3dbc5 --- /dev/null +++ b/flutter/test/options/option_mobile_network_setting_test.dart @@ -0,0 +1,74 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('RoamingEnum const', () async { + List> data = [ + [RoamingEnum.unknown, 'UNKNOWN'], + [RoamingEnum.off, 'OFF'], + [RoamingEnum.on, 'ON'] + ]; + expect(data.length, RoamingEnum.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]); + } + }); + + test('PlanEnum const', () async { + List> data = [ + [PlanEnum.unknown, 'UNKNOWN'], + [PlanEnum.SORACOM, 'SORACOM'], + [PlanEnum.SORACOM_PLAN_DU, 'SORACOM_PLAN_DU'] + ]; + expect(data.length, PlanEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['MobileNetworkSetting'] = {"roaming": "OFF", "plan": "SORACOM"}; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.mobileNetworkSetting]); + expect(options.mobileNetworkSetting, + MobileNetworkSetting(RoamingEnum.off, PlanEnum.SORACOM), + reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['MobileNetworkSetting'], + {"roaming": "ON", "plan": "SORACOM_PLAN_DU"}, + reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.mobileNetworkSetting = + MobileNetworkSetting(RoamingEnum.on, PlanEnum.SORACOM_PLAN_DU); + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_network_type_test.dart b/flutter/test/options/option_network_type_test.dart new file mode 100644 index 00000000000..ea3ecf1311b --- /dev/null +++ b/flutter/test/options/option_network_type_test.dart @@ -0,0 +1,65 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('NetworkTypeEnum const', () async { + List> data = [ + [NetworkTypeEnum.unknown, 'UNKNOWN'], + [NetworkTypeEnum.direct, 'DIRECT'], + [NetworkTypeEnum.client, 'CLIENT'], + [NetworkTypeEnum.ethernet, 'ETHERNET'], + [NetworkTypeEnum.off, 'OFF'], + [NetworkTypeEnum.lteD, 'LTE_D'], + [NetworkTypeEnum.lteDU, 'LTE_DU'], + [NetworkTypeEnum.lte01S, 'LTE_01S'], + [NetworkTypeEnum.lteX3, 'LTE_X3'], + [NetworkTypeEnum.lteP1, 'LTE_P1'], + [NetworkTypeEnum.lteK2, 'LTE_K2'], + [NetworkTypeEnum.lteK, 'LTE_K'], + ]; + expect(data.length, NetworkTypeEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['NetworkType'] = 'ETHERNET'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.networkType]); + expect(options.networkType?.rawValue, 'ETHERNET', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['NetworkType'], 'LTE_X3', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.networkType = NetworkTypeEnum.lteX3; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_off_delay_usb_test.dart b/flutter/test/options/option_off_delay_usb_test.dart new file mode 100644 index 00000000000..503bd6dadce --- /dev/null +++ b/flutter/test/options/option_off_delay_usb_test.dart @@ -0,0 +1,102 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('OffDelayUsbEnum const', () async { + List> data = [ + [OffDelayUsbEnum.disable, 'DISABLE', 6555], + [OffDelayUsbEnum.offDelay_10m, 'OFF_DELAY_10M', 60 * 10], + [OffDelayUsbEnum.offDelay_1h, 'OFF_DELAY_1H', 60 * 60], + [OffDelayUsbEnum.offDelay_2h, 'OFF_DELAY_2H', 60 * 60 * 2], + [OffDelayUsbEnum.offDelay_4h, 'OFF_DELAY_4H', 60 * 60 * 4], + [OffDelayUsbEnum.offDelay_8h, 'OFF_DELAY_8H', 60 * 60 * 8], + [OffDelayUsbEnum.offDelay_12h, 'OFF_DELAY_12H', 60 * 60 * 12], + [OffDelayUsbEnum.offDelay_18h, 'OFF_DELAY_18H', 60 * 60 * 18], + [OffDelayUsbEnum.offDelay_24h, 'OFF_DELAY_24H', 60 * 60 * 24], + [OffDelayUsbEnum.offDelay_2d, 'OFF_DELAY_2D', 60 * 60 * 24 * 2], + ]; + expect(data.length, OffDelayUsbEnum.values.length, reason: 'enum count'); + for (int i = 0; i < data.length; i++) { + OffDelayUsbEnum enumValue = data[i][0]; + expect(data[i][0].toString(), data[i][1], reason: data[i][1]); + expect(enumValue.sec, data[i][2], reason: data[i][1]); + } + }); + + test('OffDelayUsbEnum number', () async { + final offDelayUsb = OffDelayUsbSec(600); + expect(offDelayUsb.rawValue, 600, reason: 'rawValue'); + expect(offDelayUsb.sec, 600, reason: 'number'); + + final offDelayUsb2 = OffDelaySec(600); + expect(offDelayUsb, offDelayUsb2, reason: 'number equal'); + + expect(offDelayUsb, OffDelayUsbEnum.offDelay_10m, reason: 'number equal'); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['OffDelayUsb'] = 'OFF_DELAY_10M'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.offDelayUsb]); + expect(options.offDelayUsb?.rawValue, 'OFF_DELAY_10M', + reason: 'offDelayUsb const'); + }); + + test('getOptions number', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + expect(methodCall.arguments, ['OffDelayUsb']); + Map optionMap = {}; + optionMap['OffDelayUsb'] = 600; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.offDelayUsb]); + expect(options.offDelayUsb?.rawValue, 600, reason: 'number'); + expect((options.offDelayUsb as OffDelayUsbSec).sec, 600, reason: 'number'); + }); + + test('setOptions const', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['OffDelayUsb'], 'OFF_DELAY_10M', reason: 'const'); + return Future.value(); + }); + final options = Options(); + options.offDelayUsb = OffDelayUsbEnum.offDelay_10m; + await platform.setOptions(options); + }); + + test('setOptions number', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['OffDelayUsb'], 1800, reason: 'number'); + return Future.value(); + }); + final options = Options(); + options.offDelayUsb = OffDelayUsbSec(1800); + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_preview_format_test.dart b/flutter/test/options/option_preview_format_test.dart new file mode 100644 index 00000000000..39dc7419705 --- /dev/null +++ b/flutter/test/options/option_preview_format_test.dart @@ -0,0 +1,66 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('PreviewFormatEnum const', () async { + List> data = [ + [PreviewFormatEnum.unknown, 'UNKNOWN'], + [PreviewFormatEnum.w1024_h512_f30, 'W1024_H512_F30'], + [PreviewFormatEnum.w1920_h960_f8, 'W1920_H960_F8'], + [PreviewFormatEnum.w1024_h512_f30, 'W1024_H512_F30'], + [PreviewFormatEnum.w1024_h512_f15, 'W1024_H512_F15'], + [PreviewFormatEnum.w1024_h512_f10, 'W1024_H512_F10'], + [PreviewFormatEnum.w1024_h512_f8, 'W1024_H512_F8'], + [PreviewFormatEnum.w640_h320_f30, 'W640_H320_F30'], + [PreviewFormatEnum.w640_h320_f10, 'W640_H320_F10'], + [PreviewFormatEnum.w640_h320_f8, 'W640_H320_F8'], + [PreviewFormatEnum.w512_h512_f30, 'W512_H512_F30'], + [PreviewFormatEnum.w3840_h1920_f30, 'W3840_H1920_F30'], + ]; + expect(data.length, PreviewFormatEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['PreviewFormat'] = 'W1024_H512_F10'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.previewFormat]); + expect(options.previewFormat?.rawValue, 'W1024_H512_F10', + reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['PreviewFormat'], 'W1920_H960_F8', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.previewFormat = PreviewFormatEnum.w1920_h960_f8; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_top_bottom_correction_rotation_support_test.dart b/flutter/test/options/option_top_bottom_correction_rotation_support_test.dart new file mode 100644 index 00000000000..21d515692fe --- /dev/null +++ b/flutter/test/options/option_top_bottom_correction_rotation_support_test.dart @@ -0,0 +1,86 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + final pitchData = {'max': '10.0', 'min': '-15.0', 'stepSize': '1.0'}; + final rollData = {'max': '20.0', 'min': '-5.0', 'stepSize': '0.5'}; + final yawData = {'max': '30.0', 'min': '-10.0', 'stepSize': '2.0'}; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['TopBottomCorrectionRotationSupport'] = { + "pitch": pitchData, + "roll": rollData, + "yaw": yawData + }; + return Future.value(optionMap); + }); + Options options = await platform + .getOptions([OptionNameEnum.topBottomCorrectionRotationSupport]); + expect( + options.topBottomCorrectionRotationSupport, + TopBottomCorrectionRotationSupport( + pitch: ValueRange( + double.parse(pitchData['max'] ?? '0.0'), + double.parse(pitchData['min'] ?? '0.0'), + double.parse(pitchData['stepSize'] ?? '0.0'), + ), + roll: ValueRange( + double.parse(rollData['max'] ?? '0.0'), + double.parse(rollData['min'] ?? '0.0'), + double.parse(rollData['stepSize'] ?? '0.0'), + ), + yaw: ValueRange( + double.parse(yawData['max'] ?? '0.0'), + double.parse(yawData['min'] ?? '0.0'), + double.parse(yawData['stepSize'] ?? '0.0'), + )), + reason: 'quality'); + }); + + test('getOptionsError', () async { + const pitchData = null; + final rollData = {'max': '20.0', 'min': '-5.0', 'stepSize': '0.5'}; + final yawData = {'max': 'abc'}; + + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['TopBottomCorrectionRotationSupport'] = { + "pitch": pitchData, + "roll": rollData, + "yaw": yawData + }; + return Future.value(optionMap); + }); + try { + await platform + .getOptions([OptionNameEnum.topBottomCorrectionRotationSupport]); + fail('Expected an exception due to invalid yaw data'); + } on NoSuchMethodError catch (e) { + expect(e, isA(), + reason: 'Expecting NoSuchMethodError due to invalid yaw data'); + } catch (e) { + fail('Unexpected exception type: $e'); + } + }); +} diff --git a/flutter/test/options/option_usb_connection_test.dart b/flutter/test/options/option_usb_connection_test.dart new file mode 100644 index 00000000000..9526c2e68e8 --- /dev/null +++ b/flutter/test/options/option_usb_connection_test.dart @@ -0,0 +1,56 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('UsbConnectionEnum const', () async { + List> data = [ + [UsbConnectionEnum.unknown, 'UNKNOWN'], + [UsbConnectionEnum.mtp, 'MTP'], + [UsbConnectionEnum.msc, 'MSC'] + ]; + expect(data.length, UsbConnectionEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['UsbConnection'] = 'MTP'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.usbConnection]); + expect(options.usbConnection?.rawValue, 'MTP', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['UsbConnection'], 'MSC', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.usbConnection = UsbConnectionEnum.msc; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_wlan_antenna_config_test.dart b/flutter/test/options/option_wlan_antenna_config_test.dart new file mode 100644 index 00000000000..353b6dae8ad --- /dev/null +++ b/flutter/test/options/option_wlan_antenna_config_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('WlanAntennaConfigEnum const', () async { + List> data = [ + [WlanAntennaConfigEnum.unknown, 'UNKNOWN'], + [WlanAntennaConfigEnum.siso, 'SISO'], + [WlanAntennaConfigEnum.mimo, 'MIMO'] + ]; + expect(data.length, WlanAntennaConfigEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['WlanAntennaConfig'] = 'SISO'; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.wlanAntennaConfig]); + expect(options.wlanAntennaConfig?.rawValue, 'SISO', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['WlanAntennaConfig'], 'MIMO', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.wlanAntennaConfig = WlanAntennaConfigEnum.mimo; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/options/option_wlan_frequency_support_test.dart b/flutter/test/options/option_wlan_frequency_support_test.dart new file mode 100644 index 00000000000..c8dacce7624 --- /dev/null +++ b/flutter/test/options/option_wlan_frequency_support_test.dart @@ -0,0 +1,48 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['WlanFrequencySupport'] = [ + 'GHZ_2_4', + 'GHZ_5', + 'GHZ_5_2', + 'GHZ_5_8', + 'UNKNOWN' + ]; + return Future.value(optionMap); + }); + Options options = + await platform.getOptions([OptionNameEnum.wlanFrequencySupport]); + expect( + options.wlanFrequencySupport, + [ + WlanFrequencyEnum.ghz_2_4, + WlanFrequencyEnum.ghz_5, + WlanFrequencyEnum.ghz_5_2, + WlanFrequencyEnum.ghz_5_8, + WlanFrequencyEnum.unknown + ], + reason: 'quality'); + }); +} diff --git a/flutter/test/options/option_wlan_frequency_test.dart b/flutter/test/options/option_wlan_frequency_test.dart new file mode 100644 index 00000000000..07ffdf2bf2b --- /dev/null +++ b/flutter/test/options/option_wlan_frequency_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:theta_client_flutter/theta_client_flutter.dart'; +import 'package:theta_client_flutter/theta_client_flutter_method_channel.dart'; + +void main() { + MethodChannelThetaClientFlutter platform = MethodChannelThetaClientFlutter(); + const MethodChannel channel = MethodChannel('theta_client_flutter'); + + TestWidgetsFlutterBinding.ensureInitialized(); + + setUp(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + tearDown(() { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, null); + }); + + test('WlanFrequencyEnum const', () async { + List> data = [ + [WlanFrequencyEnum.unknown, 'UNKNOWN'], + [WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4'], + [WlanFrequencyEnum.ghz_5, 'GHZ_5'], + [WlanFrequencyEnum.ghz_5_2, 'GHZ_5_2'], + [WlanFrequencyEnum.ghz_5_8, 'GHZ_5_8'], + ]; + expect(data.length, WlanFrequencyEnum.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]); + } + }); + + test('getOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + Map optionMap = {}; + optionMap['WlanFrequency'] = 'GHZ_5'; + return Future.value(optionMap); + }); + Options options = await platform.getOptions([OptionNameEnum.wlanFrequency]); + expect(options.wlanFrequency?.rawValue, 'GHZ_5', reason: 'quality'); + }); + + test('setOptions', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + var arguments = methodCall.arguments as Map; + expect(arguments['WlanFrequency'], 'GHZ_5_8', reason: 'quality'); + return Future.value(); + }); + final options = Options(); + options.wlanFrequency = WlanFrequencyEnum.ghz_5_8; + await platform.setOptions(options); + }); +} diff --git a/flutter/test/state/capture_status_test.dart b/flutter/test/state/capture_status_test.dart index dac0b15063b..aa927653c52 100644 --- a/flutter/test/state/capture_status_test.dart +++ b/flutter/test/state/capture_status_test.dart @@ -21,6 +21,7 @@ void main() { 'RETROSPECTIVE_IMAGE_RECORDING' ], [CaptureStatusEnum.burstShooting, 'BURST_SHOOTING'], + [CaptureStatusEnum.timeShiftShootingIdle, 'TIME_SHIFT_SHOOTING_IDLE'], ]; expect(data.length, CaptureStatusEnum.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 8e8c1f239aa..43baa222256 100644 --- a/flutter/test/theta_client_flutter_method_channel_test.dart +++ b/flutter/test/theta_client_flutter_method_channel_test.dart @@ -405,6 +405,8 @@ void main() { 'ipAddress': '', 'subnetMask': '', 'defaultGateway': '', + 'dns1': '', + 'dns2': '', 'proxy': { 'use': false, 'url': '', @@ -515,7 +517,7 @@ void main() { [ OptionNameEnum.ethernetConfig, 'EthernetConfig', - EthernetConfig(true, '', '', '', Proxy(false, '', 0, '', '')), + EthernetConfig(true, '', '', '', '', '', Proxy(false, '', 0, '', '')), ethernetConfigMap ], [ @@ -681,6 +683,16 @@ void main() { WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4' ], + [ + OptionNameEnum.wlanFrequencyClMode, + 'WlanFrequencyClMode', + WlanFrequencyClMode(true, false, true), + { + "enable2_4": true, + "enable5_2": false, + "enable5_8": true, + } + ], ]; Map optionMap = {}; @@ -728,6 +740,8 @@ void main() { 'ipAddress': '192.168.1.111', 'subnetMask': '255.255.255.0', 'defaultGateway': '192.168.1.1', + 'dns1': '192.168.1.55', + 'dns2': '192.168.1.66', 'proxy': { 'use': true, 'url': '192.168.1.222', @@ -826,7 +840,13 @@ void main() { [ OptionNameEnum.ethernetConfig, 'EthernetConfig', - EthernetConfig(false, '192.168.1.111', '255.255.255.0', '192.168.1.1', + EthernetConfig( + false, + '192.168.1.111', + '255.255.255.0', + '192.168.1.1', + '192.168.1.55', + '192.168.1.66', Proxy(true, '192.168.1.222', 80, 'abc', '123')), ethernetConfigMap ], @@ -987,6 +1007,16 @@ void main() { WlanFrequencyEnum.ghz_2_4, 'GHZ_2_4' ], + [ + OptionNameEnum.wlanFrequencyClMode, + 'WlanFrequencyClMode', + WlanFrequencyClMode(false, true, false), + { + "enable2_4": false, + "enable5_2": true, + "enable5_8": false, + } + ], ]; Map optionMap = {}; @@ -1225,6 +1255,15 @@ void main() { expect(metadata.xmp.fullPanoHeightPixels, imageHeight); }); + test('reboot', () async { + TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger + .setMockMethodCallHandler(channel, (MethodCall methodCall) async { + expect(methodCall.method, 'reboot'); + return Future.value(); + }); + await platform.reboot(); + }); + test('reset', () async { TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger .setMockMethodCallHandler(channel, (MethodCall methodCall) async { @@ -1280,6 +1319,8 @@ void main() { 'ipAddress': '192.168.1.2', 'subnetMask': '255.255.255.0', 'defaultGateway': '192.168.1.100', + 'dns1': '192.168.1.55', + 'dns2': '192.168.1.66', 'proxy': {'use': false, 'url': '', 'port': 0, 'userid': ''}, }, { @@ -1310,6 +1351,8 @@ void main() { expect(resultList[i].ipAddress, data[i]['ipAddress']); expect(resultList[i].subnetMask, data[i]['subnetMask']); expect(resultList[i].defaultGateway, data[i]['defaultGateway']); + expect(resultList[i].dns1, data[i]['dns1']); + expect(resultList[i].dns2, data[i]['dns2']); expect(resultList[i].proxy?.use, (data[i]['proxy'] as Map)['use']); expect(resultList[i].proxy?.url, @@ -1359,6 +1402,8 @@ void main() { const ipAddress = '192.168.1.2'; const subnetMask = '255.255.255.0'; const defaultGateway = '192.168.1.3'; + const dns1 = '192.168.1.55'; + const dns2 = '192.168.1.66'; var proxy = Proxy(true, 'https://xxx', 8081, 'abc', 'pwpwpwp111'); TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger @@ -1374,6 +1419,8 @@ void main() { expect(arguments['ipAddress'], ipAddress); expect(arguments['subnetMask'], subnetMask); expect(arguments['defaultGateway'], defaultGateway); + expect(arguments['dns1'], dns1); + expect(arguments['dns2'], dns2); expect(arguments['proxy']['use'], proxy.use); expect(arguments['proxy']['url'], proxy.url); expect(arguments['proxy']['port'], proxy.port); @@ -1390,6 +1437,8 @@ void main() { ipAddress, subnetMask, defaultGateway, + dns1, + dns2, proxy); }); diff --git a/flutter/test/theta_client_flutter_test.dart b/flutter/test/theta_client_flutter_test.dart index 86e6148b0c7..1f789363c65 100644 --- a/flutter/test/theta_client_flutter_test.dart +++ b/flutter/test/theta_client_flutter_test.dart @@ -37,6 +37,11 @@ class MockThetaClientFlutterPlatform return onGetThetaState(); } + @override + Future setApiLogListener(void Function(String message)? listener) { + return onCallSetApiLogListener(); + } + @override Future initialize( String endpoint, ThetaConfig? config, ThetaTimeout? timeout) { @@ -94,6 +99,36 @@ class MockThetaClientFlutterPlatform return onCallStopTimeShiftCapture(); } + @override + Future getTimeShiftManualCaptureBuilder() { + return onCallGetTimeShiftManualCaptureBuilder(); + } + + @override + Future buildTimeShiftManualCapture( + Map options, int interval) { + return onCallBuildTimeShiftManualCapture(options, interval); + } + + @override + Future startTimeShiftManualCapture( + void Function(double)? onProgress, + void Function(Exception exception)? onStopFailed, + void Function(CapturingStatusEnum status)? onCapturing) { + return onCallStartTimeShiftManualCapture( + onProgress, onStopFailed, onCapturing); + } + + @override + Future startTimeShiftManualSecondCapture() { + return onCallStartTimeShiftManualSecondCapture(); + } + + @override + Future stopTimeShiftManualCapture() { + return onCallStopTimeShiftManualCapture(); + } + @override Future getVideoCaptureBuilder() { return onCallGetVideoCaptureBuilder(); @@ -302,6 +337,11 @@ class MockThetaClientFlutterPlatform return Future.value(Metadata(Exif('', '', 0, 0, 0.0, 0.0), Xmp(0.0, 0, 0))); } + @override + Future reboot() { + return Future.value(); + } + @override Future reset() { return Future.value(); @@ -313,10 +353,8 @@ class MockThetaClientFlutterPlatform } @override - Future convertVideoFormats(String fileUrl, - bool toLowResolution, - bool applyTopBottomCorrection, - void Function(double)? onProgress) { + Future convertVideoFormats(String fileUrl, bool toLowResolution, + bool applyTopBottomCorrection, void Function(double)? onProgress) { return Future.value(''); } @@ -338,10 +376,10 @@ class MockThetaClientFlutterPlatform @override Future setAccessPointDynamically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, Proxy? proxy) { return Future.value(); } @@ -349,13 +387,15 @@ class MockThetaClientFlutterPlatform @override Future setAccessPointStatically( String ssid, - bool ssidStealth, + bool? ssidStealth, AuthModeEnum authMode, - String password, - int connectionPriority, + String? password, + int? connectionPriority, String ipAddress, String subnetMask, String defaultGateway, + String? dns1, + String? dns2, Proxy? proxy) { return Future.value(); } @@ -426,6 +466,7 @@ class MockThetaClientFlutterPlatform } } +Future Function() onCallSetApiLogListener = Future.value; Future Function() onCallInitialize = Future.value; Future Function() onCallIsInitialized = Future.value; Future Function() onGetThetaModel = Future.value; @@ -452,6 +493,18 @@ Future Function( (onProgress, onStopFailed, onCapturing) => Future.value(); Future Function() onCallStopTimeShiftCapture = Future.value; +Future Function() onCallGetTimeShiftManualCaptureBuilder = Future.value; +Future Function(Map options, int interval) + onCallBuildTimeShiftManualCapture = (options, interval) => Future.value(); +Future Function( + void Function(double)? onProgress, + void Function(Exception exception)? onStopFailed, + void Function(CapturingStatusEnum status)? onCapturing) + onCallStartTimeShiftManualCapture = + (onProgress, onStopFailed, onCapturing) => Future.value(); +Future Function() onCallStartTimeShiftManualSecondCapture = Future.value; +Future Function() onCallStopTimeShiftManualCapture = Future.value; + Future Function() onCallGetVideoCaptureBuilder = Future.value; Future Function(Map options, int interval) onCallBuildVideoCapture = (options, interval) => Future.value(); diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 37d58d8b8c4..7eb52ccc2a9 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ #Tue Sep 06 11:47:29 JST 2022 distributionBase=GRADLE_USER_HOME -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.5-bin.zip distributionPath=wrapper/dists zipStorePath=wrapper/dists zipStoreBase=GRADLE_USER_HOME diff --git a/kotlin-multiplatform/build.gradle.kts b/kotlin-multiplatform/build.gradle.kts index df7a9a39821..2bb5b876421 100644 --- a/kotlin-multiplatform/build.gradle.kts +++ b/kotlin-multiplatform/build.gradle.kts @@ -1,29 +1,26 @@ import org.jetbrains.dokka.versioning.VersioningConfiguration import org.jetbrains.dokka.versioning.VersioningPlugin -import java.util.Properties +import com.vanniktech.maven.publish.SonatypeHost plugins { kotlin("multiplatform") - kotlin("plugin.serialization") version "1.9.10" + kotlin("plugin.serialization") version "1.9.20" id("com.android.library") - id("maven-publish") - id("org.jetbrains.dokka") version "1.9.10" + id("org.jetbrains.dokka") version "1.9.20" kotlin("native.cocoapods") signing id("io.gitlab.arturbosch.detekt").version("1.23.3") + id("com.vanniktech.maven.publish") version "0.32.0" } dependencies { - dokkaPlugin("org.jetbrains.dokka:versioning-plugin:1.9.10") + dokkaPlugin("org.jetbrains.dokka:versioning-plugin:1.9.20") } -val thetaClientVersion = "1.12.1" +val thetaClientVersion = "1.13.0" group = "com.ricoh360.thetaclient" version = thetaClientVersion -// Init publish property -initProp() - kotlin { androidTarget { compilations.all { @@ -55,7 +52,7 @@ kotlin { sourceSets { val coroutinesVersion = "1.7.3" - val ktorVersion = "2.3.9" + val ktorVersion = "2.3.13" val kryptoVersion = "4.0.10" val commonMain by getting { @@ -118,66 +115,47 @@ val javadocJar by tasks.registering(Jar::class) { archiveClassifier.set("javadoc") } -// Publish the library to GitHub Packages Mavan repository. +// Publish the library to Mavan repository. // Because the components are created only during the afterEvaluate phase, you must // configure your publications using the afterEvaluate() lifecycle method. afterEvaluate { - initProp() - publishing { - publications.withType(MavenPublication::class) { - artifact(javadocJar.get()) - when (name) { - "androidRelease" -> { - artifactId = "theta-client" - } - - else -> { - artifactId = "theta-client-$name" + mavenPublishing { + // publishing to https://central.sonatype.com/ + publishToMavenCentral(SonatypeHost.CENTRAL_PORTAL) + signAllPublications() + coordinates(group.toString(), "theta-client", version.toString()) + pom { + name.set("theta-client") + description.set("This library provides a way to control RICOH THETA using RICOH THETA API v2.1") + inceptionYear.set("2023") + url.set("https://github.com/ricohapi/theta-client") + licenses { + license { + name.set("MIT") + url.set("https://github.com/ricohapi/theta-client/blob/main/LICENSE") } } - pom { - name.set("theta-client") - description.set("This library provides a way to control RICOH THETA using RICOH THETA API v2.1") - url.set("https://github.com/ricohapi/theta-client") - licenses { - license { - name.set("MIT") - url.set("https://github.com/ricohapi/theta-client/blob/main/LICENSE") - } - } - developers { - developer { - organization.set("RICOH360") - organizationUrl.set("https://github.com/ricohapi/theta-client") - } - } - scm { - connection.set("scm:git:git@github.com:ricohapi/theta-client.git") - developerConnection.set("scm:git:git@github.com:ricohapi/theta-client.git") - url.set("https://github.com/ricohapi/theta-client/tree/main") + developers { + developer { + organization.set("RICOH360") + organizationUrl.set("https://github.com/ricohapi/theta-client") } } - } - repositories { - maven { - url = uri("https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/") - credentials { - username = getExtraString("ossrhUsername") - password = getExtraString("ossrhPassword") - } + scm { + connection.set("scm:git:git@github.com:ricohapi/theta-client.git") + developerConnection.set("scm:git:git@github.com:ricohapi/theta-client.git") + url.set("https://github.com/ricohapi/theta-client/tree/main") } } - } -} - -signing { - if (getExtraString("signing.keyId") != null) { - useInMemoryPgpKeys( - getExtraString("signing.keyId"), - getExtraString("signing.key"), - getExtraString("signing.password") - ) - sign(publishing.publications) + /* Secrets + * Set following environment variables for Central Portal user token + * * ORG_GRADLE_PROJECT_mavenCentralUsername: username of the user token of Central Portal + * * ORG_GRADLE_PROJECT_mavenCentralPassword: password of the user token of Central Portal + * Set following environment variables for GPG key. See https://vanniktech.github.io/gradle-maven-publish-plugin/central/#secrets + * * ORG_GRADLE_PROJECT_signingInMemoryKey : Secret key in PEM format + * * ORG_GRADLE_PROJECT_signingInMemoryKeyId : 8 characters key id + * * ORG_GRADLE_PROJECT_signingInMemoryKeyPassword + */ } } @@ -194,38 +172,6 @@ detekt { ) // the folders to be checked } -ext["signing.keyId"] = null -ext["signing.key"] = null -ext["signing.password"] = null -ext["ossrhUsername"] = null -ext["ossrhPassword"] = null - -fun initProp() { - val secretPropsFile = project.rootProject.file("local.properties") - if (secretPropsFile.exists()) { - secretPropsFile.reader().use { - Properties().apply { - load(it) - } - }.onEach { (name, value) -> - ext[name.toString()] = value - } - } else { - ext["signing.keyId"] = System.getenv("SIGNING_KEY_ID") - ext["signing.key"] = System.getenv("SIGNING_KEY") - ext["signing.password"] = System.getenv("SIGNING_PASSWORD") - ext["ossrhUsername"] = System.getenv("OSSRH_USERNAME") - ext["ossrhPassword"] = System.getenv("OSSRH_PASSWORD") - } -} - -fun getExtraString(name: String): String? { - if (ext.has(name)) { - return ext[name]?.toString() - } - return null -} - tasks.dokkaHtml.configure { moduleName.set("theta-client") diff --git a/kotlin-multiplatform/src/androidMain/kotlin/com/ricoh360/thetaclient/Platform.kt b/kotlin-multiplatform/src/androidMain/kotlin/com/ricoh360/thetaclient/Platform.kt index 367e0c64b15..8948ffe6639 100644 --- a/kotlin-multiplatform/src/androidMain/kotlin/com/ricoh360/thetaclient/Platform.kt +++ b/kotlin-multiplatform/src/androidMain/kotlin/com/ricoh360/thetaclient/Platform.kt @@ -5,6 +5,7 @@ package com.ricoh360.thetaclient import android.graphics.Bitmap import android.graphics.BitmapFactory +import java.lang.ref.WeakReference import java.util.UUID /** @@ -33,3 +34,5 @@ actual fun randomUUID(): String { actual fun currentTimeMillis(): Long { return System.currentTimeMillis() } + +actual typealias WeakReference = WeakReference diff --git a/kotlin-multiplatform/src/androidUnitTest/kotlin/com/ricoh360/thetaclient/UpdateFirmwareOnActualTheta.kt b/kotlin-multiplatform/src/androidUnitTest/kotlin/com/ricoh360/thetaclient/UpdateFirmwareOnActualTheta.kt index 4d5371d3526..b4a44ca5c14 100644 --- a/kotlin-multiplatform/src/androidUnitTest/kotlin/com/ricoh360/thetaclient/UpdateFirmwareOnActualTheta.kt +++ b/kotlin-multiplatform/src/androidUnitTest/kotlin/com/ricoh360/thetaclient/UpdateFirmwareOnActualTheta.kt @@ -62,8 +62,9 @@ class UpdateFirmwareOnActualTheta { assertTrue(true, "call updateFirmware()") } - fun getSentPercentage(percent: Int) { + fun getSentPercentage(percent: Int): Boolean { println("Sent $percent %") + return true } /** diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ApiClient.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ApiClient.kt index f2693fb1b88..8ad3ad4ab3c 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ApiClient.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ApiClient.kt @@ -8,9 +8,7 @@ import io.ktor.client.engine.cio.CIO import io.ktor.client.engine.cio.endpoint import io.ktor.client.plugins.contentnegotiation.ContentNegotiation import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.logging.SIMPLE import io.ktor.serialization.kotlinx.json.json import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json @@ -55,7 +53,7 @@ internal object ApiClient { } // See [Logging](https://ktor.io/docs/client-logging.html) install(Logging) { - logger = Logger.SIMPLE // DEFAULT, SIMPLE or EMPTY + logger = ThetaApiLogger() level = LogLevel.ALL // ALL, HEADERS, BODY, INFO or NONE } } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartPostClient.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartPostClient.kt index 277eef64859..c606bbc072b 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartPostClient.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/MultipartPostClient.kt @@ -498,7 +498,7 @@ interface MultipartPostClient { filePaths: List, connectionTimeout: Long, socketTimeout: Long, - callback: ((Int) -> Unit)?, + callback: ((Int) -> Boolean)?, boundary: String = BOUNDARY, ): ByteArray } @@ -679,14 +679,25 @@ class MultipartPostClientImpl : MultipartPostClient, BaseHttpClient() { filePaths: List, connectionTimeout: Long, socketTimeout: Long, - callback: ((Int) -> Unit)?, + callback: ((Int) -> Boolean)?, boundary: String, ): ByteArray { val authorizationHeader: String? = checkAuthenticationNeeded(endpoint, path, connectionTimeout, socketTimeout) - requestWithAuth(endpoint, path, filePaths, connectionTimeout, socketTimeout, callback, boundary, authorizationHeader) + val isSuccess = requestWithAuth( + endpoint, + path, + filePaths, + connectionTimeout, + socketTimeout, + callback, + boundary, + authorizationHeader + ) close() val httpStatusCode = HttpStatusCode(this.status, this.statusMessage ?: "") - if (httpStatusCode.isSuccess() || httpStatusCode.value == 0) { + if (!isSuccess) { + throw (BaseHttpClientException("Request canceled")) + } else if (httpStatusCode.isSuccess() || httpStatusCode.value == 0) { return this.responseBody ?: byteArrayOf() } else if (httpStatusCode == HttpStatusCode.NotFound) { throw (BaseHttpClientException("Request failed: ${this.status} ${this.statusMessage}: API path \"$path\" may be wrong")) @@ -720,10 +731,10 @@ class MultipartPostClientImpl : MultipartPostClient, BaseHttpClient() { filePaths: List, connectionTimeout: Long, socketTimeout: Long, - callback: ((Int) -> Unit)?, + callback: ((Int) -> Boolean)?, boundary: String, digest: String? = null, - ) { + ): Boolean { val contentLength = getContentLength(filePaths, boundary) if (!isConnected()) connect(endpoint, connectionTimeout, socketTimeout) writeRequestLine(path, HttpMethod.Post) @@ -731,6 +742,8 @@ class MultipartPostClientImpl : MultipartPostClient, BaseHttpClient() { val buffer = ByteArray(READ_BUFFER_SIZE) var sentCount = 0L var lastPercent = 0 + var isActive = true + var isCanceled = false filePaths.forEach { var src: Source? = null try { @@ -744,16 +757,25 @@ class MultipartPostClientImpl : MultipartPostClient, BaseHttpClient() { callback?.let { val percent = (sentCount * PERCENTAGE_100 / contentLength).toInt() if (percent > lastPercent) { - it(percent) + if (!it(percent)) { + isActive = false + } lastPercent = percent } } count = src.readAtMostTo(buffer, 0, READ_BUFFER_SIZE) - } while (count != -1) + } while (count != -1 && isActive) + if (count != -1 && !isActive) { + isCanceled = true + } } finally { src?.close() } } + if (isCanceled) { + close() + return false + } writeCloseDelimiter(boundary) callback?.let { it(PERCENTAGE_100) @@ -767,6 +789,7 @@ class MultipartPostClientImpl : MultipartPostClient, BaseHttpClient() { } finally { close() } + return true } /** diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Platform.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Platform.kt index b2015d228a5..bacc2301f0b 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Platform.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Platform.kt @@ -23,3 +23,7 @@ expect fun frameFrom(packet: Pair): FrameSource internal expect fun randomUUID(): String internal expect fun currentTimeMillis(): Long + +expect class WeakReference internal constructor(referred: T) { + fun get(): T? +} 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 b2b52273044..5f188818e8d 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/PreviewClient.kt @@ -3,6 +3,7 @@ */ package com.ricoh360.thetaclient +import com.ricoh360.thetaclient.PreviewClient.Companion.timeout import io.ktor.http.HttpHeaders import io.ktor.http.HttpMethod import io.ktor.http.HttpStatusCode @@ -33,6 +34,11 @@ import kotlinx.coroutines.withTimeout * http client interface for preview only */ internal interface PreviewClient { + companion object { + /** Timeout of HTTP call */ + var timeout = ThetaRepository.Timeout() + } + /** * request [method] [path] with [contentType] of [body] to [endpoint] */ @@ -110,21 +116,18 @@ internal class PreviewClientImpl : PreviewClient { /** fixed buffers */ companion object { /** receive buffer */ - val buffer: ByteArray = ByteArray(10 * 1024) + val buffer: ByteArray = ByteArray(64 * 1024) /** part buffer */ var parts: ByteArray = ByteArray(100 * 1024) /** line buffer for headers */ val lineBuffer: ByteArray = ByteArray(1024) - - /** connection timeout (ms) */ - const val connectionTimeout: Long = 20_000 - - /** read/write timeout (ms) */ - const val socketTimeout: Long = 30_000 } + /** logger */ + internal val logger = ThetaApiLogger() + /** input channel */ private var input: ByteReadChannel? = null @@ -192,12 +195,12 @@ internal class PreviewClientImpl : PreviewClient { val builder = aSocket(selector!!).tcpNoDelay().tcp() val self = this val context = currentCoroutineContext() - withTimeout(connectionTimeout) { + withTimeout(timeout.connectTimeout) { val socket = builder.connect( InetSocketAddress(url.host, url.port), ) { - socketTimeout = Companion.socketTimeout - receiveBufferSize = buffer.size + socketTimeout = timeout.socketTimeout + // Do not set receiveBufferSize since the performance is better. }.let { when (url.protocol) { "https" -> it.tls(context) { @@ -218,6 +221,11 @@ internal class PreviewClientImpl : PreviewClient { * close connection */ override suspend fun close() { + logger.log("PreviewClient: close") + closeImple() + } + + suspend fun closeImple() { try { input?.cancel() } catch (_: Throwable) { @@ -261,7 +269,7 @@ internal class PreviewClientImpl : PreviewClient { private suspend fun fillBuffer(): Int { pos = 0 try { - return withTimeout(socketTimeout) { + return withTimeout(timeout.socketTimeout) { curr = input!!.readAvailable(buffer, 0, buffer.size) curr } @@ -368,6 +376,7 @@ internal class PreviewClientImpl : PreviewClient { */ private suspend fun responseStatus(): PreviewClientImpl { val line: String = readUtf8Line() ?: throw PreviewClientException("no response status") + logger.log("PreviewClient: responseStatus: $line") status = 0 statusMessage = null val match = Regex("HTTP/[0-9.]+ ([0-9]+) (.*)").find(line) @@ -465,7 +474,8 @@ internal class PreviewClientImpl : PreviewClient { contentType: String, digest: String? = null, ): PreviewClient { - close() // To prevent resource leaks + logger.log("PreviewClient:\nendpoint: $endpoint\nmethod: $method\npath: $path\nbody: $body\ncontentType: $contentType\ndigest: $digest") + closeImple() // To prevent resource leaks val url = URL(endpoint) connect(url) write("$method $path HTTP/1.1\r\n") diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApi.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApi.kt index e26aec8bb68..516197be942 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApi.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApi.kt @@ -47,6 +47,8 @@ internal object ThetaApi { var lastSetTimeConsumingOptionTime: Long = 0 var currentOptions = Options() + var apiLogListener: ((message: String) -> Unit)? = null + fun initOptions() { currentOptions = Options() lastSetTimeConsumingOptionTime = 0 @@ -68,7 +70,12 @@ internal object ThetaApi { endpoint: String, ): InfoApiResponse { return syncExecutor(requestScope, ApiClient.timeout.requestTimeout) { - httpClient.get(getApiUrl(endpoint, InfoApi.PATH)).body() + httpClient.get(getApiUrl(endpoint, InfoApi.PATH)) { + headers { + append("Content-Type", "application/json; charset=utf-8") + append("Cache-Control", "no-store") + } + }.body() } } @@ -106,7 +113,12 @@ internal object ThetaApi { endpoint: String, ): StateApiResponse { return syncExecutor(requestScope, ApiClient.timeout.requestTimeout) { - httpClient.post(getApiUrl(endpoint, StateApi.PATH)).body() + httpClient.post(getApiUrl(endpoint, StateApi.PATH)) { + headers { + append("Content-Type", "application/json; charset=utf-8") + append("Cache-Control", "no-store") + } + }.body() } } @@ -133,7 +145,7 @@ internal object ThetaApi { val response = httpClient.post(getApiUrl(endpoint, StatusApi.PATH)) { headers { append("Content-Type", "application/json; charset=utf-8") - append("Cache-Control", "no-cache") + append("Cache-Control", "no-store") } setBody(request) } @@ -166,7 +178,7 @@ internal object ThetaApi { filePaths: List, connectTimeout: Long, socketTimeout: Long, - callback: ((Int) -> Unit)?, + callback: ((Int) -> Boolean)?, ): UpdateFirmwareApiResponse { val DUMMY_RESPONSE = "{\"name\":\"camera.${apiPath}\",\"state\":\"done\"}" if (filePaths.isEmpty()) { @@ -434,6 +446,25 @@ internal object ThetaApi { return postCommandApi(endpoint, request).body() } + /** + * Call camera._reboot + * @param endpoint Endpoint of Theta web API + * @return response of reboot command + * @see ResetResponse + * @exception java.net.ConnectException can not connect to target endpoint + * @exception io.ktor.client.network.sockets.ConnectTimeoutException timeout to connect target endpoint + * @exception io.ktor.client.plugins.RedirectResponseException target response 3xx status + * @exception io.ktor.client.plugins.ClientRequestException target response 4xx status + * @exception io.ktor.client.plugins.ServerResponseException target response 5xx status + */ + @Throws(Throwable::class) + suspend fun callRebootCommand( + endpoint: String, + ): ResetResponse { + val request = RebootRequest() + return postCommandApi(endpoint, request).body() + } + /** * Call [camera.reset](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/commands/camera.reset.md) * @param endpoint Endpoint of Theta web API @@ -904,7 +935,7 @@ internal object ThetaApi { httpClient.post(getApiUrl(endpoint, CommandApi.PATH)) { headers { append("Content-Type", "application/json; charset=utf-8") - append("Cache-Control", "no-cache") + append("Cache-Control", "no-store") } setBody(body) } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApiLogger.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApiLogger.kt new file mode 100644 index 00000000000..0ffef58af4c --- /dev/null +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaApiLogger.kt @@ -0,0 +1,11 @@ +package com.ricoh360.thetaclient + +import io.ktor.client.plugins.logging.Logger + +internal class ThetaApiLogger : Logger { + override fun log(message: String) { + val newMessage = "ThetaApi: $message" + println(newMessage) + ThetaApi.apiLogListener?.let { it(newMessage) } + } +} 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 0a3b9015cf4..51c43f792ae 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/ThetaRepository.kt @@ -26,6 +26,9 @@ import kotlin.reflect.KClass */ class ThetaRepository internal constructor(val endpoint: String, config: Config? = null, timeout: Timeout? = null) { + /** logger */ + internal val logger = ThetaApiLogger() + /** * Configuration of THETA */ @@ -138,6 +141,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? init { timeout?.let { ApiClient.timeout = it } ?: run { ApiClient.timeout = Timeout() } + timeout?.let { PreviewClient.timeout = it } ?: run { PreviewClient.timeout = Timeout() } config?.clientMode?.let { ApiClient.digestAuth = it } ?: run { ApiClient.digestAuth = null } initConfig = config ThetaApi.initOptions() @@ -341,7 +345,13 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * THETA SC2 for business, the first character of which serial number is always * FIRST_CHAR_OF_SERIAL_NUMBER_SC2_B. */ - THETA_SC2_B("RICOH THETA SC2", FIRST_CHAR_OF_SERIAL_NUMBER_SC2_B); + THETA_SC2_B("RICOH THETA SC2", FIRST_CHAR_OF_SERIAL_NUMBER_SC2_B), + + /** + * THETA A1 + */ + THETA_A1("RICOH360 THETA A1"), + ; companion object { /** @@ -460,11 +470,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param socketTimeout Timeout (milli seconds) of socket * @Param callback function to pass the percentage of sent firmware. * After sending firmware, several minutes may be needed to start firmware update. + * If callback returns false, the update is canceled * @exception ThetaWebApiException If an error occurs in THETA. * @exception NotConnectedException */ @Throws(Throwable::class) - suspend fun updateFirmware(apiPath: String, filePaths: List, connectionTimeout: Long = 20_000L, socketTimeout: Long = 600_000L, callback: ((Int) -> Unit)? = null) { + suspend fun updateFirmware( + apiPath: String, + filePaths: List, + connectionTimeout: Long = 20_000L, + socketTimeout: Long = 600_000L, + callback: ((Int) -> Boolean)? = null + ) { try { val response = ThetaApi.callUpdateFirmwareApi(endpoint, apiPath, filePaths, connectionTimeout, socketTimeout, callback) response.error?.let { @@ -476,6 +493,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? throw ThetaWebApiException(e.toString()) } catch (e: IllegalArgumentException) { throw ThetaWebApiException(e.toString()) + } catch (e: BaseHttpClientException) { + throw ThetaWebApiException(e.message ?: e.toString()) } catch (e: Exception) { throw NotConnectedException(e.toString()) } @@ -688,18 +707,36 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * [options name](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/options.md) */ enum class OptionNameEnum(val value: String, val valueType: KClass<*>) { + /** + * Option name + * _accessInfo + */ + AccessInfo("_accessInfo", ThetaRepository.AccessInfo::class), + /** * Option name * _aiAutoThumbnail */ AiAutoThumbnail("_aiAutoThumbnail", AiAutoThumbnailEnum::class), + /** + * Option name + * _aiAutoThumbnailSupport + */ + AiAutoThumbnailSupport("_aiAutoThumbnailSupport", List::class), + /** * Option name * aperture */ Aperture("aperture", ApertureEnum::class), + /** + * Option name + * apertureSupport + */ + ApertureSupport("apertureSupport", List::class), + /** * Option name * _autoBracket @@ -741,6 +778,23 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ CameraControlSource("_cameraControlSource", CameraControlSourceEnum::class), + /** + * _cameraControlSourceSupport + */ + CameraControlSourceSupport("_cameraControlSourceSupport", List::class), + + /** + * Option name + * _cameraLock + */ + CameraLock("_cameraLock", CameraLockEnum::class), + + /** + * Option name + * _cameraLockConfig + */ + CameraLockConfig("_cameraLockConfig", ThetaRepository.CameraLockConfig::class), + /** * Option name * _cameraMode @@ -753,6 +807,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ CameraPower("_cameraPower", CameraPowerEnum::class), + /** + * Option name + * _cameraPowerSupport + */ + CameraPowerSupport("_cameraPowerSupport", List::class), + /** * Option name * captureInterval @@ -777,6 +837,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ ColorTemperature("_colorTemperature", Int::class), + /** + * Option name + * _colorTemperatureSupport + */ + ColorTemperatureSupport("_colorTemperatureSupport", ValueRange::class), + + /** + * Option name + * _compassDirectionRef + */ + CompassDirectionRef("_compassDirectionRef", CompassDirectionRefEnum::class), + /** * Option name * _compositeShootingOutputInterval @@ -788,6 +860,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ CompositeShootingOutputInterval("_compositeShootingOutputInterval", Int::class), + /** + * Supported in-progress save interval for interval composite shooting (sec). + */ + CompositeShootingOutputIntervalSupport("_compositeShootingOutputIntervalSupport", ValueRange::class), + /** * Option name * _compositeShootingTime @@ -799,6 +876,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ CompositeShootingTime("_compositeShootingTime", Int::class), + /** + * Supported shooting time for interval composite shooting (sec). + */ + CompositeShootingTimeSupport("_compositeShootingTimeSupport", ValueRange::class), + /** * Option name * continuousNumber @@ -832,6 +914,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ ExposureDelay("exposureDelay", ExposureDelayEnum::class), + /** + * Option name + * exposureDelaySupport + */ + ExposureDelaySupport("exposureDelaySupport", List::class), + /** * Option name * exposureProgram @@ -874,6 +962,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ GpsInfo("gpsInfo", ThetaRepository.GpsInfo::class), + /** + * Option name + * _gpsTagRecordingSupport + */ + GpsTagRecordingSupport("_gpsTagRecordingSupport", List::class), + /** * Option name * _imageStitching @@ -918,6 +1012,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ MaxRecordableTime("_maxRecordableTime", MaxRecordableTimeEnum::class), + /** + * Option name + * _microphoneNoiseReduction + */ + MicrophoneNoiseReduction("_microphoneNoiseReduction", MicrophoneNoiseReductionEnum::class), + + /** + * Option name + * _mobileNetworkSetting + */ + MobileNetworkSetting("_mobileNetworkSetting", ThetaRepository.MobileNetworkSetting::class), + /** * Option name * _networkType @@ -930,6 +1036,14 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ OffDelay("offDelay", ThetaRepository.OffDelay::class), + /** + * Option name + * _offDelayUSB + * + * For RICOH THETA A1 + */ + OffDelayUsb("_offDelayUSB", ThetaRepository.OffDelayUsb::class), + /** * Option name * Password @@ -996,6 +1110,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ TopBottomCorrectionRotation("_topBottomCorrectionRotation", ThetaRepository.TopBottomCorrectionRotation::class), + /** + * Option name + * _topBottomCorrectionRotationSupport + */ + TopBottomCorrectionRotationSupport("_topBottomCorrectionRotationSupport", ThetaRepository.TopBottomCorrectionRotationSupport::class), + /** * Option name * totalSpace @@ -1026,6 +1146,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ TimeShift("_timeShift", TimeShiftSetting::class), + /** + * Option name + * _usbConnection + */ + UsbConnection("_usbConnection", UsbConnectionEnum::class), + /** * Option name * _username @@ -1056,11 +1182,29 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ WhiteBalanceAutoStrength("_whiteBalanceAutoStrength", WhiteBalanceAutoStrengthEnum::class), + /** + * Option name + * _wlanAntennaConfig + */ + WlanAntennaConfig("_wlanAntennaConfig", WlanAntennaConfigEnum::class), + /** * Option name * _wlanFrequency */ WlanFrequency("_wlanFrequency", WlanFrequencyEnum::class), + + /** + * Option name + * _wlanFrequencySupport + */ + WlanFrequencySupport("_wlanFrequencySupport", List::class), + + /** + * Option name + * _wlanFrequencyCLmode + */ + WlanFrequencyClMode("_wlanFrequencyCLmode", ThetaRepository.WlanFrequencyClMode::class), } /** @@ -1068,16 +1212,30 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * Refer to the [options category](https://github.com/ricohapi/theta-api-specs/blob/main/theta-web-api-v2.1/options.md) */ data class Options( + /** + * Connected network information. + */ + var accessInfo: AccessInfo? = null, /** * AI auto thumbnail setting. */ var aiAutoThumbnail: AiAutoThumbnailEnum? = null, + /** + * Supported AI auto thumbnail setting. + */ + var aiAutoThumbnailSupport: List? = null, + /** * Aperture value. */ var aperture: ApertureEnum? = null, + /** + * Supported aperture value. + */ + var apertureSupport: List? = null, + /** * Multi bracket shooting setting. */ @@ -1113,6 +1271,21 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var cameraControlSource: CameraControlSourceEnum? = null, + /** + * Supported Camera Control Source. + */ + var cameraControlSourceSupport: List? = null, + + /** + * @see CameraLockEnum + */ + var cameraLock: CameraLockEnum? = null, + + /** + * @see CameraLockConfig + */ + var cameraLockConfig: CameraLockConfig? = null, + /** * @see CameraModeEnum */ @@ -1123,6 +1296,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var cameraPower: CameraPowerEnum? = null, + /** + * Supported Camera Power. + */ + var cameraPowerSupport: List? = null, + /** * Shooting interval (sec.) for interval shooting. * @@ -1178,6 +1356,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var colorTemperature: Int? = null, + /** + * supported color temperature. + */ + var colorTemperatureSupport: ValueRange? = null, + + /** + * @see CompassDirectionRefEnum + */ + var compassDirectionRef: CompassDirectionRefEnum? = null, + /** * In-progress save interval for interval composite shooting (sec). * @@ -1190,6 +1378,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var compositeShootingOutputInterval: Int? = null, + /** + * Supported in-progress save interval for interval composite shooting (sec). + */ + var compositeShootingOutputIntervalSupport: ValueRange? = null, + /** * Shooting time for interval composite shooting (sec). * @@ -1202,6 +1395,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var compositeShootingTime: Int? = null, + /** + * Supported shooting time for interval composite shooting (sec). + */ + var compositeShootingTimeSupport: ValueRange? = null, + /** * @see ContinuousNumberEnum */ @@ -1241,6 +1439,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var exposureDelay: ExposureDelayEnum? = null, + /** + * Supported operating time (sec.) of the self-timer. + */ + var exposureDelaySupport: List? = null, + /** * Exposure program. The exposure settings that take priority can be selected. * @@ -1272,9 +1475,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * Also, when filter is enabled, the exposure program is set to the Normal program. * * The condition below will result in an error. - * [fileFormat] is raw+ and _filter is Noise reduction, HDR or Handheld HDR - * shootingMethod is except for Normal shooting and [filter] is enabled - * Access during video capture mode + * + * - When attempting to set [filter] to Noise reduction, + * HDR or Handheld HDR while [fileFormat] is set to raw+, + * but this restriction is only for RICOH THETA Z1 firmware v1.80.1 or earlier. + * - [shootingMethod] is except for Normal shooting and [filter] is enabled + * - Access during video capture mode */ var filter: FilterEnum? = null, @@ -1295,6 +1501,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var gpsInfo: GpsInfo? = null, + /** + * Supported GpsTagRecording + * For THETA X + */ + var gpsTagRecordingSupport: List? = null, + /** * Still image stitching setting during shooting. */ @@ -1336,6 +1548,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var maxRecordableTime: MaxRecordableTimeEnum? = null, + /** + * @see MicrophoneNoiseReductionEnum + */ + var microphoneNoiseReduction: MicrophoneNoiseReductionEnum? = null, + + /** + * @see MobileNetworkSetting + */ + var mobileNetworkSetting: MobileNetworkSetting? = null, + /** * Network type of the camera. */ @@ -1348,6 +1570,14 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var offDelay: OffDelay? = null, + /** + * Auto power off time with USB power supply. + * + * Use in [OffDelayUsbEnum] or [OffDelayUsbSec] + * For RICOH THETA A1 + */ + var offDelayUsb: OffDelayUsb? = null, + /** * Password used for digest authentication when _networkType is set to client mode. * Can be set by camera.setOptions during direct mode. @@ -1425,6 +1655,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var topBottomCorrectionRotation: TopBottomCorrectionRotation? = null, + /** + * @see TopBottomCorrectionRotationSupport + */ + var topBottomCorrectionRotationSupport: TopBottomCorrectionRotationSupport? = null, + /** * Total storage space (byte). */ @@ -1439,6 +1674,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var shutterVolume: Int? = null, + /** + * @see UsbConnectionEnum + */ + var usbConnection: UsbConnectionEnum? = null, + /** * User name used for digest authentication when _networkType is set to client mode. * Can be set by camera.setOptions during direct mode. @@ -1468,16 +1708,41 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ var whiteBalanceAutoStrength: WhiteBalanceAutoStrengthEnum? = null, + /** + * @see WlanAntennaConfigEnum + */ + var wlanAntennaConfig: WlanAntennaConfigEnum? = null, + /** * Wireless LAN frequency of the camera * * For RICOH THETA X, Z1 and V. */ var wlanFrequency: WlanFrequencyEnum? = null, + + /** + * Supported WlanFrequency + * + * For RICOH THETA X, Z1 and V. + */ + var wlanFrequencySupport: List? = null, + + /** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + * + * Can be set and retrieved when in AP mode. + * Can be retrieved when in CL mode. + * + * For RICOH THETA A1 + */ + var wlanFrequencyClMode: WlanFrequencyClMode? = null, ) { constructor() : this( + accessInfo = null, aiAutoThumbnail = null, + aiAutoThumbnailSupport = null, aperture = null, + apertureSupport = null, autoBracket = null, bitrate = null, bluetoothPower = null, @@ -1485,19 +1750,28 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? burstMode = null, burstOption = null, cameraControlSource = null, + cameraControlSourceSupport = null, + cameraLock = null, + cameraLockConfig = null, cameraMode = null, cameraPower = null, + cameraPowerSupport = null, captureInterval = null, captureMode = null, captureNumber = null, colorTemperature = null, + colorTemperatureSupport = null, + compassDirectionRef = null, compositeShootingOutputInterval = null, + compositeShootingOutputIntervalSupport = null, compositeShootingTime = null, + compositeShootingTimeSupport = null, continuousNumber = null, dateTimeZone = null, ethernetConfig = null, exposureCompensation = null, exposureDelay = null, + exposureDelaySupport = null, exposureProgram = null, faceDetect = null, fileFormat = null, @@ -1505,6 +1779,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? function = null, gain = null, gpsInfo = null, + gpsTagRecordingSupport = null, imageStitching = null, isGpsOn = null, iso = null, @@ -1512,8 +1787,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? language = null, latestEnabledExposureDelayTime = null, maxRecordableTime = null, + microphoneNoiseReduction = null, + mobileNetworkSetting = null, networkType = null, offDelay = null, + offDelayUsb = null, password = null, powerSaving = null, preset = null, @@ -1529,18 +1807,26 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? timeShift = null, topBottomCorrection = null, topBottomCorrectionRotation = null, + topBottomCorrectionRotationSupport = null, totalSpace = null, + usbConnection = null, username = null, videoStitching = null, visibilityReduction = null, whiteBalance = null, whiteBalanceAutoStrength = null, + wlanAntennaConfig = null, wlanFrequency = null, + wlanFrequencySupport = null, + wlanFrequencyClMode = null, ) internal constructor(options: com.ricoh360.thetaclient.transferred.Options) : this( + accessInfo = options._accessInfo?.let { AccessInfo(it) }, aiAutoThumbnail = options._aiAutoThumbnail?.let { AiAutoThumbnailEnum.get(it) }, + aiAutoThumbnailSupport = options._aiAutoThumbnailSupport?.map { AiAutoThumbnailEnum.get(it) }, aperture = options.aperture?.let { ApertureEnum.get(it) }, + apertureSupport = options.apertureSupport?.map { ApertureEnum.get(it) }, autoBracket = options._autoBracket?.let { BracketSettingList.get(it) }, bitrate = options._bitrate?.let { BitrateEnum.get(it) }, bluetoothPower = options._bluetoothPower?.let { BluetoothPowerEnum.get(it) }, @@ -1548,14 +1834,22 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? burstMode = options._burstMode?.let { BurstModeEnum.get(it) }, burstOption = options._burstOption?.let { BurstOption(it) }, cameraControlSource = options._cameraControlSource?.let { CameraControlSourceEnum.get(it) }, + cameraControlSourceSupport = options._cameraControlSourceSupport?.map { CameraControlSourceEnum.get(it) }, + cameraLock = options._cameraLock?.let { CameraLockEnum.get(it) }, + cameraLockConfig = options._cameraLockConfig?.let { CameraLockConfig(it) }, cameraMode = options._cameraMode?.let { CameraModeEnum.get(it) }, cameraPower = options._cameraPower?.let { CameraPowerEnum.get(it) }, + cameraPowerSupport = options._cameraPowerSupport?.map { CameraPowerEnum.get(it) }, captureInterval = options.captureInterval, captureMode = options.captureMode?.let { CaptureModeEnum.get(it) }, captureNumber = options.captureNumber, colorTemperature = options._colorTemperature, + colorTemperatureSupport = options._colorTemperatureSupport?.let { ValueRange(it) }, + compassDirectionRef = options._compassDirectionRef?.let { CompassDirectionRefEnum.get(it) }, compositeShootingOutputInterval = options._compositeShootingOutputInterval, + compositeShootingOutputIntervalSupport = options._compositeShootingOutputIntervalSupport?.let { ValueRange(it) }, compositeShootingTime = options._compositeShootingTime, + compositeShootingTimeSupport = options._compositeShootingTimeSupport?.let { ValueRange(it) }, continuousNumber = options.continuousNumber?.let { ContinuousNumberEnum.get(it) }, dateTimeZone = options.dateTimeZone, ethernetConfig = options._ethernetConfig?.let { EthernetConfig(it) }, @@ -1565,6 +1859,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ) }, exposureDelay = options.exposureDelay?.let { ExposureDelayEnum.get(it) }, + exposureDelaySupport = options.exposureDelaySupport?.map { ExposureDelayEnum.get(it) }, exposureProgram = options.exposureProgram?.let { ExposureProgramEnum.get(it) }, faceDetect = options._faceDetect?.let { FaceDetectEnum.get(it) }, fileFormat = options.fileFormat?.let { FileFormatEnum.get(it) }, @@ -1572,6 +1867,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? function = options._function?.let { ShootingFunctionEnum.get(it) }, gain = options._gain?.let { GainEnum.get(it) }, gpsInfo = options.gpsInfo?.let { GpsInfo(it) }, + gpsTagRecordingSupport = options._gpsTagRecordingSupport?.map { GpsTagRecordingEnum.get(it) }, imageStitching = options._imageStitching?.let { ImageStitchingEnum.get(it) }, isGpsOn = options._gpsTagRecording?.let { it == GpsTagRecording.ON }, iso = options.iso?.let { IsoEnum.get(it) }, @@ -1579,8 +1875,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? language = options._language?.let { LanguageEnum.get(it) }, latestEnabledExposureDelayTime = options._latestEnabledExposureDelayTime?.let { ExposureDelayEnum.get(it) }, maxRecordableTime = options._maxRecordableTime?.let { MaxRecordableTimeEnum.get(it) }, + microphoneNoiseReduction = options._microphoneNoiseReduction?.let { MicrophoneNoiseReductionEnum.get(it) }, + mobileNetworkSetting = options._mobileNetworkSetting?.let { MobileNetworkSetting(it) }, networkType = options._networkType?.let { NetworkTypeEnum.get(it) }, offDelay = options.offDelay?.let { OffDelayEnum.get(it) }, + offDelayUsb = options._offDelayUSB?.let { OffDelayUsbEnum.get(it) }, password = options._password, powerSaving = options._powerSaving?.let { PowerSavingEnum.get(it) }, preset = options._preset?.let { PresetEnum.get(it) }, @@ -1595,14 +1894,19 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? timeShift = options._timeShift?.let { TimeShiftSetting(it) }, topBottomCorrection = options._topBottomCorrection?.let { TopBottomCorrectionOptionEnum.get(it) }, topBottomCorrectionRotation = options._topBottomCorrectionRotation?.let { TopBottomCorrectionRotation(it) }, + topBottomCorrectionRotationSupport = options._topBottomCorrectionRotationSupport?.let { TopBottomCorrectionRotationSupport(it) }, totalSpace = options.totalSpace, shutterVolume = options._shutterVolume, + usbConnection = options._usbConnection?.let { UsbConnectionEnum.get(it) }, username = options._username, videoStitching = options.videoStitching?.let { VideoStitchingEnum.get(it) }, visibilityReduction = options._visibilityReduction?.let { VisibilityReductionEnum.get(it) }, whiteBalance = options.whiteBalance?.let { WhiteBalanceEnum.get(it) }, whiteBalanceAutoStrength = options._whiteBalanceAutoStrength?.let { WhiteBalanceAutoStrengthEnum.get(it) }, + wlanAntennaConfig = options._wlanAntennaConfig?.let { WlanAntennaConfigEnum.get(it) }, wlanFrequency = options._wlanFrequency?.let { WlanFrequencyEnum.get(it) }, + wlanFrequencySupport = options._wlanFrequencySupport?.map { WlanFrequencyEnum.get(it) }, + wlanFrequencyClMode = options._wlanFrequencyCLmode?.let { WlanFrequencyClMode(it) }, ) /** @@ -1611,7 +1915,9 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ internal fun toOptions(): com.ricoh360.thetaclient.transferred.Options { return Options( + _accessInfo = accessInfo?.toTransferredAccessInfo(), _aiAutoThumbnail = aiAutoThumbnail?.value, + _aiAutoThumbnailSupport = aiAutoThumbnailSupport?.map { it.value }, aperture = aperture?.value, _autoBracket = autoBracket?.toTransferredAutoBracket(), _bitrate = bitrate?.rawValue, @@ -1620,12 +1926,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? _burstMode = burstMode?.value, _burstOption = burstOption?.toTransferredBurstOption(), _cameraControlSource = cameraControlSource?.value, + _cameraControlSourceSupport = cameraControlSourceSupport?.map { it.value }, + _cameraLock = cameraLock?.value, + _cameraLockConfig = cameraLockConfig?.toTransferredCameraLockConfig(), _cameraMode = cameraMode?.value, _cameraPower = cameraPower?.value, captureInterval = captureInterval, captureMode = captureMode?.value, captureNumber = captureNumber, _colorTemperature = colorTemperature, + _compassDirectionRef = compassDirectionRef?.value, _compositeShootingOutputInterval = compositeShootingOutputInterval, _compositeShootingTime = compositeShootingTime, continuousNumber = continuousNumber?.value, @@ -1647,8 +1957,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? _language = language?.value, _latestEnabledExposureDelayTime = latestEnabledExposureDelayTime?.sec, _maxRecordableTime = maxRecordableTime?.sec, + _microphoneNoiseReduction = microphoneNoiseReduction?.value, + _mobileNetworkSetting = mobileNetworkSetting?.toTransferredMobileNetworkSetting(), _networkType = networkType?.value, offDelay = offDelay?.sec, + _offDelayUSB = offDelayUsb?.sec, _password = password, _powerSaving = powerSaving?.value, _preset = preset?.value, @@ -1665,12 +1978,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? _shootingMethod = shootingMethod?.value, shutterSpeed = shutterSpeed?.value, _shutterVolume = shutterVolume, + _usbConnection = usbConnection?.value, _username = username, videoStitching = videoStitching?.value, _visibilityReduction = visibilityReduction?.value, whiteBalance = whiteBalance?.value, _whiteBalanceAutoStrength = whiteBalanceAutoStrength?.value, + _wlanAntennaConfig = wlanAntennaConfig?.value, _wlanFrequency = wlanFrequency?.value, + _wlanFrequencySupport = wlanFrequencySupport?.map { it.value }, + _wlanFrequencyCLmode = wlanFrequencyClMode?.toTransferred(), ) } @@ -1686,8 +2003,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? fun getValue(name: OptionNameEnum): T? { @Suppress("UNCHECKED_CAST") return when (name) { + OptionNameEnum.AccessInfo -> accessInfo OptionNameEnum.AiAutoThumbnail -> aiAutoThumbnail + OptionNameEnum.AiAutoThumbnailSupport -> aiAutoThumbnailSupport OptionNameEnum.Aperture -> aperture + OptionNameEnum.ApertureSupport -> apertureSupport OptionNameEnum.AutoBracket -> autoBracket OptionNameEnum.Bitrate -> bitrate OptionNameEnum.BluetoothPower -> bluetoothPower @@ -1695,19 +2015,30 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.BurstMode -> burstMode OptionNameEnum.BurstOption -> burstOption OptionNameEnum.CameraControlSource -> cameraControlSource + OptionNameEnum.CameraControlSourceSupport -> cameraControlSourceSupport + OptionNameEnum.CameraMode -> cameraMode + OptionNameEnum.CameraPower -> cameraPower + OptionNameEnum.CameraPowerSupport -> cameraPowerSupport + OptionNameEnum.CameraLock -> cameraLock + OptionNameEnum.CameraLockConfig -> cameraLockConfig OptionNameEnum.CameraMode -> cameraMode OptionNameEnum.CameraPower -> cameraPower + OptionNameEnum.CompassDirectionRef -> compassDirectionRef OptionNameEnum.CaptureInterval -> captureInterval OptionNameEnum.CaptureMode -> captureMode OptionNameEnum.CaptureNumber -> captureNumber OptionNameEnum.ColorTemperature -> colorTemperature + OptionNameEnum.ColorTemperatureSupport -> colorTemperatureSupport OptionNameEnum.CompositeShootingOutputInterval -> compositeShootingOutputInterval + OptionNameEnum.CompositeShootingOutputIntervalSupport -> compositeShootingOutputIntervalSupport OptionNameEnum.CompositeShootingTime -> compositeShootingTime + OptionNameEnum.CompositeShootingTimeSupport -> compositeShootingTimeSupport OptionNameEnum.ContinuousNumber -> continuousNumber OptionNameEnum.DateTimeZone -> dateTimeZone OptionNameEnum.EthernetConfig -> ethernetConfig OptionNameEnum.ExposureCompensation -> exposureCompensation OptionNameEnum.ExposureDelay -> exposureDelay + OptionNameEnum.ExposureDelaySupport -> exposureDelaySupport OptionNameEnum.ExposureProgram -> exposureProgram OptionNameEnum.FaceDetect -> faceDetect OptionNameEnum.FileFormat -> fileFormat @@ -1715,6 +2046,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.Function -> function OptionNameEnum.Gain -> gain OptionNameEnum.GpsInfo -> gpsInfo + OptionNameEnum.GpsTagRecordingSupport -> gpsTagRecordingSupport OptionNameEnum.ImageStitching -> imageStitching OptionNameEnum.IsGpsOn -> isGpsOn OptionNameEnum.Iso -> iso @@ -1722,8 +2054,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.Language -> language OptionNameEnum.LatestEnabledExposureDelayTime -> latestEnabledExposureDelayTime OptionNameEnum.MaxRecordableTime -> maxRecordableTime + OptionNameEnum.MicrophoneNoiseReduction -> microphoneNoiseReduction + OptionNameEnum.MobileNetworkSetting -> mobileNetworkSetting OptionNameEnum.NetworkType -> networkType OptionNameEnum.OffDelay -> offDelay + OptionNameEnum.OffDelayUsb -> offDelayUsb OptionNameEnum.Password -> password OptionNameEnum.PowerSaving -> powerSaving OptionNameEnum.Preset -> preset @@ -1739,13 +2074,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.TimeShift -> timeShift OptionNameEnum.TopBottomCorrection -> topBottomCorrection OptionNameEnum.TopBottomCorrectionRotation -> topBottomCorrectionRotation + OptionNameEnum.TopBottomCorrectionRotationSupport -> topBottomCorrectionRotationSupport OptionNameEnum.TotalSpace -> totalSpace + OptionNameEnum.UsbConnection -> usbConnection OptionNameEnum.Username -> username OptionNameEnum.VideoStitching -> videoStitching OptionNameEnum.VisibilityReduction -> visibilityReduction OptionNameEnum.WhiteBalance -> whiteBalance OptionNameEnum.WhiteBalanceAutoStrength -> whiteBalanceAutoStrength + OptionNameEnum.WlanAntennaConfig -> wlanAntennaConfig OptionNameEnum.WlanFrequency -> wlanFrequency + OptionNameEnum.WlanFrequencySupport -> wlanFrequencySupport + OptionNameEnum.WlanFrequencyClMode -> wlanFrequencyClMode } as T } @@ -1762,8 +2102,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? throw ThetaWebApiException("Invalid value type") } when (name) { + OptionNameEnum.AccessInfo -> accessInfo = value as AccessInfo OptionNameEnum.AiAutoThumbnail -> aiAutoThumbnail = value as AiAutoThumbnailEnum + OptionNameEnum.AiAutoThumbnailSupport -> aiAutoThumbnailSupport = value as List OptionNameEnum.Aperture -> aperture = value as ApertureEnum + OptionNameEnum.ApertureSupport -> apertureSupport = value as List OptionNameEnum.AutoBracket -> autoBracket = value as BracketSettingList OptionNameEnum.Bitrate -> bitrate = value as Bitrate OptionNameEnum.BluetoothPower -> bluetoothPower = value as BluetoothPowerEnum @@ -1771,19 +2114,30 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.BurstMode -> burstMode = value as BurstModeEnum OptionNameEnum.BurstOption -> burstOption = value as BurstOption OptionNameEnum.CameraControlSource -> cameraControlSource = value as CameraControlSourceEnum + OptionNameEnum.CameraControlSourceSupport -> cameraControlSourceSupport = value as List OptionNameEnum.CameraMode -> cameraMode = value as CameraModeEnum OptionNameEnum.CameraPower -> cameraPower = value as CameraPowerEnum + OptionNameEnum.CameraPowerSupport -> cameraPowerSupport = value as List + OptionNameEnum.CameraLock -> cameraLock = value as CameraLockEnum + OptionNameEnum.CameraLockConfig -> cameraLockConfig = value as CameraLockConfig + OptionNameEnum.CameraMode -> cameraMode = value as CameraModeEnum + OptionNameEnum.CameraPower -> cameraPower = value as CameraPowerEnum + OptionNameEnum.CompassDirectionRef -> compassDirectionRef = value as CompassDirectionRefEnum OptionNameEnum.CaptureInterval -> captureInterval = value as Int OptionNameEnum.CaptureMode -> captureMode = value as CaptureModeEnum OptionNameEnum.CaptureNumber -> captureNumber = value as Int OptionNameEnum.ColorTemperature -> colorTemperature = value as Int + OptionNameEnum.ColorTemperatureSupport -> colorTemperatureSupport = value as ValueRange OptionNameEnum.CompositeShootingOutputInterval -> compositeShootingOutputInterval = value as Int + OptionNameEnum.CompositeShootingOutputIntervalSupport -> compositeShootingOutputIntervalSupport = value as ValueRange OptionNameEnum.CompositeShootingTime -> compositeShootingTime = value as Int + OptionNameEnum.CompositeShootingTimeSupport -> compositeShootingTimeSupport = value as ValueRange OptionNameEnum.ContinuousNumber -> continuousNumber = value as ContinuousNumberEnum OptionNameEnum.DateTimeZone -> dateTimeZone = value as String OptionNameEnum.EthernetConfig -> ethernetConfig = value as EthernetConfig OptionNameEnum.ExposureCompensation -> exposureCompensation = value as ExposureCompensationEnum OptionNameEnum.ExposureDelay -> exposureDelay = value as ExposureDelayEnum + OptionNameEnum.ExposureDelaySupport -> exposureDelaySupport = value as List OptionNameEnum.ExposureProgram -> exposureProgram = value as ExposureProgramEnum OptionNameEnum.FaceDetect -> faceDetect = value as FaceDetectEnum OptionNameEnum.FileFormat -> fileFormat = value as FileFormatEnum @@ -1791,6 +2145,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.Function -> function = value as ShootingFunctionEnum OptionNameEnum.Gain -> gain = value as GainEnum OptionNameEnum.GpsInfo -> gpsInfo = value as GpsInfo + OptionNameEnum.GpsTagRecordingSupport -> gpsTagRecordingSupport = value as List OptionNameEnum.ImageStitching -> imageStitching = value as ImageStitchingEnum OptionNameEnum.IsGpsOn -> isGpsOn = value as Boolean OptionNameEnum.Iso -> iso = value as IsoEnum @@ -1798,8 +2153,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.Language -> language = value as LanguageEnum OptionNameEnum.LatestEnabledExposureDelayTime -> latestEnabledExposureDelayTime = value as ExposureDelayEnum OptionNameEnum.MaxRecordableTime -> maxRecordableTime = value as MaxRecordableTimeEnum + OptionNameEnum.MicrophoneNoiseReduction -> microphoneNoiseReduction = value as MicrophoneNoiseReductionEnum + OptionNameEnum.MobileNetworkSetting -> mobileNetworkSetting = value as MobileNetworkSetting OptionNameEnum.NetworkType -> networkType = value as NetworkTypeEnum OptionNameEnum.OffDelay -> offDelay = value as OffDelay + OptionNameEnum.OffDelayUsb -> offDelayUsb = value as OffDelayUsb OptionNameEnum.Password -> password = value as String OptionNameEnum.PowerSaving -> powerSaving = value as PowerSavingEnum OptionNameEnum.Preset -> preset = value as PresetEnum @@ -1815,13 +2173,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? OptionNameEnum.TimeShift -> timeShift = value as TimeShiftSetting OptionNameEnum.TopBottomCorrection -> topBottomCorrection = value as TopBottomCorrectionOptionEnum OptionNameEnum.TopBottomCorrectionRotation -> topBottomCorrectionRotation = value as TopBottomCorrectionRotation + OptionNameEnum.TopBottomCorrectionRotationSupport -> topBottomCorrectionRotationSupport = value as TopBottomCorrectionRotationSupport OptionNameEnum.TotalSpace -> totalSpace = value as Long + OptionNameEnum.UsbConnection -> usbConnection = value as UsbConnectionEnum OptionNameEnum.Username -> username = value as String OptionNameEnum.VideoStitching -> videoStitching = value as VideoStitchingEnum OptionNameEnum.VisibilityReduction -> visibilityReduction = value as VisibilityReductionEnum OptionNameEnum.WhiteBalance -> whiteBalance = value as WhiteBalanceEnum OptionNameEnum.WhiteBalanceAutoStrength -> whiteBalanceAutoStrength = value as WhiteBalanceAutoStrengthEnum + OptionNameEnum.WlanAntennaConfig -> wlanAntennaConfig = value as WlanAntennaConfigEnum OptionNameEnum.WlanFrequency -> wlanFrequency = value as WlanFrequencyEnum + OptionNameEnum.WlanFrequencySupport -> wlanFrequencySupport = value as List + OptionNameEnum.WlanFrequencyClMode -> wlanFrequencyClMode = value as WlanFrequencyClMode } } } @@ -1830,6 +2193,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * AI auto thumbnail setting. */ enum class AiAutoThumbnailEnum(internal val value: AiAutoThumbnail) { + /** + * Undefined value + */ + UNKNOWN(AiAutoThumbnail.UNKNOWN), + /** * AI auto setting ON */ @@ -1847,8 +2215,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value AI auto thumbnail setting. * @return AiAutoThumbnailEnum */ - internal fun get(value: AiAutoThumbnail): AiAutoThumbnailEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: AiAutoThumbnail): AiAutoThumbnailEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -1856,7 +2224,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Aperture value. */ - enum class ApertureEnum(val value: Float) { + enum class ApertureEnum(val value: Float?) { + /** + * Undefined value + */ + UNKNOWN(null), + /** * Aperture value. * AUTO(0) @@ -1910,8 +2283,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value Aperture value. * @return ApertureEnum */ - fun get(value: Float): ApertureEnum? { - return values().firstOrNull { it.value == value } + fun get(value: Float): ApertureEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -2565,6 +2938,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * For RICOH THETA X */ enum class CameraControlSourceEnum(internal val value: CameraControlSource) { + /** + * Undefined value + */ + UNKNOWN(CameraControlSource.UNKNOWN), + /** * Operation is possible with the camera. Locks the smartphone * application UI (supported app only). @@ -2584,8 +2962,112 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value camera control source * @return CameraControlSourceEnum */ - internal fun get(value: CameraControlSource): CameraControlSourceEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: CameraControlSource): CameraControlSourceEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN + } + } + } + + /** + * Camera Lock Config + * + * - It is possible to enable/disable the function for each HW key. + * - It is possible to enable/disable the operation of the panel. + * - When all supported buttons/components are in the "unlock" state, it will be the same as the normal setting. + * - For THETA models, if there are no wlanKey, fnKey, or panel, it will return "lock". If there are no supported buttons/components, setting to "unlock" or "lock" will not return an error and will not perform any action. + */ + data class CameraLockConfig( + /** + * power key locked or unlocked. + */ + val isPowerKeyLocked: Boolean? = null, + + /** + * Shutter key locked or unlocked. + */ + val isShutterKeyLocked: Boolean? = null, + + /** + * mode key locked or unlocked. + */ + val isModeKeyLocked: Boolean? = null, + + /** + * wlan key locked or unlocked. + */ + val isWlanKeyLocked: Boolean? = null, + + /** + * fn key locked or unlocked. + */ + val isFnKeyLocked: Boolean? = null, + + /** + * panel locked or unlocked. + * Fixed to true. Does not cause an error when false, but it is not reflected either. + */ + val isPanelLocked: Boolean? = null, + ) { + internal constructor(config: com.ricoh360.thetaclient.transferred.CameraLockConfig) : this( + isPowerKeyLocked = config.powerKey?.isLocked, + isShutterKeyLocked = config.shutterKey?.isLocked, + isModeKeyLocked = config.modeKey?.isLocked, + isWlanKeyLocked = config.wlanKey?.isLocked, + isFnKeyLocked = config.fnKey?.isLocked, + isPanelLocked = config.panel?.isLocked, + ) + + /** + * Convert CameraLockConfig to transferred.CameraLockConfig + * + * @return transferred.CameraLockConfig + */ + internal fun toTransferredCameraLockConfig(): com.ricoh360.thetaclient.transferred.CameraLockConfig { + return CameraLockConfig( + powerKey = CameraLockType.from(isPowerKeyLocked), + shutterKey = CameraLockType.from(isShutterKeyLocked), + modeKey = CameraLockType.from(isModeKeyLocked), + wlanKey = CameraLockType.from(isWlanKeyLocked), + fnKey = CameraLockType.from(isFnKeyLocked), + panel = CameraLockType.from(isPanelLocked), + ) + } + } + + /** + * Control camera lock/unlock + */ + enum class CameraLockEnum(internal val value: CameraLock) { + /** + * Undefined value + */ + UNKNOWN(CameraLock.UNKNOWN), + + /** + * Camera is unlocked + */ + UNLOCK(CameraLock.UNLOCK), + + /** + * Camera basic lock state + * (Mode button, WLAN button, and Fn button presses are inhibited) + */ + BASIC_LOCK(CameraLock.BASIC_LOCK), + + /** + * Lock according to the parameters set in _cameraLockConfig. + */ + CUSTOM_LOCK(CameraLock.CUSTOM_LOCK); + + companion object { + /** + * Convert CameraLock to CameraLockEnum + * + * @param value CameraLock. + * @return CameraLockEnum + */ + internal fun get(value: CameraLock): CameraLockEnum? { + return entries.firstOrNull { it.value == value } } } } @@ -2631,6 +3113,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } /** + * Camera power state. * _cameraPower is the power status of camera. * * For RICOH THETA X v2.61.0 or later @@ -2651,6 +3134,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ OFF(CameraPower.OFF), + /** + * Sleep + */ + SLEEP(CameraPower.SLEEP), + /** * Power on, power saving mode. Camera is closed. * Unavailable parameter when plugin is running. In this case, invalidParameterValue error will be returned. @@ -2670,8 +3158,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value CameraPower. * @return CameraPowerEnum */ - internal fun get(value: CameraPower): CameraPowerEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: CameraPower): CameraPowerEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -2725,39 +3213,78 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } /** - * Number of shots for continuous shooting. - * It can be acquired by camera.getOptions. - * - * For RICOH THETA X - * - 11k image: Maximum value 8 - * - 5.5k image: Maximum value 20 - * - * Depending on available storage capacity, the value may be less than maximum. + * _compassDirectionRef */ - enum class ContinuousNumberEnum(val value: Int) { + enum class CompassDirectionRefEnum(internal val value: CompassDirectionRef) { /** - * Disable continuous shooting. + * Undefined value */ - OFF(0), + UNKNOWN(CompassDirectionRef.UNKNOWN), /** - * Maximum value 1 + * If GPS positioning is available, record in true north; + * if GPS is off or not available, record in magnetic north. */ - MAX_1(1), + AUTO(CompassDirectionRef.AUTO), /** - * Maximum value 2 + * If the azimuth is set to true north, GPS is turned off, or positioning is not possible, + * the azimuth information is not recorded (positioning information is required for conversion). */ - MAX_2(2), + TRUE_NORTH(CompassDirectionRef.TRUE_NORTH), /** - * Maximum value 3 + * Do not set azimuth to true north, set azimuth to magnetic north */ - MAX_3(3), + MAGNETIC(CompassDirectionRef.MAGNETIC); - /** - * Maximum value 4 - */ + companion object { + /** + * Convert CompassDirectionRef to CompassDirectionRefEnum + * + * @param value CompassDirectionRef. + * @return CompassDirectionRefEnum + */ + internal fun get(value: CompassDirectionRef): CompassDirectionRefEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + + /** + * Number of shots for continuous shooting. + * It can be acquired by camera.getOptions. + * + * For RICOH THETA X + * - 11k image: Maximum value 8 + * - 5.5k image: Maximum value 20 + * + * Depending on available storage capacity, the value may be less than maximum. + */ + enum class ContinuousNumberEnum(val value: Int) { + /** + * Disable continuous shooting. + */ + OFF(0), + + /** + * Maximum value 1 + */ + MAX_1(1), + + /** + * Maximum value 2 + */ + MAX_2(2), + + /** + * Maximum value 3 + */ + MAX_3(3), + + /** + * Maximum value 4 + */ MAX_4(4), /** @@ -2875,19 +3402,34 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? val usingDhcp: Boolean, /** * (optional) IPv4 for IP address + * Do not specify this property when usingDhcp is true. */ val ipAddress: String? = null, /** * (optional) IPv4 for subnet mask + * Do not specify this property when usingDhcp is true. */ val subnetMask: String? = null, /** * (optional) IPv4 for default gateway + * Do not specify this property when usingDhcp is true. */ val defaultGateway: String? = null, + /** + * (optional) IPv4 for Primary DNS server + * Do not specify this property when usingDhcp is true. + */ + var dns1: String? = null, + + /** + * (optional) IPv4 for Secondary DNS server + * Do not specify this property when usingDhcp is true. + */ + var dns2: String? = null, + /** * (optional) refer to _proxy for detail * @@ -2905,6 +3447,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ipAddress = config.ipAddress, subnetMask = config.subnetMask, defaultGateway = config.defaultGateway, + dns1 = config.dns1, + dns2 = config.dns2, proxy = config._proxy?.let { Proxy(info = it) }, ) @@ -2923,6 +3467,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, _proxy = proxy?.toTransferredProxy() ) } @@ -2931,7 +3477,54 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Exposure compensation (EV). */ - enum class ExposureCompensationEnum(val value: Float) { + enum class ExposureCompensationEnum(val value: Float?) { + /** + * Undefined value + */ + UNKNOWN(null), + + /** + * Exposure compensation -4.0 + * + * For RICOH THETA A1 + */ + M4_0(-4.0f), + + /** + * Exposure compensation -3.7 + * + * For RICOH THETA A1 + */ + M3_7(-3.7f), + + /** + * Exposure compensation -3.3 + * + * For RICOH THETA A1 + */ + M3_3(-3.3f), + + /** + * Exposure compensation -3.0 + * + * For RICOH THETA A1 + */ + M3_0(-3.0f), + + /** + * Exposure compensation -2.7 + * + * For RICOH THETA A1 + */ + M2_7(-2.7f), + + /** + * Exposure compensation -2.3 + * + * For RICOH THETA A1 + */ + M2_3(-2.3f), + /** * Exposure compensation -2.0 */ @@ -2995,7 +3588,49 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Exposure compensation 2.0 */ - P2_0(2.0f); + P2_0(2.0f), + + /** + * Exposure compensation 2.3 + * + * For RICOH THETA A1 + */ + P2_3(2.3f), + + /** + * Exposure compensation 2.7 + * + * For RICOH THETA A1 + */ + P2_7(2.7f), + + /** + * Exposure compensation 3.0 + * + * For RICOH THETA A1 + */ + P3_0(3.0f), + + /** + * Exposure compensation 3.3 + * + * For RICOH THETA A1 + */ + P3_3(3.3f), + + /** + * Exposure compensation 3.7 + * + * For RICOH THETA A1 + */ + P3_7(3.7f), + + /** + * Exposure compensation 4.0 + * + * For RICOH THETA A1 + */ + P4_0(4.0f); companion object { /** @@ -3004,8 +3639,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value Exposure compensation value. * @return ExposureCompensationEnum */ - fun get(value: Float): ExposureCompensationEnum? { - return values().firstOrNull { it.value == value } + fun get(value: Float?): ExposureCompensationEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -3013,7 +3648,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Operating time (sec.) of the self-timer. */ - enum class ExposureDelayEnum(val sec: Int) { + enum class ExposureDelayEnum(val sec: Int?) { + /** + * Undefined value + */ + UNKNOWN(null), + /** * Disable self-timer. */ @@ -3081,8 +3721,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param sec Self-timer time. * @return ExposureDelayEnum */ - fun get(sec: Int): ExposureDelayEnum? { - return values().firstOrNull { it.sec == sec } + fun get(sec: Int): ExposureDelayEnum { + return entries.firstOrNull { it.sec == sec } ?: UNKNOWN } } } @@ -3343,7 +3983,19 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? VIDEO_4K_NO_CODEC(FileFormatTypeEnum.MP4, 3840, 1920, null, null), /** - * Video File format. + * Streaming format. + * + * type: mp4 + * size: 1920 x 960 + * codec: H.264/MPEG-4 AVC + * frame rate: 15 + * + * For RICOH THETA A1 + */ + VIDEO_2K_15F(FileFormatTypeEnum.MP4, 1920, 960, "H.264/MPEG-4 AVC", 15), + + /** + * Video File / Streaming format. * * type: mp4 * size: 1920 x 960 @@ -3482,6 +4134,30 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ VIDEO_3_6K_2F(FileFormatTypeEnum.MP4, 3648, 3648, "H.264/MPEG-4 AVC", 2), + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.264/MPEG-4 AVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_4K_2F(FileFormatTypeEnum.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 2), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.264/MPEG-4 AVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_4K_5F(FileFormatTypeEnum.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 5), + /** * Video File format. * @@ -3495,7 +4171,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? VIDEO_4K_10F(FileFormatTypeEnum.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 10), /** - * Video File format. + * Video File / Streaming format. * * type: mp4 * size: 3840 x 1920 @@ -3507,7 +4183,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? VIDEO_4K_15F(FileFormatTypeEnum.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 15), /** - * Video File format. + * Video File / Streaming format. * * type: mp4 * size: 3840 x 1920 @@ -3626,6 +4302,138 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ VIDEO_7K_10F(FileFormatTypeEnum.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 10), + /** + * Video File format. + * + * type: mp4 + * size: 1920 x 960 + * codec: H.265/HEVC + * frame rate: 30 + * + * For RICOH THETA A1 + */ + VIDEO_2K_30F_H265_HEVC(FileFormatTypeEnum.MP4, 1920, 960, "H.265/HEVC", 30), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_4K_2F_H265_HEVC(FileFormatTypeEnum.MP4, 3840, 1920, "H.265/HEVC", 2), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_4K_5F_H265_HEVC(FileFormatTypeEnum.MP4, 3840, 1920, "H.265/HEVC", 5), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_4K_10F_H265_HEVC(FileFormatTypeEnum.MP4, 3840, 1920, "H.265/HEVC", 10), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 30 + * + * For RICOH THETA A1 + */ + VIDEO_4K_30F_H265_HEVC(FileFormatTypeEnum.MP4, 3840, 1920, "H.265/HEVC", 30), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_2F_H265_HEVC(FileFormatTypeEnum.MP4, 5760, 2880, "H.265/HEVC", 2), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_5F_H265_HEVC(FileFormatTypeEnum.MP4, 5760, 2880, "H.265/HEVC", 5), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_10F_H265_HEVC(FileFormatTypeEnum.MP4, 5760, 2880, "H.265/HEVC", 10), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_7K_2F_H265_HEVC(FileFormatTypeEnum.MP4, 7680, 3840, "H.265/HEVC", 2), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_7K_5F_H265_HEVC(FileFormatTypeEnum.MP4, 7680, 3840, "H.265/HEVC", 5), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_7K_10F_H265_HEVC(FileFormatTypeEnum.MP4, 7680, 3840, "H.265/HEVC", 10), + /** * Just used by getMySetting/setMySetting command */ @@ -3815,6 +4623,18 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? */ VIDEO_4K_NO_CODEC(FileFormatEnum.VIDEO_4K_NO_CODEC), + /** + * Video File format. + * + * type: mp4 + * size: 1920 x 960 + * codec: H.264/MPEG-4 AVC + * frame rate: 15 + * + * For RICOH THETA A1 + */ + VIDEO_2K_15F(FileFormatEnum.VIDEO_2K_15F), + /** * Video File format. * @@ -3961,11 +4781,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * type: mp4 * size: 3840 x 1920 * codec: H.264/MPEG-4 AVC - * frame rate: 10 + * frame rate: 2 * - * For RICOH THETA X or later + * For RICOH THETA A1 */ - VIDEO_4K_10F(FileFormatEnum.VIDEO_4K_10F), + VIDEO_4K_2F(FileFormatEnum.VIDEO_4K_2F), /** * Video File format. @@ -3973,11 +4793,35 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * type: mp4 * size: 3840 x 1920 * codec: H.264/MPEG-4 AVC - * frame rate: 15 + * frame rate: 5 * - * For RICOH THETA X or later + * For RICOH THETA A1 */ - VIDEO_4K_15F(FileFormatEnum.VIDEO_4K_15F), + VIDEO_4K_5F(FileFormatEnum.VIDEO_4K_5F), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.264/MPEG-4 AVC + * frame rate: 10 + * + * For RICOH THETA X or later + */ + VIDEO_4K_10F(FileFormatEnum.VIDEO_4K_10F), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.264/MPEG-4 AVC + * frame rate: 15 + * + * For RICOH THETA X or later + */ + VIDEO_4K_15F(FileFormatEnum.VIDEO_4K_15F), /** * Video File format. @@ -4097,7 +4941,140 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * * For RICOH THETA X or later */ - VIDEO_7K_10F(FileFormatEnum.VIDEO_7K_10F); + VIDEO_7K_10F(FileFormatEnum.VIDEO_7K_10F), + + + /** + * Video File format. + * + * type: mp4 + * size: 1920 x 960 + * codec: H.265/HEVC + * frame rate: 30 + * + * For RICOH THETA A1 + */ + VIDEO_2K_30F_H265_HEVC(FileFormatEnum.VIDEO_2K_30F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_4K_2F_H265_HEVC(FileFormatEnum.VIDEO_4K_2F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_4K_5F_H265_HEVC(FileFormatEnum.VIDEO_4K_5F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_4K_10F_H265_HEVC(FileFormatEnum.VIDEO_4K_10F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 3840 x 1920 + * codec: H.265/HEVC + * frame rate: 30 + * + * For RICOH THETA A1 + */ + VIDEO_4K_30F_H265_HEVC(FileFormatEnum.VIDEO_4K_30F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_2F_H265_HEVC(FileFormatEnum.VIDEO_5_7K_2F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_5F_H265_HEVC(FileFormatEnum.VIDEO_5_7K_5F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 5760 x 2880 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_5_7K_10F_H265_HEVC(FileFormatEnum.VIDEO_5_7K_10F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 2 + * + * For RICOH THETA A1 + */ + VIDEO_7K_2F_H265_HEVC(FileFormatEnum.VIDEO_7K_2F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 5 + * + * For RICOH THETA A1 + */ + VIDEO_7K_5F_H265_HEVC(FileFormatEnum.VIDEO_7K_5F_H265_HEVC), + + /** + * Video File format. + * + * type: mp4 + * size: 7680 x 3840 + * codec: H.265/HEVC + * frame rate: 10 + * + * For RICOH THETA A1 + */ + VIDEO_7K_10F_H265_HEVC(FileFormatEnum.VIDEO_7K_10F_H265_HEVC); companion object { /** @@ -4294,6 +5271,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * For RICOH THETA X */ enum class GpsTagRecordingEnum(internal val value: GpsTagRecording) { + /** + * Undefined value + */ + UNKNOWN(GpsTagRecording.UNKNOWN), + /** * Position information assigning ON. */ @@ -4311,8 +5293,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value Turns position information assigning for ThetaApi. * @return GpsTagRecordingEnum */ - internal fun get(value: GpsTagRecording): GpsTagRecordingEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: GpsTagRecording): GpsTagRecordingEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -4853,10 +5835,145 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } } + /** + * Built-in microphone noise reduction. + * + * 1) Video: Ignore changes during recording + * 2) Live Streaming: Ignore changes during delivery + */ + enum class MicrophoneNoiseReductionEnum(internal val value: MicrophoneNoiseReduction) { + /** + * Undefined value + */ + UNKNOWN(MicrophoneNoiseReduction.UNKNOWN), + + /** + * ON + */ + ON(MicrophoneNoiseReduction.ON), + + /** + * OFF + */ + OFF(MicrophoneNoiseReduction.OFF); + + companion object { + /** + * Convert MicrophoneNoiseReduction to MicrophoneNoiseReductionEnum + * + * @param value MicrophoneNoiseReduction. + * @return MicrophoneNoiseReductionEnum + */ + internal fun get(value: MicrophoneNoiseReduction): MicrophoneNoiseReductionEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + + /** + * Mobile Network Settings + */ + data class MobileNetworkSetting( + /** + * roaming + */ + val roaming: RoamingEnum?, + /** + * plan + */ + val plan: PlanEnum?, + ) { + internal constructor(setting: com.ricoh360.thetaclient.transferred.MobileNetworkSetting) : this( + roaming = setting.roaming?.let { RoamingEnum.get(value = it) }, + plan = setting.plan?.let { PlanEnum.get(value = it) } + ) + + /** + * Convert MobileNetworkSetting to transferred.MobileNetworkSetting + * + * @return transferred.MobileNetworkSetting + */ + internal fun toTransferredMobileNetworkSetting(): com.ricoh360.thetaclient.transferred.MobileNetworkSetting { + return MobileNetworkSetting( + roaming = roaming?.value, + plan = plan?.value + ) + } + } + + /** + * Roaming of MobileNetworkSetting + */ + enum class RoamingEnum(internal val value: Roaming) { + /** + * Undefined value + */ + UNKNOWN(Roaming.UNKNOWN), + + /** + * OFF + */ + OFF(Roaming.OFF), + + /** + * ON + */ + ON(Roaming.ON); + + companion object { + /** + * Convert Roaming to RoamingEnum + * + * @param value Roaming. + * @return RoamingEnum + */ + internal fun get(value: Roaming): RoamingEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + + /** + * Plan of MobileNetworkSetting + */ + enum class PlanEnum(internal val value: Plan) { + /** + * Undefined value + */ + UNKNOWN(Plan.UNKNOWN), + + /** + * Communicate with APN settings for plan-D + */ + SORACOM(Plan.SORACOM), + + /** + * Communicate with APN settings for plan-DU + */ + SORACOM_PLAN_DU(Plan.SORACOM_PLAN_DU); + + companion object { + /** + * Convert Plan to PlanEnum + * + * @param value Plan. + * @return PlanEnum + */ + internal fun get(value: Plan): PlanEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + /** * Network type supported by Theta V, Z1 and X. */ enum class NetworkTypeEnum(internal val value: NetworkType) { + /** + * Undefined value + */ + UNKNOWN(NetworkType.UNKNOWN), + /** * Direct mode */ @@ -4875,7 +5992,56 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Network is off. This value can be gotten only by plugin */ - OFF(NetworkType.OFF); + OFF(NetworkType.OFF), + + /** + * LTE plan-D + * + * For RICOH THETA A1 + */ + LTE_D(NetworkType.LTE_D), + + /** + * LTE plan-DU + * + * For RICOH THETA A1 + */ + LTE_DU(NetworkType.LTE_DU), + + /** + * LTE plan01s + * + * For RICOH THETA A1 + */ + LTE_01S(NetworkType.LTE_01S), + + /** + * LTE planX3 + * + * For RICOH THETA A1 + */ + LTE_X3(NetworkType.LTE_X3), + + /** + * LTE planP1 + * + * For RICOH THETA A1 + */ + LTE_P1(NetworkType.LTE_P1), + + /** + * LTE plan-K2 + * + * For RICOH THETA A1 + */ + LTE_K2(NetworkType.LTE_K2), + + /** + * LTE plan-K + * + * For RICOH THETA A1 + */ + LTE_K(NetworkType.LTE_K); companion object { /** @@ -4884,8 +6050,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value Network type. * @return NetworkTypeEnum */ - internal fun get(value: NetworkType): NetworkTypeEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: NetworkType?): NetworkTypeEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -4916,58 +6082,159 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? other as OffDelaySec - if (sec != other.sec) return false + return sec == other.sec + } - return true + override fun hashCode(): Int { + return sec } + } + + /** + * Length of standby time before the camera automatically powers OFF. + * + * For RICOH THETA V or later + */ + enum class OffDelayEnum(override val sec: Int) : OffDelay { + /** + * Do not turn power off. + */ + DISABLE(65535), + + /** + * Power off after 5 minutes.(300sec) + */ + OFF_DELAY_5M(300), + + /** + * Power off after 10 minutes.(600sec) + */ + OFF_DELAY_10M(600), + + /** + * Power off after 15 minutes.(900sec) + */ + OFF_DELAY_15M(900), + + /** + * Power off after 30 minutes.(1,800sec) + */ + OFF_DELAY_30M(1800); + + companion object { + /** + * Convert second to OffDelay + * + * @return [OffDelayEnum] or [OffDelay] + */ + fun get(sec: Int): OffDelay { + if (sec == 0) { + return DISABLE + } + return values().firstOrNull { it.sec == sec } ?: OffDelaySec(sec) + } + } + } + + /** + * Auto power off time with USB power supply. + * + * Use in [OffDelayUsbEnum] or [OffDelayUsbSec] + */ + interface OffDelayUsb { + val sec: Int + } + + /** + * Auto power off time with USB power supply. + * + * For RICOH THETA A1 + * 0, or a value that is a multiple of 60 out of 600 or more and 2592000 or less (unit: second), or 65535. + * Return 0 when 65535 is set and obtained (Do not turn power OFF). + */ + class OffDelayUsbSec(override val sec: Int) : OffDelayUsb { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (other == null || this::class != other::class) return false + + other as OffDelayUsbSec + + return sec == other.sec + } + + override fun hashCode(): Int { + return sec + } + } + + /** + * Auto power off time with USB power supply. + * + * For RICOH THETA A1 + */ + enum class OffDelayUsbEnum(override val sec: Int) : OffDelayUsb { + /** + * Do not turn power off. + */ + DISABLE(65535), + + /** + * Power off after 10 minutes.(600sec) + */ + OFF_DELAY_10M(600), + + /** + * Power off after 1 hour.(3600sec) + */ + OFF_DELAY_1H(3600), + + /** + * Power off after 2 hour.(7200sec) + */ + OFF_DELAY_2H(7200), - override fun hashCode(): Int { - return sec - } - } + /** + * Power off after 4 hour.(14400sec) + */ + OFF_DELAY_4H(14400), - /** - * Length of standby time before the camera automatically powers OFF. - * - * For RICOH THETA V or later - */ - enum class OffDelayEnum(override val sec: Int) : OffDelay { /** - * Do not turn power off. + * Power off after 8 hour.(28800sec) */ - DISABLE(65535), + OFF_DELAY_8H(28800), /** - * Power off after 5 minutes.(300sec) + * Power off after 12 hour.(43200sec) */ - OFF_DELAY_5M(300), + OFF_DELAY_12H(43200), /** - * Power off after 10 minutes.(600sec) + * Power off after 18 hour.(64800sec) */ - OFF_DELAY_10M(600), + OFF_DELAY_18H(64800), /** - * Power off after 15 minutes.(900sec) + * Power off after 24 hour.(86400sec) */ - OFF_DELAY_15M(900), + OFF_DELAY_24H(86400), /** - * Power off after 30 minutes.(1,800sec) + * Power off after 2 days.(172800sec) */ - OFF_DELAY_30M(1800); + OFF_DELAY_2D(172800), + ; companion object { /** - * Convert second to OffDelay + * Convert second to OffDelayUsb * - * @return [OffDelayEnum] or [OffDelay] + * @return [OffDelayUsbEnum] or [OffDelayUsb] */ - fun get(sec: Int): OffDelay { + fun get(sec: Int): OffDelayUsb { if (sec == 0) { return DISABLE } - return values().firstOrNull { it.sec == sec } ?: OffDelaySec(sec) + return entries.firstOrNull { it.sec == sec } ?: OffDelayUsbSec(sec) } } } @@ -5058,14 +6325,17 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * Format of live view */ enum class PreviewFormatEnum(val width: Int, val height: Int, val framerate: Int) { - W1024_H512_F30(1024, 512, 30), // For Theta X, Z1, V and SC2 - W1024_H512_F15(1024, 512, 15), // For Theta X. This value can't set. - W512_H512_F30(512, 512, 30), // For Theta X + UNKNOWN(0, 0, 0), // Undefined value + W1920_H960_F30(1920, 960, 30), // For Theta X firmware v2.71.1 or later W1920_H960_F8(1920, 960, 8), // For Theta Z1 and V + W1024_H512_F30(1024, 512, 30), // For Theta A1, X, Z1, V, SC2 + W1024_H512_F15(1024, 512, 15), // For Theta X. This value can't set. + W1024_H512_F10(1024, 512, 10), // For Theta A1 W1024_H512_F8(1024, 512, 8), // For Theta Z1 and V W640_H320_F30(640, 320, 30), // For Theta Z1 and V - W640_H320_F8(640, 320, 8), // For Theta Z1 and V W640_H320_F10(640, 320, 10), // For Theta S and SC + W640_H320_F8(640, 320, 8), // For Theta Z1 and V + W512_H512_F30(512, 512, 30), // For Theta X W3840_H1920_F30(3840, 1920, 30); // For Theta X /** @@ -5079,12 +6349,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Convert PreviewFormat to PreviewFormatEnum */ - internal fun get(value: PreviewFormat): PreviewFormatEnum? { - return PreviewFormatEnum.values().firstOrNull { - it.height == value.height && + internal fun get(value: PreviewFormat?): PreviewFormatEnum { + return entries.firstOrNull { + it.height == value?.height && it.width == value.width && it.framerate == value.framerate - } + } ?: UNKNOWN } } } @@ -5654,9 +6924,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? other as SleepDelaySec - if (sec != other.sec) return false - - return true + return sec == other.sec } override fun hashCode(): Int { @@ -5874,6 +7142,41 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } } + /** + * USB connection of the camera. + * + * Default value is "MTP". + * After switching the setting value, reconnect the camera to USB to enable it. + */ + enum class UsbConnectionEnum(internal val value: UsbConnection) { + /** + * Undefined value + */ + UNKNOWN(UsbConnection.UNKNOWN), + + /** + * MTP + */ + MTP(UsbConnection.MTP), + + /** + * MSC + */ + MSC(UsbConnection.MSC); + + companion object { + /** + * Convert UsbConnection to UsbConnectionEnum + * + * @param value UsbConnection. + * @return UsbConnectionEnum + */ + internal fun get(value: UsbConnection): UsbConnectionEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + /** * Video Stitching * @@ -5934,9 +7237,9 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? val yaw: Float ) { internal constructor(rotation: com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation) : this( - pitch = rotation.pitch ?: 0f, - roll = rotation.roll ?: 0f, - yaw = rotation.yaw ?: 0f + pitch = rotation.pitch.toFloatOrNull() ?: 0.0f, + roll = rotation.roll.toFloatOrNull() ?: 0.0f, + yaw = rotation.yaw.toFloatOrNull() ?: 0.0f ) /** @@ -5945,14 +7248,40 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @return transferred.TopBottomCorrectionRotation */ internal fun toTransferredTopBottomCorrectionRotation(): com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation { - return com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation( - pitch = pitch, - roll = roll, - yaw = yaw + return TopBottomCorrectionRotation( + pitch = pitch.toString(), + roll = roll.toString(), + yaw = yaw.toString() ) } } + /** + * Supported TopBottomCorrectionRotation + */ + data class TopBottomCorrectionRotationSupport( + /** + * Supported pitch + */ + val pitch: ValueRange, + + /** + * Supported roll + */ + val roll: ValueRange, + + /** + * Supported yaw + */ + val yaw: ValueRange + ) { + internal constructor(rotation: com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotationSupport) : this( + pitch = ValueRange(rotation.pitch), + roll = ValueRange(rotation.roll), + yaw = ValueRange(rotation.yaw) + ) + } + /** * Visibility Reduction * @@ -6116,10 +7445,47 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } } + /** + * Configure SISO or MIMO for Wireless LAN. + */ + enum class WlanAntennaConfigEnum(internal val value: WlanAntennaConfig) { + /** + * Undefined value + */ + UNKNOWN(WlanAntennaConfig.UNKNOWN), + + /** + * SISO + */ + SISO(WlanAntennaConfig.SISO), + + /** + * MIMO + */ + MIMO(WlanAntennaConfig.MIMO); + + companion object { + /** + * Convert WlanAntennaConfig to WlanAntennaConfigEnum + * + * @param value WlanAntennaConfig. + * @return WlanAntennaConfigEnum + */ + internal fun get(value: WlanAntennaConfig): WlanAntennaConfigEnum? { + return entries.firstOrNull { it.value == value } + } + } + } + /** * Wireless LAN frequency of the camera supported by Theta V, Z1 and X. */ enum class WlanFrequencyEnum(internal val value: WlanFrequency) { + /** + * Undefined value + */ + UNKNOWN(WlanFrequency.UNKNOWN), + /** * 2.4GHz */ @@ -6128,7 +7494,21 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * 5GHz */ - GHZ_5(WlanFrequency.GHZ_5); + GHZ_5(WlanFrequency.GHZ_5), + + /** + * 5.2GHz + * + * For RICOH THETA A1 + */ + GHZ_5_2(WlanFrequency.GHZ_5_2), + + /** + * 5.8GHz + * + * For RICOH THETA A1 + */ + GHZ_5_8(WlanFrequency.GHZ_5_8); companion object { /** @@ -6137,13 +7517,52 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value wlan frequency * @return WlanFrequencyEnum */ - internal fun get(value: WlanFrequency): WlanFrequencyEnum? { - return values().firstOrNull { it.value == value } + internal fun get(value: WlanFrequency?): WlanFrequencyEnum { + return entries.firstOrNull { it.value == value } ?: UNKNOWN } } } + /** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + */ + data class WlanFrequencyClMode( + /** + * 2.4GHz + */ + val enable2_4: Boolean, + + /** + * 5.2GHz + */ + val enable5_2: Boolean, + + /** + * 5.8GHz + */ + val enable5_8: Boolean, + ) { + internal constructor(value: com.ricoh360.thetaclient.transferred.WlanFrequencyClMode) : this( + enable2_4 = value.enable2_4, + enable5_2 = value.enable5_2, + enable5_8 = value.enable5_8, + ) + + /** + * Convert WlanFrequencyClMode to transferred.WlanFrequencyClMode. for ThetaApi. + * + * @return transferred.WlanFrequencyClMode + */ + internal fun toTransferred(): com.ricoh360.thetaclient.transferred.WlanFrequencyClMode { + return com.ricoh360.thetaclient.transferred.WlanFrequencyClMode( + enable2_4 = enable2_4, + enable5_2 = enable5_2, + enable5_8 = enable5_8, + ) + } + } + /** * File type in Theta. */ @@ -6226,11 +7645,21 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Video codec */ - enum class CodecEnum(val value: String) { + enum class CodecEnum(val value: String?) { + /** + * Undefined value + */ + UNKNOWN(null), + /** * codec H.264/MPEG-4 AVC */ H264MP4AVC("H.264/MPEG-4 AVC"), + + /** + * codec H.265/HEVC + */ + H265HEVC("H.265/HEVC"), ; companion object { @@ -6240,8 +7669,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @param value codec * @return CodecEnum */ - fun get(value: String): CodecEnum? { - return CodecEnum.values().firstOrNull { it.value == value } + fun get(value: String?): CodecEnum { + return CodecEnum.values().firstOrNull { it.value == value } ?: UNKNOWN } } } @@ -6314,7 +7743,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? cameraFileInfo._recordTime, cameraFileInfo.isProcessed, cameraFileInfo.previewUrl, - cameraFileInfo._codec?.let { CodecEnum.get(it) }, + codec = cameraFileInfo._codec?.let { CodecEnum.get(it) }, cameraFileInfo._projectionType?.let { ProjectionTypeEnum.get(it) }, cameraFileInfo._continuousShootingGroupId, cameraFileInfo._frameRate, @@ -6345,10 +7774,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? try { return ThetaApi.callGetLivePreviewCommand(endpoint) } catch (e: PreviewClientException) { + logger.log("PreviewClient: $e") throw ThetaWebApiException(e.toString()) } catch (e: CancellationException) { throw e // Coroutine was cancelled. } catch (e: Exception) { + logger.log("PreviewClient: $e") throw NotConnectedException(e.toString()) } } @@ -6369,10 +7800,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? return@callGetLivePreviewCommand frameHandler(it) } } catch (e: PreviewClientException) { + logger.log("PreviewClient: $e") throw ThetaWebApiException(e.toString()) } catch (e: CancellationException) { // Preview coroutine was cancelled. No need to do anything. } catch (e: Exception) { + logger.log("PreviewClient: $e") throw NotConnectedException(e.toString()) } } @@ -6404,6 +7837,15 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? return TimeShiftCapture.Builder(endpoint, cameraModel) } + /** + * Get TimeShiftCapture.Builder for time-shift manual. + * + * @return TimeShiftManualCapture.Builder + */ + fun getTimeShiftManualCaptureBuilder(): TimeShiftManualCapture.Builder { + return TimeShiftManualCapture.Builder(endpoint) + } + /** * Get LimitlessIntervalCapture.Builder for capture video. * @@ -6716,6 +8158,12 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * Performing burst shooting */ BURST_SHOOTING, + + /** + * In the case of time-lag shooting by manual lens, + * set while waiting for the second shot to be taken after the first shot is completed. + */ + TIME_SHIFT_SHOOTING_IDLE, ; companion object { @@ -6737,6 +8185,7 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? CaptureStatus.CONTINUOUS_SHOOTING -> CONTINUOUS_SHOOTING CaptureStatus.RETROSPECTIVE_IMAGE_RECORDING -> RETROSPECTIVE_IMAGE_RECORDING CaptureStatus.BURST_SHOOTING -> BURST_SHOOTING + CaptureStatus.TIME_SHIFT_SHOOTING_IDLE -> TIME_SHIFT_SHOOTING_IDLE } } } @@ -6999,6 +8448,55 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } } + /** + * WLAN frequency of the access point. + */ + enum class WlanFrequencyAccessInfoEnum(internal val value: WlanFrequencyAccessInfo) { + /** + * Undefined value + */ + UNKNOWN(WlanFrequencyAccessInfo.UNKNOWN), + + /** + * 2.4GHz + */ + GHZ_2_4(WlanFrequencyAccessInfo.GHZ_2_4), + + /** + * 5.2GHz + */ + GHZ_5_2(WlanFrequencyAccessInfo.GHZ_5_2), + + /** + * 5.8GHz + */ + GHZ_5_8(WlanFrequencyAccessInfo.GHZ_5_8), + + /** + * Initial value + */ + INITIAL_VALUE(WlanFrequencyAccessInfo.INITIAL_VALUE); + + companion object { + /** + * Convert WlanFrequencyAccessInfo to WlanFrequencyAccessInfoEnum + * + * @param value wlan frequency + * @return WlanFrequencyAccessInfoEnum + */ + internal fun get(value: WlanFrequencyAccessInfo): WlanFrequencyAccessInfoEnum { + return when (value) { + WlanFrequencyAccessInfo.UNKNOWN -> UNKNOWN + WlanFrequencyAccessInfo.GHZ_2_4 -> GHZ_2_4 + WlanFrequencyAccessInfo.GHZ_5_2 -> GHZ_5_2 + WlanFrequencyAccessInfo.GHZ_5_8 -> GHZ_5_8 + WlanFrequencyAccessInfo.INITIAL_VALUE -> INITIAL_VALUE + } + } + } + + } + /** * Get metadata of a still image * @@ -7077,6 +8575,35 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ) } + /** + * Turn off/on (reboot) the camera. + * Supported models are THETA A1 only. + * + * Error when connecting in CL mode for Japan-bound models only + * + * @exception ThetaWebApiException If an error occurs in THETA. + * @exception NotConnectedException + */ + @Throws(Throwable::class) + suspend fun reboot() { + if (cameraModel != ThetaModel.THETA_A1) { + throw ThetaWebApiException("disabledCommand") + } + try { + ThetaApi.callRebootCommand(endpoint).error?.let { + throw ThetaWebApiException(it.message) + } + } catch (e: JsonConvertException) { + throw ThetaWebApiException(e.message ?: e.toString()) + } catch (e: ResponseException) { + throw ThetaWebApiException.create(e) + } catch (e: ThetaWebApiException) { + throw e + } catch (e: Exception) { + throw NotConnectedException(e.message ?: e.toString()) + } + } + /** * Reset all device settings and capture settings. * After reset, the camera will be restarted. @@ -7282,15 +8809,17 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Set access point. IP address is set statically. * - * @param ssid SSID of the access point. - * @param ssidStealth True if SSID stealth is enabled. - * @param authMode Authentication mode. - * @param password Password. If [authMode] is "NONE", pass empty String. - * @param ipAddressAllocation [IpAddressAllocation] IP address allocation. DYNAMIC or STATIC. - * @param connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) - * @param ipAddress IP address assigns to Theta. If DYNAMIC ip is null. - * @param subnetMask Subnet mask. If DYNAMIC ip is null. - * @param defaultGateway Default gateway. If DYNAMIC ip is null. + * @param ssid SSID + * @param ssidStealth SSID stealth. Default is false. + * @param authMode [AuthModeEnum] Authentication mode. + * @param password Password. This can be set when security is not "none". + * @param ipAddressAllocation [IpAddressAllocation] IP address allocation. DYNAMIC or STATIC. Default is DYNAMIC. + * @param connectionPriority Connection priority (1 to 5). Default is 1. Theta X fixed to 1 (The access point registered later has a higher priority.) + * @param ipAddress IP address assigned to camera. This setting must be set when ipAddressAllocation is STATIC. + * @param subnetMask Subnet mask. This setting must be set when ipAddressAllocation is STATIC. + * @param defaultGateway Default gateway. This setting must be set when ipAddressAllocation is STATIC. + * @param dns1 Primary DNS server. This setting must be set when ipAddressAllocation is STATIC. + * @param dns2 Secondary DNS server. This setting must be set when ipAddressAllocation is STATIC. * @param proxy Proxy information to be used for the access point. * @exception ThetaWebApiException If an error occurs in THETA. * @exception NotConnectedException @@ -7298,14 +8827,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? @Throws(Throwable::class) internal suspend fun setAccessPoint( ssid: String, - ssidStealth: Boolean = false, + ssidStealth: Boolean? = null, authMode: AuthModeEnum = AuthModeEnum.NONE, password: String? = null, - connectionPriority: Int = 1, + connectionPriority: Int? = null, ipAddressAllocation: IpAddressAllocation, ipAddress: String? = null, subnetMask: String? = null, defaultGateway: String? = null, + dns1: String? = null, + dns2: String? = null, proxy: Proxy? = null, ) { val params = SetAccessPointParams( @@ -7318,6 +8849,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, proxy = proxy?.toTransferredProxy() ) try { @@ -7338,11 +8871,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Set access point. IP address is set dynamically. * - * @param ssid SSID of the access point. - * @param ssidStealth True if SSID stealth is enabled. - * @param authMode Authentication mode. - * @param password Password. If [authMode] is "NONE", pass empty String. - * @param connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) + * @param ssid SSID + * @param ssidStealth SSID stealth. Default is false. + * @param authMode [AuthModeEnum] Authentication mode. + * @param password Password. This can be set when security is not "none". + * @param connectionPriority Connection priority (1 to 5). Default is 1. Theta X fixed to 1 (The access point registered later has a higher priority.) * @param proxy Proxy information to be used for the access point. * @exception ThetaWebApiException If an error occurs in THETA. * @exception NotConnectedException @@ -7350,10 +8883,10 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? @Throws(Throwable::class) suspend fun setAccessPointDynamically( ssid: String, - ssidStealth: Boolean = false, + ssidStealth: Boolean? = null, authMode: AuthModeEnum = AuthModeEnum.NONE, - password: String = "", - connectionPriority: Int = 1, + password: String? = null, + connectionPriority: Int? = null, proxy: Proxy? = null, ) { setAccessPoint( @@ -7370,14 +8903,16 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? /** * Set access point. IP address is set statically. * - * @param ssid SSID of the access point. - * @param ssidStealth True if SSID stealth is enabled. - * @param authMode Authentication mode. - * @param password Password. If [authMode] is "NONE", pass empty String. - * @param connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) - * @param ipAddress IP address assigns to Theta. + * @param ssid SSID + * @param ssidStealth SSID stealth. Default is false. + * @param authMode [AuthModeEnum] Authentication mode. + * @param password Password. This can be set when security is not "none". + * @param connectionPriority Connection priority (1 to 5). Default is 1. Theta X fixed to 1 (The access point registered later has a higher priority.) + * @param ipAddress IP address assigned to camera. * @param subnetMask Subnet mask. * @param defaultGateway Default gateway. + * @param dns1 Primary DNS server. + * @param dns2 Secondary DNS server. * @param proxy Proxy information to be used for the access point. * @exception ThetaWebApiException If an error occurs in THETA. * @exception NotConnectedException @@ -7385,13 +8920,15 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? @Throws(Throwable::class) suspend fun setAccessPointStatically( ssid: String, - ssidStealth: Boolean = false, + ssidStealth: Boolean? = null, authMode: AuthModeEnum = AuthModeEnum.NONE, password: String? = null, - connectionPriority: Int = 1, + connectionPriority: Int? = null, ipAddress: String, subnetMask: String, defaultGateway: String, + dns1: String? = null, + dns2: String? = null, proxy: Proxy? = null, ) { setAccessPoint( @@ -7404,6 +8941,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, proxy = proxy ) } @@ -7434,17 +8973,123 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? } } + /** + * Connected network information. + * + * @property ssid SSID of the wireless LAN access point that THETA connects to. The initial value is ""(empty string). + * @property ipAddress IP address of access point. The initial value is ""(empty string). + * @property subnetMask subnet mask of access point. The initial value is ""(empty string). + * @property defaultGateway default gateway of access point. The initial value is ""(empty string). + * @property dns1 Primary DNS server + * @property dns2 Secondary DNS server + * @property proxyURL proxy URL of access point. The initial value is ""(empty string). + * @property frequency Radio frequency. The initial enum value is INITIAL_VALUE. + * @property wlanSignalStrength WLAN signal strength. The initial value is 0. + * @property wlanSignalLevel WLAN signal level. The initial value is 0. + * @property lteSignalStrength LTE signal strength. The initial value is 0. + * @property lteSignalLevel LTE signal level. The initial value is 0. + */ + data class AccessInfo( + val ssid: String, + val ipAddress: String, + val subnetMask: String, + val defaultGateway: String, + val dns1: String?, + val dns2: String?, + val proxyURL: String, + val frequency: WlanFrequencyAccessInfoEnum, + val wlanSignalStrength: Int, + val wlanSignalLevel: Int, + val lteSignalStrength: Int, + val lteSignalLevel: Int, + val dhcpLeaseAddress: List?, + ) { + internal constructor(accessInfo: com.ricoh360.thetaclient.transferred.AccessInfo) : this( + ssid = accessInfo.ssid, + ipAddress = accessInfo.ipAddress, + subnetMask = accessInfo.subnetMask, + defaultGateway = accessInfo.defaultGateway, + dns1 = accessInfo.dns1, + dns2 = accessInfo.dns2, + proxyURL = accessInfo.proxyURL, + frequency = accessInfo.frequency.let { WlanFrequencyAccessInfoEnum.get(it) }, + wlanSignalStrength = accessInfo.wlanSignalStrength, + wlanSignalLevel = accessInfo.wlanSignalLevel, + lteSignalStrength = accessInfo.lteSignalStrength, + lteSignalLevel = accessInfo.lteSignalLevel, + dhcpLeaseAddress = accessInfo.dhcpLeaseAddress?.let { list -> + list.map { DhcpLeaseAddress(it) } + } + ) + + internal fun toTransferredAccessInfo(): com.ricoh360.thetaclient.transferred.AccessInfo { + return AccessInfo( + ssid = ssid, + ipAddress = ipAddress, + subnetMask = subnetMask, + defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, + proxyURL = proxyURL, + frequency = frequency.value, + wlanSignalStrength = wlanSignalStrength, + wlanSignalLevel = wlanSignalLevel, + lteSignalStrength = lteSignalStrength, + lteSignalLevel = lteSignalLevel, + dhcpLeaseAddress = dhcpLeaseAddress?.let { list -> + list.map { it.toTransferred() } + } + ) + } + } + + /** + * client devices information + */ + data class DhcpLeaseAddress( + /** + * IP address of client device + */ + val ipAddress: String, + + /** + * MAC address of client device + */ + val macAddress: String, + + /** + * host name of client device + */ + val hostName: String, + ) { + internal constructor(value: com.ricoh360.thetaclient.transferred.DhcpLeaseAddress) : this( + ipAddress = value.ipAddress, + macAddress = value.macAddress, + hostName = value.hostName, + ) + + internal fun toTransferred(): com.ricoh360.thetaclient.transferred.DhcpLeaseAddress { + return com.ricoh360.thetaclient.transferred.DhcpLeaseAddress( + ipAddress = ipAddress, + macAddress = macAddress, + hostName = hostName, + ) + } + } + /** * Access point information. * - * @property ssid SSID of the access point. - * @property ssidStealth True if SSID stealth is enabled. + * @property ssid SSID + * @property ssidStealth SSID stealth. Default is false. * @property authMode Authentication mode. - * @property connectionPriority Connection priority 1 to 5. Theta X fixes to 1 (The access point registered later has a higher priority.) + * @property connectionPriority Connection priority (1 to 5). Default is 1. Theta X fixes to 1 (The access point registered later has a higher priority.) * @property usingDhcp Using DHCP or not. This can be acquired when SSID is registered as an enable access point. * @property ipAddress IP address assigned to camera. This setting can be acquired when “usingDhcp” is false. * @property subnetMask Subnet Mask. This setting can be acquired when “usingDhcp” is false. * @property defaultGateway Default Gateway. This setting can be acquired when “usingDhcp” is false. + * @property dns1 Primary DNS server. This setting can be acquired when “usingDhcp” is false. + * @property dns2 Secondary DNS server. This setting can be acquired when “usingDhcp” is false. * @property proxy Proxy information to be used for the access point. */ data class AccessPoint( @@ -7456,6 +9101,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? val ipAddress: String?, val subnetMask: String?, val defaultGateway: String?, + val dns1: String?, + val dns2: String?, val proxy: Proxy?, ) { internal constructor(accessPoint: com.ricoh360.thetaclient.transferred.AccessPoint) : this( @@ -7467,6 +9114,8 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? ipAddress = accessPoint.ipAddress, subnetMask = accessPoint.subnetMask, defaultGateway = accessPoint.defaultGateway, + dns1 = accessPoint.dns1, + dns2 = accessPoint.dns2, proxy = accessPoint.proxy?.let { Proxy(info = it) }, ) } @@ -7477,6 +9126,11 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * @property value AuthenticationMode. */ enum class AuthModeEnum(internal val value: AuthenticationMode) { + /** + * Undefined value + */ + UNKNOWN(AuthenticationMode.UNKNOWN), + /** * Authentication mode * none @@ -7493,7 +9147,13 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? * Authentication mode * WPA/WPA2 PSK */ - WPA(AuthenticationMode.WPA_WPA2_PSK); + WPA(AuthenticationMode.WPA_WPA2_PSK), + + /** + * Authentication mode + * WPA3-SAE + */ + WPA3(AuthenticationMode.WPA3_SAE); companion object { /** @@ -7915,6 +9575,65 @@ class ThetaRepository internal constructor(val endpoint: String, config: Config? fun getEventWebSocket(): EventWebSocket { return EventWebSocket(endpoint) } + + data class ValueRange( + /** + * maximum value + */ + val max: T, + + /** + * minimum value + */ + val min: T, + + /** + * step size + */ + val stepSize: T + ) { + @Suppress("UNCHECKED_CAST") + internal constructor(support: ColorTemperatureSupport) : this( + max = support.maxTemperature as T, + min = support.minTemperature as T, + stepSize = support.stepSize as T + ) + + @Suppress("UNCHECKED_CAST") + internal constructor(support: CompositeShootingOutputIntervalSupport) : this( + max = support.maxInterval as T, + min = support.minInterval as T, + stepSize = support.stepSize as T + ) + + @Suppress("UNCHECKED_CAST") + internal constructor(support: CompositeShootingTimeSupport) : this( + max = support.maxTime as T, + min = support.minTime as T, + stepSize = support.stepSize as T + ) + + @Suppress("UNCHECKED_CAST") + internal constructor(support: PitchSupport) : this( + max = support.maxPitch as T, + min = support.minPitch as T, + stepSize = support.stepSize as T + ) + + @Suppress("UNCHECKED_CAST") + internal constructor(support: RollSupport) : this( + max = support.maxRoll as T, + min = support.minRoll as T, + stepSize = support.stepSize as T + ) + + @Suppress("UNCHECKED_CAST") + internal constructor(support: YawSupport) : this( + max = support.maxYaw as T, + min = support.minYaw as T, + stepSize = support.stepSize as T + ) + } } diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Util.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Util.kt index ab919fc788b..4d8a31e1590 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Util.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/Util.kt @@ -46,3 +46,13 @@ internal suspend fun syncExecutor( } return deferred.await() } + +/** + * Set up a log listener for THETA API calls + * + * @param listener Called when there is a THETA API request and response; if null, unregister. + */ +@Throws(Throwable::class) +fun setApiLogListener(listener: ((message: String) -> Unit)?) { + ThetaApi.apiLogListener = listener +} diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/BurstCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/BurstCapture.kt index 42a0adf7f56..c20a97361b6 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/BurstCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/BurstCapture.kt @@ -190,6 +190,7 @@ class BurstCapture private constructor( callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = error.message)) return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) startCaptureResponse.id?.let { monitorCommandStatus(it, callback) diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/Capture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/Capture.kt index 9862b1d2e00..998af990de3 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/Capture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/Capture.kt @@ -12,6 +12,11 @@ import io.ktor.client.statement.HttpResponse * Identify the self-timer during capture */ enum class CapturingStatusEnum { + /** + * The process is starting + */ + STARTING, + /** * Capture in progress */ @@ -21,6 +26,22 @@ enum class CapturingStatusEnum { * Self-timer in progress */ SELF_TIMER_COUNTDOWN, + + /** + * Performing timeShift shooting + */ + TIME_SHIFT_SHOOTING, + + /** + * Performing second capture of manual timeShift shooting + */ + TIME_SHIFT_SHOOTING_SECOND, + + /** + * In the case of time-lag shooting by manual lens, + * set while waiting for the second shot to be taken after the first shot is completed. + */ + TIME_SHIFT_SHOOTING_IDLE, } /* diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCapture.kt index 9af3014290f..b02fb6c7515 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCapture.kt @@ -3,10 +3,22 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* -import kotlinx.coroutines.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.CommandApiResponse +import com.ricoh360.thetaclient.transferred.CommandState +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import com.ricoh360.thetaclient.transferred.StartCaptureResponse +import com.ricoh360.thetaclient.transferred.StatusApiParams +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch /* * CompositeIntervalCapture @@ -159,6 +171,7 @@ class CompositeIntervalCapture private constructor( callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = error.message)) return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) startCaptureResponse.id?.let { monitorCommandStatus(it, callback) diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ContinuousCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ContinuousCapture.kt index 673d660574b..f5f6ce97746 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ContinuousCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ContinuousCapture.kt @@ -193,6 +193,7 @@ class ContinuousCapture private constructor( callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = error.message)) return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) startCaptureResponse.id?.let { monitorCommandStatus(it, callback) diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCapture.kt index a89c38931e6..34793165d87 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCapture.kt @@ -3,9 +3,15 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch @@ -150,7 +156,9 @@ class LimitlessIntervalCapture private constructor( } ThetaApi.callStartCaptureCommand(endpoint, params).error?.let { callOnCaptureFailed(ThetaRepository.ThetaWebApiException(it.message)) + return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) } catch (e: JsonConvertException) { callOnCaptureFailed(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) } catch (e: ResponseException) { diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCapture.kt index 5c7328b94ec..2411013b365 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCapture.kt @@ -267,6 +267,7 @@ class MultiBracketCapture private constructor( ) return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) when (cameraModel) { ThetaRepository.ThetaModel.THETA_SC2, ThetaRepository.ThetaModel.THETA_SC2_B -> { diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/PhotoCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/PhotoCapture.kt index 595b226efa6..9d109ff2193 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/PhotoCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/PhotoCapture.kt @@ -3,10 +3,20 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* -import kotlinx.coroutines.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.CommandState +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.StatusApiParams +import com.ricoh360.thetaclient.transferred.TakePictureResponse +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch /* * PhotoCapture @@ -18,7 +28,7 @@ class PhotoCapture private constructor( private val endpoint: String, options: Options, private val checkStatusCommandInterval: Long - ) : PhotoCaptureBase(options) { +) : PhotoCaptureBase(options) { private val scope = CoroutineScope(Dispatchers.Default) @@ -97,6 +107,9 @@ class PhotoCapture private constructor( ) try { takePictureResponse = ThetaApi.callTakePictureCommand(endpoint = endpoint) + takePictureResponse.error ?: let { + callback.onCapturing(CapturingStatusEnum.STARTING) + } monitor.start() val id = takePictureResponse.id while (takePictureResponse.state == CommandState.IN_PROGRESS) { diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCapture.kt index a6fbdd8203d..184980436c7 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCapture.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCapture.kt @@ -3,10 +3,23 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* -import kotlinx.coroutines.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.CommandApiResponse +import com.ricoh360.thetaclient.transferred.CommandState +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import com.ricoh360.thetaclient.transferred.StartCaptureResponse +import com.ricoh360.thetaclient.transferred.StatusApiParams +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch /* * ShotCountSpecifiedIntervalCapture @@ -203,6 +216,7 @@ class ShotCountSpecifiedIntervalCapture private constructor( callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = error.message)) return@launch } + callback.onCapturing(CapturingStatusEnum.STARTING) when (cameraModel) { ThetaRepository.ThetaModel.THETA_SC2, ThetaRepository.ThetaModel.THETA_SC2_B -> { 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 e1bfeac8f6d..22052130b46 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 @@ -3,10 +3,28 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* -import kotlinx.coroutines.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.CommandApiResponse +import com.ricoh360.thetaclient.transferred.CommandState +import com.ricoh360.thetaclient.transferred.FirstShootingEnum +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.Preset +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import com.ricoh360.thetaclient.transferred.StartCaptureResponse +import com.ricoh360.thetaclient.transferred.StatusApiParams +import com.ricoh360.thetaclient.transferred.TakePictureResponse +import com.ricoh360.thetaclient.transferred.TimeShift +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking /* * TimeShiftCapture @@ -107,6 +125,10 @@ class TimeShiftCapture private constructor( endpoint = endpoint, params = StartCaptureParams(_mode = ShootingMode.TIME_SHIFT_SHOOTING) ) + startCaptureResponse.error ?: let { + callback.onCapturing(CapturingStatusEnum.STARTING) + } + monitor.start() /* diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapture.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapture.kt new file mode 100644 index 00000000000..0086bc98703 --- /dev/null +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapture.kt @@ -0,0 +1,343 @@ +package com.ricoh360.thetaclient.capture + +import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL +import com.ricoh360.thetaclient.ThetaApi +import com.ricoh360.thetaclient.ThetaRepository +import com.ricoh360.thetaclient.WeakReference +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.CommandApiResponse +import com.ricoh360.thetaclient.transferred.CommandState +import com.ricoh360.thetaclient.transferred.FirstShootingEnum +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import com.ricoh360.thetaclient.transferred.StartCaptureResponse +import com.ricoh360.thetaclient.transferred.StatusApiParams +import com.ricoh360.thetaclient.transferred.TimeShift +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking + +/* + * TimeShiftManualCapture + * + * @property endpoint URL of Theta web API endpoint + * @property options option of manual time-shift capture + * @property checkCommandStatusInterval the interval for executing commands/status API when state "inProgress" + */ +class TimeShiftManualCapture private constructor( + private val endpoint: String, + options: Options, + private val checkStatusCommandInterval: Long, +) : Capture(options) { + + private val scope = CoroutineScope(Dispatchers.Default) + + internal var weakCapturing: WeakReference? = null + internal fun getCapturing(): TimeShiftManualCapturing? { + return weakCapturing?.get() + } + + fun getCheckStatusCommandInterval(): Long { + return checkStatusCommandInterval + } + + /** + * Get manual time-shift setting object. + * @return ThetaRepository.TimeShiftSetting + */ + fun getTimeShiftSetting() = options._timeShift?.let { ThetaRepository.TimeShiftSetting(it) } + + + // TODO: Add get photo option property + + /** + * Callback of startCapture + */ + interface StartCaptureCallback { + /** + * Called when state "inProgress". + * + * @param completion Progress rate of command executed + */ + fun onProgress(completion: Float) + + /** + * Called when change capture status. + * + * @param status Capturing status + */ + fun onCapturing(status: CapturingStatusEnum) {} + + /** + * Called when stopCapture error occurs. + * + * @param exception Exception of error occurs + */ + fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) + + /** + * Called when error occurs. + * + * @param exception Exception of error occurs + */ + fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) + + /** + * Called when successful. + * + * @param fileUrl URL of the manual time-shift. When the manual time-shift is canceled, this URL will be null. + */ + fun onCaptureCompleted(fileUrl: String?) + } + + /** + * Starts manual time-shift. + * + * Later, need to call TimeShiftManualCapturing.startSecondCapture() or TimeShiftManualCapturing.stopCapture() + * + * @param callback Success or failure of the call + * @return TimeShiftManualCapturing instance + */ + fun startCapture(callback: StartCaptureCallback): TimeShiftManualCapturing { + var capturing: TimeShiftManualCapturing? = null + scope.launch { + val monitor = CaptureStatusMonitor( + endpoint, + { newStatus, _ -> + getCapturing()?.captureStatus = newStatus + when (newStatus) { + CaptureStatus.SELF_TIMER_COUNTDOWN -> callback.onCapturing( + CapturingStatusEnum.SELF_TIMER_COUNTDOWN + ) + + CaptureStatus.TIME_SHIFT_SHOOTING -> { + callback.onCapturing( + CapturingStatusEnum.TIME_SHIFT_SHOOTING + ) + } + + CaptureStatus.TIME_SHIFT_SHOOTING_IDLE -> { + callback.onCapturing( + CapturingStatusEnum.TIME_SHIFT_SHOOTING_IDLE + ) + } + + CaptureStatus.SHOOTING -> callback.onCapturing( + CapturingStatusEnum.TIME_SHIFT_SHOOTING_SECOND + ) + + else -> callback.onCapturing(CapturingStatusEnum.CAPTURING) + } + }, + { error -> + println("CaptureStatusMonitor error: ${error.message}") + }, + checkStatusCommandInterval, + 1 + ) + lateinit var startCaptureResponse: StartCaptureResponse + try { + startCaptureResponse = ThetaApi.callStartCaptureCommand( + endpoint = endpoint, + params = StartCaptureParams(_mode = ShootingMode.TIMESHIFT_MANUAL_SHOOTING) + ) + monitor.start() + + runBlocking { + var response: CommandApiResponse = startCaptureResponse + while (response.state == CommandState.IN_PROGRESS && getCapturing() != null) { + delay(timeMillis = checkStatusCommandInterval) + + var id: String? + var capt = getCapturing() + capt ?: break + id = if (capt.secondCalled) { + capt.secondCaptureResponse?.id + } else { + startCaptureResponse.id + } + if (id == null) { + capt = null + continue + } + capt = null + + response = ThetaApi.callStatusApi( + endpoint = endpoint, + params = StatusApiParams(id = id) + ) + callback.onProgress(completion = response.progress?.completion ?: 0f) + + if (monitor.currentStatus == CaptureStatus.TIME_SHIFT_SHOOTING_IDLE && capturing != null) { + // What to do if startSecondCapture is not called + capturing = null + } + } + monitor.stop() + if (getCapturing() == null && monitor.currentStatus == CaptureStatus.TIME_SHIFT_SHOOTING_IDLE) { + // If startSecondCapture is not called, it is not finished and stopCapture must be called. + // If you don't call it up, you won't be able to operate it. + println("TimeShiftManual cancel") + ThetaApi.callStopCaptureCommand(endpoint) + } + + if (response.state == CommandState.DONE) { + val captureResponse = response as StartCaptureResponse + val fileUrl: String? = captureResponse.results?.fileUrls?.firstOrNull() ?: captureResponse.results?.fileUrl + callback.onCaptureCompleted(fileUrl = fileUrl) + return@runBlocking + } + + val error = response.error + if (error != null && !error.isCanceledShootingCode()) { + callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = error.message)) + } else if (response.name == "unknown") { + callback.onCaptureFailed(exception = ThetaRepository.ThetaWebApiException(message = "Unknown response")) + } else { + callback.onCaptureCompleted(fileUrl = null) // canceled + } + } + } catch (e: JsonConvertException) { + monitor.stop() + callback.onCaptureFailed( + exception = ThetaRepository.ThetaWebApiException( + message = e.message ?: e.toString() + ) + ) + } catch (e: ResponseException) { + monitor.stop() + if (isCanceledShootingResponse(e.response)) { + callback.onCaptureCompleted(fileUrl = null) // canceled + } else { + callback.onCaptureFailed( + exception = ThetaRepository.ThetaWebApiException.create( + exception = e + ) + ) + } + } catch (e: Exception) { + monitor.stop() + callback.onCaptureFailed( + exception = ThetaRepository.NotConnectedException( + message = e.message ?: e.toString() + ) + ) + } + } + + val result = TimeShiftManualCapturing(endpoint = endpoint, callback = callback) + weakCapturing = WeakReference(result) + capturing = result + return result + } + + /* + * Builder of TimeShiftManualCapture + * + * @property endpoint URL of Theta web API endpoint + */ + class Builder internal constructor( + private val endpoint: String, + ) : Capture.Builder() { + private var interval: Long? = null + + /** + * Builds an instance of a TimeShiftCapture that has all the combined parameters of the Options that have been added to the Builder. + * + * @return VideoCapture + */ + @Throws(Throwable::class) + suspend fun build(): TimeShiftManualCapture { + try { + val modeOptions = Options( + captureMode = CaptureMode.IMAGE + ) + + ThetaApi.callSetOptionsCommand( + endpoint = endpoint, + params = SetOptionsParams(options = modeOptions) + ).error?.let { + throw ThetaRepository.ThetaWebApiException(message = it.message) + } + if (options != Options()) { + ThetaApi.callSetOptionsCommand( + endpoint = endpoint, + params = SetOptionsParams(options) + ).error?.let { + throw ThetaRepository.ThetaWebApiException(message = it.message) + } + } + } catch (e: JsonConvertException) { + throw ThetaRepository.ThetaWebApiException(message = e.message ?: e.toString()) + } catch (e: ResponseException) { + throw ThetaRepository.ThetaWebApiException.create(exception = e) + } catch (e: ThetaRepository.ThetaWebApiException) { + throw e + } catch (e: Exception) { + throw ThetaRepository.NotConnectedException(message = e.message ?: e.toString()) + } + return TimeShiftManualCapture( + endpoint = endpoint, + options = options, + checkStatusCommandInterval = interval ?: CHECK_COMMAND_STATUS_INTERVAL + ) + } + + fun setCheckStatusCommandInterval(timeMillis: Long): Builder { + this.interval = timeMillis + return this + } + + /** + * Set is front first. + * + * @param isFrontFirst is front first + * @return Builder + */ + fun setIsFrontFirst(isFrontFirst: Boolean): Builder { + checkAndInitTimeShiftSetting() + options._timeShift?.firstShooting = + if (isFrontFirst) FirstShootingEnum.FRONT else FirstShootingEnum.REAR + return this + } + + /** + * set time (sec) before 1st lens shooting + * + * @param interval 1st interval + * @return Builder + */ + fun setFirstInterval(interval: ThetaRepository.TimeShiftIntervalEnum): Builder { + checkAndInitTimeShiftSetting() + options._timeShift?.firstInterval = interval.sec + return this + } + + /** + * set time (sec) from 1st lens shooting until start of 2nd lens shooting. + * + * @param interval 2nd interval + * @return Builder + */ + fun setSecondInterval(interval: ThetaRepository.TimeShiftIntervalEnum): Builder { + checkAndInitTimeShiftSetting() + options._timeShift?.secondInterval = interval.sec + return this + } + + private fun checkAndInitTimeShiftSetting() { + if (options._timeShift == null) { + options._timeShift = TimeShift() + } + } + + // TODO: Add set photo option property + } +} diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapturing.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapturing.kt new file mode 100644 index 00000000000..5d889494475 --- /dev/null +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCapturing.kt @@ -0,0 +1,116 @@ +package com.ricoh360.thetaclient.capture + +import com.ricoh360.thetaclient.ThetaApi +import com.ricoh360.thetaclient.ThetaRepository +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.ShootingMode +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import com.ricoh360.thetaclient.transferred.StartCaptureResponse +import com.ricoh360.thetaclient.transferred.StopCaptureResponse +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlin.concurrent.Volatile + +/* + * TimeShiftManualCapturing + * + * @property endpoint URL of Theta web API endpoint + * @property callback Success or failure of the call + */ +class TimeShiftManualCapturing internal constructor( + private val endpoint: String, + private val callback: TimeShiftManualCapture.StartCaptureCallback +) : Capturing() { + @Volatile + internal var secondCaptureResponse: StartCaptureResponse? = null + + @Volatile + internal var captureStatus: CaptureStatus? = null + val isAvailableSecondCapture: Boolean + get() = captureStatus == CaptureStatus.TIME_SHIFT_SHOOTING_IDLE + + private val scope = CoroutineScope(Dispatchers.Default) + + // Has second startCapture been called. + @Volatile + internal var secondCalled = false + + /** + * Starts manual time-shift second capture. + */ + fun startSecondCapture() { + if (!isAvailableSecondCapture) { + println("First Capture is not yet finish.") + return + } + if (secondCalled) { + println("Second startCapture() has been called.") + return + } + + scope.launch { + try { + secondCalled = true + secondCaptureResponse = ThetaApi.callStartCaptureCommand( + endpoint = endpoint, + params = StartCaptureParams(_mode = ShootingMode.TIMESHIFT_MANUAL_SHOOTING) + ) + } catch (e: JsonConvertException) { + callback.onCaptureFailed( + exception = ThetaRepository.ThetaWebApiException( + message = e.message ?: e.toString() + ) + ) + } catch (e: ResponseException) { + if (isCanceledShootingResponse(e.response)) { + callback.onCaptureCompleted(fileUrl = null) // canceled + } else { + callback.onCaptureFailed( + exception = ThetaRepository.ThetaWebApiException.create( + exception = e + ) + ) + } + } catch (e: Exception) { + callback.onCaptureFailed( + exception = ThetaRepository.NotConnectedException( + message = e.message ?: e.toString() + ) + ) + } + } + } + + fun cancelCapture() { + stopCapture() + } + + /** + * Stops time-shift manual. + * When call stopCapture() then call property callback. + */ + override fun stopCapture() { + scope.launch { + lateinit var response: StopCaptureResponse + try { + response = ThetaApi.callStopCaptureCommand(endpoint = endpoint) + response.error?.let { + callback.onStopFailed(exception = ThetaRepository.ThetaWebApiException(message = it.message)) + return@launch + } + } catch (e: JsonConvertException) { + callback.onStopFailed(exception = ThetaRepository.ThetaWebApiException(message = e.message ?: e.toString())) + return@launch + } catch (e: ResponseException) { + callback.onStopFailed(exception = ThetaRepository.ThetaWebApiException.create(exception = e)) + return@launch + } catch (e: Exception) { + callback.onStopFailed(exception = ThetaRepository.NotConnectedException(message = e.message ?: e.toString())) + return@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 4145fd4c269..c76acb9a5fb 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 @@ -3,9 +3,13 @@ package com.ricoh360.thetaclient.capture import com.ricoh360.thetaclient.CHECK_COMMAND_STATUS_INTERVAL import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.plugins.* -import io.ktor.serialization.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.SetOptionsParams +import com.ricoh360.thetaclient.transferred.StartCaptureParams +import io.ktor.client.plugins.ResponseException +import io.ktor.serialization.JsonConvertException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -184,6 +188,7 @@ class VideoCapture private constructor( response.error?.let { callOnCaptureFailed(ThetaRepository.ThetaWebApiException(it.message)) } + callback.onCapturing(CapturingStatusEnum.STARTING) } catch (e: JsonConvertException) { callOnCaptureFailed(ThetaRepository.ThetaWebApiException(e.message ?: e.toString())) } catch (e: ResponseException) { diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/commandApi.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/commandApi.kt index b80a55e217e..5dd8349aa23 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/commandApi.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/commandApi.kt @@ -3,7 +3,7 @@ */ package com.ricoh360.thetaclient.transferred -import io.ktor.http.* +import io.ktor.http.HttpMethod import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable import kotlinx.serialization.json.JsonElement 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 1986dc831d8..91171e2f75f 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 @@ -37,6 +37,10 @@ internal data class InfoApiResponse( /** * MAC address of wireless LAN * (RICOH THETA V firmware v2.11.1 or later) + * + * For THETA X, firmware versions v2.63.0 and earlier display `the communication MAC address`, + * while v2.71.1 and later diplay `the physical MAC address`. + * For other than THETA X, `the physical MAC address` is displayed. */ val _wlanMacAddress: String?, diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/listAccessPointsCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/listAccessPointsCommand.kt index 1fd4c7e97f3..f349f43d343 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/listAccessPointsCommand.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/listAccessPointsCommand.kt @@ -75,49 +75,77 @@ internal data class ResultListAccessPoints( @Serializable internal data class AccessPoint( /** - * SSID + * SSID (Up to 32 bytes) */ val ssid: String, /** - * True if SSID stealth is enabled + * (Optional) + * SSID stealth + * (true: enable, false: disable. Default is false.) */ val ssidStealth: Boolean, /** + * (Mandatory) * Authentication mode + * ("none", "WEP", "WPA/WPA2 PSK") + * This can be optional parameter only when overwriting access point information. */ val security: AuthenticationMode, /** - * Connection priority + * (Optional) + * (RICOH THETA V, Z1) + * Connection priority (1 to 5). Default is 1. + * + * (RICOH THETA X) + * Fixed to 1 (The access point registered later has a higher priority.) */ val connectionPriority: Int, /** - * IP address allocation. This can be acquired when SSID is - * registered as an enable access point. + * (Optional) + * IP address allocation + * "dynamic" or "static". Default is "dynamic" */ val ipAddressAllocation: IpAddressAllocation, /** - * IP address assigned to camera. This setting can be acquired - * when "ipAddressAllocation" is "static" + * (Optional) + * IP address assigned to camera + * This setting must be set when ipAddressAllocation is "static" */ val ipAddress: String?, /** - * Subnet Mask. This setting can be acquired when - * "ipAddressAllocation" is "static" + * (Optional) + * Subnet mask + * This setting must be set when ipAddressAllocation is "static" */ val subnetMask: String?, /** - * Default Gateway. This setting can be acquired when - * "ipAddressAllocation" is "static + * (Optional) + * Default gateway + * This setting must be set when ipAddressAllocation is "static" */ val defaultGateway: String?, + /** + * (Optional) + * Primary DNS server. + * This setting must be set when ipAddressAllocation is "static" + */ + var dns1: String?, + + /** + * (Optional) + * Secondary DNS server. + * This setting must be set when ipAddressAllocation is "static" + */ + var dns2: String?, + /** * Proxy information to be used for the access point. * Also refer to _proxy option spec to set each parameter. @@ -130,28 +158,46 @@ internal data class AccessPoint( val proxy: Proxy? = null, ) +internal object AuthenticationModeSerializer : + SerialNameEnumIgnoreUnknownSerializer(AuthenticationMode.entries, AuthenticationMode.UNKNOWN) + /** * authentication mode */ -@Serializable -internal enum class AuthenticationMode { +@Serializable(with = AuthenticationModeSerializer::class) +internal enum class AuthenticationMode : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + /** * no authetication required */ - @SerialName("none") - NONE, + NONE { + override val serialName: String = "none" + }, /** * WEP authentication */ - @SerialName("WEP") - WEP, + WEP { + override val serialName: String = "WEP" + }, /** * WPA or WPA2 PSK authentication */ - @SerialName("WPA/WPA2 PSK") - WPA_WPA2_PSK, + WPA_WPA2_PSK { + override val serialName: String = "WPA/WPA2 PSK" + }, + + /** + * WPA3-SAE authentication + */ + WPA3_SAE { + override val serialName: String = "WPA3-SAE" + }, } /** diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/rebootCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/rebootCommand.kt new file mode 100644 index 00000000000..a460d5c1e01 --- /dev/null +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/rebootCommand.kt @@ -0,0 +1,57 @@ +/* + * camera._reboot + */ +package com.ricoh360.thetaclient.transferred + +import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement + +/** + * _reboot request + */ +@Serializable +internal data class RebootRequest( + override val name: String = "camera._reboot", + override val parameters: EmptyParameter = EmptyParameter(), +) : CommandApiRequest + +/** + * _reboot response + */ +@Serializable +internal data class RebootResponse( + /** + * Executed command + */ + override val name: String, + + /** + * Command execution status + * @see CommandState + */ + override val state: CommandState, + + /** + * Command ID used to check the execution status with + * Commands/Status + */ + override val id: String? = null, + + /** + * Results when each command is successfully executed. This + * output occurs in state "done" + */ + override val results: JsonElement? = null, + + /** + * Error information (See Errors for details). This output occurs + * in state "error" + */ + override val error: CommandError? = null, + + /** + * Progress information. This output occurs in state + * "inProgress" + */ + override val progress: CommandProgress? = null, +) : CommandApiResponse diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/serializer.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/serializer.kt index 8b8c60f876f..da49a75c0de 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/serializer.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/serializer.kt @@ -218,10 +218,9 @@ internal abstract class SerialNameEnumIgnoreUnknownSerializer( values: List, private val defaultValue: T, ) : KSerializer - where T : Enum, T : SerialNameEnum -{ + where T : Enum, T : SerialNameEnum { override val descriptor: SerialDescriptor = - PrimitiveSerialDescriptor(values.first()::class.qualifiedName ?: "" , PrimitiveKind.STRING) + PrimitiveSerialDescriptor(values.first()::class.qualifiedName ?: "", PrimitiveKind.STRING) private val lookup = values.associateBy({ it }, { it.serialName }) private val revLookup = values.associateBy { it.serialName @@ -238,7 +237,65 @@ internal abstract class SerialNameEnumIgnoreUnknownSerializer( println("Web API unknown value. ${defaultValue::class.simpleName}: $decodeString") defaultValue } + else -> value } } } + +/** + * Boolean list serializer + * + * If there is only one value, do not make it an array. + */ +@kotlinx.serialization.ExperimentalSerializationApi +internal object BooleanListSerializer : KSerializer> { + /** + * serial descriptor + */ + override val descriptor: SerialDescriptor = listSerialDescriptor() + + /** + * serialize value with encoder + * @param encoder encoder object + * @param value value to encode + */ + override fun serialize(encoder: Encoder, value: List) { + if (value.size == 1) { + encoder.encodeBoolean(value[0]) + return + } + val composite = encoder.beginCollection(descriptor, value.size) + val iterator = value.iterator() + for (index in value.indices) { + composite.encodeBooleanElement(descriptor, index, iterator.next()) + } + composite.endStructure(descriptor) + } + + /** + * deserialize value with decoder and return decoded value + * @param decoder decoder object + * @return decoded value + */ + override fun deserialize(decoder: Decoder): List { + try { + // Not an array if it has a single value + val value = decoder.decodeBoolean() + return listOf(value) + } catch (_: Throwable) { + } + + val result = mutableListOf() + val compositeDecoder = decoder.beginStructure(descriptor) + while (true) { + val index = compositeDecoder.decodeElementIndex(descriptor) + if (index == CompositeDecoder.DECODE_DONE) { + break + } + result.add(decoder.decodeBoolean()) + } + compositeDecoder.endStructure(descriptor) + return result + } +} diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setAccessPointCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setAccessPointCommand.kt index 095b4a251e3..4eb48d10075 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setAccessPointCommand.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setAccessPointCommand.kt @@ -27,55 +27,80 @@ internal data class SetAccessPointParams( val ssid: String, /** - * (Optional) SSID stealth (true: enable, false: disable. Default - * is false.) + * (Optional) + * SSID stealth + * (true: enable, false: disable. Default is false.) */ val ssidStealth: Boolean? = null, /** - * (Optional) Authentication mode("none", "WEP","WPA/WPA2 PSK") + * (Mandatory) + * Authentication mode + * ("none", "WEP", "WPA/WPA2 PSK") + * This can be optional parameter only when overwriting access point information. */ val security: AuthenticationMode? = null, /** - * (Optional) Password. + * (Optional) + * Password * This can be set when security is not "none" */ val password: String? = null, /** * (Optional) - * (RICOH THETA V, Z1) Connection priority (1 to 5). Default is - * 1.(RICOH THETA X) Fixed to 1 (The access point registered later - * has a higher priority. + * (RICOH THETA V, Z1) + * Connection priority (1 to 5). Default is 1. + * + * (RICOH THETA X) + * Fixed to 1 (The access point registered later has a higher priority.) */ @Serializable(with = NumberAsIntSerializer::class) val connectionPriority: Int? = null, /** - * (Optional) IP address allocation "dynamic" or - * "static". Default is "dynamic" + * (Optional) + * IP address allocation + * "dynamic" or "static". Default is "dynamic" */ val ipAddressAllocation: IpAddressAllocation? = null, /** - * (Optional) IP address assigned to camera. This setting can be - * set when ipAddressAllocation is "static" + * (Optional) + * IP address assigned to camera + * This setting must be set when ipAddressAllocation is "static" */ val ipAddress: String? = null, /** - * (Optional) Subnet mask. This setting can be set when - * ipAddressAllocation is "static" + * (Optional) + * Subnet mask + * This setting must be set when ipAddressAllocation is "static" */ val subnetMask: String? = null, /** - * (Optional) Default gateway. This setting can be set when - * ipAddressAllocation is "static" + * (Optional) + * Default gateway + * This setting must be set when ipAddressAllocation is "static" */ val defaultGateway: String? = null, + /** + * (Optional) + * Primary DNS server. + * This setting must be set when ipAddressAllocation is "static" + */ + var dns1: String?, + + /** + * (Optional) + * Secondary DNS server. + * This setting must be set when ipAddressAllocation is "static" + */ + var dns2: String?, + /** * Proxy information to be used for the access point. * Also refer to _proxy option spec to set each parameter. diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setOptionsCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setOptionsCommand.kt index 218129c4d3d..59416a10963 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setOptionsCommand.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/setOptionsCommand.kt @@ -3,6 +3,7 @@ */ package com.ricoh360.thetaclient.transferred +import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -15,7 +16,7 @@ import kotlinx.serialization.encoding.Encoder /** * set options request */ -@OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) +@OptIn(ExperimentalSerializationApi::class) @Serializable internal data class SetOptionsRequest( override val name: String = "camera.setOptions", @@ -98,6 +99,10 @@ internal data class ResultSetOptions( @Serializable @Suppress("ConstructorParameterNaming") internal data class Options( + /** + * Connected network information. + */ + var _accessInfo: AccessInfo? = null, /** * Turns the AI auto setting ON/OFF. */ @@ -180,6 +185,24 @@ internal data class Options( */ var _cameraControlSourceSupport: List? = null, + /** + * Camera lock + * @see CameraLock + */ + var _cameraLock: CameraLock? = null, + + /** + * Camera lock support + * @see CameraLock + */ + var _cameraLockSupport: List? = null, + + /** + * Camera lock config + * @see CameraLockConfig + */ + var _cameraLockConfig: CameraLockConfig? = null, + /** * Camera mode. * @@ -250,7 +273,7 @@ internal data class Options( /** * supported client versions. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var clientVersionSupport: List? = null, @@ -264,6 +287,25 @@ internal data class Options( */ var _colorTemperature: Int? = null, + /** + * supported color temperature. + */ + var _colorTemperatureSupport: ColorTemperatureSupport? = null, + + /** + * _compassDirectionRef + * + * @see CompassDirectionRef + */ + var _compassDirectionRef: CompassDirectionRef? = null, + + /** + * _compassDirectionRef support + * + * @see CompassDirectionRef + */ + var _compassDirectionRefSupport: List? = null, + /** * In-progress save interval for interval composite shooting (sec). * @@ -279,7 +321,7 @@ internal data class Options( /** * Supported in-progress save interval for interval composite shooting (sec). */ - var _compositeShootingOutputIntervalSupport: List? = null, + var _compositeShootingOutputIntervalSupport: CompositeShootingOutputIntervalSupport? = null, /** * Shooting time for interval composite shooting (sec). @@ -294,9 +336,9 @@ internal data class Options( var _compositeShootingTime: Int? = null, /** - * Supported shooting time for interval composite shooting (sec + * Supported shooting time for interval composite shooting (sec). */ - var _compositeShootingTimeSupport: List? = null, + var _compositeShootingTimeSupport: CompositeShootingTimeSupport? = null, /** * Number of shots for continuous shooting. @@ -360,7 +402,7 @@ internal data class Options( /** * Supported operating time (sec.) of the self-timer. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var exposureDelaySupport: List? = null, @@ -379,7 +421,7 @@ internal data class Options( /** * Supported exposure program */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var exposureProgramSupport: List? = null, @@ -423,9 +465,11 @@ internal data class Options( * * The condition below will result in an error. * - * fileFormat is raw+ and _filter is Noise reduction, HDR or - * Handheld HDR _shootingMethod is except for Normal shooting and - * _filter is enabled Access during video capture mode + * - When attempting to set [_filter] to Noise reduction, + * HDR or Handheld HDR while [fileFormat] is set to raw+, + * but this restriction is only for RICOH THETA Z1 firmware v1.80.1 or earlier. + * - [_shootingMethod] is except for Normal shooting and [_filter] is enabled + * - Access during video capture mode * * @see ImageFilter */ @@ -479,6 +523,11 @@ internal data class Options( */ var _gpsTagRecording: GpsTagRecording? = null, + /** + * Supported GpsTagRecording + */ + var _gpsTagRecordingSupport: List? = null, + /** * Still image stitching setting during shooting. */ @@ -502,7 +551,7 @@ internal data class Options( /** * Supported ISO sensitivity. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var isoSupport: List? = null, @@ -516,7 +565,7 @@ internal data class Options( /** * supported ISO sensitivity upper limit. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var isoAutoHighLimitSupport: List? = null, @@ -547,7 +596,7 @@ internal data class Options( /** * Supported maximum recordable time (in seconds) of the camera. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var _maxRecordableTimeSupport: List? = null, @@ -571,6 +620,27 @@ internal data class Options( */ var _microphoneChannelSupport: List? = null, + /** + * @see MicrophoneNoiseReduction + */ + var _microphoneNoiseReduction: MicrophoneNoiseReduction? = null, + + /** + * _microphoneNoiseReduction support + * @see MicrophoneNoiseReduction + */ + var _microphoneNoiseReductionSupport: List? = null, + + /** + * @see MobileNetworkSetting + */ + var _mobileNetworkSetting: MobileNetworkSetting? = null, + + /** + * @see MobileNetworkSettingSupport + */ + var _mobileNetworkSettingSupport: MobileNetworkSettingSupport? = null, + /** * Network type. */ @@ -601,10 +671,27 @@ internal data class Options( /** * Length of standby time before the camera automatically powers OFF. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var offDelaySupport: List? = null, + /** + * Auto power off time with USB power supply. + * + * For RICOH THETA A1 + * 0, or a value that is a multiple of 60 out of 600 or more and 2592000 or less (unit: second), or 65535. + * Return 0 when 65535 is set and obtained (Do not turn power OFF). + */ + @Serializable(with = NumberAsIntSerializer::class) + var _offDelayUSB: Int? = null, + + /** + * Supported auto power off time with USB power supply. + */ + @OptIn(ExperimentalSerializationApi::class) + @Serializable(with = NumbersAsIntsSerializer::class) + var offDelayUSBSupport: List? = null, + /** * Password used for digest authentication when _networkType is set to client mode. */ @@ -723,7 +810,7 @@ internal data class Options( /** * Length of standby time before the camera enters the sleep mode. */ - @OptIn(kotlinx.serialization.ExperimentalSerializationApi::class) + @OptIn(ExperimentalSerializationApi::class) @Serializable(with = NumbersAsIntsSerializer::class) var sleepDelaySupport: List? = null, @@ -754,12 +841,28 @@ internal data class Options( */ var _topBottomCorrectionRotation: TopBottomCorrectionRotation? = null, + /** + * @see TopBottomCorrectionRotationSupport + */ + var _topBottomCorrectionRotationSupport: TopBottomCorrectionRotationSupport? = null, + /** * Total storage space (byte). */ @Serializable(with = NumberAsLongSerializer::class) var totalSpace: Long? = null, + /** + * @see UsbConnection + */ + var _usbConnection: UsbConnection? = null, + + /** + * _usbConnection support + * @see UsbConnection + */ + var _usbConnectionSupport: List? = null, + /** * User name used for digest authentication when _networkType is set to client mode. */ @@ -807,6 +910,18 @@ internal data class Options( */ var _whiteBalanceAutoStrengthSupport: List? = null, + /** + * @see WlanAntennaConfig + */ + var _wlanAntennaConfig: WlanAntennaConfig? = null, + + /** + * WlanAntennaConfig support + * + * @see WlanAntennaConfig + */ + var _wlanAntennaConfigSupport: List? = null, + /** * Wireless LAN frequency of the camera supported by Theta V, Z1 and X. */ @@ -816,6 +931,115 @@ internal data class Options( * Supported WlanFrequency */ var _wlanFrequencySupport: List? = null, + + /** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + * + * For RICOH THETA A1 + */ + var _wlanFrequencyCLmode: WlanFrequencyClMode? = null, + + /** + * Supported WlanFrequencyClMode + */ + var _wlanFrequencyCLmodeSupport: WlanFrequencyClModeSupport? = null, +) + +/** + * _accessInfo + */ +@Serializable +internal data class AccessInfo( + /** + * SSID of the wireless LAN access point that THETA connects to + */ + var ssid: String, + + /** + * IP address of access point + */ + var ipAddress: String, + + /** + * subnet mask of access point + */ + var subnetMask: String, + + /** + * default gateway of access point + */ + var defaultGateway: String, + + /** + * Primary DNS server + */ + var dns1: String?, + + /** + * Secondary DNS server + */ + var dns2: String?, + + /** + * proxy URL of access point + */ + var proxyURL: String, + + /** + * Radio frequency, e.g. 2.4 5.2 + */ + var frequency: WlanFrequencyAccessInfo, + + /** + * WLAN signal strength + * [dBm] + */ + var wlanSignalStrength: Int, + + /** + * WLAN signal level + * 0~4 + */ + var wlanSignalLevel: Int, + + /** + * LTE signal strength + * [dBm] + */ + var lteSignalStrength: Int, + + /** + * LTE signal level + * 0~4 + */ + var lteSignalLevel: Int, + + /** + * client devices information + */ + @SerialName("_dhcpLeaseAddress") + var dhcpLeaseAddress: List? = null, +) + +/** + * client devices information + */ +@Serializable +internal data class DhcpLeaseAddress( + /** + * IP address of client device + */ + val ipAddress: String, + + /** + * MAC address of client device + */ + val macAddress: String, + + /** + * host name of client device + */ + val hostName: String, ) /** @@ -1254,6 +1478,9 @@ internal object BurstOrderSerializer : KSerializer { } } +internal object CameraControlSourceSerializer : + SerialNameEnumIgnoreUnknownSerializer(CameraControlSource.entries, CameraControlSource.UNKNOWN) + /** * camera control source * Sets whether to lock/unlock the camera UI. @@ -1261,21 +1488,140 @@ internal object BurstOrderSerializer : KSerializer { * * For RICOH THETA X */ -@Serializable -internal enum class CameraControlSource { +@Serializable(with = CameraControlSourceSerializer::class) +internal enum class CameraControlSource : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + /** * Operation is possible with the camera. Locks the smartphone * application UI (supported app only). */ - @SerialName("camera") - CAMERA, + CAMERA { + override val serialName: String = "camera" + }, /** * Operation is possible with the smartphone application. Locks * the UI on the shooting screen on the camera. */ - @SerialName("app") - APP, + APP { + override val serialName: String = "app" + }, +} + +internal object CameraLockSerializer : + SerialNameEnumIgnoreUnknownSerializer(CameraLock.entries, CameraLock.UNKNOWN) + +/** + * Control camera lock/unlock + */ +@Serializable(with = CameraLockSerializer::class) +internal enum class CameraLock : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * Camera is unlocked + */ + UNLOCK { + override val serialName: String = "unlock" + }, + + /** + * Camera basic lock state + * (Mode button, WLAN button, and Fn button presses are inhibited) + */ + BASIC_LOCK { + override val serialName: String = "basicLock" + }, + + /** + * Lock according to the parameters set in _cameraLockConfig. + */ + CUSTOM_LOCK { + override val serialName: String = "customLock" + }, +} + +/** + * Camera Lock Config + * + * - It is possible to enable/disable the function for each HW key. + * - It is possible to enable/disable the operation of the panel. + * - When all supported buttons/components are in the "unlock" state, it will be the same as the normal setting. + * - For THETA models, if there are no wlanKey, fnKey, or panel, it will return "lock". If there are no supported buttons/components, setting to "unlock" or "lock" will not return an error and will not perform any action. + */ +@Serializable +internal data class CameraLockConfig( + /** + * power key locked or unlocked. + */ + val powerKey: CameraLockType? = null, + + /** + * Shutter key locked or unlocked. + */ + val shutterKey: CameraLockType? = null, + + /** + * mode key locked or unlocked. + */ + val modeKey: CameraLockType? = null, + + /** + * wlan key locked or unlocked. + */ + val wlanKey: CameraLockType? = null, + + /** + * fn key locked or unlocked. + */ + val fnKey: CameraLockType? = null, + + /** + * panel locked or unlocked. + * Fixed to LOCK. UNLOCK does not cause an error, but it is not reflected either. + */ + val panel: CameraLockType? = null, +) + +internal object CameraLockTypeSerializer : + SerialNameEnumIgnoreUnknownSerializer(CameraLockType.entries, CameraLockType.UNKNOWN) + +/** + * Camera lock type + */ +@Serializable(with = CameraLockTypeSerializer::class) +internal enum class CameraLockType(val isLocked: Boolean) : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN(true), + + /** + * Camera is locked + */ + LOCK(true) { + override val serialName: String = "lock" + }, + + /** + * Camera is unlocked + */ + UNLOCK(false) { + override val serialName: String = "unlock" + }; + + companion object { + fun from(isLocked: Boolean?): CameraLockType? { + return isLocked?.let { if (it) LOCK else UNLOCK } + } + } } /** @@ -1315,6 +1661,7 @@ internal object CameraPowerSerializer : SerialNameEnumIgnoreUnknownSerializer(CameraPower.entries, CameraPower.UNKNOWN) /** + * Camera power state. * _cameraPower is the power status of camera. * * For RICOH THETA X v2.61.0 or later @@ -1341,6 +1688,14 @@ internal enum class CameraPower : SerialNameEnum { }, /** + * Sleep + */ + SLEEP { + override val serialName: String = "sleep" + }, + + /** + * Power Saving Mode * Power on, power saving mode. Camera is closed. * Unavailable parameter when plugin is running. In this case, invalidParameterValue error will be returned. */ @@ -1393,6 +1748,43 @@ internal enum class CaptureMode { PRESET, } +internal object CompassDirectionRefSerializer : + SerialNameEnumIgnoreUnknownSerializer(CompassDirectionRef.entries, CompassDirectionRef.UNKNOWN) + +/** + * _compassDirectionRef + */ +@Serializable(with = CompassDirectionRefSerializer::class) +internal enum class CompassDirectionRef : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * If GPS positioning is available, record in true north; + * if GPS is off or not available, record in magnetic north. + */ + AUTO { + override val serialName: String = "auto" + }, + + /** + * If the azimuth is set to true north, GPS is turned off, or positioning is not possible, + * the azimuth information is not recorded (positioning information is required for conversion). + */ + TRUE_NORTH { + override val serialName: String = "true" + }, + + /** + * Do not set azimuth to true north, set azimuth to magnetic north + */ + MAGNETIC { + override val serialName: String = "magnetic" + }, +} + /** * Face detection * @@ -1444,22 +1836,32 @@ internal enum class Gain { MUTE, } +internal object AiAutoThumbnailSerializer : + SerialNameEnumIgnoreUnknownSerializer(AiAutoThumbnail.entries, AiAutoThumbnail.UNKNOWN) + /** * AI auto thumbnail setting */ -@Serializable -internal enum class AiAutoThumbnail { +@Serializable(with = AiAutoThumbnailSerializer::class) +internal enum class AiAutoThumbnail : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + /** * AI auto setting ON */ - @SerialName("ON") - ON, + ON { + override val serialName: String = "ON" + }, /** * AI auto setting OFF */ - @SerialName("OFF") - OFF, + OFF { + override val serialName: String = "OFF" + }, } /** @@ -1475,21 +1877,37 @@ internal data class EthernetConfig( /** * (optional) IPv4 for IP address + * Do not specify this property when ipAddressAllocation is dynamic. */ val ipAddress: String? = null, /** * (optional) IPv4 for subnet mask + * Do not specify this property when ipAddressAllocation is dynamic. */ val subnetMask: String? = null, /** * (optional) IPv4 for default gateway + * Do not specify this property when ipAddressAllocation is dynamic. */ val defaultGateway: String? = null, + /** + * (optional) IPv4 for Primary DNS server + * Do not specify this property when ipAddressAllocation is dynamic. + */ + var dns1: String? = null, + + /** + * (optional) IPv4 for Secondary DNS server + * Do not specify this property when ipAddressAllocation is dynamic. + */ + var dns2: String? = null, + /** * (optional) refer to _proxy for detail + * Do not specify this property when ipAddressAllocation is dynamic. */ val _proxy: Proxy? = null, ) @@ -1512,34 +1930,229 @@ internal enum class MicrophoneChannel { MONAURAL, } + +internal object MicrophoneNoiseReductionSerializer : + SerialNameEnumIgnoreUnknownSerializer(MicrophoneNoiseReduction.entries, MicrophoneNoiseReduction.UNKNOWN) + /** - * Network type setting supported by Theta V, Z1, and X. + * Built-in microphone noise reduction. + * + * 1) Video: Ignore changes during recording + * 2) Live Streaming: Ignore changes during delivery */ -@Serializable -internal enum class NetworkType { +@Serializable(with = MicrophoneNoiseReductionSerializer::class) +internal enum class MicrophoneNoiseReduction : SerialNameEnum { /** - * Direct mode + * Undefined value */ - @SerialName("AP") - DIRECT, + UNKNOWN, /** - * Client mode + * ON */ - @SerialName("CL") - CLIENT, + ON { + override val serialName: String = "on" + }, /** - * Client mode via Ethernet cable, supported only by Theta V and Z1. + * OFF */ - @SerialName("ETHERNET") - ETHERNET, + OFF { + override val serialName: String = "off" + }, +} + +/** + * Mobile Network Settings + */ +@Serializable +internal data class MobileNetworkSetting( + /** + * roaming + */ + val roaming: Roaming?, + + /** + * plan + */ + val plan: Plan? +) + +/** + * Mobile Network Settings Support + */ +@Serializable +internal data class MobileNetworkSettingSupport( + /** + * roaming support + */ + val roaming: List, + + /** + * plan support + */ + val plan: List +) + +internal object RoamingSerializer : + SerialNameEnumIgnoreUnknownSerializer(Roaming.entries, Roaming.UNKNOWN) + +/** + * Roaming of MobileNetworkSetting + */ +@Serializable(with = RoamingSerializer::class) +internal enum class Roaming : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * OFF + */ + OFF { + override val serialName: String = "OFF" + }, + + /** + * ON + */ + ON { + override val serialName: String = "ON" + }, +} + +internal object PlanSerializer : + SerialNameEnumIgnoreUnknownSerializer(Plan.entries, Plan.UNKNOWN) + +/** + * Plan of MobileNetworkSetting + */ +@Serializable(with = PlanSerializer::class) +internal enum class Plan : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * Communicate with APN settings for plan-D + */ + SORACOM { + override val serialName: String = "SORACOM" + }, + + /** + * Communicate with APN settings for plan-DU + */ + SORACOM_PLAN_DU { + override val serialName: String = "SORACOM Plan-DU" + }, +} + +internal object NetworkTypeSerializer : + SerialNameEnumIgnoreUnknownSerializer(NetworkType.entries, NetworkType.UNKNOWN) + +/** + * Network type setting supported by Theta V, Z1, and X. + */ +@Serializable(with = NetworkTypeSerializer::class) +internal enum class NetworkType : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * Direct mode + */ + DIRECT { + override val serialName: String = "AP" + }, + + /** + * Client mode + */ + CLIENT { + override val serialName: String = "CL" + }, + + /** + * Client mode via Ethernet cable, supported only by Theta V and Z1. + */ + ETHERNET { + override val serialName: String = "ETHERNET" + }, /** * Network is off. This value can be gotten only by plugin. */ - @SerialName("OFF") - OFF, + OFF { + override val serialName: String = "OFF" + }, + + /** + * LTE plan-D + * + * For RICOH THETA A1 + */ + LTE_D { + override val serialName: String = "LTE plan-D" + }, + + /** + * LTE plan-DU + * + * For RICOH THETA A1 + */ + LTE_DU { + override val serialName: String = "LTE plan-DU" + }, + + /** + * LTE plan01s + * + * For RICOH THETA A1 + */ + LTE_01S { + override val serialName: String = "LTE plan01s" + }, + + /** + * LTE planX3 + * + * For RICOH THETA A1 + */ + LTE_X3 { + override val serialName: String = "LTE planX3" + }, + + /** + * LTE planP1 + * + * For RICOH THETA A1 + */ + LTE_P1 { + override val serialName: String = "LTE planP1" + }, + + /** + * LTE plan-K2 + * + * For RICOH THETA A1 + */ + LTE_K2 { + override val serialName: String = "LTE plan-K2" + }, + + /** + * LTE plan-K + * + * For RICOH THETA A1 + */ + LTE_K { + override val serialName: String = "LTE plan-K" + }, } /** @@ -1844,21 +2457,137 @@ internal data class TopBottomCorrectionRotation( * Specifies the pitch. * Specified range is -90.0 to +90.0, stepSize is 0.1 */ - val pitch: Float? = null, + val pitch: String, /** * Specifies the roll. * Specified range is -180.0 to +180.0, stepSize is 0.1 */ - val roll: Float? = null, + val roll: String, /** * Specifies the yaw. * Specified range is -180.0 to +180.0, stepSize is 0.1 */ - val yaw: Float? = null + val yaw: String +) + +/** + * Supported TopBottomCorrectionRotation + */ +@Serializable +internal data class TopBottomCorrectionRotationSupport( + /** + * Supported pitch + */ + val pitch: PitchSupport, + + /** + * Supported roll + */ + val roll: RollSupport, + + /** + * Supported yaw + */ + val yaw: YawSupport +) + +/** + * Supported pitch of TopBottomCorrectionRotation + */ +@Serializable +internal data class PitchSupport( + /** + * maximum pitch volume + */ + val maxPitch: Float, + + /** + * minimum pitch volume + */ + val minPitch: Float, + + /** + * pitch step size + */ + val stepSize: Float +) + +/** + * Supported roll of TopBottomCorrectionRotation + */ +@Serializable +internal data class RollSupport( + /** + * maximum roll volume + */ + val maxRoll: Float, + + /** + * minimum roll volume + */ + val minRoll: Float, + + /** + * roll step size + */ + val stepSize: Float +) + +/** + * Supported yaw of TopBottomCorrectionRotation + */ +@Serializable +internal data class YawSupport( + /** + * maximum yaw volume + */ + val maxYaw: Float, + + /** + * minimum yaw volume + */ + val minYaw: Float, + + /** + * yaw step size + */ + val stepSize: Float ) + +internal object UsbConnectionSerializer : + SerialNameEnumIgnoreUnknownSerializer(UsbConnection.entries, UsbConnection.UNKNOWN) + +/** + * USB connection of the camera. + * + * Default value is "MTP". + * After switching the setting value, reconnect the camera to USB to enable it. + */ +@Serializable(with = UsbConnectionSerializer::class) +internal enum class UsbConnection : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * MTP + */ + MTP { + override val serialName: String = "MTP" + }, + + /** + * MSC + */ + MSC { + override val serialName: String = "MSC" + }, +} + /** * White balance setting */ @@ -1961,11 +2690,44 @@ internal enum class WhiteBalanceAutoStrength { OFF, } +internal object WlanAntennaConfigSerializer : + SerialNameEnumIgnoreUnknownSerializer(WlanAntennaConfig.entries, WlanAntennaConfig.UNKNOWN) + +/** + * Configure SISO or MIMO for Wireless LAN. + */ +@Serializable(with = WlanAntennaConfigSerializer::class) +internal enum class WlanAntennaConfig : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * SISO + */ + SISO { + override val serialName: String = "SISO" + }, + + /** + * MIMO + */ + MIMO { + override val serialName: String = "MIMO" + }, +} + /** * Wireless LAN frequency of the camera supported by Theta V, Z1 and X. */ @Serializable(with = WlanFrequencySerializer::class) internal enum class WlanFrequency(val frequency: Double) { + /** + * Undefined value + */ + UNKNOWN(0.0), + /** * 2.4GHz */ @@ -1975,6 +2737,20 @@ internal enum class WlanFrequency(val frequency: Double) { * 5GHz */ GHZ_5(5.0), + + /** + * 5.2GHz + * + * For RICOH THETA A1 + */ + GHZ_5_2(5.2), + + /** + * 5.8GHz + * + * For RICOH THETA A1 + */ + GHZ_5_8(5.8), } /** @@ -1991,10 +2767,55 @@ internal object WlanFrequencySerializer : KSerializer { override fun deserialize(decoder: Decoder): WlanFrequency { val frequency = decoder.decodeDouble() - return if (frequency < 5) WlanFrequency.GHZ_2_4 else WlanFrequency.GHZ_5 + return WlanFrequency.entries.firstOrNull { it.frequency == frequency } ?: let { + println("Web API unknown value. ${WlanFrequency::class.simpleName}: $frequency") + WlanFrequency.UNKNOWN + } } } +internal object WlanFrequencyAccessInfoSerializer : + SerialNameEnumIgnoreUnknownSerializer(WlanFrequencyAccessInfo.entries, WlanFrequencyAccessInfo.UNKNOWN) + +/** + * WLAN frequency of the access point. + */ +@Serializable(with = WlanFrequencyAccessInfoSerializer::class) +internal enum class WlanFrequencyAccessInfo : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + + /** + * 2.4GHz + */ + GHZ_2_4 { + override val serialName: String = "2.4" + }, + + /** + * 5.2GHz + */ + GHZ_5_2 { + override val serialName: String = "5.2" + }, + + /** + * 5.8GHz + */ + GHZ_5_8 { + override val serialName: String = "5.8" + }, + + /** + * Initial value + */ + INITIAL_VALUE { + override val serialName: String = "" + }, +} + /** * Visibility reduction setting */ @@ -2094,22 +2915,32 @@ internal enum class Language { KO, } +internal object GpsTagRecordingSerializer : + SerialNameEnumIgnoreUnknownSerializer(GpsTagRecording.entries, GpsTagRecording.UNKNOWN) + /** * gps position information */ -@Serializable -internal enum class GpsTagRecording { +@Serializable(with = GpsTagRecordingSerializer::class) +internal enum class GpsTagRecording : SerialNameEnum { + /** + * Undefined value + */ + UNKNOWN, + /** * Assign position information */ - @SerialName("on") - ON, + ON { + override val serialName: String = "on" + }, /** * Do not assign position information */ - @SerialName("off") - OFF, + OFF { + override val serialName: String = "off" + }, } /** @@ -2416,3 +3247,127 @@ internal data class CaptureNumberSupport( @Serializable(with = NumberAsIntSerializer::class) val maxNumber: Int? = null, ) + +/** + * supported color temperature. + */ +@Serializable +internal data class ColorTemperatureSupport( + /** + * maximum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val maxTemperature: Int, + + /** + * minimum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val minTemperature: Int, + + /** + * step size + */ + @Serializable(with = NumberAsIntSerializer::class) + val stepSize: Int +) + +/** + * Supported in-progress save interval for interval composite shooting (sec). + */ +@Serializable +internal data class CompositeShootingOutputIntervalSupport( + /** + * maximum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val maxInterval: Int, + + /** + * minimum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val minInterval: Int, + + /** + * step size + */ + @Serializable(with = NumberAsIntSerializer::class) + val stepSize: Int +) + +/** + * Supported shooting time for interval composite shooting (sec). + */ +@Serializable +internal data class CompositeShootingTimeSupport( + /** + * maximum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val maxTime: Int, + + /** + * minimum value + */ + @Serializable(with = NumberAsIntSerializer::class) + val minTime: Int, + + /** + * step size + */ + @Serializable(with = NumberAsIntSerializer::class) + val stepSize: Int +) + +/** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + */ +@Serializable +internal data class WlanFrequencyClMode( + /** + * 2.4GHz + */ + @SerialName("enable2.4GHz") + val enable2_4: Boolean, + + /** + * 5.2GHz + */ + @SerialName("enable5.2GHz") + val enable5_2: Boolean, + + /** + * 5.8GHz + */ + @SerialName("enable5.8GHz") + val enable5_8: Boolean, +) + +/** + * Supported WlanFrequencyClMode + */ +@OptIn(ExperimentalSerializationApi::class) +@Serializable +internal data class WlanFrequencyClModeSupport( + /** + * 2.4GHz + */ + @SerialName("enable2.4GHz") + @Serializable(with = BooleanListSerializer::class) + val enable2_4: List, + + /** + * 5.2GHz + */ + @SerialName("enable5.2GHz") + @Serializable(with = BooleanListSerializer::class) + val enable5_2: List, + + /** + * 5.8GHz + */ + @SerialName("enable5.8GHz") + @Serializable(with = BooleanListSerializer::class) + val enable5_8: List, +) diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/startCaptureCommand.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/startCaptureCommand.kt index 592ed173814..43bb07c9510 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/startCaptureCommand.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/startCaptureCommand.kt @@ -72,6 +72,12 @@ internal enum class ShootingMode { CONTINUOUS_SHOOTING, //MOVIE_SHOOTING("") + + /** + * Start time-lapse photography by manual lens + */ + @SerialName("timeShift_manual") + TIMESHIFT_MANUAL_SHOOTING, } /** diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/stateApi.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/stateApi.kt index 750dfc29956..a53bee9a9cb 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/stateApi.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/transferred/stateApi.kt @@ -3,7 +3,7 @@ */ package com.ricoh360.thetaclient.transferred -import io.ktor.http.* +import io.ktor.http.HttpMethod import kotlinx.serialization.SerialName import kotlinx.serialization.Serializable @@ -296,6 +296,14 @@ internal enum class CaptureStatus : SerialNameEnum { BURST_SHOOTING { override val serialName: String = "burst shooting" }, + + /** + * In the case of time-lag shooting by manual lens, + * set while waiting for the second shot to be taken after the first shot is completed. + */ + TIME_SHIFT_SHOOTING_IDLE { + override val serialName: String = "timeShift shooting idle" + }, } /** 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 f0f8c399ee7..046fc27855b 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 @@ -5,7 +5,6 @@ package com.ricoh360.thetaclient.transferred import io.ktor.http.HttpMethod import kotlinx.serialization.Serializable -import kotlinx.serialization.decodeFromString import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonObject import kotlinx.serialization.json.jsonPrimitive diff --git a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/websocket/WebSocketHttpClient.kt b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/websocket/WebSocketHttpClient.kt index 5d57cb71df0..053135e27ac 100644 --- a/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/websocket/WebSocketHttpClient.kt +++ b/kotlin-multiplatform/src/commonMain/kotlin/com/ricoh360/thetaclient/websocket/WebSocketHttpClient.kt @@ -1,12 +1,11 @@ package com.ricoh360.thetaclient.websocket +import com.ricoh360.thetaclient.ThetaApiLogger import com.ricoh360.thetaclient.setupDigestAuth import io.ktor.client.HttpClient import io.ktor.client.engine.cio.CIO import io.ktor.client.plugins.logging.LogLevel -import io.ktor.client.plugins.logging.Logger import io.ktor.client.plugins.logging.Logging -import io.ktor.client.plugins.logging.SIMPLE import io.ktor.client.plugins.websocket.DefaultClientWebSocketSession import io.ktor.client.plugins.websocket.WebSockets import io.ktor.client.plugins.websocket.webSocketSession @@ -31,7 +30,7 @@ internal class WebSocketHttpClientImpl : WebSocketHttpClient { pingInterval = WEBSOCKET_PING_INTERVAL } install(Logging) { - logger = Logger.SIMPLE // DEFAULT, SIMPLE or EMPTY + logger = ThetaApiLogger() level = LogLevel.ALL // ALL, HEADERS, BODY, INFO or NONE } } 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 b90ea15f8c4..9d9d71e91d9 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/CheckRequest.kt @@ -1,8 +1,54 @@ package com.ricoh360.thetaclient -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.request.* -import io.ktor.http.content.* +import com.ricoh360.thetaclient.transferred.AccessInfo +import com.ricoh360.thetaclient.transferred.AiAutoThumbnail +import com.ricoh360.thetaclient.transferred.AutoBracket +import com.ricoh360.thetaclient.transferred.BluetoothPower +import com.ricoh360.thetaclient.transferred.BluetoothRole +import com.ricoh360.thetaclient.transferred.BurstMode +import com.ricoh360.thetaclient.transferred.BurstOption +import com.ricoh360.thetaclient.transferred.CameraControlSource +import com.ricoh360.thetaclient.transferred.CameraLock +import com.ricoh360.thetaclient.transferred.CameraLockConfig +import com.ricoh360.thetaclient.transferred.CameraMode +import com.ricoh360.thetaclient.transferred.CameraPower +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.CommandApiRequest +import com.ricoh360.thetaclient.transferred.CompassDirectionRef +import com.ricoh360.thetaclient.transferred.EthernetConfig +import com.ricoh360.thetaclient.transferred.FaceDetect +import com.ricoh360.thetaclient.transferred.Gain +import com.ricoh360.thetaclient.transferred.GetOptionsRequest +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.MicrophoneNoiseReduction +import com.ricoh360.thetaclient.transferred.MobileNetworkSetting +import com.ricoh360.thetaclient.transferred.NetworkType +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.SetOptionsRequest +import com.ricoh360.thetaclient.transferred.ShootingFunction +import com.ricoh360.thetaclient.transferred.ShootingMethod +import com.ricoh360.thetaclient.transferred.TimeShift +import com.ricoh360.thetaclient.transferred.TopBottomCorrectionOption +import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation +import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotationSupport +import com.ricoh360.thetaclient.transferred.UsbConnection +import com.ricoh360.thetaclient.transferred.VideoStitching +import com.ricoh360.thetaclient.transferred.VisibilityReduction +import com.ricoh360.thetaclient.transferred.WhiteBalance +import com.ricoh360.thetaclient.transferred.WhiteBalanceAutoStrength +import com.ricoh360.thetaclient.transferred.WlanAntennaConfig +import com.ricoh360.thetaclient.transferred.WlanFrequency +import com.ricoh360.thetaclient.transferred.WlanFrequencyClMode +import io.ktor.client.request.HttpRequestData +import io.ktor.http.content.TextContent import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json @@ -56,6 +102,7 @@ internal class CheckRequest { fun checkSetOptions( request: HttpRequestData, + accessInfo: AccessInfo? = null, aiAutoThumbnail: AiAutoThumbnail? = null, aperture: Float? = null, autoBracket: AutoBracket? = null, @@ -65,11 +112,14 @@ internal class CheckRequest { burstMode: BurstMode? = null, burstOption: BurstOption? = null, cameraControlSource: CameraControlSource? = null, + cameraLock: CameraLock? = null, + cameraLockConfig: CameraLockConfig? = null, cameraMode: CameraMode? = null, cameraPower: CameraPower? = null, captureInterval: Int? = null, captureMode: CaptureMode? = null, captureNumber: Int? = null, + compassDirectionRef: CompassDirectionRef? = null, compositeShootingOutputInterval: Int? = null, compositeShootingTime: Int? = null, clientVersion: Int? = null, @@ -91,8 +141,11 @@ internal class CheckRequest { isoAutoHighLimit: Int? = null, language: Language? = null, maxRecordableTime: Int? = null, + microphoneNoiseReduction: MicrophoneNoiseReduction? = null, + mobileNetworkSetting: MobileNetworkSetting? = null, networkType: NetworkType? = null, offDelay: Int? = null, + offDelayUsb: Int? = null, powerSaving: PowerSaving? = null, preset: Preset? = null, previewFormat: PreviewFormat? = null, @@ -104,11 +157,15 @@ internal class CheckRequest { timeShift: TimeShift? = null, topBottomCorrection: TopBottomCorrectionOption? = null, topBottomCorrectionRotation: TopBottomCorrectionRotation? = null, + topBottomCorrectionRotationSupport: TopBottomCorrectionRotationSupport? = null, + usbConnection: UsbConnection? = null, videoStitching: VideoStitching? = null, visibilityReduction: VisibilityReduction? = null, whiteBalance: WhiteBalance? = null, whiteBalanceAutoStrength: WhiteBalanceAutoStrength? = null, + wlanAntennaConfig: WlanAntennaConfig? = null, wlanFrequency: WlanFrequency? = null, + wlanFrequencyClMode: WlanFrequencyClMode? = null, ) { assertEquals(request.url.encodedPath, "/osc/commands/execute", "command request path") @@ -120,6 +177,9 @@ internal class CheckRequest { } val optionsRequest = js.decodeFromString(body.text) + accessInfo?.let { + assertEquals(optionsRequest.parameters.options._accessInfo, it, "setOptions _accessInfo") + } aiAutoThumbnail?.let { assertEquals(optionsRequest.parameters.options._aiAutoThumbnail, it, "setOptions _aiAutoThumbnail") } @@ -147,6 +207,12 @@ internal class CheckRequest { cameraControlSource?.let { assertEquals(optionsRequest.parameters.options._cameraControlSource, it, "setOptions cameraControlSource") } + cameraLock?.let { + assertEquals(optionsRequest.parameters.options._cameraLock, it, "setOptions cameraLock") + } + cameraLockConfig?.let { + assertEquals(optionsRequest.parameters.options._cameraLockConfig, it, "setOptions cameraLockConfig") + } cameraMode?.let { assertEquals(optionsRequest.parameters.options._cameraMode, it, "setOptions cameraMode") } @@ -162,6 +228,9 @@ internal class CheckRequest { captureNumber?.let { assertEquals(optionsRequest.parameters.options.captureNumber, it, "setOptions captureNumber") } + compassDirectionRef?.let { + assertEquals(optionsRequest.parameters.options._compassDirectionRef, it, "setOptions compassDirectionRef") + } compositeShootingOutputInterval?.let { assertEquals(optionsRequest.parameters.options._compositeShootingOutputInterval, it, "setOptions compositeShootingOutputInterval") } @@ -225,12 +294,21 @@ internal class CheckRequest { maxRecordableTime?.let { assertEquals(optionsRequest.parameters.options._maxRecordableTime, it, "setOptions maxRecordableTime") } + microphoneNoiseReduction?.let { + assertEquals(optionsRequest.parameters.options._microphoneNoiseReduction, it, "setOptions microphoneNoiseReduction") + } + mobileNetworkSetting?.let { + assertEquals(optionsRequest.parameters.options._mobileNetworkSetting, it, "setOptions mobileNetworkSetting") + } networkType?.let { assertEquals(optionsRequest.parameters.options._networkType, it, "setOptions networkType") } offDelay?.let { assertEquals(optionsRequest.parameters.options.offDelay, it, "setOptions offDelay") } + offDelayUsb?.let { + assertEquals(optionsRequest.parameters.options._offDelayUSB, it, "setOptions offDelayUsb") + } powerSaving?.let { assertEquals(optionsRequest.parameters.options._powerSaving, it, "setOptions powerSaving") } @@ -264,6 +342,12 @@ internal class CheckRequest { topBottomCorrectionRotation?.let { assertEquals(optionsRequest.parameters.options._topBottomCorrectionRotation, it, "setOptions topBottomCorrectionRotation") } + topBottomCorrectionRotationSupport?.let { + assertEquals(optionsRequest.parameters.options._topBottomCorrectionRotationSupport, it, "setOptions topBottomCorrectionRotationSupport") + } + usbConnection?.let { + assertEquals(optionsRequest.parameters.options._usbConnection, it, "setOptions usbConnection") + } videoStitching?.let { assertEquals(optionsRequest.parameters.options.videoStitching, it, "setOptions videoStitching") } @@ -276,9 +360,29 @@ internal class CheckRequest { whiteBalanceAutoStrength?.let { assertEquals(optionsRequest.parameters.options._whiteBalanceAutoStrength, it, "setOptions whiteBalanceAutoStrength") } + wlanAntennaConfig?.let { + assertEquals(optionsRequest.parameters.options._wlanAntennaConfig, it, "setOptions wlanAntennaConfig") + } wlanFrequency?.let { assertEquals(optionsRequest.parameters.options._wlanFrequency, it, "setOptions wlanFrequency") } + wlanFrequencyClMode?.let { + assertEquals( + optionsRequest.parameters.options._wlanFrequencyCLmode?.enable2_4, + it.enable2_4, + "setOptions wlanFrequencyCLmode" + ) + assertEquals( + optionsRequest.parameters.options._wlanFrequencyCLmode?.enable5_2, + it.enable5_2, + "setOptions wlanFrequencyCLmode" + ) + assertEquals( + optionsRequest.parameters.options._wlanFrequencyCLmode?.enable5_8, + it.enable5_8, + "setOptions wlanFrequencyCLmode" + ) + } } fun checkGetOptions(request: HttpRequestData, optionNames: List) { 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 a4166c0f621..b3474af877a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/MockApiClient.kt @@ -113,7 +113,7 @@ internal object MockApiClient { filePaths: List, connectionTimeout: Long, socketTimeout: Long, - callback: ((Int) -> Unit)?, + callback: ((Int) -> Boolean)?, boundary: String, ): ByteArray { onMultipartPostRequest?.let { 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 d56ccdac0e0..cbe6fc57694 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/PreviewTest.kt @@ -1,14 +1,15 @@ package com.ricoh360.thetaclient import com.ricoh360.thetaclient.transferred.GetLivePreviewRequest -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +import io.ktor.client.request.HttpRequestData +import io.ktor.http.HttpStatusCode +import io.ktor.http.content.TextContent import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.json.Json -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class PreviewTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/BurstCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/BurstCaptureTest.kt index 63ee1a44b0b..94068b52649 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/BurstCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/BurstCaptureTest.kt @@ -1052,7 +1052,12 @@ class BurstCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when { - stateCounter < 2 -> assertEquals( + stateCounter == 0 -> assertEquals( + status, + CapturingStatusEnum.STARTING + ) + + stateCounter == 1 -> assertEquals( status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCaptureTest.kt index 6a0457e18f1..b9efb07ea71 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/CompositeIntervalCaptureTest.kt @@ -963,7 +963,12 @@ class CompositeIntervalCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when { - stateCounter < 2 -> assertEquals( + stateCounter == 0 -> assertEquals( + status, + CapturingStatusEnum.STARTING + ) + + stateCounter == 1 -> assertEquals( status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ContinuousCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ContinuousCaptureTest.kt index 1df02cf8822..878b47ad518 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ContinuousCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ContinuousCaptureTest.kt @@ -5,13 +5,21 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.network.sockets.* -import io.ktor.http.* -import io.ktor.utils.io.* -import kotlinx.coroutines.* +import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.MediaFileFormat +import com.ricoh360.thetaclient.transferred.MediaType +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlinx.coroutines.withTimeout +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue class ContinuousCaptureTest { private val endpoint = "http://192.168.1.1:80/" @@ -576,7 +584,12 @@ class ContinuousCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when { - stateCounter < 2 -> assertEquals( + stateCounter == 0 -> assertEquals( + status, + CapturingStatusEnum.STARTING + ) + + stateCounter == 1 -> assertEquals( status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCaptureTest.kt index c756e886888..cc9ba226772 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/LimitlessIntervalCaptureTest.kt @@ -6,12 +6,20 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaApi import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CaptureMode -import io.ktor.client.network.sockets.* -import io.ktor.http.* -import io.ktor.utils.io.* -import kotlinx.coroutines.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.CompletableDeferred +import kotlinx.coroutines.delay +import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlinx.coroutines.withTimeout +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue class LimitlessIntervalCaptureTest { private val endpoint = "http://192.168.1.1:80/" @@ -120,6 +128,7 @@ class LimitlessIntervalCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when (stateCounter) { + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) 1 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) else -> assertEquals(status, CapturingStatusEnum.CAPTURING) } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCaptureTest.kt index b0404baa818..1fafe946ae1 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/MultiBracketCaptureTest.kt @@ -130,11 +130,13 @@ class MultiBracketCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { onCapturingCounter++ - if (onSelfTimer) { - assertEquals(status, CapturingStatusEnum.CAPTURING) - } else { - onSelfTimer = true - assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + when { + onCapturingCounter == 1 -> assertEquals(status, CapturingStatusEnum.STARTING) + onSelfTimer -> assertEquals(status, CapturingStatusEnum.CAPTURING) + else -> { + onSelfTimer = true + assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + } } } @@ -243,6 +245,7 @@ class MultiBracketCaptureTest { ThetaApi.lastSetTimeConsumingOptionTime = 0 var files: List? = null + var onCapturingCount = 0 multiBracketCapture.startCapture(object : MultiBracketCapture.StartCaptureCallback { override fun onCaptureCompleted(fileUrls: List?) { assertTrue(true, "onCaptureCompleted") @@ -255,7 +258,11 @@ class MultiBracketCaptureTest { } override fun onCapturing(status: CapturingStatusEnum) { - assertEquals(status, CapturingStatusEnum.CAPTURING) + when (onCapturingCount) { + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) + else -> assertEquals(status, CapturingStatusEnum.CAPTURING) + } + onCapturingCount += 1 } override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { @@ -345,6 +352,7 @@ class MultiBracketCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when (counter) { + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) 4 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) 5 -> assertEquals(status, CapturingStatusEnum.CAPTURING) 6 -> assertEquals(status, CapturingStatusEnum.CAPTURING) 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 0493661b1d9..70db8e9ff0f 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 @@ -8,14 +8,19 @@ import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CaptureMode import com.ricoh360.thetaclient.transferred.MediaFileFormat import com.ricoh360.thetaclient.transferred.MediaType -import io.ktor.client.network.sockets.* -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest import kotlinx.coroutines.withTimeout -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 class PhotoCaptureTest { private val endpoint = "http://192.168.1.1:80/" @@ -75,6 +80,7 @@ class PhotoCaptureTest { else -> { when (request.url.encodedPath) { "/osc/commands/status" -> { + assertEquals(request.headers.get("Cache-Control"), "no-store") assertEquals( request.url.encodedPath, requestPathArray[2], @@ -82,6 +88,7 @@ class PhotoCaptureTest { ) responseArray[2] } + else -> stateIdleResponse } } @@ -102,6 +109,7 @@ class PhotoCaptureTest { assertNull(photoCapture.getFileFormat(), "set option fileFormat") var file: String? = null + var onCapturingCount = 0 photoCapture.takePicture(object : PhotoCapture.TakePictureCallback { override fun onSuccess(fileUrl: String?) { file = fileUrl @@ -109,7 +117,11 @@ class PhotoCaptureTest { } override fun onCapturing(status: CapturingStatusEnum) { - assertEquals(status, CapturingStatusEnum.CAPTURING) + when (onCapturingCount) { + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) + else -> assertEquals(status, CapturingStatusEnum.CAPTURING) + } + onCapturingCount += 1 } override fun onError(exception: ThetaRepository.ThetaRepositoryException) { @@ -531,7 +543,7 @@ class PhotoCaptureTest { // check result assertEquals( photoCapture.getAperture(), - it, + if (it == ThetaRepository.ApertureEnum.UNKNOWN) null else it, "set option aperture $valueIndex" ) @@ -641,7 +653,7 @@ class PhotoCaptureTest { // check result assertEquals( - photoCapture.getExposureCompensation(), + photoCapture.getExposureCompensation() ?: ThetaRepository.ExposureCompensationEnum.UNKNOWN, it, "set option exposureCompensation $valueIndex" ) @@ -696,7 +708,7 @@ class PhotoCaptureTest { // check result assertEquals( - photoCapture.getExposureDelay(), + photoCapture.getExposureDelay() ?: ThetaRepository.ExposureDelayEnum.UNKNOWN, it, "set option exposureDelay $valueIndex" ) @@ -1501,7 +1513,8 @@ class PhotoCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when (onCapturingCount) { - 0 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) + 1 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) else -> assertEquals(status, CapturingStatusEnum.CAPTURING) } onCapturingCount += 1 @@ -1520,6 +1533,6 @@ class PhotoCaptureTest { // check result assertTrue(file?.startsWith("http://") ?: false, "take picture") - assertEquals(onCapturingCount, 2) + assertEquals(onCapturingCount, 3) } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCaptureTest.kt index 010da96cc95..626abd868c5 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCaptureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/ShotCountSpecifiedIntervalCaptureTest.kt @@ -106,12 +106,14 @@ class ShotCountSpecifiedIntervalCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { onCapturingCounter++ - if (onSelfTimer) { - assertEquals(status, CapturingStatusEnum.CAPTURING) - } else { - onSelfTimer = true - assertEquals(onCapturingCounter, 1) - assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + when { + onCapturingCounter == 1 -> assertEquals(status, CapturingStatusEnum.STARTING) + onSelfTimer -> assertEquals(status, CapturingStatusEnum.CAPTURING) + else -> { + onSelfTimer = true + assertEquals(onCapturingCounter, 2) + assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + } } } @@ -180,8 +182,10 @@ class ShotCountSpecifiedIntervalCaptureTest { override fun onProgress(completion: Float) { assertTrue(false, "onProgress") } + override fun onCapturing(status: CapturingStatusEnum) { when (counter) { + 3 -> assertEquals(status, CapturingStatusEnum.STARTING) 4 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) else -> assertEquals(status, CapturingStatusEnum.CAPTURING) } 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 af315e7120e..1f9e534799a 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 @@ -132,12 +132,14 @@ class TimeShiftCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { onCapturingCounter++ - if (onSelfTimer) { - assertEquals(status, CapturingStatusEnum.CAPTURING) - } else { - onSelfTimer = true - assertEquals(onCapturingCounter, 1) - assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + when { + onCapturingCounter == 1 -> assertEquals(status, CapturingStatusEnum.STARTING) + onSelfTimer -> assertEquals(status, CapturingStatusEnum.CAPTURING) + else -> { + onSelfTimer = true + assertEquals(onCapturingCounter, 2) + assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) + } } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCaptureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCaptureTest.kt new file mode 100644 index 00000000000..f6f4782b4a0 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/capture/TimeShiftManualCaptureTest.kt @@ -0,0 +1,1033 @@ +package com.ricoh360.thetaclient.capture + +import com.goncalossilva.resources.Resource +import com.ricoh360.thetaclient.CheckRequest +import com.ricoh360.thetaclient.MockApiClient +import com.ricoh360.thetaclient.ThetaApi +import com.ricoh360.thetaclient.ThetaRepository +import com.ricoh360.thetaclient.transferred.FirstShootingEnum +import com.ricoh360.thetaclient.transferred.TimeShift +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.CompletableDeferred +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.withTimeout +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull +import kotlin.test.assertTrue + +class TimeShiftManualCaptureTest { + private val endpoint = "http://192.168.1.1:80/" + + private var onCommandRequest: ((HttpRequestData) -> ByteReadChannel)? = null + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + MockApiClient.onRequest = { request -> + if (request.url.encodedPath == "/osc/state") { + MockApiClient.status = HttpStatusCode.OK + ByteReadChannel(Resource("src/commonTest/resources/TimeShiftManualCapture/state_shooting.json").readText()) + } else { + onCommandRequest?.let { it(request) } + ?: throw Exception("Not implement onCommandRequest") + } + } + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + MockApiClient.onRequest = null + onCommandRequest = null + } + + enum class TimeShiftState { + FIRST, + IDLE, + SECOND, + DONE, + } + + /** + * call startCapture + */ + @Test + fun startCaptureTest() = runTest { + // setup + val resCaptureProgress = + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json").readText() + val resCaptureDone = + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_done.json").readText() + val resStateShooting = + Resource("src/commonTest/resources/TimeShiftManualCapture/state_shooting.json").readText() + val resStateIdle = + Resource("src/commonTest/resources/TimeShiftManualCapture/state_idle.json").readText() + val resStateTimeShiftIdle = + Resource("src/commonTest/resources/TimeShiftManualCapture/state_timeshift_idle.json").readText() + val resStateTimeShiftShooting = + Resource("src/commonTest/resources/TimeShiftManualCapture/state_timeshift_shooting.json").readText() + val resSetOptions = + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText() + + val captureResponse = mapOf( + TimeShiftState.FIRST to resCaptureProgress, + TimeShiftState.IDLE to resCaptureProgress, + TimeShiftState.SECOND to resCaptureProgress, + TimeShiftState.DONE to resStateIdle, + ) + val stateResponse = mapOf( + TimeShiftState.FIRST to resStateTimeShiftShooting, + TimeShiftState.IDLE to resStateTimeShiftIdle, + TimeShiftState.SECOND to resStateShooting, + TimeShiftState.DONE to resStateIdle, + ) + + var timeShiftState = TimeShiftState.FIRST + MockApiClient.onRequest = { request -> + val response = when (request.url.encodedPath) { + "/osc/commands/execute" -> { + val command = CheckRequest.getCommandName(request) + when (command) { + "camera.setOptions" -> resSetOptions + "camera.startCapture" -> { + captureResponse[timeShiftState] + } + + else -> null + } + } + + "/osc/state" -> { + stateResponse[timeShiftState] + } + + "/osc/commands/status" -> { + when (timeShiftState) { + TimeShiftState.DONE -> resCaptureDone + else -> captureResponse[timeShiftState] + } + } + + else -> null + } + if (response == null) { + assertTrue(false, "error no response") + throw Exception() + } + ByteReadChannel(response) + } + + val deferred = CompletableDeferred() + val deferredStart = CompletableDeferred() + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .build() + + var file: String? = null + val capturing = capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + assertTrue(completion >= 0f, "onProgress") + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + + override fun onCapturing(status: CapturingStatusEnum) { + super.onCapturing(status) + when (timeShiftState) { + TimeShiftState.FIRST -> { + timeShiftState = TimeShiftState.IDLE + } + + TimeShiftState.IDLE -> { + deferredStart.complete(Unit) + } + + TimeShiftState.SECOND -> { + timeShiftState = TimeShiftState.DONE + } + + else -> Unit + } + } + }) + + runBlocking { + withTimeout(30_000) { + deferredStart.await() + } + } + + capturing.startSecondCapture() + timeShiftState = TimeShiftState.SECOND + + runBlocking { + withTimeout(30_000) { + deferred.await() + } + } + + // check result + assertTrue(file?.startsWith("http://") ?: false, "start time-shift manual") + } + + /** + * call cancelCapture test + */ + @Test + fun cancelCaptureTest() = runTest { + // setup + var isStop = false + onCommandRequest = { request -> + val textBody = request.body as TextContent + val path = if (textBody.text.contains("camera.stopCapture")) { + isStop = true + "src/commonTest/resources/TimeShiftManualCapture/stop_capture_done.json" + } else if (textBody.text.contains("camera.setOptions")) { + "src/commonTest/resources/setOptions/set_options_done.json" + } else { + if (isStop) + "src/commonTest/resources/TimeShiftManualCapture/start_capture_done_empty.json" + else + "src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json" + } + + ByteReadChannel(Resource(path).readText()) + } + + val deferred = CompletableDeferred() + val deferredStart = CompletableDeferred() + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setCheckStatusCommandInterval(100) + .build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + var file: String? = "" + val capturing = + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + assertEquals(completion, 0f, "onProgress") + if (!deferredStart.isCompleted) { + deferredStart.complete(Unit) + } + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(5000) { + deferredStart.await() + } + } + + capturing.cancelCapture() + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + // check result + assertTrue(isStop, "cancel time-shift is stop") + assertNull(file, "cancel time-shift manual") + } + + /** + * cancel shooting. + */ + @Test + fun cancelShootingTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_cancel.json").readText(), + ) + var counter = 0 + onCommandRequest = { _ -> + val index = counter++ + ByteReadChannel(responseArray[index]) + } + val deferred = CompletableDeferred() + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setCheckStatusCommandInterval(100) + .build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + var file: String? = "" + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + assertTrue(completion >= 0f, "onProgress") + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(30_000) { + deferred.await() + } + } + + // check result + assertNull(file, "cancel time-shift manual") + } + + /** + * cancel shooting with exception. + */ + @Test + fun cancelShootingWithExceptionTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_cancel.json").readText(), + ) + var counter = 0 + onCommandRequest = { _ -> + val index = counter++ + MockApiClient.status = if (index == 2) HttpStatusCode.Forbidden else HttpStatusCode.OK + ByteReadChannel(responseArray[index]) + } + val deferred = CompletableDeferred() + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setCheckStatusCommandInterval(100) + .build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + var file: String? = "" + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + file = fileUrl + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + assertTrue(completion >= 0f, "onProgress") + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "error start time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(30_000) { + deferred.await() + } + } + + // check result + assertNull(file, "cancel time-shift manual") + } + + /** + * Setting CheckStatusCommandInterval. + */ + @Test + fun settingCheckStatusCommandIntervalTest() = runTest { + val timeMillis = 1500L + + MockApiClient.onRequest = { + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setCheckStatusCommandInterval(timeMillis) + .build() + + // check result + assertEquals( + capture.getCheckStatusCommandInterval(), + timeMillis, + "set CheckStatusCommandInterval $timeMillis" + ) + } + + /** + * Setting IsFrontFirst. + */ + @Test + fun settingIsFrontFirstTest() = runTest { + val isFrontFirst = false + + var index = 0 + MockApiClient.onRequest = { request -> + // check request + when (index) { + 1 -> { + CheckRequest.checkSetOptions( + request, + timeShift = TimeShift(firstShooting = FirstShootingEnum.REAR) + ) + } + } + index += 1 + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setIsFrontFirst(isFrontFirst) + .build() + + // check result + assertEquals( + capture.getTimeShiftSetting()?.isFrontFirst ?: true, + isFrontFirst, + "set setIsFrontFirst $isFrontFirst" + ) + } + + /** + * Setting FirstInterval. + */ + @Test + fun settingFirstIntervalTest() = runTest { + val interval: ThetaRepository.TimeShiftIntervalEnum = + ThetaRepository.TimeShiftIntervalEnum.INTERVAL_3 + + var index = 0 + MockApiClient.onRequest = { request -> + // check request + when (index) { + 1 -> { + CheckRequest.checkSetOptions(request, timeShift = TimeShift(firstInterval = 3)) + } + } + index += 1 + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setFirstInterval(interval) + .build() + + // check result + assertEquals( + capture.getTimeShiftSetting()?.firstInterval, + interval, + "set setFirstInterval $interval" + ) + } + + /** + * Setting SecondInterval. + */ + @Test + fun settingSecondIntervalTest() = runTest { + val interval: ThetaRepository.TimeShiftIntervalEnum = + ThetaRepository.TimeShiftIntervalEnum.INTERVAL_5 + + var index = 0 + MockApiClient.onRequest = { request -> + // check request + when (index) { + 1 -> { + CheckRequest.checkSetOptions(request, timeShift = TimeShift(secondInterval = 5)) + } + } + index += 1 + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setSecondInterval(interval) + .build() + + // check result + assertEquals( + capture.getTimeShiftSetting()?.secondInterval, + interval, + "set setSecondInterval $interval" + ) + } + + /** + * Error response to build call. + */ + @Test + fun buildErrorResponseTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_error.json").readText(), // set captureMode error + "Not json" // json error + ) + var counter = 0 + + MockApiClient.onRequest = { _ -> + val index = counter++ + ByteReadChannel(responseArray[index]) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + + var exceptionSetCaptureMode = false + try { + thetaRepository.getTimeShiftManualCaptureBuilder().build() + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue((e.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, "") + exceptionSetCaptureMode = true + } + assertTrue(exceptionSetCaptureMode, "setOptions captureMode error response") + + // execute not json response + var exceptionNotJson = false + try { + thetaRepository.getTimeShiftManualCaptureBuilder().build() + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue( + (e.message?.indexOf("json", 0, true) ?: -1) >= 0 + || (e.message?.indexOf("Illegal", 0, true) ?: -1) >= 0, + "setOptions option not json error response" + ) + exceptionNotJson = true + } + assertTrue(exceptionNotJson, "setOptions option error response") + } + + /** + * Error exception to build call. + */ + @Test + fun buildExceptionTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_error.json").readText(), // status error & error json + "timeout UnitTest" // timeout + ) + var counter = 0 + + MockApiClient.onRequest = { _ -> + val index = counter++ + when (index) { + 0 -> MockApiClient.status = HttpStatusCode.ServiceUnavailable + 1 -> throw ConnectTimeoutException("timeout") + } + ByteReadChannel(responseArray[index]) + } + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + + // execute status error and json response + var exceptionStatusJson = false + try { + thetaRepository.getTimeShiftManualCaptureBuilder().build() + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue( + (e.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, + "status error and json response" + ) + exceptionStatusJson = true + } + assertTrue(exceptionStatusJson, "status error and json response") + + // execute timeout exception + var exceptionOther = false + try { + thetaRepository.getTimeShiftManualCaptureBuilder().build() + } catch (e: ThetaRepository.NotConnectedException) { + assertTrue((e.message?.indexOf("time", 0, true) ?: -1) >= 0, "timeout exception") + exceptionOther = true + } + assertTrue(exceptionOther, "other exception") + } + + /** + * Error response to startCapture call + */ + @Test + fun startCaptureErrorResponseTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_error.json").readText(), // startCapture error + "Not json" // json error + ) + var counter = 0 + MockApiClient.onRequest = { _ -> + val index = counter++ + ByteReadChannel(responseArray[index]) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + // execute error response + var deferred = CompletableDeferred() + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, + "capture time-shift error response" + ) + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + // execute json error response + deferred = CompletableDeferred() + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.length ?: -1) >= 0, + "capture time-shift json error response" + ) + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(2000) { + deferred.await() + } + } + } + + /** + * Error exception to startCapture call + */ + @Test + fun startCaptureExceptionTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_error.json").readText(), // startCapture error + "Status error UnitTest", // status error not json + "timeout UnitTest" // timeout + ) + var counter = 0 + MockApiClient.onRequest = { _ -> + val index = counter++ + when (index) { + 2 -> MockApiClient.status = HttpStatusCode.ServiceUnavailable + 3 -> MockApiClient.status = HttpStatusCode.ServiceUnavailable + 4 -> throw ConnectTimeoutException("timeout") + else -> MockApiClient.status = HttpStatusCode.OK + } + ByteReadChannel(responseArray[index]) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder().build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + // execute status error and json response + var deferred = CompletableDeferred() + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, + "status error and json response" + ) + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + // execute status error and not json response + deferred = CompletableDeferred() + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue((exception.message?.indexOf("503", 0, true) ?: -1) >= 0, "status error") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + // execute timeout exception + deferred = CompletableDeferred() + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("time", 0, true) ?: -1) >= 0, + "timeout exception" + ) + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + }) + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + } + + /** + * Error response to stopCapture call + */ + @Test + fun stopCaptureErrorResponseTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json").readText(), + Resource("src/commonTest/resources/TimeShiftManualCapture/stop_capture_error.json").readText(), // stopCapture error + "Not json" // json error + ) + var counter = 0 + onCommandRequest = { _ -> + val index = counter++ + ByteReadChannel(responseArray[index]) + } + val deferred = CompletableDeferred() + val deferredStart = CompletableDeferred() + + // execute + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_X + val capture = thetaRepository.getTimeShiftManualCaptureBuilder() + .setCheckStatusCommandInterval(100) + .build() + ThetaApi.lastSetTimeConsumingOptionTime = 0 + + var isCaptureFailed = false + var isStopFailed = false + val capturing = + capture.startCapture(object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + deferredStart.complete(Unit) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + isCaptureFailed = true + assertTrue( + (exception.message?.indexOf("Unknown", 0, true) ?: -1) >= 0, + "stop capture error response" + ) + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + isStopFailed = true + assertTrue( + (exception.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, + "stop capture error response" + ) + } + }) + + runBlocking { + withTimeout(5000) { + deferredStart.await() + } + } + + capturing.cancelCapture() + + runBlocking { + withTimeout(2000) { + deferred.await() + } + } + assertTrue(isStopFailed, "isStopFailed") + assertTrue(isCaptureFailed, "isCaptureFailed") + } + + /** + * Error exception to stopCapture call + */ + @Test + fun stopCaptureExceptionTest() = runTest { + // setup + val responseArray = arrayOf( + Resource("src/commonTest/resources/TimeShiftManualCapture/stop_capture_error.json").readText(), // status error & error json + "Status error UnitTest", // status error not json + "timeout UnitTest" // timeout + ) + var counter = 0 + MockApiClient.onRequest = { _ -> + val index = counter++ + when (index) { + 1 -> MockApiClient.status = HttpStatusCode.ServiceUnavailable + 2 -> MockApiClient.status = HttpStatusCode.ServiceUnavailable + 3 -> throw ConnectTimeoutException("timeout") + } + ByteReadChannel(responseArray[index]) + } + + var deferred = CompletableDeferred() + + var capturing = + TimeShiftManualCapturing( + endpoint, + object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("UnitTest", 0, true) ?: -1) >= 0, + "status error and json response" + ) + deferred.complete(Unit) + } + }) + + capturing.cancelCapture() + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + deferred = CompletableDeferred() + capturing = TimeShiftManualCapturing( + endpoint, + object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("503", 0, true) ?: -1) >= 0, + "status error" + ) + deferred.complete(Unit) + } + }) + + capturing.cancelCapture() + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + + deferred = CompletableDeferred() + capturing = TimeShiftManualCapturing( + endpoint, + object : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onProgress(completion: Float) { + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue(false, "capture time-shift manual") + deferred.complete(Unit) + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + assertTrue( + (exception.message?.indexOf("time", 0, true) ?: -1) >= 0, + "timeout exception" + ) + deferred.complete(Unit) + } + }) + + capturing.cancelCapture() + + runBlocking { + withTimeout(5000) { + deferred.await() + } + } + } +} 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 594eb57740e..952f48e1eb8 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 @@ -125,6 +125,7 @@ class VideoCaptureTest { override fun onCapturing(status: CapturingStatusEnum) { when (stateCounter) { + 0 -> assertEquals(status, CapturingStatusEnum.STARTING) 1 -> assertEquals(status, CapturingStatusEnum.SELF_TIMER_COUNTDOWN) else -> assertEquals(status, CapturingStatusEnum.CAPTURING) } @@ -370,6 +371,7 @@ class VideoCaptureTest { Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2K_NO_CODEC, MediaFileFormat(MediaType.MP4, 1920, 960, null, null)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", null)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_NO_CODEC, MediaFileFormat(MediaType.MP4, 3840, 1920, null, null)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2K_15F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 15)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2K_30F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 30)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2K_60F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 60)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2_7K_2752_2F, MediaFileFormat(MediaType.MP4, 2752, 2752, "H.264/MPEG-4 AVC", 2)), @@ -380,6 +382,8 @@ class VideoCaptureTest { Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2_7K_2F, MediaFileFormat(MediaType.MP4, 2688, 2688, "H.264/MPEG-4 AVC", 2)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_3_6K_1F, MediaFileFormat(MediaType.MP4, 3648, 3648, "H.264/MPEG-4 AVC", 1)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_3_6K_2F, MediaFileFormat(MediaType.MP4, 3648, 3648, "H.264/MPEG-4 AVC", 2)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_2F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 2)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_5F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 5)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_10F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 10)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_15F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 15)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_30F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 30)), @@ -392,8 +396,21 @@ class VideoCaptureTest { Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_2F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 2)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_5F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 5)), Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_10F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 10)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_2K_30F_H265_HEVC, MediaFileFormat(MediaType.MP4, 1920, 960, "H.265/HEVC", 30)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 2)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 5)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 10)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_4K_30F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 30)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_5_7K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 2)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_5_7K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 5)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_5_7K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 10)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 2)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 5)), + Pair(ThetaRepository.VideoFileFormatEnum.VIDEO_7K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 10)), ) + assertEquals(ThetaRepository.VideoFileFormatEnum.entries.size, valueList.size) + val responseArray = arrayOf( Resource("src/commonTest/resources/setOptions/set_options_done.json").readText(), Resource("src/commonTest/resources/setOptions/set_options_done.json").readText() 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 c6c377f8a30..6a9d2cce040 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 @@ -4,14 +4,15 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -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.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class CancelVideoConvertTest { 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 53161131f7c..20d0c9b9f3d 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 @@ -4,16 +4,20 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.DeleteAccessPointRequest -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.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(ExperimentalCoroutinesApi::class) class DeleteAccessPointTest { 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 12d4bd1c388..0afaefa779e 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 @@ -4,14 +4,15 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -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.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class FinishWlanTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetLivePreviewTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetLivePreviewTest.kt index f498c52fa23..9fa033b2c65 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetLivePreviewTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetLivePreviewTest.kt @@ -1,17 +1,20 @@ package com.ricoh360.thetaclient.repository import com.ricoh360.thetaclient.MockApiClient +import com.ricoh360.thetaclient.PreviewClient import com.ricoh360.thetaclient.PreviewClientException import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.network.sockets.* -import kotlinx.coroutines.ExperimentalCoroutinesApi +import io.ktor.client.network.sockets.ConnectTimeoutException import kotlinx.coroutines.cancel import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestResult import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue -@OptIn(ExperimentalCoroutinesApi::class) class GetLivePreviewTest { private val endpoint = "http://192.168.1.1:80/" @@ -174,4 +177,20 @@ class GetLivePreviewTest { assertTrue(e.message!!.indexOf("timeout") >= 0, "Exception NotConnectedException") } } + + /** + * Timeout test + */ + @Test + fun previewClientTimeoutSettingTest(): TestResult = runTest { + var thetaRepository = ThetaRepository(endpoint) + assertEquals(20_000, PreviewClient.timeout.connectTimeout) + assertEquals(20_000, PreviewClient.timeout.requestTimeout) + assertEquals(20_000, PreviewClient.timeout.socketTimeout) + + thetaRepository = ThetaRepository(endpoint, null, ThetaRepository.Timeout(1, 2, 3)) + assertEquals(1, PreviewClient.timeout.connectTimeout) + assertEquals(2, PreviewClient.timeout.requestTimeout) + assertEquals(3, PreviewClient.timeout.socketTimeout) + } } 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 28ba8d63f6d..e62ec288c32 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 @@ -39,6 +39,7 @@ class GetOptionsTest { } private fun checkRequest(request: HttpRequestData, optionNames: List) { + assertEquals(request.headers.get("Cache-Control"), "no-store") val body = request.body as TextContent val js = Json { encodeDefaults = true // Encode properties with default value. 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 bb9faa77aa6..0f3d46ccb02 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 @@ -4,13 +4,18 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.EndPoint -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.utils.io.* +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -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(ExperimentalCoroutinesApi::class) class GetThetaInfoTest { @@ -34,6 +39,7 @@ class GetThetaInfoTest { // setup val jsonString = Resource("src/commonTest/resources/info/info_z1.json").readText() MockApiClient.onRequest = { request -> + assertEquals(request.headers.get("Cache-Control"), "no-store") assertEquals(request.url.encodedPath, "/osc/info", "request path") ByteReadChannel(jsonString) } @@ -302,6 +308,7 @@ class GetThetaInfoTest { ThetaRepository.ThetaModel.THETA_V, ThetaRepository.ThetaModel.THETA_Z1, ThetaRepository.ThetaModel.THETA_X, + ThetaRepository.ThetaModel.THETA_A1, ) val modelNameArray = arrayOf( "RICOH THETA S", @@ -311,6 +318,7 @@ class GetThetaInfoTest { "RICOH THETA V", "RICOH THETA Z1", "RICOH THETA X", + "RICOH360 THETA A1", ) modelEnumArray.forEachIndexed { index, it -> diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaLicenseTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaLicenseTest.kt index 36e77de6e6d..5b6c70b060b 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaLicenseTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/GetThetaLicenseTest.kt @@ -12,6 +12,7 @@ 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) @@ -32,6 +33,7 @@ class GetThetaLicenseTest { fun getPluginLicenseTest() = runTest { MockApiClient.onRequest = { request -> // check request + assertNull(request.headers.get("Cache-Control")) assertEquals(request.url.encodedPath, "/legal-information/open-source-licenses", "request path") ByteReadChannel(Resource("src/commonTest/resources/getThetaLicense/license.html").readText()) } 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 b41202ba596..da42856e843 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 @@ -3,12 +3,26 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import com.ricoh360.thetaclient.transferred.* -import io.ktor.client.network.sockets.* -import io.ktor.http.* -import io.ktor.utils.io.* +import com.ricoh360.thetaclient.transferred.CameraError +import com.ricoh360.thetaclient.transferred.CameraState +import com.ricoh360.thetaclient.transferred.CaptureStatus +import com.ricoh360.thetaclient.transferred.ChargingState +import com.ricoh360.thetaclient.transferred.MicrophoneOption +import com.ricoh360.thetaclient.transferred.ShootingFunction +import com.ricoh360.thetaclient.transferred.StateApiResponse +import com.ricoh360.thetaclient.transferred.StorageOption +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertNull +import kotlin.test.assertTrue class GetThetaStateTest { private val endpoint = "http://192.168.1.1:80/" @@ -31,6 +45,7 @@ class GetThetaStateTest { // setup val jsonString = Resource("src/commonTest/resources/state/state_z1.json").readText() MockApiClient.onRequest = { request -> + assertEquals(request.headers.get("Cache-Control"), "no-store") assertEquals(request.url.encodedPath, "/osc/state", "request path") ByteReadChannel(jsonString) } @@ -86,6 +101,7 @@ class GetThetaStateTest { // setup val jsonString = Resource("src/commonTest/resources/state/state_x.json").readText() MockApiClient.onRequest = { request -> + assertEquals(request.headers.get("Cache-Control"), "no-store") assertEquals(request.url.encodedPath, "/osc/state", "request path") ByteReadChannel(jsonString) } @@ -570,6 +586,11 @@ class GetThetaStateTest { ThetaRepository.CaptureStatusEnum.BURST_SHOOTING, "CaptureStatusEnum" ) + assertEquals( + ThetaRepository.CaptureStatusEnum.get(CaptureStatus.TIME_SHIFT_SHOOTING_IDLE), + ThetaRepository.CaptureStatusEnum.TIME_SHIFT_SHOOTING_IDLE, + "CaptureStatusEnum" + ) // check ChargingStateEnum assertEquals( diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListAccessPointsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListAccessPointsTest.kt index 26b9ca1ff79..3a1a20d0abf 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListAccessPointsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListAccessPointsTest.kt @@ -7,14 +7,18 @@ import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.AccessPoint import com.ricoh360.thetaclient.transferred.AuthenticationMode import com.ricoh360.thetaclient.transferred.IpAddressAllocation -import io.ktor.client.network.sockets.* -import io.ktor.http.* -import io.ktor.utils.io.* -import kotlinx.coroutines.ExperimentalCoroutinesApi +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.test.runTest -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(ExperimentalCoroutinesApi::class) class ListAccessPointsTest { private val endpoint = "http://192.168.1.1:80/" @@ -59,6 +63,24 @@ class ListAccessPointsTest { } } + /** + * call listAccessPoints. The security is unknown. + */ + @Test + fun listAccessPointsSecurityUnknownTest() = runTest { + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkCommandName(request, "camera._listAccessPoints") + ByteReadChannel(Resource("src/commonTest/resources/listAccessPoints/list_access_points_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val response = thetaRepository.listAccessPoints() + + assertEquals(response.size, 1, "Has One AccessPoints") + assertEquals(response[0].authMode, ThetaRepository.AuthModeEnum.UNKNOWN) + } + /** * call listAccessPoints. Response is empty data. */ @@ -186,7 +208,9 @@ class ListAccessPointsTest { ipAddressAllocation = ipAddressAllocation, ipAddress = "ipAddress", subnetMask = "subnetMask", - defaultGateway = "defaultGateway" + defaultGateway = "defaultGateway", + dns1 = "dns1", + dns2 = "dns2" ) val accessPoint = ThetaRepository.AccessPoint(orgAccessPoint) @@ -199,6 +223,8 @@ class ListAccessPointsTest { assertEquals(accessPoint.ipAddress, orgAccessPoint.ipAddress, "ipAddress") assertEquals(accessPoint.subnetMask, orgAccessPoint.subnetMask, "subnetMask") assertEquals(accessPoint.defaultGateway, orgAccessPoint.defaultGateway, "defaultGateway") + assertEquals(accessPoint.dns1, orgAccessPoint.dns1, "dns1") + assertEquals(accessPoint.dns2, orgAccessPoint.dns2, "dns2") } /** @@ -206,8 +232,10 @@ class ListAccessPointsTest { */ @Test fun checkAccessPointsTest() = runTest { + checkAccessPoint(AuthenticationMode.UNKNOWN, IpAddressAllocation.DYNAMIC, ThetaRepository.AuthModeEnum.UNKNOWN, true) checkAccessPoint(AuthenticationMode.NONE, IpAddressAllocation.DYNAMIC, ThetaRepository.AuthModeEnum.NONE, true) checkAccessPoint(AuthenticationMode.WEP, IpAddressAllocation.STATIC, ThetaRepository.AuthModeEnum.WEP, false) checkAccessPoint(AuthenticationMode.WPA_WPA2_PSK, IpAddressAllocation.DYNAMIC, ThetaRepository.AuthModeEnum.WPA, true) + checkAccessPoint(AuthenticationMode.WPA3_SAE, IpAddressAllocation.DYNAMIC, ThetaRepository.AuthModeEnum.WPA3, true) } } 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 0888d95d5f5..d60a77ad073 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 @@ -7,16 +7,21 @@ import com.ricoh360.thetaclient.transferred.FileType import com.ricoh360.thetaclient.transferred.ListFilesRequest import com.ricoh360.thetaclient.transferred.Storage 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.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.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class ListFilesTest { @@ -512,7 +517,9 @@ class ListFilesTest { @Test fun convertCodecEnumTest() = runTest { val values = listOf( + Pair(ThetaRepository.CodecEnum.UNKNOWN, null), Pair(ThetaRepository.CodecEnum.H264MP4AVC, "H.264/MPEG-4 AVC"), + Pair(ThetaRepository.CodecEnum.H265HEVC, "H.265/HEVC"), ) assertEquals(ThetaRepository.CodecEnum.values().size, values.size) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListPluginsTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListPluginsTest.kt index a596439ed61..ac84007e4bd 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListPluginsTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/ListPluginsTest.kt @@ -3,13 +3,15 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class ListPluginsTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/PluginControlTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/PluginControlTest.kt index b8dd794c4dd..743d1a7f7bb 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/PluginControlTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/PluginControlTest.kt @@ -3,15 +3,17 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -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.delay import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class PluginControlTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RebootTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RebootTest.kt new file mode 100644 index 00000000000..7c56075c2e0 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/RebootTest.kt @@ -0,0 +1,176 @@ +package com.ricoh360.thetaclient.repository + +import com.goncalossilva.resources.Resource +import com.ricoh360.thetaclient.CheckRequest +import com.ricoh360.thetaclient.MockApiClient +import com.ricoh360.thetaclient.ThetaRepository +import io.ktor.client.network.sockets.ConnectTimeoutException +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNotEquals +import kotlin.test.assertTrue + +class RebootTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * call reboot. + */ + @Test + fun rebootTest() = runTest { + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkCommandName(request, "camera._reboot") + + ByteReadChannel(Resource("src/commonTest/resources/reboot/reboot_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + thetaRepository.reboot() + assertTrue(true, "call reboot") + } + + /** + * Error not json response to reboot call + */ + @Test + fun rebootNotJsonResponseTest() = runTest { + MockApiClient.onRequest = { _ -> + ByteReadChannel("Not json") + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + try { + thetaRepository.reboot() + assertTrue(false, "response is normal.") + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue( + e.message!!.indexOf("json", 0, true) >= 0 || + e.message!!.indexOf("Illegal", 0, true) >= 0, + "error response" + ) + } + } + + /** + * Error response to reboot call + */ + @Test + fun rebootErrorResponseTest() = runTest { + MockApiClient.onRequest = { _ -> + ByteReadChannel(Resource("src/commonTest/resources/reboot/reboot_error.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + try { + thetaRepository.reboot() + assertTrue(false, "response is normal.") + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue(e.message!!.indexOf("UnitTest", 0, true) >= 0, "error response") + } + } + + /** + * Error response and status error to reboot call + */ + @Test + fun rebootErrorResponseAndStatusErrorTest() = runTest { + MockApiClient.onRequest = { _ -> + MockApiClient.status = HttpStatusCode.ServiceUnavailable + ByteReadChannel(Resource("src/commonTest/resources/reboot/reboot_error.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + try { + thetaRepository.reboot() + assertTrue(false, "response is normal.") + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue(e.message!!.indexOf("UnitTest", 0, true) >= 0, "error response") + } + } + + /** + * Status error to reboot call + */ + @Test + fun rebootStatusErrorTest() = runTest { + MockApiClient.onRequest = { _ -> + MockApiClient.status = HttpStatusCode.ServiceUnavailable + ByteReadChannel("Not json") + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + try { + thetaRepository.reboot() + assertTrue(false, "response is normal.") + } catch (e: ThetaRepository.ThetaWebApiException) { + assertTrue(e.message!!.indexOf("503", 0, true) >= 0, "status error") + } + } + + /** + * Error exception to reboot call + */ + @Test + fun rebootExceptionTest() = runTest { + MockApiClient.onRequest = { _ -> + throw ConnectTimeoutException("timeout") + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + try { + thetaRepository.reboot() + assertTrue(false, "response is normal.") + } catch (e: ThetaRepository.NotConnectedException) { + assertTrue(e.message!!.indexOf("time", 0, true) >= 0, "timeout exception") + } + } + + /** + * call reboot. + */ + @Test + fun supportedTest() = runTest { + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkCommandName(request, "camera._reboot") + + ByteReadChannel(Resource("src/commonTest/resources/reboot/reboot_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + thetaRepository.cameraModel = ThetaRepository.ThetaModel.THETA_A1 + ThetaRepository.ThetaModel.entries.forEach { + thetaRepository.cameraModel = it + + try { + thetaRepository.reboot() + assertEquals(it, ThetaRepository.ThetaModel.THETA_A1, "call reboot") + } catch (e: Throwable) { + assertNotEquals(it, ThetaRepository.ThetaModel.THETA_A1) + assertEquals(e.message, "disabledCommand") + } + } + } +} 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 7168bc4b7a1..ac6ca05913a 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 @@ -4,14 +4,15 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -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.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class ResetTest { 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 e8b41e9f0d5..f526195dd00 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 @@ -4,14 +4,16 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -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.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class RestoreSettingsTest { 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 8df071618f5..a22690952f8 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 @@ -11,7 +11,6 @@ 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.json.Json @@ -21,7 +20,7 @@ import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertTrue -@OptIn(ExperimentalSerializationApi::class, ExperimentalCoroutinesApi::class) +@OptIn(ExperimentalSerializationApi::class) class SetAccessPointTest { private val endpoint = "http://192.168.1.1:80/" @@ -46,6 +45,8 @@ class SetAccessPointTest { ipAddress: String?, subnetMask: String?, defaultGateway: String?, + dns1: String?, + dns2: String?, proxy: ThetaRepository.Proxy? ) { assertEquals(request.url.encodedPath, "/osc/commands/execute", "request path") @@ -69,6 +70,8 @@ class SetAccessPointTest { assertEquals(it.ipAddress, ipAddress, "ipAddress") assertEquals(it.subnetMask, subnetMask, "subnetMask") assertEquals(it.defaultGateway, defaultGateway, "defaultGateway") + assertEquals(it.dns1, dns1, "dns1") + assertEquals(it.dns2, dns2, "dns2") assertEquals(it.proxy, proxy?.toTransferredProxy(), "_proxy") } } @@ -98,6 +101,8 @@ class SetAccessPointTest { null, null, null, + null, + null, proxy = proxy ) @@ -129,6 +134,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") MockApiClient.onRequest = { request -> @@ -144,6 +151,8 @@ class SetAccessPointTest { ipAddress, subnetMask, defaultGateway, + dns1, + dns2, proxy ) @@ -160,6 +169,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, proxy = proxy ) assertTrue(true, "response is normal.") @@ -184,6 +195,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val ipAddressAllocation = IpAddressAllocation.STATIC val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") @@ -196,6 +209,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, ipAddressAllocation = ipAddressAllocation, proxy = proxy ) @@ -228,6 +243,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val ipAddressAllocation = IpAddressAllocation.STATIC val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") @@ -240,6 +257,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, ipAddressAllocation = ipAddressAllocation, proxy = proxy ) @@ -269,6 +288,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val ipAddressAllocation = IpAddressAllocation.STATIC val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") @@ -281,6 +302,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, ipAddressAllocation = ipAddressAllocation, proxy = proxy ) @@ -310,6 +333,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val ipAddressAllocation = IpAddressAllocation.STATIC val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") @@ -322,6 +347,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, ipAddressAllocation = ipAddressAllocation, proxy = proxy ) @@ -350,6 +377,8 @@ class SetAccessPointTest { val ipAddress = "192.168.1.2" val subnetMask = "255.255.255.0" val defaultGateway = "192.168.1.3" + val dns1 = "192.168.1.55" + val dns2 = "192.168.1.66" val ipAddressAllocation = IpAddressAllocation.STATIC val proxy = ThetaRepository.Proxy(use = true, url = "https://xxx", port = 8081, userid = "abc", password = "pwpwpw111") @@ -362,6 +391,8 @@ class SetAccessPointTest { ipAddress = ipAddress, subnetMask = subnetMask, defaultGateway = defaultGateway, + dns1 = dns1, + dns2 = dns2, ipAddressAllocation = ipAddressAllocation, proxy = proxy ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginTest.kt index d988c4d11ee..77545597d89 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/SetPluginTest.kt @@ -3,13 +3,15 @@ package com.ricoh360.thetaclient.repository import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.network.sockets.* -import io.ktor.client.request.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class SetPluginTest { 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 081b745db2c..7227b2e4ba7 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 @@ -4,14 +4,15 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -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.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class StopSelfTimerTest { 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 458922ebae4..acc63b85d03 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 @@ -8,14 +8,22 @@ import com.ricoh360.thetaclient.KEY_AUTH_QOP import com.ricoh360.thetaclient.KEY_AUTH_REALM import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.http.* +import io.ktor.http.HttpHeaders +import io.ktor.http.HttpStatusCode import io.ktor.http.auth.AuthScheme import io.ktor.http.auth.HttpAuthHeader import io.ktor.http.auth.parseAuthorizationHeader -import io.ktor.utils.io.* +import io.ktor.http.headersOf +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -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(ExperimentalCoroutinesApi::class) class ThetaRepositoryClientModeTest { 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 5533cb6112d..8a82378a65a 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 @@ -5,14 +5,20 @@ import com.ricoh360.thetaclient.ApiClient import com.ricoh360.thetaclient.CheckRequest 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.delay import kotlinx.coroutines.joinAll import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest -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 class ThetaRepositoryTest { private val endpoint = "http://192.168.1.1:80/" @@ -756,6 +762,7 @@ class ThetaRepositoryTest { 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), + arrayOf("RICOH360 THETA A1", null, ThetaRepository.ThetaModel.THETA_A1), ) for (item in data) { assertEquals(ThetaRepository.ThetaModel.get(item[0] as String, item[1] as? String), item[2], (item[2] as? ThetaRepository.ThetaModel)?.name ?: "null") diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AccessInfoTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AccessInfoTest.kt new file mode 100644 index 00000000000..df15d1361a2 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AccessInfoTest.kt @@ -0,0 +1,197 @@ +package com.ricoh360.thetaclient.repository.options + +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.AccessInfo +import com.ricoh360.thetaclient.transferred.DhcpLeaseAddress +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.WlanFrequencyAccessInfo +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class AccessInfoTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option _accessInfo. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.AccessInfo + ) + val stringOptionNames = listOf( + "_accessInfo" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_access_info.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.accessInfo?.ssid, "ssid_test") + assertEquals(options.accessInfo?.ipAddress, "192.168.1.2") + assertEquals(options.accessInfo?.subnetMask, "255.255.0.0") + assertEquals(options.accessInfo?.defaultGateway, "192.168.1.12") + assertEquals(options.accessInfo?.dns1, "192.168.1.55") + assertEquals(options.accessInfo?.dns2, "192.168.1.66") + assertEquals(options.accessInfo?.proxyURL, "http://192.168.1.3") + assertEquals( + options.accessInfo?.frequency, + ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4 + ) + assertEquals(options.accessInfo?.wlanSignalStrength, -60) + assertEquals(options.accessInfo?.wlanSignalLevel, 4) + assertEquals(options.accessInfo?.lteSignalStrength, 0) + assertEquals(options.accessInfo?.lteSignalLevel, 0) + assertEquals(options.accessInfo?.dhcpLeaseAddress?.size, 1) + assertEquals(options.accessInfo?.dhcpLeaseAddress?.get(0)?.ipAddress, "192.168.1.5") + assertEquals(options.accessInfo?.dhcpLeaseAddress?.get(0)?.macAddress, "58:38:79:12:34:56") + assertEquals(options.accessInfo?.dhcpLeaseAddress?.get(0)?.hostName, "Macbook-Pro") + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertTest() = runTest { + val values = listOf( + Pair( + ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ), + AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = WlanFrequencyAccessInfo.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ) + ) + + values.forEach { + val orgOptions = Options( + _accessInfo = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.accessInfo, it.first, "accessInfo ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + accessInfo = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._accessInfo, it.second, "_accessInfo ${it.second}") + } + } + + @Test + fun convertWlanFrequencyAccessInfoTest() = runTest { + val values = listOf( + Pair(ThetaRepository.WlanFrequencyAccessInfoEnum.UNKNOWN, WlanFrequencyAccessInfo.UNKNOWN), + Pair(ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, WlanFrequencyAccessInfo.GHZ_2_4), + Pair(ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_5_2, WlanFrequencyAccessInfo.GHZ_5_2), + Pair(ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_5_8, WlanFrequencyAccessInfo.GHZ_5_8), + Pair(ThetaRepository.WlanFrequencyAccessInfoEnum.INITIAL_VALUE, WlanFrequencyAccessInfo.INITIAL_VALUE), + ) + + values.forEach { + val orgOptions = Options( + _accessInfo = AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = it.second, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.accessInfo?.frequency, it.first, "WlanFrequencyAccessInfo ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + accessInfo = ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = it.first, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ) + val options = orgOptions.toOptions() + assertEquals(options._accessInfo?.frequency, it.second, "WlanFrequencyAccessInfo ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailSupportTest.kt new file mode 100644 index 00000000000..8b52342faa0 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailSupportTest.kt @@ -0,0 +1,95 @@ +package com.ricoh360.thetaclient.repository.options + +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.AiAutoThumbnail +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class AiAutoThumbnailSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.AiAutoThumbnailSupport + ) + val stringOptionNames = listOf( + "_aiAutoThumbnailSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_ai_auto_thumbnail_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.aiAutoThumbnailSupport, + listOf( + ThetaRepository.AiAutoThumbnailEnum.ON, + ThetaRepository.AiAutoThumbnailEnum.OFF, + ThetaRepository.AiAutoThumbnailEnum.UNKNOWN, + ), + "aiAutoThumbnailSupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(AiAutoThumbnail.ON, AiAutoThumbnail.OFF), + listOf( + ThetaRepository.AiAutoThumbnailEnum.ON, + ThetaRepository.AiAutoThumbnailEnum.OFF + ) + ) + + val orgOptions = Options( + _aiAutoThumbnailSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.aiAutoThumbnailSupport, + values.second, + "aiAutoThumbnailSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + aiAutoThumbnailSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._aiAutoThumbnailSupport, + values.first, + "_aiAutoThumbnailSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailTest.kt index 620fcbd3c37..ab9cca46c4d 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AiAutoThumbnailTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.AiAutoThumbnail import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class AiAutoThumbnailTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureSupportTest.kt new file mode 100644 index 00000000000..8100e5786fe --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureSupportTest.kt @@ -0,0 +1,94 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class ApertureSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.ApertureSupport + ) + val stringOptionNames = listOf( + "apertureSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_aperture_support_X.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.apertureSupport, + listOf(ThetaRepository.ApertureEnum.APERTURE_2_4), + "apertureSupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(0.0f, 2.0f, 2.1f, 2.4f, 3.5f, 5.6f), + listOf( + ThetaRepository.ApertureEnum.APERTURE_AUTO, + ThetaRepository.ApertureEnum.APERTURE_2_0, + ThetaRepository.ApertureEnum.APERTURE_2_1, + ThetaRepository.ApertureEnum.APERTURE_2_4, + ThetaRepository.ApertureEnum.APERTURE_3_5, + ThetaRepository.ApertureEnum.APERTURE_5_6 + ) + ) + + val orgOptions = Options( + apertureSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.apertureSupport, + values.second, + "apertureSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + apertureSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options.apertureSupport, + null, + "apertureSupport ${values.first}" + ) + } +} diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureTest.kt index dd22a27598f..02a6fe85af9 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ApertureTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ApertureTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AutoBracketTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AutoBracketTest.kt index 3cb7b49e021..5cd1fc09008 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AutoBracketTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/AutoBracketTest.kt @@ -8,8 +8,8 @@ import com.ricoh360.thetaclient.transferred.AutoBracket import com.ricoh360.thetaclient.transferred.BracketParameter import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.WhiteBalance -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 kotlin.test.AfterTest 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 63550976f82..065310290b2 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,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class BitrateTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothPowerTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothPowerTest.kt index 3f70837dd87..d30fc125de3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothPowerTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothPowerTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.BluetoothPower import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class BluetoothPowerTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothRoleTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothRoleTest.kt index 403af278d0b..c5ee68998cf 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothRoleTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BluetoothRoleTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.BluetoothRole import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class BluetoothRoleTest { 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 a3706784488..f13894abe08 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 @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.BurstMode import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class BurstModeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstOptionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstOptionTest.kt index 90cccdff3c4..a1c16e62b73 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstOptionTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/BurstOptionTest.kt @@ -4,14 +4,22 @@ 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.* -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* +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.BurstOption +import com.ricoh360.thetaclient.transferred.BurstOrder +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class BurstOptionTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceSupportTest.kt new file mode 100644 index 00000000000..cad46116cdb --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceSupportTest.kt @@ -0,0 +1,95 @@ +package com.ricoh360.thetaclient.repository.options + +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.CameraControlSource +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CameraControlSourceSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraControlSourceSupport + ) + val stringOptionNames = listOf( + "_cameraControlSourceSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_control_source_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.cameraControlSourceSupport, + listOf( + ThetaRepository.CameraControlSourceEnum.CAMERA, + ThetaRepository.CameraControlSourceEnum.APP, + ThetaRepository.CameraControlSourceEnum.UNKNOWN, + ), + "cameraControlSourceSupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(CameraControlSource.CAMERA, CameraControlSource.APP), + listOf( + ThetaRepository.CameraControlSourceEnum.CAMERA, + ThetaRepository.CameraControlSourceEnum.APP + ) + ) + + val orgOptions = Options( + _cameraControlSourceSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.cameraControlSourceSupport, + values.second, + "cameraControlSourceSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + cameraControlSourceSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._cameraControlSourceSupport, + values.first, + "_cameraControlSourceSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceTest.kt index 6e64b2e85d0..53bbc6accb4 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraControlSourceTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CameraControlSource import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CameraControlSourceTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockConfigTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockConfigTest.kt new file mode 100644 index 00000000000..a589d4a43d7 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockConfigTest.kt @@ -0,0 +1,166 @@ +package com.ricoh360.thetaclient.repository.options + +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.CameraLockConfig +import com.ricoh360.thetaclient.transferred.CameraLockType +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CameraLockConfigTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraLockConfig + ) + val stringOptionNames = listOf( + "_cameraLockConfig" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_lock_config_A1.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.cameraLockConfig?.isPowerKeyLocked, true) + assertEquals(options.cameraLockConfig?.isShutterKeyLocked, false) + assertEquals(options.cameraLockConfig?.isModeKeyLocked, false) + assertEquals(options.cameraLockConfig?.isWlanKeyLocked, false) + assertEquals(options.cameraLockConfig?.isFnKeyLocked, false) + assertEquals(options.cameraLockConfig?.isPanelLocked, true) + } + + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraLockConfig + ) + val stringOptionNames = listOf( + "_cameraLockConfig" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_lock_config_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.cameraLockConfig?.isPowerKeyLocked, true) + assertEquals(options.cameraLockConfig?.isShutterKeyLocked, true) + assertEquals(options.cameraLockConfig?.isModeKeyLocked, true) + assertEquals(options.cameraLockConfig?.isWlanKeyLocked, true) + assertEquals(options.cameraLockConfig?.isFnKeyLocked, true) + assertEquals(options.cameraLockConfig?.isPanelLocked, true) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair( + ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = false, + isModeKeyLocked = true, + isWlanKeyLocked = true, + isFnKeyLocked = false, + isPanelLocked = true + ), + CameraLockConfig( + powerKey = CameraLockType.LOCK, + shutterKey = CameraLockType.UNLOCK, + modeKey = CameraLockType.LOCK, + wlanKey = CameraLockType.LOCK, + fnKey = CameraLockType.UNLOCK, + panel = CameraLockType.LOCK + ) + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, cameraLockConfig = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + cameraLockConfig = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair( + ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = false, + isModeKeyLocked = true, + isWlanKeyLocked = true, + isFnKeyLocked = false, + isPanelLocked = true + ), + CameraLockConfig( + powerKey = CameraLockType.LOCK, + shutterKey = CameraLockType.UNLOCK, + modeKey = CameraLockType.LOCK, + wlanKey = CameraLockType.LOCK, + fnKey = CameraLockType.UNLOCK, + panel = CameraLockType.LOCK + ) + ), + ) + + values.forEach { + val orgOptions = Options( + _cameraLockConfig = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.cameraLockConfig, it.first, "cameraLockConfig ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + cameraLockConfig = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._cameraLockConfig, it.second, "_cameraLockConfig ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockTest.kt new file mode 100644 index 00000000000..258ffff1fc4 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraLockTest.kt @@ -0,0 +1,127 @@ +package com.ricoh360.thetaclient.repository.options + +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.CameraLock +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CameraLockTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraLock + ) + val stringOptionNames = listOf( + "_cameraLock" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_lock_unlock.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.cameraLock, ThetaRepository.CameraLockEnum.UNLOCK) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraLock + ) + val stringOptionNames = listOf( + "_cameraLock" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_lock_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.cameraLock, ThetaRepository.CameraLockEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair(ThetaRepository.CameraLockEnum.BASIC_LOCK, CameraLock.BASIC_LOCK) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, cameraLock = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + cameraLock = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair(ThetaRepository.CameraLockEnum.UNKNOWN, CameraLock.UNKNOWN), + Pair(ThetaRepository.CameraLockEnum.UNLOCK, CameraLock.UNLOCK), + Pair(ThetaRepository.CameraLockEnum.BASIC_LOCK, CameraLock.BASIC_LOCK), + Pair(ThetaRepository.CameraLockEnum.CUSTOM_LOCK, CameraLock.CUSTOM_LOCK), + ) + + values.forEach { + val orgOptions = Options( + _cameraLock = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.cameraLock, it.first, "cameraLock ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + cameraLock = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._cameraLock, it.second, "_cameraLock ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraModeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraModeTest.kt index 700751a273e..e881815fcb1 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraModeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraModeTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CameraMode import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CameraModeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerSupportTest.kt new file mode 100644 index 00000000000..33ec21f3301 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerSupportTest.kt @@ -0,0 +1,99 @@ +package com.ricoh360.thetaclient.repository.options + +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.CameraPower +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CameraPowerSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CameraPowerSupport + ) + val stringOptionNames = listOf( + "_cameraPowerSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_camera_power_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.cameraPowerSupport, + listOf( + ThetaRepository.CameraPowerEnum.ON, + ThetaRepository.CameraPowerEnum.OFF, + ThetaRepository.CameraPowerEnum.POWER_SAVING, + ThetaRepository.CameraPowerEnum.SILENT_MODE, + ThetaRepository.CameraPowerEnum.UNKNOWN, + ), + "cameraPowerSupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(CameraPower.ON, CameraPower.OFF, CameraPower.POWER_SAVING, CameraPower.SILENT_MODE), + listOf( + ThetaRepository.CameraPowerEnum.ON, + ThetaRepository.CameraPowerEnum.OFF, + ThetaRepository.CameraPowerEnum.POWER_SAVING, + ThetaRepository.CameraPowerEnum.SILENT_MODE + ) + ) + + val orgOptions = Options( + _cameraPowerSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.cameraPowerSupport, + values.second, + "cameraPowerSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + cameraPowerSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._cameraPowerSupport, + null, + "_cameraPowerSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerTest.kt index 9402dbd1877..1ae9817e34f 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CameraPowerTest.kt @@ -105,6 +105,7 @@ class CameraPowerTest { Pair(ThetaRepository.CameraPowerEnum.UNKNOWN, CameraPower.UNKNOWN), Pair(ThetaRepository.CameraPowerEnum.ON, CameraPower.ON), Pair(ThetaRepository.CameraPowerEnum.OFF, CameraPower.OFF), + Pair(ThetaRepository.CameraPowerEnum.SLEEP, CameraPower.SLEEP), Pair(ThetaRepository.CameraPowerEnum.POWER_SAVING, CameraPower.POWER_SAVING), Pair(ThetaRepository.CameraPowerEnum.SILENT_MODE, CameraPower.SILENT_MODE), ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureIntervalTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureIntervalTest.kt index 17070b8880a..e95450ddada 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureIntervalTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureIntervalTest.kt @@ -4,13 +4,14 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CaptureIntervalTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureModeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureModeTest.kt index 3847100f455..eefc039415a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureModeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureModeTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.CaptureMode import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CaptureModeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureNumberTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureNumberTest.kt index a646df0509a..ad3b3aff644 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureNumberTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CaptureNumberTest.kt @@ -4,13 +4,14 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CaptureNumberTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ColorTemperatureSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ColorTemperatureSupportTest.kt new file mode 100644 index 00000000000..8dd7b116a2d --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ColorTemperatureSupportTest.kt @@ -0,0 +1,86 @@ +package com.ricoh360.thetaclient.repository.options + +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.ColorTemperatureSupport +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class ColorTemperatureSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOption() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.ColorTemperatureSupport + ) + val stringOptionNames = listOf( + "_colorTemperatureSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_color_temperature_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.colorTemperatureSupport?.max, 10000, "maxTemperature") + assertEquals(options.colorTemperatureSupport?.min, 2500, "minTemperature") + assertEquals(options.colorTemperatureSupport?.stepSize, 100, "stepSize") + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + ColorTemperatureSupport(10000, 3000, 100), + ThetaRepository.ValueRange(10000, 3000, 100) + ) + + val orgOptions = Options( + _colorTemperatureSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.colorTemperatureSupport, + values.second, + "colorTemperatureSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + colorTemperatureSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._colorTemperatureSupport, + null, + "_colorTemperatureSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompassDirectionRefTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompassDirectionRefTest.kt new file mode 100644 index 00000000000..53f3fe334d1 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompassDirectionRefTest.kt @@ -0,0 +1,127 @@ +package com.ricoh360.thetaclient.repository.options + +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.CompassDirectionRef +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CompassDirectionRefTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CompassDirectionRef + ) + val stringOptionNames = listOf( + "_compassDirectionRef" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_compass_direction_ref_auto.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.compassDirectionRef, ThetaRepository.CompassDirectionRefEnum.AUTO) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CompassDirectionRef + ) + val stringOptionNames = listOf( + "_compassDirectionRef" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_compass_direction_ref_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.compassDirectionRef, ThetaRepository.CompassDirectionRefEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair(ThetaRepository.CompassDirectionRefEnum.MAGNETIC, CompassDirectionRef.MAGNETIC) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, compassDirectionRef = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + compassDirectionRef = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair(ThetaRepository.CompassDirectionRefEnum.UNKNOWN, CompassDirectionRef.UNKNOWN), + Pair(ThetaRepository.CompassDirectionRefEnum.AUTO, CompassDirectionRef.AUTO), + Pair(ThetaRepository.CompassDirectionRefEnum.TRUE_NORTH, CompassDirectionRef.TRUE_NORTH), + Pair(ThetaRepository.CompassDirectionRefEnum.MAGNETIC, CompassDirectionRef.MAGNETIC), + ) + + values.forEach { + val orgOptions = Options( + _compassDirectionRef = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.compassDirectionRef, it.first, "compassDirectionRef ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + compassDirectionRef = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._compassDirectionRef, it.second, "_compassDirectionRef ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalSupportTest.kt new file mode 100644 index 00000000000..35f844d0914 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalSupportTest.kt @@ -0,0 +1,86 @@ +package com.ricoh360.thetaclient.repository.options + +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.CompositeShootingOutputIntervalSupport +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CompositeShootingOutputIntervalSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOption() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CompositeShootingOutputIntervalSupport + ) + val stringOptionNames = listOf( + "_compositeShootingOutputIntervalSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_composite_shooting_output_interval_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.compositeShootingOutputIntervalSupport?.max, 600, "maxInterval") + assertEquals(options.compositeShootingOutputIntervalSupport?.min, 0, "minInterval") + assertEquals(options.compositeShootingOutputIntervalSupport?.stepSize, 60, "stepSize") + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + CompositeShootingOutputIntervalSupport(600, 0, 60), + ThetaRepository.ValueRange(600, 0, 60) + ) + + val orgOptions = Options( + _compositeShootingOutputIntervalSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.compositeShootingOutputIntervalSupport, + values.second, + "compositeShootingOutputIntervalSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + compositeShootingOutputIntervalSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._compositeShootingOutputIntervalSupport, + null, + "_compositeShootingOutputIntervalSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalTest.kt index 8291daf953e..c20fa0ab7ba 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingOutputIntervalTest.kt @@ -4,13 +4,14 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CompositeShootingOutputIntervalTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeSupportTest.kt new file mode 100644 index 00000000000..59b31a9e004 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeSupportTest.kt @@ -0,0 +1,86 @@ +package com.ricoh360.thetaclient.repository.options + +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.CompositeShootingTimeSupport +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class CompositeShootingTimeSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOption() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.CompositeShootingTimeSupport + ) + val stringOptionNames = listOf( + "_compositeShootingTimeSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_composite_shooting_time_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.compositeShootingTimeSupport?.max, 86400, "maxTime") + assertEquals(options.compositeShootingTimeSupport?.min, 600, "minTime") + assertEquals(options.compositeShootingTimeSupport?.stepSize, 600, "stepSize") + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + CompositeShootingTimeSupport(86400, 600, 600), + ThetaRepository.ValueRange(86400, 600, 600) + ) + + val orgOptions = Options( + _compositeShootingTimeSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.compositeShootingTimeSupport, + values.second, + "compositeShootingTimeSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + compositeShootingTimeSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._compositeShootingTimeSupport, + null, + "_compositeShootingTimeSupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeTest.kt index 79f35a9cadf..562a0fe1f05 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/CompositeShootingTimeTest.kt @@ -4,13 +4,14 @@ import com.goncalossilva.resources.Resource import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class CompositeShootingTimeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ContinuousNumberTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ContinuousNumberTest.kt index 1afdd9c4b85..5b8a00228e3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ContinuousNumberTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ContinuousNumberTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ContinuousNumberTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureCompensationTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureCompensationTest.kt index cbdb3ddab9c..eece4c90bad 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureCompensationTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureCompensationTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ExposureCompensationTest { @@ -51,6 +52,30 @@ class ExposureCompensationTest { assertEquals(options.exposureCompensation, ThetaRepository.ExposureCompensationEnum.P2_0) } + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.ExposureCompensation + ) + val stringOptionNames = listOf( + "exposureCompensation" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_exposure_compensation_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.exposureCompensation, ThetaRepository.ExposureCompensationEnum.UNKNOWN) + } + /** * Set option exposureCompensation. */ @@ -78,6 +103,13 @@ class ExposureCompensationTest { @Test fun convertOptionExposureCompensationTest() = runTest { val values = listOf( + Pair(ThetaRepository.ExposureCompensationEnum.UNKNOWN, 999f), + Pair(ThetaRepository.ExposureCompensationEnum.M4_0, -4.0f), + Pair(ThetaRepository.ExposureCompensationEnum.M3_7, -3.7f), + Pair(ThetaRepository.ExposureCompensationEnum.M3_3, -3.3f), + Pair(ThetaRepository.ExposureCompensationEnum.M3_0, -3.0f), + Pair(ThetaRepository.ExposureCompensationEnum.M2_7, -2.7f), + Pair(ThetaRepository.ExposureCompensationEnum.M2_3, -2.3f), Pair(ThetaRepository.ExposureCompensationEnum.M2_0, -2.0f), Pair(ThetaRepository.ExposureCompensationEnum.M1_7, -1.7f), Pair(ThetaRepository.ExposureCompensationEnum.M1_3, -1.3f), @@ -90,15 +122,23 @@ class ExposureCompensationTest { Pair(ThetaRepository.ExposureCompensationEnum.P1_0, 1.0f), Pair(ThetaRepository.ExposureCompensationEnum.P1_3, 1.3f), Pair(ThetaRepository.ExposureCompensationEnum.P1_7, 1.7f), - Pair(ThetaRepository.ExposureCompensationEnum.P2_0, 2.0f) + Pair(ThetaRepository.ExposureCompensationEnum.P2_0, 2.0f), + Pair(ThetaRepository.ExposureCompensationEnum.P2_3, 2.3f), + Pair(ThetaRepository.ExposureCompensationEnum.P2_7, 2.7f), + Pair(ThetaRepository.ExposureCompensationEnum.P3_0, 3.0f), + Pair(ThetaRepository.ExposureCompensationEnum.P3_3, 3.3f), + Pair(ThetaRepository.ExposureCompensationEnum.P3_7, 3.7f), + Pair(ThetaRepository.ExposureCompensationEnum.P4_0, 4.0f) ) + assertEquals(ThetaRepository.ExposureCompensationEnum.entries.size, values.size) + values.forEach { val orgOptions = Options( exposureCompensation = it.second ) val options = ThetaRepository.Options(orgOptions) - assertEquals(options.exposureCompensation, it.first, "exposureCompensation ${it.second}") + assertEquals(options.exposureCompensation, it.first, "exposureCompensation ${it.first}") } values.forEach { @@ -106,7 +146,17 @@ class ExposureCompensationTest { exposureCompensation = it.first ) val options = orgOptions.toOptions() - assertEquals(options.exposureCompensation, it.second, "exposureCompensation ${it.second}") + when (it.first) { + ThetaRepository.ExposureCompensationEnum.UNKNOWN -> { + assertEquals(options.exposureCompensation, null) + } + + else -> assertEquals( + options.exposureCompensation, + it.second, + "exposureCompensation ${it.first}" + ) + } } } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelaySupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelaySupportTest.kt new file mode 100644 index 00000000000..057be5b8d58 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelaySupportTest.kt @@ -0,0 +1,107 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class ExposureDelaySupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOption() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.ExposureDelaySupport + ) + val stringOptionNames = listOf( + "exposureDelaySupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_exposure_delay_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.exposureDelaySupport?.get(0), ThetaRepository.ExposureDelayEnum.DELAY_OFF) + assertEquals(options.exposureDelaySupport?.get(1), ThetaRepository.ExposureDelayEnum.DELAY_1) + assertEquals(options.exposureDelaySupport?.get(2), ThetaRepository.ExposureDelayEnum.DELAY_2) + assertEquals(options.exposureDelaySupport?.get(3), ThetaRepository.ExposureDelayEnum.DELAY_3) + assertEquals(options.exposureDelaySupport?.get(4), ThetaRepository.ExposureDelayEnum.DELAY_4) + assertEquals(options.exposureDelaySupport?.get(5), ThetaRepository.ExposureDelayEnum.DELAY_5) + assertEquals(options.exposureDelaySupport?.get(6), ThetaRepository.ExposureDelayEnum.DELAY_6) + assertEquals(options.exposureDelaySupport?.get(7), ThetaRepository.ExposureDelayEnum.DELAY_7) + assertEquals(options.exposureDelaySupport?.get(8), ThetaRepository.ExposureDelayEnum.DELAY_8) + assertEquals(options.exposureDelaySupport?.get(9), ThetaRepository.ExposureDelayEnum.DELAY_9) + assertEquals(options.exposureDelaySupport?.get(10), ThetaRepository.ExposureDelayEnum.DELAY_10) + + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(99, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10), + listOf( + ThetaRepository.ExposureDelayEnum.UNKNOWN, + ThetaRepository.ExposureDelayEnum.DELAY_OFF, + ThetaRepository.ExposureDelayEnum.DELAY_1, + ThetaRepository.ExposureDelayEnum.DELAY_2, + ThetaRepository.ExposureDelayEnum.DELAY_3, + ThetaRepository.ExposureDelayEnum.DELAY_4, + ThetaRepository.ExposureDelayEnum.DELAY_5, + ThetaRepository.ExposureDelayEnum.DELAY_6, + ThetaRepository.ExposureDelayEnum.DELAY_7, + ThetaRepository.ExposureDelayEnum.DELAY_8, + ThetaRepository.ExposureDelayEnum.DELAY_9, + ThetaRepository.ExposureDelayEnum.DELAY_10, + ) + ) + + val orgOptions = Options( + exposureDelaySupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.exposureDelaySupport, + values.second, + "exposureDelaySupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + exposureDelaySupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options.exposureDelaySupport, + null, + "exposureDelaySupport ${values.first}" + ) + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelayTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelayTest.kt index f8a328531e9..6804bc6886f 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelayTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureDelayTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ExposureDelayTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureProgramTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureProgramTest.kt index e308b775c5b..97cea654c57 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureProgramTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ExposureProgramTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ExposureProgramTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FaceDetectTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FaceDetectTest.kt index 915650628c4..b9e0371e8a6 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FaceDetectTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FaceDetectTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.FaceDetect import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class FaceDetectTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FileFormatTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FileFormatTest.kt index a0650150ee4..340b07a41c8 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FileFormatTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FileFormatTest.kt @@ -118,6 +118,7 @@ class FileFormatTest { Pair(ThetaRepository.FileFormatEnum.VIDEO_2K_NO_CODEC, MediaFileFormat(MediaType.MP4, 1920, 960, null, null)), Pair(ThetaRepository.FileFormatEnum.VIDEO_4K, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", null)), Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_NO_CODEC, MediaFileFormat(MediaType.MP4, 3840, 1920, null, null)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_2K_15F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 15)), Pair(ThetaRepository.FileFormatEnum.VIDEO_2K_30F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 30)), Pair(ThetaRepository.FileFormatEnum.VIDEO_2K_60F, MediaFileFormat(MediaType.MP4, 1920, 960, "H.264/MPEG-4 AVC", 60)), Pair(ThetaRepository.FileFormatEnum.VIDEO_2_7K_2752_2F, MediaFileFormat(MediaType.MP4, 2752, 2752, "H.264/MPEG-4 AVC", 2)), @@ -128,6 +129,8 @@ class FileFormatTest { Pair(ThetaRepository.FileFormatEnum.VIDEO_2_7K_2F, MediaFileFormat(MediaType.MP4, 2688, 2688, "H.264/MPEG-4 AVC", 2)), Pair(ThetaRepository.FileFormatEnum.VIDEO_3_6K_1F, MediaFileFormat(MediaType.MP4, 3648, 3648, "H.264/MPEG-4 AVC", 1)), Pair(ThetaRepository.FileFormatEnum.VIDEO_3_6K_2F, MediaFileFormat(MediaType.MP4, 3648, 3648, "H.264/MPEG-4 AVC", 2)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_2F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 2)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_5F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 5)), Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_10F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 10)), Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_15F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 15)), Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_30F, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.264/MPEG-4 AVC", 30)), @@ -140,11 +143,24 @@ class FileFormatTest { Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_2F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 2)), Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_5F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 5)), Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_10F, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.264/MPEG-4 AVC", 10)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_2K_30F_H265_HEVC, MediaFileFormat(MediaType.MP4, 1920, 960, "H.265/HEVC", 30)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 2)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 5)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 10)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_4K_30F_H265_HEVC, MediaFileFormat(MediaType.MP4, 3840, 1920, "H.265/HEVC", 30)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_5_7K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 2)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_5_7K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 5)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_5_7K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 5760, 2880, "H.265/HEVC", 10)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_2F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 2)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_5F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 5)), + Pair(ThetaRepository.FileFormatEnum.VIDEO_7K_10F_H265_HEVC, MediaFileFormat(MediaType.MP4, 7680, 3840, "H.265/HEVC", 10)), Pair(ThetaRepository.FileFormatEnum.UNKNOWN, MediaFileFormat(MediaType.MP4, 0, 0, "H.264/MPEG-4 AVC", 10)), Pair(ThetaRepository.FileFormatEnum.IMAGE_DO_NOT_UPDATE_MY_SETTING_CONDITION, MediaFileFormat(MediaType.JPEG, 0, 0, null, null)), Pair(ThetaRepository.FileFormatEnum.VIDEO_DO_NOT_UPDATE_MY_SETTING_CONDITION, MediaFileFormat(MediaType.MP4, 0, 0, null, null)) ) + assertEquals(ThetaRepository.FileFormatEnum.entries.size, values.size) + values.forEach { val orgOptions = Options( fileFormat = it.second diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FilterTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FilterTest.kt index 8c033aa9382..17127355a6b 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FilterTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FilterTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.ImageFilter import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class FilterTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FunctionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FunctionTest.kt index 7289d3a9ca6..e4c23f41254 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FunctionTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/FunctionTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.ShootingFunction -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class FunctionTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GainTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GainTest.kt index c2b21d6410d..b52407ae2e1 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GainTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GainTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Gain import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class GainTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GpsTagRecordingSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GpsTagRecordingSupportTest.kt new file mode 100644 index 00000000000..78da4523e26 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/GpsTagRecordingSupportTest.kt @@ -0,0 +1,95 @@ +package com.ricoh360.thetaclient.repository.options + +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.GpsTagRecording +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class GpsTagRecordingSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.GpsTagRecordingSupport + ) + val stringOptionNames = listOf( + "_gpsTagRecordingSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_gps_tag_recording_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.gpsTagRecordingSupport, + listOf( + ThetaRepository.GpsTagRecordingEnum.ON, + ThetaRepository.GpsTagRecordingEnum.OFF, + ThetaRepository.GpsTagRecordingEnum.UNKNOWN, + ), + "gpsTagRecordingSupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = Pair( + listOf(GpsTagRecording.ON, GpsTagRecording.OFF), + listOf( + ThetaRepository.GpsTagRecordingEnum.ON, + ThetaRepository.GpsTagRecordingEnum.OFF + ) + ) + + val orgOptions = Options( + _gpsTagRecordingSupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.gpsTagRecordingSupport, + values.second, + "gpsTagRecordingSupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + gpsTagRecordingSupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._gpsTagRecordingSupport, + null, + "_gpsTagRecordingSupport ${values.first}" + ) + } +} \ No newline at end of file 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 e4619a9b6ec..707eb71016b 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 @@ -6,11 +6,15 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.ImageStitching import com.ricoh360.thetaclient.transferred.Options -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class ImageStitchingTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsGpsOnTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsGpsOnTest.kt index cf0b54f519b..af140f0bb4c 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsGpsOnTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsGpsOnTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.GpsTagRecording import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class IsGpsOnTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoAutoHighLimitTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoAutoHighLimitTest.kt index 9cdd88e8cc3..91c96a3abdc 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoAutoHighLimitTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoAutoHighLimitTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class IsoAutoHighLimitTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoTest.kt index efcf1d0f747..39449942654 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/IsoTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class IsoTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LanguageTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LanguageTest.kt index 5d5fa4626d4..95db69f7b5c 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LanguageTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LanguageTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Language import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class LanguageTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LatestEnabledExposureDelayTimeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LatestEnabledExposureDelayTimeTest.kt index fb63a58e039..3dda7ac867a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LatestEnabledExposureDelayTimeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/LatestEnabledExposureDelayTimeTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class LatestEnabledExposureDelayTimeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MaxRecordableTimeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MaxRecordableTimeTest.kt index 636a66b049f..b01fd62401a 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MaxRecordableTimeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MaxRecordableTimeTest.kt @@ -5,13 +5,15 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertNull @OptIn(ExperimentalCoroutinesApi::class) class MaxRecordableTimeTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MicrophoneNoiseReductionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MicrophoneNoiseReductionTest.kt new file mode 100644 index 00000000000..01aaa5b3407 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MicrophoneNoiseReductionTest.kt @@ -0,0 +1,126 @@ +package com.ricoh360.thetaclient.repository.options + +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.MicrophoneNoiseReduction +import com.ricoh360.thetaclient.transferred.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class MicrophoneNoiseReductionTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.MicrophoneNoiseReduction + ) + val stringOptionNames = listOf( + "_microphoneNoiseReduction" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_microphone_noise_reduction_on.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.microphoneNoiseReduction, ThetaRepository.MicrophoneNoiseReductionEnum.ON) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.MicrophoneNoiseReduction + ) + val stringOptionNames = listOf( + "_microphoneNoiseReduction" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_microphone_noise_reduction_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.microphoneNoiseReduction, ThetaRepository.MicrophoneNoiseReductionEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair(ThetaRepository.MicrophoneNoiseReductionEnum.OFF, MicrophoneNoiseReduction.OFF) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, microphoneNoiseReduction = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + microphoneNoiseReduction = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair(ThetaRepository.MicrophoneNoiseReductionEnum.UNKNOWN, MicrophoneNoiseReduction.UNKNOWN), + Pair(ThetaRepository.MicrophoneNoiseReductionEnum.ON, MicrophoneNoiseReduction.ON), + Pair(ThetaRepository.MicrophoneNoiseReductionEnum.OFF, MicrophoneNoiseReduction.OFF), + ) + + values.forEach { + val orgOptions = Options( + _microphoneNoiseReduction = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.microphoneNoiseReduction, it.first, "microphoneNoiseReduction ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + microphoneNoiseReduction = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._microphoneNoiseReduction, it.second, "_microphoneNoiseReduction ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MobileNetworkSettingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MobileNetworkSettingTest.kt new file mode 100644 index 00000000000..c8aab2bda5b --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/MobileNetworkSettingTest.kt @@ -0,0 +1,143 @@ +package com.ricoh360.thetaclient.repository.options + +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.MobileNetworkSetting +import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.Plan +import com.ricoh360.thetaclient.transferred.Roaming +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class MobileNetworkSettingTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.MobileNetworkSetting + ) + val stringOptionNames = listOf( + "_mobileNetworkSetting" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_mobile_network_setting_normal.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.mobileNetworkSetting?.roaming, ThetaRepository.RoamingEnum.OFF) + assertEquals(options.mobileNetworkSetting?.plan, ThetaRepository.PlanEnum.SORACOM) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.MobileNetworkSetting + ) + val stringOptionNames = listOf( + "_mobileNetworkSetting" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_mobile_network_setting_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.mobileNetworkSetting?.roaming, ThetaRepository.RoamingEnum.UNKNOWN) + assertEquals(options.mobileNetworkSetting?.plan, ThetaRepository.PlanEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair( + ThetaRepository.MobileNetworkSetting( + roaming = ThetaRepository.RoamingEnum.ON, + plan = ThetaRepository.PlanEnum.SORACOM_PLAN_DU, + ), MobileNetworkSetting( + roaming = Roaming.ON, + plan = Plan.SORACOM_PLAN_DU, + ) + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, mobileNetworkSetting = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + mobileNetworkSetting = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair( + ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.ON, plan = ThetaRepository.PlanEnum.SORACOM), + MobileNetworkSetting(roaming = Roaming.ON, plan = Plan.SORACOM) + ), + Pair( + ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.OFF, plan = ThetaRepository.PlanEnum.SORACOM_PLAN_DU), + MobileNetworkSetting(roaming = Roaming.OFF, plan = Plan.SORACOM_PLAN_DU) + ), + ) + + values.forEach { + val orgOptions = Options( + _mobileNetworkSetting = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.mobileNetworkSetting, it.first, "mobileNetworkSetting ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + mobileNetworkSetting = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._mobileNetworkSetting, it.second, "mobileNetworkSetting ${it.second}") + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/NetworkTypeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/NetworkTypeTest.kt index 013a99c0450..6c5c7092133 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/NetworkTypeTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/NetworkTypeTest.kt @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.NetworkType import com.ricoh360.thetaclient.transferred.Options -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 kotlin.test.AfterTest @@ -53,6 +53,30 @@ class NetworkTypeTest { assertEquals(options.networkType, ThetaRepository.NetworkTypeEnum.DIRECT, "networkType") } + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.NetworkType + ) + val stringOptionNames = listOf( + "_networkType" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_network_type_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.networkType, ThetaRepository.NetworkTypeEnum.UNKNOWN, "networkType") + } + /** * Set option networkType. */ @@ -80,10 +104,18 @@ class NetworkTypeTest { @Test fun convertOptionNetworkTypeTest() = runTest { val values = listOf( + Pair(ThetaRepository.NetworkTypeEnum.UNKNOWN, NetworkType.UNKNOWN), Pair(ThetaRepository.NetworkTypeEnum.CLIENT, NetworkType.CLIENT), Pair(ThetaRepository.NetworkTypeEnum.DIRECT, NetworkType.DIRECT), Pair(ThetaRepository.NetworkTypeEnum.ETHERNET, NetworkType.ETHERNET), Pair(ThetaRepository.NetworkTypeEnum.OFF, NetworkType.OFF), + Pair(ThetaRepository.NetworkTypeEnum.LTE_D, NetworkType.LTE_D), + Pair(ThetaRepository.NetworkTypeEnum.LTE_DU, NetworkType.LTE_DU), + Pair(ThetaRepository.NetworkTypeEnum.LTE_01S, NetworkType.LTE_01S), + Pair(ThetaRepository.NetworkTypeEnum.LTE_X3, NetworkType.LTE_X3), + Pair(ThetaRepository.NetworkTypeEnum.LTE_P1, NetworkType.LTE_P1), + Pair(ThetaRepository.NetworkTypeEnum.LTE_K2, NetworkType.LTE_K2), + Pair(ThetaRepository.NetworkTypeEnum.LTE_K, NetworkType.LTE_K), ) values.forEach { 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 f978a4a0537..dc90967c8b3 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 @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class OffDelayTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayUsbTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayUsbTest.kt new file mode 100644 index 00000000000..b16f009564f --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/OffDelayUsbTest.kt @@ -0,0 +1,111 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class OffDelayUsbTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option offDelay. + */ + @Test + fun getOptionOffDelayUsbTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.OffDelayUsb + ) + val stringOptionNames = listOf( + "_offDelayUSB" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_off_delay_usb_600.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.offDelayUsb, ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M, "offDelayUsb") + } + + /** + * Set option offDelayUsb. + */ + @Test + fun setOptionOffDelayUsbTest() = runTest { + val value = Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M, 600) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, offDelayUsb = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + offDelayUsb = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionOffDelayUsbTest() { + val values = listOf( + Pair(ThetaRepository.OffDelayUsbEnum.DISABLE, 65535), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M, 60 * 10), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_1H, 60 * 60), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_2H, 60 * 60 * 2), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_4H, 60 * 60 * 4), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_8H, 60 * 60 * 8), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_12H, 60 * 60 * 12), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_18H, 60 * 60 * 18), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_24H, 60 * 60 * 24), + Pair(ThetaRepository.OffDelayUsbEnum.OFF_DELAY_2D, 60 * 60 * 24 * 2), + Pair(ThetaRepository.OffDelayUsbSec(60), 60) + ) + + values.forEach { + val orgOptions = Options( + _offDelayUSB = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.offDelayUsb, it.first, "offDelayUsb ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + offDelayUsb = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._offDelayUSB, it.second, "offDelayUsb ${it.second}") + } + + assertEquals(ThetaRepository.OffDelayUsbEnum.get(0), ThetaRepository.OffDelayUsbEnum.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 0e851864817..9a82681d73b 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,6 +1,7 @@ package com.ricoh360.thetaclient.repository.options import com.ricoh360.thetaclient.ThetaRepository +import com.ricoh360.thetaclient.transferred.AccessInfo import com.ricoh360.thetaclient.transferred.AiAutoThumbnail import com.ricoh360.thetaclient.transferred.AutoBracket import com.ricoh360.thetaclient.transferred.BluetoothPower @@ -15,9 +16,17 @@ 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.CameraLock +import com.ricoh360.thetaclient.transferred.CameraLockConfig +import com.ricoh360.thetaclient.transferred.CameraLockType import com.ricoh360.thetaclient.transferred.CameraMode import com.ricoh360.thetaclient.transferred.CameraPower import com.ricoh360.thetaclient.transferred.CaptureMode +import com.ricoh360.thetaclient.transferred.ColorTemperatureSupport +import com.ricoh360.thetaclient.transferred.CompassDirectionRef +import com.ricoh360.thetaclient.transferred.CompositeShootingOutputIntervalSupport +import com.ricoh360.thetaclient.transferred.CompositeShootingTimeSupport +import com.ricoh360.thetaclient.transferred.DhcpLeaseAddress import com.ricoh360.thetaclient.transferred.EthernetConfig import com.ricoh360.thetaclient.transferred.FaceDetect import com.ricoh360.thetaclient.transferred.FirstShootingEnum @@ -30,21 +39,34 @@ import com.ricoh360.thetaclient.transferred.IpAddressAllocation import com.ricoh360.thetaclient.transferred.Language import com.ricoh360.thetaclient.transferred.MediaFileFormat import com.ricoh360.thetaclient.transferred.MediaType +import com.ricoh360.thetaclient.transferred.MicrophoneNoiseReduction +import com.ricoh360.thetaclient.transferred.MobileNetworkSetting import com.ricoh360.thetaclient.transferred.NetworkType import com.ricoh360.thetaclient.transferred.Options +import com.ricoh360.thetaclient.transferred.PitchSupport +import com.ricoh360.thetaclient.transferred.Plan 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.Roaming +import com.ricoh360.thetaclient.transferred.RollSupport import com.ricoh360.thetaclient.transferred.ShootingFunction import com.ricoh360.thetaclient.transferred.ShootingMethod import com.ricoh360.thetaclient.transferred.TimeShift import com.ricoh360.thetaclient.transferred.TopBottomCorrectionOption import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation +import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotationSupport +import com.ricoh360.thetaclient.transferred.UsbConnection import com.ricoh360.thetaclient.transferred.VideoStitching import com.ricoh360.thetaclient.transferred.VisibilityReduction import com.ricoh360.thetaclient.transferred.WhiteBalance import com.ricoh360.thetaclient.transferred.WhiteBalanceAutoStrength +import com.ricoh360.thetaclient.transferred.WlanAntennaConfig +import com.ricoh360.thetaclient.transferred.WlanFrequency +import com.ricoh360.thetaclient.transferred.WlanFrequencyAccessInfo +import com.ricoh360.thetaclient.transferred.WlanFrequencyClMode +import com.ricoh360.thetaclient.transferred.YawSupport import kotlin.test.AfterTest import kotlin.test.BeforeTest import kotlin.test.Test @@ -65,8 +87,28 @@ class OptionsTest { */ @Test fun optionsPrimaryConstructorTest() { + val accessInfo = ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) val aiAutoThumbnail = ThetaRepository.AiAutoThumbnailEnum.ON + val aiAutoThumbnailSupport = listOf(ThetaRepository.AiAutoThumbnailEnum.ON, ThetaRepository.AiAutoThumbnailEnum.OFF) val aperture = ThetaRepository.ApertureEnum.APERTURE_2_1 + val apertureSupport = listOf(ThetaRepository.ApertureEnum.APERTURE_2_1, ThetaRepository.ApertureEnum.APERTURE_3_5) val autoBracket = ThetaRepository.BracketSettingList().add( ThetaRepository.BracketSetting( exposureProgram = ThetaRepository.ExposureProgramEnum.NORMAL_PROGRAM, @@ -95,14 +137,34 @@ class OptionsTest { burstOrder = ThetaRepository.BurstOrderEnum.BURST_BRACKET_ORDER_0 ) val cameraControlSource = ThetaRepository.CameraControlSourceEnum.CAMERA + val cameraControlSourceSupport = listOf(ThetaRepository.CameraControlSourceEnum.CAMERA, ThetaRepository.CameraControlSourceEnum.APP) + val cameraLock = ThetaRepository.CameraLockEnum.UNLOCK + val cameraLockConfig = ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = false, + isModeKeyLocked = false, + isWlanKeyLocked = false, + isFnKeyLocked = true, + isPanelLocked = false + ) val cameraMode = ThetaRepository.CameraModeEnum.CAPTURE val cameraPower = ThetaRepository.CameraPowerEnum.ON + val cameraPowerSupport = listOf( + ThetaRepository.CameraPowerEnum.ON, + ThetaRepository.CameraPowerEnum.OFF, + ThetaRepository.CameraPowerEnum.POWER_SAVING, + ThetaRepository.CameraPowerEnum.SILENT_MODE + ) val captureInterval = 6 val captureMode = ThetaRepository.CaptureModeEnum.IMAGE val captureNumber = 0 val colorTemperature = 10 + val colorTemperatureSupport = ThetaRepository.ValueRange(10000, 2000, 100) + val compassDirectionRef = ThetaRepository.CompassDirectionRefEnum.AUTO val compositeShootingOutputInterval = 60 + val compositeShootingOutputIntervalSupport = ThetaRepository.ValueRange(600, 0, 60) val compositeShootingTime = 600 + val compositeShootingTimeSupport = ThetaRepository.ValueRange(86400, 600, 600) val continuousNumber = ThetaRepository.ContinuousNumberEnum.MAX_1 val dateTimeZone = "2014:05:18 01:04:29+08:00" val ethernetConfig = ThetaRepository.EthernetConfig( @@ -110,10 +172,13 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", proxy = ThetaRepository.Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ) val exposureCompensation = ThetaRepository.ExposureCompensationEnum.M0_3 val exposureDelay = ThetaRepository.ExposureDelayEnum.DELAY_10 + val exposureDelaySupport = listOf(ThetaRepository.ExposureDelayEnum.DELAY_OFF, ThetaRepository.ExposureDelayEnum.DELAY_10) val exposureProgram = ThetaRepository.ExposureProgramEnum.NORMAL_PROGRAM val faceDetect = ThetaRepository.FaceDetectEnum.ON val fileFormat = ThetaRepository.FileFormatEnum.IMAGE_11K @@ -121,6 +186,7 @@ class OptionsTest { val function = ThetaRepository.ShootingFunctionEnum.NORMAL val gain = ThetaRepository.GainEnum.NORMAL val gpsInfo = ThetaRepository.GpsInfo.disabled + val gpsTagRecordingSupport = listOf(ThetaRepository.GpsTagRecordingEnum.ON, ThetaRepository.GpsTagRecordingEnum.OFF) val imageStitching = ThetaRepository.ImageStitchingEnum.AUTO val isGpsOn = true val iso = ThetaRepository.IsoEnum.ISO_125 @@ -128,8 +194,11 @@ class OptionsTest { val language = ThetaRepository.LanguageEnum.JA val latestEnabledExposureDelayTime = ThetaRepository.ExposureDelayEnum.DELAY_10 val maxRecordableTime = ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_1500 + val microphoneNoiseReduction = ThetaRepository.MicrophoneNoiseReductionEnum.ON + val mobileNetworkSetting = ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.OFF, plan = ThetaRepository.PlanEnum.SORACOM) val networkType = ThetaRepository.NetworkTypeEnum.DIRECT val offDelay = ThetaRepository.OffDelayEnum.OFF_DELAY_10M + val offDelayUsb = ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M val password = "password" val powerSaving = ThetaRepository.PowerSavingEnum.ON val preset = ThetaRepository.PresetEnum.FACE @@ -149,17 +218,33 @@ class OptionsTest { ) val topBottomCorrection = ThetaRepository.TopBottomCorrectionOptionEnum.APPLY_AUTO val topBottomCorrectionRotation = ThetaRepository.TopBottomCorrectionRotation(1.0f, 2.0f, 3.0f) + val topBottomCorrectionRotationSupport = ThetaRepository.TopBottomCorrectionRotationSupport( + pitch = ThetaRepository.ValueRange(100f, -100f, 0.2f), + roll = ThetaRepository.ValueRange(200f, -200f, 0.4f), + yaw = ThetaRepository.ValueRange(300f, -300f, 0.6f) + ) val totalSpace = 100L + val usbConnection = ThetaRepository.UsbConnectionEnum.MSC val username = "username" val videoStitching = ThetaRepository.VideoStitchingEnum.ONDEVICE val visibilityReduction = ThetaRepository.VisibilityReductionEnum.ON val whiteBalance = ThetaRepository.WhiteBalanceEnum.WARM_WHITE_FLUORESCENT val whiteBalanceAutoStrength = ThetaRepository.WhiteBalanceAutoStrengthEnum.OFF + val wlanAntennaConfig = ThetaRepository.WlanAntennaConfigEnum.MIMO val wlanFrequency = ThetaRepository.WlanFrequencyEnum.GHZ_2_4 + val wlanFrequencySupport = listOf(ThetaRepository.WlanFrequencyEnum.GHZ_2_4, ThetaRepository.WlanFrequencyEnum.GHZ_5) + val wlanFrequencyClMode = ThetaRepository.WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ) val options = ThetaRepository.Options( + accessInfo = accessInfo, aiAutoThumbnail = aiAutoThumbnail, + aiAutoThumbnailSupport = aiAutoThumbnailSupport, aperture = aperture, + apertureSupport = apertureSupport, autoBracket = autoBracket, bitrate = bitrate, bluetoothPower = bluetoothPower, @@ -167,19 +252,28 @@ class OptionsTest { burstMode = burstMode, burstOption = burstOption, cameraControlSource = cameraControlSource, + cameraControlSourceSupport = cameraControlSourceSupport, + cameraPowerSupport = cameraPowerSupport, + cameraLock = cameraLock, + cameraLockConfig = cameraLockConfig, cameraMode = cameraMode, cameraPower = cameraPower, captureInterval = captureInterval, captureMode = captureMode, captureNumber = captureNumber, colorTemperature = colorTemperature, + colorTemperatureSupport = colorTemperatureSupport, + compassDirectionRef = compassDirectionRef, compositeShootingOutputInterval = compositeShootingOutputInterval, + compositeShootingOutputIntervalSupport = compositeShootingOutputIntervalSupport, compositeShootingTime = compositeShootingTime, + compositeShootingTimeSupport = compositeShootingTimeSupport, continuousNumber = continuousNumber, dateTimeZone = dateTimeZone, ethernetConfig = ethernetConfig, exposureCompensation = exposureCompensation, exposureDelay = exposureDelay, + exposureDelaySupport = exposureDelaySupport, exposureProgram = exposureProgram, faceDetect = faceDetect, fileFormat = fileFormat, @@ -187,6 +281,7 @@ class OptionsTest { function = function, gain = gain, gpsInfo = gpsInfo, + gpsTagRecordingSupport = gpsTagRecordingSupport, imageStitching = imageStitching, isGpsOn = isGpsOn, iso = iso, @@ -194,8 +289,11 @@ class OptionsTest { language = language, latestEnabledExposureDelayTime = latestEnabledExposureDelayTime, maxRecordableTime = maxRecordableTime, + microphoneNoiseReduction = microphoneNoiseReduction, + mobileNetworkSetting = mobileNetworkSetting, networkType = networkType, offDelay = offDelay, + offDelayUsb = offDelayUsb, password = password, powerSaving = powerSaving, preset = preset, @@ -211,21 +309,29 @@ class OptionsTest { timeShift = timeShift, topBottomCorrection = topBottomCorrection, topBottomCorrectionRotation = topBottomCorrectionRotation, + topBottomCorrectionRotationSupport = topBottomCorrectionRotationSupport, totalSpace = totalSpace, + usbConnection = usbConnection, username = username, videoStitching = videoStitching, visibilityReduction = visibilityReduction, whiteBalance = whiteBalance, whiteBalanceAutoStrength = whiteBalanceAutoStrength, + wlanAntennaConfig = wlanAntennaConfig, wlanFrequency = wlanFrequency, + wlanFrequencySupport = wlanFrequencySupport, + wlanFrequencyClMode = wlanFrequencyClMode, ) - ThetaRepository.OptionNameEnum.values().forEach { + ThetaRepository.OptionNameEnum.entries.forEach { assertNotNull(options.getValue(it), "option: ${it.value}") } + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.AccessInfo), accessInfo, "accessInfo") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.AiAutoThumbnail), aiAutoThumbnail, "aiAutoThumbnail") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.AiAutoThumbnailSupport), aiAutoThumbnailSupport, "aiAutoThumbnailSupport") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Aperture), aperture, "aperture") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ApertureSupport), apertureSupport, "apertureSupport") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.AutoBracket), autoBracket, "autoBracket") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Bitrate), bitrate, "bitrate") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.BluetoothPower), bluetoothPower, "bluetoothPower") @@ -233,19 +339,31 @@ class OptionsTest { assertEquals(options.getValue(ThetaRepository.OptionNameEnum.BurstMode), burstMode, "burstMode") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.BurstOption), burstOption, "burstOption") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraControlSource), cameraControlSource, "cameraControlSource") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraControlSourceSupport), cameraControlSourceSupport, "cameraControlSourceSupport") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraPowerSupport), cameraPowerSupport, "cameraPowerSupport") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraLock), cameraLock, "cameraLock") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraLockConfig), cameraLockConfig, "cameraLockConfig") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraMode), cameraMode, "cameraMode") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CameraPower), cameraPower, "cameraPower") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CaptureInterval), captureInterval, "captureInterval") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CaptureMode), captureMode, "captureMode") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CaptureNumber), captureNumber, "captureNumber") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ColorTemperature), colorTemperature, "colorTemperature") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ColorTemperatureSupport), colorTemperatureSupport, "colorTemperatureSupport") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CompassDirectionRef), compassDirectionRef, "compassDirectionRef") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CompositeShootingOutputInterval), compositeShootingOutputInterval, "compositeShootingOutputInterval") + assertEquals( + options.getValue(ThetaRepository.OptionNameEnum.CompositeShootingOutputIntervalSupport), + compositeShootingOutputIntervalSupport, + "compositeShootingOutputIntervalSupport" + ) assertEquals(options.getValue(ThetaRepository.OptionNameEnum.CompositeShootingTime), compositeShootingTime, "compositeShootingTime") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ContinuousNumber), continuousNumber, "continuousNumber") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.DateTimeZone), dateTimeZone, "dateTimeZone") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.EthernetConfig), ethernetConfig, "ethernetConfig") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ExposureCompensation), exposureCompensation, "exposureCompensation") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ExposureDelay), exposureDelay, "exposureDelay") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ExposureDelaySupport), exposureDelaySupport, "exposureDelaySupport") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ExposureProgram), exposureProgram, "exposureProgram") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.FaceDetect), faceDetect, "faceDetect") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.FileFormat), fileFormat, "fileFormat") @@ -253,6 +371,7 @@ class OptionsTest { assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Function), function, "function") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Gain), gain, "gain") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.GpsInfo), gpsInfo, "gpsInfo") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.GpsTagRecordingSupport), gpsTagRecordingSupport, "gpsTagRecordingSupport") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.ImageStitching), imageStitching, "imageStitching") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.IsGpsOn), isGpsOn, "isGpsOn") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Iso), iso, "iso") @@ -260,8 +379,11 @@ class OptionsTest { assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Language), language, "language") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.LatestEnabledExposureDelayTime), latestEnabledExposureDelayTime, "latestEnabledExposureDelayTime") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.MaxRecordableTime), maxRecordableTime, "maxRecordableTime") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.MicrophoneNoiseReduction), microphoneNoiseReduction, "microphoneNoiseReduction") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.MobileNetworkSetting), mobileNetworkSetting, "mobileNetworkSetting") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.NetworkType), networkType, "networkType") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.OffDelay), offDelay, "offDelay") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.OffDelayUsb), offDelayUsb, "offDelayUsb") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Password), password, "password") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.PowerSaving), powerSaving, "powerSaving") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Preset), preset, "preset") @@ -277,13 +399,18 @@ class OptionsTest { assertEquals(options.getValue(ThetaRepository.OptionNameEnum.TimeShift), timeShift, "timeShift") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.TopBottomCorrection), topBottomCorrection, "topBottomCorrection") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.TopBottomCorrectionRotation), topBottomCorrectionRotation, "topBottomCorrectionRotation") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.TopBottomCorrectionRotationSupport), topBottomCorrectionRotationSupport, "topBottomCorrectionRotationSupport") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.TotalSpace), totalSpace, "totalSpace") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.UsbConnection), usbConnection, "usbConnection") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.Username), username, "userName") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.VideoStitching), videoStitching, "videoStitching") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.VisibilityReduction), visibilityReduction, "visibilityReduction") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WhiteBalance), whiteBalance, "whiteBalance") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WhiteBalanceAutoStrength), whiteBalanceAutoStrength, "whiteBalanceAutoStrength") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WlanAntennaConfig), wlanAntennaConfig, "wlanAntennaConfig") assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WlanFrequency), wlanFrequency, "wlanFrequency") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WlanFrequencySupport), wlanFrequencySupport, "wlanFrequencySupport") + assertEquals(options.getValue(ThetaRepository.OptionNameEnum.WlanFrequencyClMode), wlanFrequencyClMode, "wlanFrequencyClMode") } /** @@ -292,8 +419,29 @@ class OptionsTest { @Test fun optionsSetValueTest() { val values = listOf( + Pair( + ThetaRepository.OptionNameEnum.AccessInfo, ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ), Pair(ThetaRepository.OptionNameEnum.AiAutoThumbnail, ThetaRepository.AiAutoThumbnailEnum.OFF), Pair(ThetaRepository.OptionNameEnum.Aperture, ThetaRepository.ApertureEnum.APERTURE_2_1), + Pair(ThetaRepository.OptionNameEnum.ApertureSupport, listOf(ThetaRepository.ApertureEnum.APERTURE_AUTO, ThetaRepository.ApertureEnum.APERTURE_2_4)), Pair( ThetaRepository.OptionNameEnum.AutoBracket, ThetaRepository.BracketSettingList().add( @@ -327,14 +475,30 @@ class OptionsTest { ) ), Pair(ThetaRepository.OptionNameEnum.CameraControlSource, ThetaRepository.CameraControlSourceEnum.CAMERA), + Pair(ThetaRepository.OptionNameEnum.CameraLock, ThetaRepository.CameraLockEnum.BASIC_LOCK), + Pair( + ThetaRepository.OptionNameEnum.CameraLockConfig, + ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = false, + isModeKeyLocked = true, + isWlanKeyLocked = true, + isFnKeyLocked = false, + isPanelLocked = false + ) + ), Pair(ThetaRepository.OptionNameEnum.CameraMode, ThetaRepository.CameraModeEnum.CAPTURE), Pair(ThetaRepository.OptionNameEnum.CameraPower, ThetaRepository.CameraPowerEnum.ON), Pair(ThetaRepository.OptionNameEnum.CaptureInterval, 4), Pair(ThetaRepository.OptionNameEnum.CaptureMode, ThetaRepository.CaptureModeEnum.IMAGE), Pair(ThetaRepository.OptionNameEnum.CaptureNumber, 0), Pair(ThetaRepository.OptionNameEnum.ColorTemperature, 10), + Pair(ThetaRepository.OptionNameEnum.ColorTemperatureSupport, ThetaRepository.ValueRange(10000, 2000, 100)), + Pair(ThetaRepository.OptionNameEnum.CompassDirectionRef, ThetaRepository.CompassDirectionRefEnum.TRUE_NORTH), Pair(ThetaRepository.OptionNameEnum.CompositeShootingOutputInterval, 60), + Pair(ThetaRepository.OptionNameEnum.CompositeShootingOutputIntervalSupport, ThetaRepository.ValueRange(600, 0, 60)), Pair(ThetaRepository.OptionNameEnum.CompositeShootingTime, 600), + Pair(ThetaRepository.OptionNameEnum.CompositeShootingTimeSupport, ThetaRepository.ValueRange(86400, 600, 600)), Pair(ThetaRepository.OptionNameEnum.DateTimeZone, "2014:05:18 01:04:29+08:00"), Pair( ThetaRepository.OptionNameEnum.EthernetConfig, @@ -343,11 +507,14 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", proxy = ThetaRepository.Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ) ), Pair(ThetaRepository.OptionNameEnum.ExposureCompensation, ThetaRepository.ExposureCompensationEnum.M0_3), Pair(ThetaRepository.OptionNameEnum.ExposureDelay, ThetaRepository.ExposureDelayEnum.DELAY_10), + Pair(ThetaRepository.OptionNameEnum.ExposureDelaySupport, listOf(ThetaRepository.ExposureDelayEnum.DELAY_OFF, ThetaRepository.ExposureDelayEnum.DELAY_10)), Pair(ThetaRepository.OptionNameEnum.ExposureProgram, ThetaRepository.ExposureProgramEnum.NORMAL_PROGRAM), Pair(ThetaRepository.OptionNameEnum.FaceDetect, ThetaRepository.FaceDetectEnum.OFF), Pair(ThetaRepository.OptionNameEnum.FileFormat, ThetaRepository.FileFormatEnum.IMAGE_11K), @@ -362,8 +529,14 @@ class OptionsTest { Pair(ThetaRepository.OptionNameEnum.Language, ThetaRepository.LanguageEnum.JA), Pair(ThetaRepository.OptionNameEnum.LatestEnabledExposureDelayTime, ThetaRepository.ExposureDelayEnum.DELAY_10), Pair(ThetaRepository.OptionNameEnum.MaxRecordableTime, ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_1500), + Pair(ThetaRepository.OptionNameEnum.MicrophoneNoiseReduction, ThetaRepository.MicrophoneNoiseReductionEnum.ON), + Pair( + ThetaRepository.OptionNameEnum.MobileNetworkSetting, + ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.ON, plan = ThetaRepository.PlanEnum.SORACOM) + ), Pair(ThetaRepository.OptionNameEnum.NetworkType, ThetaRepository.NetworkTypeEnum.ETHERNET), Pair(ThetaRepository.OptionNameEnum.OffDelay, ThetaRepository.OffDelayEnum.OFF_DELAY_10M), + Pair(ThetaRepository.OptionNameEnum.OffDelayUsb, ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M), Pair(ThetaRepository.OptionNameEnum.Password, "password"), Pair(ThetaRepository.OptionNameEnum.PowerSaving, ThetaRepository.PowerSavingEnum.OFF), Pair(ThetaRepository.OptionNameEnum.Preset, ThetaRepository.PresetEnum.NIGHT_VIEW), @@ -382,13 +555,30 @@ class OptionsTest { ), Pair(ThetaRepository.OptionNameEnum.TopBottomCorrection, ThetaRepository.TopBottomCorrectionOptionEnum.APPLY), Pair(ThetaRepository.OptionNameEnum.TopBottomCorrectionRotation, ThetaRepository.TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f)), + Pair( + ThetaRepository.OptionNameEnum.TopBottomCorrectionRotationSupport, ThetaRepository.TopBottomCorrectionRotationSupport( + pitch = ThetaRepository.ValueRange(100f, -100f, 0.2f), + roll = ThetaRepository.ValueRange(200f, -200f, 0.4f), + yaw = ThetaRepository.ValueRange(300f, -300f, 0.6f) + ) + ), Pair(ThetaRepository.OptionNameEnum.TotalSpace, 104L), + Pair(ThetaRepository.OptionNameEnum.UsbConnection, ThetaRepository.UsbConnectionEnum.MTP), Pair(ThetaRepository.OptionNameEnum.Username, "username"), Pair(ThetaRepository.OptionNameEnum.VideoStitching, ThetaRepository.VideoStitchingEnum.NONE), Pair(ThetaRepository.OptionNameEnum.VisibilityReduction, ThetaRepository.VisibilityReductionEnum.OFF), Pair(ThetaRepository.OptionNameEnum.WhiteBalance, ThetaRepository.WhiteBalanceEnum.WARM_WHITE_FLUORESCENT), Pair(ThetaRepository.OptionNameEnum.WhiteBalanceAutoStrength, ThetaRepository.WhiteBalanceAutoStrengthEnum.ON), + Pair(ThetaRepository.OptionNameEnum.WlanAntennaConfig, ThetaRepository.WlanAntennaConfigEnum.SISO), Pair(ThetaRepository.OptionNameEnum.WlanFrequency, ThetaRepository.WlanFrequencyEnum.GHZ_5), + Pair( + ThetaRepository.OptionNameEnum.WlanFrequencyClMode, + ThetaRepository.WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ) + ), ) val options = ThetaRepository.Options() values.forEach { @@ -405,8 +595,47 @@ class OptionsTest { */ @Test fun optionsSecondaryConstructorTest() { + val accessInfo = Pair( + AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = WlanFrequencyAccessInfo.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ), ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ) val aiAutoThumbnail = Pair(AiAutoThumbnail.OFF, ThetaRepository.AiAutoThumbnailEnum.OFF) + val aiAutoThumbnailSupport = Pair(listOf(AiAutoThumbnail.ON, AiAutoThumbnail.OFF), listOf(ThetaRepository.AiAutoThumbnailEnum.ON, ThetaRepository.AiAutoThumbnailEnum.OFF)) val aperture = Pair(2.1f, ThetaRepository.ApertureEnum.APERTURE_2_1) + val apertureSupport = Pair(listOf(2.1f, 3.5f), listOf(ThetaRepository.ApertureEnum.APERTURE_2_1, ThetaRepository.ApertureEnum.APERTURE_3_5)) val autoBracket = Pair( AutoBracket( 2, listOf( @@ -459,14 +688,48 @@ class OptionsTest { ) ) val cameraControlSource = Pair(CameraControlSource.CAMERA, ThetaRepository.CameraControlSourceEnum.CAMERA) + val cameraControlSourceSupport = + Pair(listOf(CameraControlSource.CAMERA, CameraControlSource.APP), listOf(ThetaRepository.CameraControlSourceEnum.CAMERA, ThetaRepository.CameraControlSourceEnum.APP)) + val cameraLock = Pair(CameraLock.UNLOCK, ThetaRepository.CameraLockEnum.UNLOCK) + val cameraLockConfig = Pair( + CameraLockConfig( + powerKey = CameraLockType.LOCK, + shutterKey = CameraLockType.LOCK, + modeKey = CameraLockType.LOCK, + wlanKey = CameraLockType.UNLOCK, + fnKey = CameraLockType.UNLOCK, + panel = CameraLockType.UNLOCK + ), + ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = true, + isModeKeyLocked = true, + isWlanKeyLocked = false, + isFnKeyLocked = false, + isPanelLocked = false + ) + ) val cameraMode = Pair(CameraMode.CAPTURE, ThetaRepository.CameraModeEnum.CAPTURE) val cameraPower = Pair(CameraPower.OFF, ThetaRepository.CameraPowerEnum.OFF) + val cameraPowerSupport = Pair( + listOf(CameraPower.ON, CameraPower.OFF, CameraPower.POWER_SAVING, CameraPower.SILENT_MODE), + listOf( + ThetaRepository.CameraPowerEnum.ON, + ThetaRepository.CameraPowerEnum.OFF, + ThetaRepository.CameraPowerEnum.POWER_SAVING, + ThetaRepository.CameraPowerEnum.SILENT_MODE + ) + ) val captureInterval = Pair(5, 5) val captureMode = Pair(CaptureMode.IMAGE, ThetaRepository.CaptureModeEnum.IMAGE) val captureNumber = Pair(9999, 9999) val colorTemperature = Pair(10, 10) + val colorTemperatureSupport = Pair(ColorTemperatureSupport(10000, 2000, 100), ThetaRepository.ValueRange(10000, 2000, 100)) + val compassDirectionRef = Pair(CompassDirectionRef.MAGNETIC, ThetaRepository.CompassDirectionRefEnum.MAGNETIC) val compositeShootingOutputInterval = Pair(60, 60) + val compositeShootingOutputIntervalSupport = Pair(CompositeShootingOutputIntervalSupport(600, 0, 60), ThetaRepository.ValueRange(600, 0, 60)) val compositeShootingTime = Pair(600, 600) + val compositeShootingTimeSupport = Pair(CompositeShootingTimeSupport(86400, 600, 600), ThetaRepository.ValueRange(86400, 600, 600)) val dateTimeZone = Pair("2014:05:18 01:04:29+08:00", "2014:05:18 01:04:29+08:00") val ethernetConfig = Pair( EthernetConfig( @@ -474,6 +737,8 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", _proxy = Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ), ThetaRepository.EthernetConfig( @@ -481,11 +746,14 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", proxy = ThetaRepository.Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ) ) val exposureCompensation = Pair(-0.3f, ThetaRepository.ExposureCompensationEnum.M0_3) val exposureDelay = Pair(10, ThetaRepository.ExposureDelayEnum.DELAY_10) + val exposureDelaySupport = Pair(listOf(0, 10), listOf(ThetaRepository.ExposureDelayEnum.DELAY_OFF, ThetaRepository.ExposureDelayEnum.DELAY_10)) val exposureProgram = Pair(2, ThetaRepository.ExposureProgramEnum.NORMAL_PROGRAM) val faceDetect = Pair(FaceDetect.OFF, ThetaRepository.FaceDetectEnum.OFF) val fileFormat = Pair( @@ -496,6 +764,7 @@ class OptionsTest { val function = Pair(ShootingFunction.NORMAL, ThetaRepository.ShootingFunctionEnum.NORMAL) val gain = Pair(Gain.NORMAL, ThetaRepository.GainEnum.NORMAL) val gpsInfo = Pair(GpsInfo(65535.0f, 65535.0f, 0f, null, null), ThetaRepository.GpsInfo.disabled) + val gpsTagRecordingSupport = Pair(listOf(GpsTagRecording.ON, GpsTagRecording.OFF), listOf(ThetaRepository.GpsTagRecordingEnum.ON, ThetaRepository.GpsTagRecordingEnum.OFF)) val imageStitching = Pair(ImageStitching.AUTO, ThetaRepository.ImageStitchingEnum.AUTO) val isGpsOn = Pair(GpsTagRecording.ON, true) val iso = Pair(125, ThetaRepository.IsoEnum.ISO_125) @@ -503,8 +772,14 @@ class OptionsTest { val language = Pair(Language.JA, ThetaRepository.LanguageEnum.JA) val latestEnabledExposureDelayTime = Pair(10, ThetaRepository.ExposureDelayEnum.DELAY_10) val maxRecordableTime = Pair(1500, ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_1500) + val microphoneNoiseReduction = Pair(MicrophoneNoiseReduction.OFF, ThetaRepository.MicrophoneNoiseReductionEnum.OFF) + val mobileNetworkSetting = Pair( + MobileNetworkSetting(roaming = Roaming.ON, plan = Plan.SORACOM_PLAN_DU), + ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.ON, plan = ThetaRepository.PlanEnum.SORACOM_PLAN_DU) + ) val networkType = Pair(NetworkType.DIRECT, ThetaRepository.NetworkTypeEnum.DIRECT) val offDelay = Pair(600, ThetaRepository.OffDelayEnum.OFF_DELAY_10M) + val offDelayUsb = Pair(600, ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M) val password = Pair("password", "password") val powerSaving = Pair(PowerSaving.OFF, ThetaRepository.PowerSavingEnum.OFF) val preset = Pair(Preset.LENS_BY_LENS_EXPOSURE, ThetaRepository.PresetEnum.LENS_BY_LENS_EXPOSURE) @@ -522,17 +797,48 @@ class OptionsTest { ThetaRepository.TimeShiftSetting(true, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_4, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_5) ) val topBottomCorrection = Pair(TopBottomCorrectionOption.DISAPPLY, ThetaRepository.TopBottomCorrectionOptionEnum.DISAPPLY) - val topBottomCorrectionRotation = Pair(TopBottomCorrectionRotation(3.0f, 2.0f, 1.0f), ThetaRepository.TopBottomCorrectionRotation(3.0f, 2.0f, 1.0f)) + val topBottomCorrectionRotation = Pair(TopBottomCorrectionRotation("3.0", "2.0", "1.0"), ThetaRepository.TopBottomCorrectionRotation(3.0f, 2.0f, 1.0f)) + val topBottomCorrectionRotationSupport = Pair( + TopBottomCorrectionRotationSupport( + pitch = PitchSupport(100f, -100f, 0.2f), + roll = RollSupport(200f, -200f, 0.4f), + yaw = YawSupport(300f, -300f, 0.6f) + ), ThetaRepository.TopBottomCorrectionRotationSupport( + pitch = ThetaRepository.ValueRange(100f, -100f, 0.2f), + roll = ThetaRepository.ValueRange(200f, -200f, 0.4f), + yaw = ThetaRepository.ValueRange(300f, -300f, 0.6f) + ) + ) val totalSpace = Pair(104L, 104L) + val usbConnection = Pair(UsbConnection.MTP, ThetaRepository.UsbConnectionEnum.MTP) val username = Pair("username", "username") val videoStitching = Pair(VideoStitching.NONE, ThetaRepository.VideoStitchingEnum.NONE) val visibilityReduction = Pair(VisibilityReduction.OFF, ThetaRepository.VisibilityReductionEnum.OFF) val whiteBalance = Pair(WhiteBalance._WARM_WHITE_FLUORESCENT, ThetaRepository.WhiteBalanceEnum.WARM_WHITE_FLUORESCENT) + val wlanAntennaConfig = Pair(WlanAntennaConfig.SISO, ThetaRepository.WlanAntennaConfigEnum.SISO) val whiteBalanceAutoStrength = Pair(WhiteBalanceAutoStrength.OFF, ThetaRepository.WhiteBalanceAutoStrengthEnum.OFF) + val wlanFrequency = Pair(WlanFrequency.GHZ_5, ThetaRepository.WlanFrequencyEnum.GHZ_5) + val wlanFrequencySupport = + Pair(listOf(WlanFrequency.GHZ_2_4, WlanFrequency.GHZ_5), listOf(ThetaRepository.WlanFrequencyEnum.GHZ_2_4, ThetaRepository.WlanFrequencyEnum.GHZ_5)) + val wlanFrequencyClMode = Pair( + WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ), + ThetaRepository.WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ) + ) val orgOptions = Options( + _accessInfo = accessInfo.first, _aiAutoThumbnail = aiAutoThumbnail.first, + _aiAutoThumbnailSupport = aiAutoThumbnailSupport.first, aperture = aperture.first, + apertureSupport = apertureSupport.first, _autoBracket = autoBracket.first, _bitrate = bitrate.first, _bluetoothPower = bluetoothPower.first, @@ -540,18 +846,27 @@ class OptionsTest { _burstMode = burstMode.first, _burstOption = burstOption.first, _cameraControlSource = cameraControlSource.first, + _cameraControlSourceSupport = cameraControlSourceSupport.first, + _cameraPowerSupport = cameraPowerSupport.first, + _cameraLock = cameraLock.first, + _cameraLockConfig = cameraLockConfig.first, _cameraMode = cameraMode.first, _cameraPower = cameraPower.first, captureInterval = captureInterval.first, captureMode = captureMode.first, captureNumber = captureNumber.first, _colorTemperature = colorTemperature.first, + _colorTemperatureSupport = colorTemperatureSupport.first, + _compassDirectionRef = compassDirectionRef.first, _compositeShootingOutputInterval = compositeShootingOutputInterval.first, + _compositeShootingOutputIntervalSupport = compositeShootingOutputIntervalSupport.first, _compositeShootingTime = compositeShootingTime.first, + _compositeShootingTimeSupport = compositeShootingTimeSupport.first, dateTimeZone = dateTimeZone.first, _ethernetConfig = ethernetConfig.first, exposureCompensation = exposureCompensation.first, exposureDelay = exposureDelay.first, + exposureDelaySupport = exposureDelaySupport.first, exposureProgram = exposureProgram.first, _faceDetect = faceDetect.first, fileFormat = fileFormat.first, @@ -560,14 +875,18 @@ class OptionsTest { _gain = gain.first, gpsInfo = gpsInfo.first, _gpsTagRecording = isGpsOn.first, + _gpsTagRecordingSupport = gpsTagRecordingSupport.first, _imageStitching = imageStitching.first, iso = iso.first, isoAutoHighLimit = isoAutoHighLimit.first, _language = language.first, _latestEnabledExposureDelayTime = latestEnabledExposureDelayTime.first, _maxRecordableTime = maxRecordableTime.first, + _microphoneNoiseReduction = microphoneNoiseReduction.first, + _mobileNetworkSetting = mobileNetworkSetting.first, _networkType = networkType.first, offDelay = offDelay.first, + _offDelayUSB = offDelayUsb.first, _password = password.first, _powerSaving = powerSaving.first, _preset = preset.first, @@ -583,17 +902,26 @@ class OptionsTest { _timeShift = timeShift.first, _topBottomCorrection = topBottomCorrection.first, _topBottomCorrectionRotation = topBottomCorrectionRotation.first, + _topBottomCorrectionRotationSupport = topBottomCorrectionRotationSupport.first, totalSpace = totalSpace.first, + _usbConnection = usbConnection.first, _username = username.first, videoStitching = videoStitching.first, _visibilityReduction = visibilityReduction.first, whiteBalance = whiteBalance.first, - _whiteBalanceAutoStrength = whiteBalanceAutoStrength.first + _wlanAntennaConfig = wlanAntennaConfig.first, + _whiteBalanceAutoStrength = whiteBalanceAutoStrength.first, + _wlanFrequency = wlanFrequency.first, + _wlanFrequencySupport = wlanFrequencySupport.first, + _wlanFrequencyCLmode = wlanFrequencyClMode.first, ) val options = ThetaRepository.Options(orgOptions) + assertEquals(options.accessInfo, accessInfo.second, "accessInfo") assertEquals(options.aiAutoThumbnail, aiAutoThumbnail.second, "aiAutoThumbnail") + assertEquals(options.aiAutoThumbnailSupport, aiAutoThumbnailSupport.second, "aiAutoThumbnailSupport") assertEquals(options.aperture, aperture.second, "aperture") + assertEquals(options.apertureSupport, apertureSupport.second, "apertureSupport") assertEquals(options.autoBracket, autoBracket.second, "autoBracket") assertEquals(options.bitrate, bitrate.second, "bitrate") assertEquals(options.bluetoothPower, bluetoothPower.second, "bluetoothPower") @@ -601,18 +929,27 @@ class OptionsTest { assertEquals(options.burstMode, burstMode.second, "burstMode") assertEquals(options.burstOption, burstOption.second, "burstOption") assertEquals(options.cameraControlSource, cameraControlSource.second, "cameraControlSource") + assertEquals(options.cameraControlSourceSupport, cameraControlSourceSupport.second, "cameraControlSourceSupport") + assertEquals(options.cameraPowerSupport, cameraPowerSupport.second, "cameraPowerSupport") + assertEquals(options.cameraLock, cameraLock.second, "cameraLock") + assertEquals(options.cameraLockConfig, cameraLockConfig.second, "cameraLockConfig") assertEquals(options.cameraMode, cameraMode.second, "cameraMode") assertEquals(options.cameraPower, cameraPower.second, "cameraPower") assertEquals(options.captureInterval, captureInterval.second, "captureInterval") assertEquals(options.captureMode, captureMode.second, "captureMode") assertEquals(options.captureNumber, captureNumber.second, "captureNumber") assertEquals(options.colorTemperature, colorTemperature.second, "colorTemperature") + assertEquals(options.colorTemperatureSupport, colorTemperatureSupport.second, "colorTemperatureSupport") + assertEquals(options.compassDirectionRef, compassDirectionRef.second, "compassDirectionRef") assertEquals(options.compositeShootingOutputInterval, compositeShootingOutputInterval.second, "compositeShootingOutputInterval") + assertEquals(options.compositeShootingOutputIntervalSupport, compositeShootingOutputIntervalSupport.second, "compositeShootingOutputIntervalSupport") assertEquals(options.compositeShootingTime, compositeShootingTime.second, "compositeShootingTime") + assertEquals(options.compositeShootingTimeSupport, compositeShootingTimeSupport.second, "compositeShootingTimeSupport") assertEquals(options.dateTimeZone, dateTimeZone.second, "dateTimeZone") assertEquals(options.ethernetConfig, ethernetConfig.second, "ethernetConfig") assertEquals(options.exposureCompensation, exposureCompensation.second, "exposureCompensation") assertEquals(options.exposureDelay, exposureDelay.second, "exposureDelay") + assertEquals(options.exposureDelaySupport, exposureDelaySupport.second, "exposureDelaySupport") assertEquals(options.exposureProgram, exposureProgram.second, "exposureProgram") assertEquals(options.faceDetect, faceDetect.second, "faceDetect") assertEquals(options.fileFormat, fileFormat.second, "fileFormat") @@ -620,15 +957,19 @@ class OptionsTest { assertEquals(options.function, function.second, "function") assertEquals(options.gain, gain.second, "gain") assertEquals(options.gpsInfo, gpsInfo.second, "gpsInfo") + assertEquals(options.gpsTagRecordingSupport, gpsTagRecordingSupport.second, "gpsTagRecordingSupport") assertEquals(options.imageStitching, imageStitching.second, "imageStitching") assertEquals(options.isGpsOn, isGpsOn.second, "isGpsOn") assertEquals(options.iso, iso.second, "iso") assertEquals(options.isoAutoHighLimit, isoAutoHighLimit.second, "isoAutoHighLimit") assertEquals(options.language, language.second, "language") assertEquals(options.latestEnabledExposureDelayTime, latestEnabledExposureDelayTime.second, "latestEnabledExposureDelayTime") - assertEquals(options.maxRecordableTime, maxRecordableTime.second, "aperture") + assertEquals(options.maxRecordableTime, maxRecordableTime.second, "maxRecordableTime") + assertEquals(options.microphoneNoiseReduction, microphoneNoiseReduction.second, "microphoneNoiseReduction") + assertEquals(options.mobileNetworkSetting, mobileNetworkSetting.second, "mobileNetworkSetting") assertEquals(options.networkType, networkType.second, "networkType") assertEquals(options.offDelay, offDelay.second, "offDelay") + assertEquals(options.offDelayUsb, offDelayUsb.second, "offDelayUsb") assertEquals(options.password, password.second, "password") assertEquals(options.powerSaving, powerSaving.second, "powerSaving") assertEquals(options.preset, preset.second, "preset") @@ -644,16 +985,59 @@ class OptionsTest { assertEquals(options.timeShift, timeShift.second, "timeShift") assertEquals(options.topBottomCorrection, topBottomCorrection.second, "topBottomCorrection") assertEquals(options.topBottomCorrectionRotation, topBottomCorrectionRotation.second, "topBottomCorrectionRotation") + assertEquals(options.topBottomCorrectionRotationSupport, topBottomCorrectionRotationSupport.second, "topBottomCorrectionRotationSupport") assertEquals(options.totalSpace, totalSpace.second, "totalSpace") + assertEquals(options.usbConnection, usbConnection.second, "usbConnection") assertEquals(options.username, username.second, "username") assertEquals(options.videoStitching, videoStitching.second, "videoStitching") assertEquals(options.visibilityReduction, visibilityReduction.second, "visibilityReduction") assertEquals(options.whiteBalance, whiteBalance.second, "whiteBalance") + assertEquals(options.wlanAntennaConfig, wlanAntennaConfig.second, "wlanAntennaConfig") assertEquals(options.whiteBalanceAutoStrength, whiteBalanceAutoStrength.second, "whiteBalanceAutoStrength") + assertEquals(options.wlanFrequency, wlanFrequency.second, "wlanFrequency") + assertEquals(options.wlanFrequencySupport, wlanFrequencySupport.second, "wlanFrequencySupport") + assertEquals(options.wlanFrequencyClMode, wlanFrequencyClMode.second, "wlanFrequencyClMode") } @Test fun optionsConvertTest() { + val accessInfo = Pair( + AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = WlanFrequencyAccessInfo.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ), ThetaRepository.AccessInfo( + ssid = "ssid_test", + ipAddress = "192.168.1.2", + subnetMask = "255.255.0.0", + defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", + proxyURL = "http://192.168.1.3", + frequency = ThetaRepository.WlanFrequencyAccessInfoEnum.GHZ_2_4, + wlanSignalStrength = -60, + wlanSignalLevel = 4, + lteSignalStrength = 0, + lteSignalLevel = 0, + dhcpLeaseAddress = listOf( + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.5", macAddress = "192.168.1.6", hostName = "192.168.1.7"), + ThetaRepository.DhcpLeaseAddress(ipAddress = "192.168.1.8", macAddress = "192.168.1.9", hostName = "192.168.1.10") + ) + ) + ) val aiAutoThumbnail = Pair(AiAutoThumbnail.ON, ThetaRepository.AiAutoThumbnailEnum.ON) val aperture = Pair(2.1f, ThetaRepository.ApertureEnum.APERTURE_2_1) val autoBracket = Pair( @@ -708,12 +1092,34 @@ class OptionsTest { ) ) val cameraControlSource = Pair(CameraControlSource.CAMERA, ThetaRepository.CameraControlSourceEnum.CAMERA) + val cameraControlSourceSupport = + Pair(listOf(CameraControlSource.CAMERA, CameraControlSource.APP), listOf(ThetaRepository.CameraControlSourceEnum.CAMERA, ThetaRepository.CameraControlSourceEnum.APP)) + val cameraLock = Pair(CameraLock.BASIC_LOCK, ThetaRepository.CameraLockEnum.BASIC_LOCK) + val cameraLockConfig = Pair( + CameraLockConfig( + powerKey = CameraLockType.LOCK, + shutterKey = CameraLockType.LOCK, + modeKey = CameraLockType.LOCK, + wlanKey = CameraLockType.UNLOCK, + fnKey = CameraLockType.UNLOCK, + panel = CameraLockType.UNLOCK + ), + ThetaRepository.CameraLockConfig( + isPowerKeyLocked = true, + isShutterKeyLocked = true, + isModeKeyLocked = true, + isWlanKeyLocked = false, + isFnKeyLocked = false, + isPanelLocked = false + ) + ) val cameraMode = Pair(CameraMode.CAPTURE, ThetaRepository.CameraModeEnum.CAPTURE) val cameraPower = Pair(CameraPower.SILENT_MODE, ThetaRepository.CameraPowerEnum.SILENT_MODE) val captureInterval = Pair(20, 20) val captureMode = Pair(CaptureMode.IMAGE, ThetaRepository.CaptureModeEnum.IMAGE) val captureNumber = Pair(30, 30) val colorTemperature = Pair(10, 10) + val compassDirectionRef = Pair(CompassDirectionRef.AUTO, ThetaRepository.CompassDirectionRefEnum.AUTO) val compositeShootingOutputInterval = Pair(60, 60) val compositeShootingTime = Pair(600, 600) val dateTimeZone = Pair("2014:05:18 01:04:29+08:00", "2014:05:18 01:04:29+08:00") @@ -723,6 +1129,8 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", _proxy = Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ), ThetaRepository.EthernetConfig( @@ -730,6 +1138,8 @@ class OptionsTest { ipAddress = "192.168.1.2", subnetMask = "255.255.0.0", defaultGateway = "192.168.1.12", + dns1 = "192.168.1.55", + dns2 = "192.168.1.66", proxy = ThetaRepository.Proxy(use = true, url = "192.168.1.3", port = 80, userid = "abc", password = "123") ) ) @@ -755,8 +1165,14 @@ class OptionsTest { val language = Pair(Language.JA, ThetaRepository.LanguageEnum.JA) val latestEnabledExposureDelayTime = Pair(10, ThetaRepository.ExposureDelayEnum.DELAY_10) val maxRecordableTime = Pair(1500, ThetaRepository.MaxRecordableTimeEnum.RECORDABLE_TIME_1500) + val microphoneNoiseReduction = Pair(MicrophoneNoiseReduction.ON, ThetaRepository.MicrophoneNoiseReductionEnum.ON) + val mobileNetworkSetting = Pair( + MobileNetworkSetting(roaming = Roaming.OFF, plan = Plan.SORACOM), + ThetaRepository.MobileNetworkSetting(roaming = ThetaRepository.RoamingEnum.OFF, plan = ThetaRepository.PlanEnum.SORACOM) + ) val networkType = Pair(NetworkType.ETHERNET, ThetaRepository.NetworkTypeEnum.ETHERNET) val offDelay = Pair(600, ThetaRepository.OffDelayEnum.OFF_DELAY_10M) + val offDelayUsb = Pair(600, ThetaRepository.OffDelayUsbEnum.OFF_DELAY_10M) val password = Pair("password", "password") val powerSaving = Pair(PowerSaving.OFF, ThetaRepository.PowerSavingEnum.OFF) val preset = Pair(Preset.FACE, ThetaRepository.PresetEnum.FACE) @@ -774,15 +1190,33 @@ class OptionsTest { ThetaRepository.TimeShiftSetting(false, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_6, ThetaRepository.TimeShiftIntervalEnum.INTERVAL_7) ) val topBottomCorrection = Pair(TopBottomCorrectionOption.MANUAL, ThetaRepository.TopBottomCorrectionOptionEnum.MANUAL) - val topBottomCorrectionRotation = Pair(TopBottomCorrectionRotation(0.0f, 0.0f, 0.0f), ThetaRepository.TopBottomCorrectionRotation(0.0f, 0.0f, 0.0f)) + val topBottomCorrectionRotation = Pair(TopBottomCorrectionRotation("0.0", "0.0", "0.0"), ThetaRepository.TopBottomCorrectionRotation(0.0f, 0.0f, 0.0f)) val totalSpace = Pair(104L, 104L) + val usbConnection = Pair(UsbConnection.MSC, ThetaRepository.UsbConnectionEnum.MSC) val userName = Pair("username", "username") val videoStitching = Pair(VideoStitching.NONE, ThetaRepository.VideoStitchingEnum.NONE) val visibilityReduction = Pair(VisibilityReduction.OFF, ThetaRepository.VisibilityReductionEnum.OFF) val whiteBalance = Pair(WhiteBalance._WARM_WHITE_FLUORESCENT, ThetaRepository.WhiteBalanceEnum.WARM_WHITE_FLUORESCENT) + val wlanAntennaConfig = Pair(WlanAntennaConfig.MIMO, ThetaRepository.WlanAntennaConfigEnum.MIMO) val whiteBalanceAutoStrength = Pair(WhiteBalanceAutoStrength.ON, ThetaRepository.WhiteBalanceAutoStrengthEnum.ON) + val wlanFrequency = Pair(WlanFrequency.GHZ_5, ThetaRepository.WlanFrequencyEnum.GHZ_5) + val wlanFrequencySupport = + Pair(listOf(WlanFrequency.GHZ_2_4, WlanFrequency.GHZ_5), listOf(ThetaRepository.WlanFrequencyEnum.GHZ_2_4, ThetaRepository.WlanFrequencyEnum.GHZ_5)) + val wlanFrequencyClMode = Pair( + WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ), + ThetaRepository.WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = false, + ) + ) val orgOptions = ThetaRepository.Options( + accessInfo = accessInfo.second, aiAutoThumbnail = aiAutoThumbnail.second, aperture = aperture.second, autoBracket = autoBracket.second, @@ -792,12 +1226,16 @@ class OptionsTest { burstMode = burstMode.second, burstOption = burstOption.second, cameraControlSource = cameraControlSource.second, + cameraControlSourceSupport = cameraControlSourceSupport.second, + cameraLock = cameraLock.second, + cameraLockConfig = cameraLockConfig.second, cameraMode = cameraMode.second, cameraPower = cameraPower.second, captureInterval = captureInterval.second, captureMode = captureMode.second, captureNumber = captureNumber.second, colorTemperature = colorTemperature.second, + compassDirectionRef = compassDirectionRef.second, compositeShootingOutputInterval = compositeShootingOutputInterval.second, compositeShootingTime = compositeShootingTime.second, dateTimeZone = dateTimeZone.second, @@ -818,8 +1256,11 @@ class OptionsTest { language = language.second, latestEnabledExposureDelayTime = latestEnabledExposureDelayTime.second, maxRecordableTime = maxRecordableTime.second, + microphoneNoiseReduction = microphoneNoiseReduction.second, + mobileNetworkSetting = mobileNetworkSetting.second, networkType = networkType.second, offDelay = offDelay.second, + offDelayUsb = offDelayUsb.second, password = password.second, powerSaving = powerSaving.second, preset = preset.second, @@ -836,14 +1277,20 @@ class OptionsTest { topBottomCorrection = topBottomCorrection.second, topBottomCorrectionRotation = topBottomCorrectionRotation.second, totalSpace = totalSpace.second, + usbConnection = usbConnection.second, username = userName.second, videoStitching = videoStitching.second, visibilityReduction = visibilityReduction.second, whiteBalance = whiteBalance.second, - whiteBalanceAutoStrength = whiteBalanceAutoStrength.second + wlanAntennaConfig = wlanAntennaConfig.second, + whiteBalanceAutoStrength = whiteBalanceAutoStrength.second, + wlanFrequency = wlanFrequency.second, + wlanFrequencySupport = wlanFrequencySupport.second, + wlanFrequencyClMode = wlanFrequencyClMode.second, ) val options = orgOptions.toOptions() + assertEquals(options._accessInfo, accessInfo.first, "accessInfo") assertEquals(options._aiAutoThumbnail, aiAutoThumbnail.first, "aiAutoThumbnail") assertEquals(options.aperture, aperture.first, "aperture") assertEquals(options._autoBracket, autoBracket.first, "autoBracket") @@ -853,12 +1300,16 @@ class OptionsTest { assertEquals(options._burstMode, burstMode.first, "burstMode") assertEquals(options._burstOption, burstOption.first, "burstOption") assertEquals(options._cameraControlSource, cameraControlSource.first, "cameraControlSource") + assertEquals(options._cameraControlSourceSupport, cameraControlSourceSupport.first, "cameraControlSourceSupport") + assertEquals(options._cameraLock, cameraLock.first, "cameraLock") + assertEquals(options._cameraLockConfig, cameraLockConfig.first, "cameraLockConfig") assertEquals(options._cameraMode, cameraMode.first, "cameraMode") assertEquals(options._cameraPower, cameraPower.first, "cameraPower") assertEquals(options.captureInterval, captureInterval.first, "captureInterval") assertEquals(options.captureMode, captureMode.first, "captureMode") assertEquals(options.captureNumber, captureNumber.first, "captureNumber") assertEquals(options._colorTemperature, colorTemperature.first, "colorTemperature") + assertEquals(options._compassDirectionRef, compassDirectionRef.first, "compassDirectionRef") assertEquals(options._compositeShootingOutputInterval, compositeShootingOutputInterval.first, "compositeShootingOutputInterval") assertEquals(options._compositeShootingTime, compositeShootingTime.first, "compositeShootingTime") assertEquals(options.dateTimeZone, dateTimeZone.first, "dateTimeZone") @@ -878,9 +1329,12 @@ class OptionsTest { assertEquals(options.isoAutoHighLimit, isoAutoHighLimit.first, "isoAutoHighLimit") assertEquals(options._language, language.first, "language") assertEquals(options._latestEnabledExposureDelayTime, latestEnabledExposureDelayTime.first, "latestEnabledExposureDelayTime") - assertEquals(options._maxRecordableTime, maxRecordableTime.first, "aperture") + assertEquals(options._maxRecordableTime, maxRecordableTime.first, "maxRecordableTime") + assertEquals(options._microphoneNoiseReduction, microphoneNoiseReduction.first, "microphoneNoiseReduction") + assertEquals(options._mobileNetworkSetting, mobileNetworkSetting.first, "mobileNetworkSetting") assertEquals(options._networkType, networkType.first, "networkType") assertEquals(options.offDelay, offDelay.first, "offDelay") + assertEquals(options._offDelayUSB, offDelayUsb.first, "offDelayUsb") assertEquals(options._password, password.first, "password") assertEquals(options._powerSaving, powerSaving.first, "powerSaving") assertEquals(options._preset, preset.first, "preset") @@ -897,10 +1351,15 @@ class OptionsTest { assertEquals(options._topBottomCorrection, topBottomCorrection.first, "topBottomCorrection") assertEquals(options._topBottomCorrectionRotation, topBottomCorrectionRotation.first, "topBottomCorrectionRotation") assertEquals(options.totalSpace, totalSpace.first, "totalSpace") + assertEquals(options._usbConnection, usbConnection.first, "usbConnection") assertEquals(options._username, userName.first, "userName") assertEquals(options.videoStitching, videoStitching.first, "videoStitching") assertEquals(options._visibilityReduction, visibilityReduction.first, "visibilityReduction") assertEquals(options.whiteBalance, whiteBalance.first, "whiteBalance") + assertEquals(options._wlanAntennaConfig, wlanAntennaConfig.first, "wlanAntennaConfig") assertEquals(options._whiteBalanceAutoStrength, whiteBalanceAutoStrength.first, "whiteBalanceAutoStrength") + assertEquals(options._wlanFrequency, wlanFrequency.first, "wlanFrequency") + assertEquals(options._wlanFrequencySupport, wlanFrequencySupport.first, "wlanFrequencySupport") + assertEquals(options._wlanFrequencyCLmode, wlanFrequencyClMode.first, "wlanFrequencyClMode") } } diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PowerSavingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PowerSavingTest.kt index 204581e097e..b3703d4ec35 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PowerSavingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/PowerSavingTest.kt @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.PowerSaving -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 kotlin.test.AfterTest 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 d61f58de60e..0d478fedbbf 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 @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.Preset -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 kotlin.test.AfterTest 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 9ef3e018529..be77a6e915d 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 @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.PreviewFormat -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 kotlin.test.AfterTest @@ -54,6 +54,30 @@ class PreviewFormatTest { assertEquals(options.previewFormat, ThetaRepository.PreviewFormatEnum.W1024_H512_F30, "previewFormat") } + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.PreviewFormat + ) + val stringOptionNames = listOf( + "previewFormat" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_preview_format_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.previewFormat, ThetaRepository.PreviewFormatEnum.UNKNOWN, "previewFormat") + } + /** * Set option previewFormat. */ @@ -88,13 +112,17 @@ class PreviewFormatTest { @Test fun convertOptionPreviewFormatTest() = runTest { val values = listOf( + Pair(ThetaRepository.PreviewFormatEnum.UNKNOWN, PreviewFormat(0, 0, 0)), + Pair(ThetaRepository.PreviewFormatEnum.W1920_H960_F8, PreviewFormat(1920, 960, 8)), + Pair(ThetaRepository.PreviewFormatEnum.W1920_H960_F30, PreviewFormat(1920, 960, 30)), Pair(ThetaRepository.PreviewFormatEnum.W1024_H512_F30, PreviewFormat(1024, 512, 30)), + Pair(ThetaRepository.PreviewFormatEnum.W1024_H512_F15, PreviewFormat(1024, 512, 15)), + Pair(ThetaRepository.PreviewFormatEnum.W1024_H512_F10, PreviewFormat(1024, 512, 10)), Pair(ThetaRepository.PreviewFormatEnum.W1024_H512_F8, PreviewFormat(1024, 512, 8)), - Pair(ThetaRepository.PreviewFormatEnum.W1920_H960_F8, PreviewFormat(1920, 960, 8)), - Pair(ThetaRepository.PreviewFormatEnum.W512_H512_F30, PreviewFormat(512, 512, 30)), Pair(ThetaRepository.PreviewFormatEnum.W640_H320_F30, PreviewFormat(640, 320, 30)), Pair(ThetaRepository.PreviewFormatEnum.W640_H320_F10, PreviewFormat(640, 320, 10)), Pair(ThetaRepository.PreviewFormatEnum.W640_H320_F8, PreviewFormat(640, 320, 8)), + Pair(ThetaRepository.PreviewFormatEnum.W512_H512_F30, PreviewFormat(512, 512, 30)), Pair(ThetaRepository.PreviewFormatEnum.W3840_H1920_F30, PreviewFormat(3840, 1920, 30)), ) diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ProxyTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ProxyTest.kt index 0ae5ece2133..474a39b0da5 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ProxyTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ProxyTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.Proxy -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ProxyTest { 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 030da276e33..cca7a94c6e7 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 @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.ShootingMethod -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 kotlin.test.AfterTest diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShutterSpeedTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShutterSpeedTest.kt index 28c4dd650ae..125dbb919e4 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShutterSpeedTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/ShutterSpeedTest.kt @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class ShutterSpeedTest { 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 9f9b857e6e4..e37aff91482 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 @@ -5,13 +5,14 @@ import com.ricoh360.thetaclient.CheckRequest import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class SleepDelayTest { 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 0c9f72dbd83..c1b2dd4b8b1 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 @@ -7,8 +7,8 @@ import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.FirstShootingEnum import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.TimeShift -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 kotlin.test.AfterTest diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationSupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationSupportTest.kt new file mode 100644 index 00000000000..1e98019f377 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationSupportTest.kt @@ -0,0 +1,104 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import com.ricoh360.thetaclient.transferred.PitchSupport +import com.ricoh360.thetaclient.transferred.RollSupport +import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotationSupport +import com.ricoh360.thetaclient.transferred.YawSupport +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class TopBottomCorrectionRotationSupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.TopBottomCorrectionRotationSupport + ) + val stringOptionNames = listOf( + "_topBottomCorrectionRotationSupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_top_bottom_correction_rotation_support.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + options.topBottomCorrectionRotationSupport?.let { + assertEquals(it.pitch.max, 90.0f) + assertEquals(it.pitch.min, -90.0f) + assertEquals(it.pitch.stepSize, 0.1f) + + assertEquals(it.roll.max, 180.0f) + assertEquals(it.roll.min, -180.0f) + assertEquals(it.roll.stepSize, 0.1f) + + assertEquals(it.yaw.max, 180.0f) + assertEquals(it.yaw.min, -180.0f) + assertEquals(it.yaw.stepSize, 0.1f) + } + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair( + ThetaRepository.TopBottomCorrectionRotationSupport( + pitch = ThetaRepository.ValueRange(100f, -100f, 0.2f), + roll = ThetaRepository.ValueRange(200f, -200f, 0.4f), + yaw = ThetaRepository.ValueRange(300f, -300f, 0.6f) + ), TopBottomCorrectionRotationSupport( + pitch = PitchSupport(100f, -100f, 0.2f), + roll = RollSupport(200f, -200f, 0.4f), + yaw = YawSupport(300f, -300f, 0.6f) + ) + ), + ) + + values.forEach { + val orgOptions = Options( + _topBottomCorrectionRotationSupport = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.topBottomCorrectionRotationSupport, it.first, "topBottomCorrectionRotationSupport ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + topBottomCorrectionRotationSupport = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._topBottomCorrectionRotationSupport, null, "_topBottomCorrectionRotationSupport ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationTest.kt index 83c82a4658b..ab56388f059 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/TopBottomCorrectionRotationTest.kt @@ -6,15 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.TopBottomCorrectionRotation -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -import io.ktor.utils.io.* -import kotlinx.coroutines.ExperimentalCoroutinesApi +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel import kotlinx.coroutines.test.runTest -import kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals -@OptIn(ExperimentalCoroutinesApi::class) class TopBottomCorrectionRotationTest { private val endpoint = "http://192.168.1.1:80/" @@ -59,7 +58,7 @@ class TopBottomCorrectionRotationTest { */ @Test fun setOptionTest() = runTest { - val value = Pair(ThetaRepository.TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f), TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f)) + val value = Pair(ThetaRepository.TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f), TopBottomCorrectionRotation(pitch = "1.0", roll = "1.0", yaw = "1.0")) MockApiClient.onRequest = { request -> // check request @@ -81,7 +80,7 @@ class TopBottomCorrectionRotationTest { @Test fun convertOptionTest() = runTest { val values = listOf( - Pair(ThetaRepository.TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f), TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f)), + Pair(ThetaRepository.TopBottomCorrectionRotation(pitch = 1.0f, roll = 1.0f, yaw = 1.0f), TopBottomCorrectionRotation(pitch = "1.0", roll = "1.0", yaw = "1.0")), ) values.forEach { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/UsbConnectionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/UsbConnectionTest.kt new file mode 100644 index 00000000000..432296a02a5 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/UsbConnectionTest.kt @@ -0,0 +1,126 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import com.ricoh360.thetaclient.transferred.UsbConnection +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class UsbConnectionTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.UsbConnection + ) + val stringOptionNames = listOf( + "_usbConnection" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_usb_connection_msc.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.usbConnection, ThetaRepository.UsbConnectionEnum.MSC) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.UsbConnection + ) + val stringOptionNames = listOf( + "_usbConnection" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_usb_connection_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.usbConnection, ThetaRepository.UsbConnectionEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair(ThetaRepository.UsbConnectionEnum.MTP, UsbConnection.MTP) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, usbConnection = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + usbConnection = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair(ThetaRepository.UsbConnectionEnum.UNKNOWN, UsbConnection.UNKNOWN), + Pair(ThetaRepository.UsbConnectionEnum.MTP, UsbConnection.MTP), + Pair(ThetaRepository.UsbConnectionEnum.MSC, UsbConnection.MSC), + ) + + values.forEach { + val orgOptions = Options( + _usbConnection = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.usbConnection, it.first, "usbConnection ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + usbConnection = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._usbConnection, it.second, "_usbConnection ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VideoStitchingTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VideoStitchingTest.kt index 385a1c6fcbc..e8cbda0de53 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VideoStitchingTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VideoStitchingTest.kt @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.VideoStitching -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 kotlin.test.AfterTest diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VisibilityReductionTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VisibilityReductionTest.kt index f81643cb8a1..7c9c1f04092 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VisibilityReductionTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/VisibilityReductionTest.kt @@ -6,8 +6,8 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.VisibilityReduction -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 kotlin.test.AfterTest diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceAutoStrengthTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceAutoStrengthTest.kt index 90ef1a26b06..bc62588ace3 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceAutoStrengthTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceAutoStrengthTest.kt @@ -6,13 +6,14 @@ 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.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class WhiteBalanceAutoStrengthTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceTest.kt index ef3c4f3cd9f..6c483a8563e 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WhiteBalanceTest.kt @@ -6,13 +6,14 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.WhiteBalance -import io.ktor.client.request.* -import io.ktor.http.* -import io.ktor.http.content.* -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals @OptIn(ExperimentalCoroutinesApi::class) class WhiteBalanceTest { diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanAntennaConfigTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanAntennaConfigTest.kt new file mode 100644 index 00000000000..34eb8dfb63c --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanAntennaConfigTest.kt @@ -0,0 +1,126 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import com.ricoh360.thetaclient.transferred.WlanAntennaConfig +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +class WlanAntennaConfigTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option. + */ + @Test + fun getOptionTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.WlanAntennaConfig + ) + val stringOptionNames = listOf( + "_wlanAntennaConfig" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_wlan_antenna_config_siso.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.wlanAntennaConfig, ThetaRepository.WlanAntennaConfigEnum.SISO) + } + + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.WlanAntennaConfig + ) + val stringOptionNames = listOf( + "_wlanAntennaConfig" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_wlan_antenna_config_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.wlanAntennaConfig, ThetaRepository.WlanAntennaConfigEnum.UNKNOWN) + } + + /** + * Set option. + */ + @Test + fun setOptionTest() = runTest { + val value = Pair(ThetaRepository.WlanAntennaConfigEnum.SISO, WlanAntennaConfig.SISO) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, wlanAntennaConfig = value.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + wlanAntennaConfig = value.first + ) + thetaRepository.setOptions(options) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionTest() = runTest { + val values = listOf( + Pair(ThetaRepository.WlanAntennaConfigEnum.UNKNOWN, WlanAntennaConfig.UNKNOWN), + Pair(ThetaRepository.WlanAntennaConfigEnum.SISO, WlanAntennaConfig.SISO), + Pair(ThetaRepository.WlanAntennaConfigEnum.MIMO, WlanAntennaConfig.MIMO), + ) + + values.forEach { + val orgOptions = Options( + _wlanAntennaConfig = it.second + ) + val options = ThetaRepository.Options(orgOptions) + assertEquals(options.wlanAntennaConfig, it.first, "wlanAntennaConfig ${it.second}") + } + + values.forEach { + val orgOptions = ThetaRepository.Options( + wlanAntennaConfig = it.first + ) + val options = orgOptions.toOptions() + assertEquals(options._wlanAntennaConfig, it.second, "_wlanAntennaConfig ${it.second}") + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyClModeTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyClModeTest.kt new file mode 100644 index 00000000000..a69cd47c970 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencyClModeTest.kt @@ -0,0 +1,92 @@ +package com.ricoh360.thetaclient.repository.options + +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.WlanFrequencyClMode +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertFalse +import kotlin.test.assertNotNull +import kotlin.test.assertTrue + +class WlanFrequencyClModeTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option wlanFrequencyClMode. + */ + @Test + fun getOptionWlanFrequencyClModeTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.WlanFrequencyClMode + ) + val stringOptionNames = listOf( + "_wlanFrequencyCLmode" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_wlan_frequency_cl_mode.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + val value = options.wlanFrequencyClMode + assertNotNull(value, "wlanFrequencyClmode") + assertTrue(value.enable2_4, "wlanFrequencyClmode enable2_4") + assertFalse(value.enable5_2, "wlanFrequencyClmode enable5_2") + assertFalse(value.enable5_8, "wlanFrequencyClmode enable5_8") + } + + /** + * Set option wlanFrequencyClMode. + */ + @Test + fun setOptionWlanFrequencyClModeTest() = runTest { + val value1 = + Pair( + ThetaRepository.WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = true, + ), + WlanFrequencyClMode( + enable2_4 = true, + enable5_2 = false, + enable5_8 = true, + ) + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkSetOptions(request, wlanFrequencyClMode = value1.second) + + ByteReadChannel(Resource("src/commonTest/resources/setOptions/set_options_done.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = ThetaRepository.Options( + wlanFrequencyClMode = value1.first + ) + thetaRepository.setOptions(options) + assertTrue(true, "wlanFrequencyClMode") + } +} diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencySupportTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencySupportTest.kt new file mode 100644 index 00000000000..7cc86dc52de --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/repository/options/WlanFrequencySupportTest.kt @@ -0,0 +1,99 @@ +package com.ricoh360.thetaclient.repository.options + +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.Options +import com.ricoh360.thetaclient.transferred.WlanFrequency +import io.ktor.http.HttpStatusCode +import io.ktor.utils.io.ByteReadChannel +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals + +@OptIn(ExperimentalCoroutinesApi::class) +class WlanFrequencySupportTest { + private val endpoint = "http://192.168.1.1:80/" + + @BeforeTest + fun setup() { + MockApiClient.status = HttpStatusCode.OK + } + + @AfterTest + fun teardown() { + MockApiClient.status = HttpStatusCode.OK + } + + /** + * Get option wlanFrequencySupport. + */ + @Test + fun getOptionWlanFrequencySupportTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.WlanFrequencySupport + ) + val stringOptionNames = listOf( + "_wlanFrequencySupport" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_wlan_frequency_support_with_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals( + options.wlanFrequencySupport, + listOf( + ThetaRepository.WlanFrequencyEnum.GHZ_2_4, + ThetaRepository.WlanFrequencyEnum.GHZ_5_2, + ThetaRepository.WlanFrequencyEnum.GHZ_5_8, + ThetaRepository.WlanFrequencyEnum.UNKNOWN, + ThetaRepository.WlanFrequencyEnum.UNKNOWN + ), + "wlanFrequencySupport" + ) + } + + /** + * Convert ThetaRepository.Options to Options. + */ + @Test + fun convertOptionWlanFrequencySupportTest() = runTest { + val values = Pair( + listOf(WlanFrequency.GHZ_2_4, WlanFrequency.GHZ_5), + listOf( + ThetaRepository.WlanFrequencyEnum.GHZ_2_4, + ThetaRepository.WlanFrequencyEnum.GHZ_5 + ) + ) + + val orgOptions = Options( + _wlanFrequencySupport = values.first + ) + val optionsTR = ThetaRepository.Options(orgOptions) + assertEquals( + optionsTR.wlanFrequencySupport, + values.second, + "wlanFrequencySupport ${values.second}" + ) + + val orgOptionsTR = ThetaRepository.Options( + wlanFrequencySupport = values.second + ) + val options = orgOptionsTR.toOptions() + assertEquals( + options._wlanFrequencySupport, + values.first, + "_wlanFrequencySupport ${values.first}" + ) + } +} 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 2c22551bbe5..d2bf9060be4 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 @@ -6,11 +6,15 @@ import com.ricoh360.thetaclient.MockApiClient import com.ricoh360.thetaclient.ThetaRepository import com.ricoh360.thetaclient.transferred.Options import com.ricoh360.thetaclient.transferred.WlanFrequency -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 kotlin.test.* +import kotlin.test.AfterTest +import kotlin.test.BeforeTest +import kotlin.test.Test +import kotlin.test.assertEquals +import kotlin.test.assertTrue @OptIn(ExperimentalCoroutinesApi::class) class WlanFrequencyTest { @@ -50,6 +54,30 @@ class WlanFrequencyTest { assertEquals(options.wlanFrequency, ThetaRepository.WlanFrequencyEnum.GHZ_5, "wlanFrequency") } + /** + * Get option UNKNOWN. + */ + @Test + fun getOptionUnknownTest() = runTest { + val optionNames = listOf( + ThetaRepository.OptionNameEnum.WlanFrequency + ) + val stringOptionNames = listOf( + "_wlanFrequency" + ) + + MockApiClient.onRequest = { request -> + // check request + CheckRequest.checkGetOptions(request, stringOptionNames) + + ByteReadChannel(Resource("src/commonTest/resources/options/option_wlan_frequency_unknown.json").readText()) + } + + val thetaRepository = ThetaRepository(endpoint) + val options = thetaRepository.getOptions(optionNames) + assertEquals(options.wlanFrequency, ThetaRepository.WlanFrequencyEnum.UNKNOWN, "wlanFrequency") + } + /** * Set option wlanFrequency. */ @@ -90,10 +118,14 @@ class WlanFrequencyTest { @Test fun convertOptionWlanFrequencyTest() = runTest { val values = listOf( + Pair(ThetaRepository.WlanFrequencyEnum.UNKNOWN, WlanFrequency.UNKNOWN), Pair(ThetaRepository.WlanFrequencyEnum.GHZ_2_4, WlanFrequency.GHZ_2_4), Pair(ThetaRepository.WlanFrequencyEnum.GHZ_5, WlanFrequency.GHZ_5), + Pair(ThetaRepository.WlanFrequencyEnum.GHZ_5_2, WlanFrequency.GHZ_5_2), + Pair(ThetaRepository.WlanFrequencyEnum.GHZ_5_8, WlanFrequency.GHZ_5_8), ) + assertEquals(ThetaRepository.WlanFrequencyEnum.entries.size, values.size) values.forEach { val orgOptions = Options( _wlanFrequency = it.second diff --git a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/transferred/StateApiTest.kt b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/transferred/StateApiTest.kt index bb39e34f343..87619f026fc 100644 --- a/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/transferred/StateApiTest.kt +++ b/kotlin-multiplatform/src/commonTest/kotlin/com/ricoh360/thetaclient/transferred/StateApiTest.kt @@ -39,6 +39,7 @@ class StateApiTest { Pair(CaptureStatus.CONTINUOUS_SHOOTING, "continuous shooting"), Pair(CaptureStatus.RETROSPECTIVE_IMAGE_RECORDING, "retrospective image recording"), Pair(CaptureStatus.BURST_SHOOTING, "burst shooting"), + Pair(CaptureStatus.TIME_SHIFT_SHOOTING_IDLE, "timeShift shooting idle"), ) @Serializable diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done.json index 31387e24446..b46efc61dc9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done.json @@ -1 +1,9 @@ -{"results":{"fileUrls":["http://192.168.1.1/files/100RICOH/R0010026.JPG"]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_progress.json index 8818ce46914..be8fa604f5a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_done.json index 53f4b47b1c8..c5ac72eca7a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.stopCapture","state":"done"} \ No newline at end of file +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/BurstCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done.json index 31387e24446..b46efc61dc9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done.json @@ -1 +1,9 @@ -{"results":{"fileUrls":["http://192.168.1.1/files/100RICOH/R0010026.JPG"]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_progress.json index 8818ce46914..be8fa604f5a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_done.json index 53f4b47b1c8..c5ac72eca7a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.stopCapture","state":"done"} \ No newline at end of file +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/CompositeIntervalCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done.json index 31387e24446..b46efc61dc9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done.json @@ -1 +1,9 @@ -{"results":{"fileUrls":["http://192.168.1.1/files/100RICOH/R0010026.JPG"]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_progress.json index 8818ce46914..be8fa604f5a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/ContinuousCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/options.json b/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/options.json index c3af9540500..e94b972ff53 100644 --- a/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/options.json +++ b/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/options.json @@ -1 +1,9 @@ -{"options":{"fileFormat":{"height":5504,"type":"jpeg","width":11008}}} +{ + "options": { + "fileFormat": { + "height": 5504, + "type": "jpeg", + "width": 11008 + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/state.json b/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/state.json index 9dfe11522e4..b10227dfff3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/state.json +++ b/kotlin-multiplatform/src/commonTest/resources/EventWebSocket/state.json @@ -1 +1,6 @@ -{"state":{"_batteryTemp":50,"_boardTemp":43}} +{ + "state": { + "_batteryTemp": 50, + "_boardTemp": 43 + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_done.json index 6537b10f4fd..8e678173f32 100644 --- a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.startCapture","state":"done"} +{ + "name": "camera.startCapture", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_done.json index 8e20688daf9..150c8978285 100644 --- a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_done.json @@ -1 +1,9 @@ -{"name":"camera.stopCapture","results":{"fileUrls":["http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013317.MP4"]},"state":"done"} +{ + "name": "camera.stopCapture", + "results": { + "fileUrls": [ + "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013317.MP4" + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/LimitlessIntervalCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done.json index 6f8d1387ba8..4e09443c9cd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done.json @@ -1 +1,10 @@ -{"name":"camera.startCapture","results":{"fileUrls":["http://192.168.1.1/files/150100525831424d42075b6ace1cc300/100RICOH/R0010005.JPG","http://192.168.1.1/files/150100525831424d42075b6ace1cc300/100RICOH/R0010006.JPG"]},"state":"done"} +{ + "name": "camera.startCapture", + "results": { + "fileUrls": [ + "http://192.168.1.1/files/150100525831424d42075b6ace1cc300/100RICOH/R0010005.JPG", + "http://192.168.1.1/files/150100525831424d42075b6ace1cc300/100RICOH/R0010006.JPG" + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_stopped.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_stopped.json index f9e754a0c7f..459471b020a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_stopped.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_done_stopped.json @@ -1 +1,5 @@ -{"name":"camera.startCapture","results":{},"state":"done"} +{ + "name": "camera.startCapture", + "results": {}, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress.json index 40649c57b22..b86dd5a07be 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"3","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "3", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress_1.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress_1.json index a29e1c8bac4..f96b47468de 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress_1.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/start_capture_progress_1.json @@ -1 +1,8 @@ -{"id":"3","name":"camera.startCapture","progress":{"completion":0.50},"state":"inProgress"} +{ + "id": "3", + "name": "camera.startCapture", + "progress": { + "completion": 0.50 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_idle.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_idle.json index 02f3c30930f..60c94512b49 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_idle.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_idle.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0010","state":{"batteryLevel":0.5,"storageUri":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7","_apiVersion":2,"_batteryState":"disconnect","_cameraError":[],"_captureStatus":"idle","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010147.JPG","_recordableTime":0,"_recordedTime":0,"_function":"normal"}} +{ + "fingerprint": "FIG_0010", + "state": { + "batteryLevel": 0.5, + "storageUri": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7", + "_apiVersion": 2, + "_batteryState": "disconnect", + "_cameraError": [], + "_captureStatus": "idle", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010147.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "normal" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_shooting.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_shooting.json index b768e09fd8c..0cb075287f8 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_shooting.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/state_shooting.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0002","state":{"batteryLevel":0.5,"storageUri":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7","_apiVersion":2,"_batteryState":"disconnect","_cameraError":[],"_captureStatus":"bracket shooting","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010145.JPG","_recordableTime":0,"_recordedTime":0,"_function":"normal"}} +{ + "fingerprint": "FIG_0002", + "state": { + "batteryLevel": 0.5, + "storageUri": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7", + "_apiVersion": 2, + "_batteryState": "disconnect", + "_cameraError": [], + "_captureStatus": "bracket shooting", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010145.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "normal" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/stop_capture_done.json index 53f4b47b1c8..c5ac72eca7a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/MultiBracketCapture/stop_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.stopCapture","state":"done"} \ No newline at end of file +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_cancel.json index 38e0ff0271b..447eb6ad08b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.takePicture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.takePicture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_done.json b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_done.json index 2024bc2fd65..9aae61eccae 100644 --- a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_done.json @@ -1 +1,7 @@ -{"name":"camera.takePicture","results":{"fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013308.JPG"},"state":"done"} +{ + "name": "camera.takePicture", + "results": { + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013308.JPG" + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_error.json b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_error.json index f5385597297..ea979624ba4 100644 --- a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.takePicture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.takePicture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_progress.json b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_progress.json index 1b248d0a779..b9816ad6c34 100644 --- a/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/PhotoCapture/takepicture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.takePicture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.takePicture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done.json index 31387e24446..b46efc61dc9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done.json @@ -1 +1,9 @@ -{"results":{"fileUrls":["http://192.168.1.1/files/100RICOH/R0010026.JPG"]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_progress.json index 8818ce46914..be8fa604f5a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_done.json index 53f4b47b1c8..c5ac72eca7a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.stopCapture","state":"done"} \ No newline at end of file +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/ShotCountSpecifiedIntervalCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_cancel.json index 77e77d98bd4..7378dc48f20 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_cancel.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_cancel.json @@ -1,8 +1,8 @@ { - "error": { - "code": "canceledShooting", - "message": "shooting is canceled." - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done.json index 31387e24446..b46efc61dc9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done.json @@ -1 +1,9 @@ -{"results":{"fileUrls":["http://192.168.1.1/files/100RICOH/R0010026.JPG"]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_empty.json index d8c34e3908a..203942d2a5f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_empty.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_empty.json @@ -1 +1,7 @@ -{"results":{"fileUrls":[]},"name":"camera.startCapture","state":"done"} \ No newline at end of file +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b.json index 710bb954901..2558cfbbdf1 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b.json @@ -1 +1,7 @@ -{"name":"camera.takePicture","results":{"fileUrl":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010081.JPG"},"state":"done"} +{ + "name": "camera.takePicture", + "results": { + "fileUrl": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010081.JPG" + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b_alt.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b_alt.json index c01463bb9d7..fab1f65d2d3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b_alt.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_done_sc2b_alt.json @@ -1 +1,7 @@ -{"name":"camera.startCapture","results":{"fileUrl":"http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010094.JPG"},"state":"done"} +{ + "name": "camera.startCapture", + "results": { + "fileUrl": "http://192.168.1.1/files/thetasc22050e7735b9b5838795e2ee7/100RICOH/R0010094.JPG" + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress.json index 8818ce46914..be8fa604f5a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress.json @@ -1 +1,8 @@ -{"id":"2792","name":"camera.startCapture","progress":{"completion":0.00},"state":"inProgress"} +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b.json index e65352cac04..abfffa2d502 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b.json @@ -1 +1,8 @@ -{"name":"camera.takePicture","id":"6384","progress":{"completion":0.0},"state":"inProgress"} +{ + "name": "camera.takePicture", + "id": "6384", + "progress": { + "completion": 0.0 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b_alt.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b_alt.json index c341137dee1..eeb2cae7b04 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b_alt.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_progress_sc2b_alt.json @@ -1 +1,8 @@ -{"name":"camera.startCapture","id":"8661","progress":{"completion":0.6},"state":"inProgress"} +{ + "name": "camera.startCapture", + "id": "8661", + "progress": { + "completion": 0.6 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_sc2b.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_sc2b.json index 62c61c7792a..b2c8d7fa71f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_sc2b.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/start_capture_sc2b.json @@ -1 +1,8 @@ -{"name":"camera.startCapture","id":"6384","progress":{"completion":0.0},"state":"inProgress"} +{ + "name": "camera.startCapture", + "id": "6384", + "progress": { + "completion": 0.0 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_shooting.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_shooting.json index de6491203c9..b5ee24963c2 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_shooting.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/state_shooting.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"timeShift shooting","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "timeShift shooting", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_done.json index 53f4b47b1c8..c5ac72eca7a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_done.json @@ -1 +1,4 @@ -{"name":"camera.stopCapture","state":"done"} \ No newline at end of file +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_cancel.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_cancel.json new file mode 100644 index 00000000000..7378dc48f20 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_cancel.json @@ -0,0 +1,8 @@ +{ + "error": { + "code": "canceledShooting", + "message": "shooting is canceled." + }, + "name": "camera.startCapture", + "state": "error" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done.json new file mode 100644 index 00000000000..b46efc61dc9 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done.json @@ -0,0 +1,9 @@ +{ + "results": { + "fileUrls": [ + "http://192.168.1.1/files/100RICOH/R0010026.JPG" + ] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done_empty.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done_empty.json new file mode 100644 index 00000000000..203942d2a5f --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_done_empty.json @@ -0,0 +1,7 @@ +{ + "results": { + "fileUrls": [] + }, + "name": "camera.startCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_error.json new file mode 100644 index 00000000000..6de34d6a436 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_error.json @@ -0,0 +1,8 @@ +{ + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json new file mode 100644 index 00000000000..be8fa604f5a --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/start_capture_progress.json @@ -0,0 +1,8 @@ +{ + "id": "2792", + "name": "camera.startCapture", + "progress": { + "completion": 0.00 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_idle.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_idle.json new file mode 100644 index 00000000000..1380a45fc8c --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_idle.json @@ -0,0 +1,16 @@ +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "idle", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_shooting.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_shooting.json new file mode 100644 index 00000000000..37bf6bdf515 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_shooting.json @@ -0,0 +1,16 @@ +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "shooting", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_idle.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_idle.json new file mode 100644 index 00000000000..76ece8b75c4 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_idle.json @@ -0,0 +1,16 @@ +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "timeShift shooting idle", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_shooting.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_shooting.json new file mode 100644 index 00000000000..b5ee24963c2 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/state_timeshift_shooting.json @@ -0,0 +1,16 @@ +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "timeShift shooting", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_done.json new file mode 100644 index 00000000000..c5ac72eca7a --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_done.json @@ -0,0 +1,4 @@ +{ + "name": "camera.stopCapture", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_error.json new file mode 100644 index 00000000000..8342a13cdce --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/TimeShiftManualCapture/stop_capture_error.json @@ -0,0 +1,8 @@ +{ + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/start_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/start_capture_error.json index 177c5743308..6de34d6a436 100644 --- a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/start_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/start_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_self_timer.json index 1bdc92b1d76..7d9a275f5a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/state_self_timer.json @@ -1 +1,16 @@ -{"fingerprint":"FIG_0003","state":{"batteryLevel":0.8,"storageUri":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e","_apiVersion":2,"_batteryState":"charging","_cameraError":[],"_captureStatus":"self-timer countdown","_capturedPictures":0,"_latestFileUrl":"http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG","_recordableTime":0,"_recordedTime":0,"_function":"selfTimer"}} +{ + "fingerprint": "FIG_0003", + "state": { + "batteryLevel": 0.8, + "storageUri": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e", + "_apiVersion": 2, + "_batteryState": "charging", + "_cameraError": [], + "_captureStatus": "self-timer countdown", + "_capturedPictures": 0, + "_latestFileUrl": "http://192.168.1.1/files/thetasc26c21a247daf35838792bad9e/100RICOH/R0012313.JPG", + "_recordableTime": 0, + "_recordedTime": 0, + "_function": "selfTimer" + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_done.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_done.json index 8e20688daf9..150c8978285 100644 --- a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_done.json @@ -1 +1,9 @@ -{"name":"camera.stopCapture","results":{"fileUrls":["http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013317.MP4"]},"state":"done"} +{ + "name": "camera.stopCapture", + "results": { + "fileUrls": [ + "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013317.MP4" + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_error.json b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_error.json index 0fa6f9358c0..8342a13cdce 100644 --- a/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/VideoCapture/stop_capture_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.stopCapture", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.stopCapture", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_done.json b/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_done.json index 963aad713f5..41aaa4537dd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_done.json @@ -1 +1,4 @@ -{"name":"camera._cancelVideoConvert","state":"done"} +{ + "name": "camera._cancelVideoConvert", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_error.json b/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_error.json index eb18ad46a85..723c3a64e9b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/cancelVideoConvert/cancel_video_convert_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._cancelVideoConvert", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._cancelVideoConvert", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_done.json b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_done.json index 955ee69eed8..c098b6bd955 100644 --- a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_done.json @@ -1 +1,7 @@ -{"name":"camera._convertVideoFormats","results":{"fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330_er_2K_H264_tbc.MP4"},"state":"done"} +{ + "name": "camera._convertVideoFormats", + "results": { + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330_er_2K_H264_tbc.MP4" + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_error.json b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_error.json index 324446f7a5f..20481eca6a3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._convertVideoFormats", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._convertVideoFormats", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_progress.json b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_progress.json index 028d123a617..66ec2e45b09 100644 --- a/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_progress.json +++ b/kotlin-multiplatform/src/commonTest/resources/convertVideoFormats/convert_video_formats_progress.json @@ -1 +1,8 @@ -{"id":"2811","name":"camera._convertVideoFormats","progress":{"completion":0.98},"state":"inProgress"} +{ + "id": "2811", + "name": "camera._convertVideoFormats", + "progress": { + "completion": 0.98 + }, + "state": "inProgress" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/delete/delete_done.json b/kotlin-multiplatform/src/commonTest/resources/delete/delete_done.json index 923ec2e130a..de6aeb54a70 100644 --- a/kotlin-multiplatform/src/commonTest/resources/delete/delete_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/delete/delete_done.json @@ -1 +1,4 @@ -{"name":"camera.delete","state":"done"} +{ + "name": "camera.delete", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/delete/delete_error.json b/kotlin-multiplatform/src/commonTest/resources/delete/delete_error.json index 036fcd0b864..f326e9708d7 100644 --- a/kotlin-multiplatform/src/commonTest/resources/delete/delete_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/delete/delete_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.delete", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.delete", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_done.json b/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_done.json index 66da2d20e0e..f400cbbb38a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_done.json @@ -1 +1,4 @@ -{"name":"camera._deleteAccessPoint","state":"done"} +{ + "name": "camera._deleteAccessPoint", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_error.json b/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_error.json index a748447ac33..4e99c63cf2f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/deleteAccessPoint/delete_access_point_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._deleteAccessPoint", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._deleteAccessPoint", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/deleteMySetting/delete_my_setting_done.json b/kotlin-multiplatform/src/commonTest/resources/deleteMySetting/delete_my_setting_done.json index f5b68253aba..38bca9fd8bd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/deleteMySetting/delete_my_setting_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/deleteMySetting/delete_my_setting_done.json @@ -1 +1,4 @@ -{"name":"camera._deleteMySetting","state":"done"} +{ + "name": "camera._deleteMySetting", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_done.json b/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_done.json index 45eb8bc28a9..0c8f0fd3780 100644 --- a/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_done.json @@ -1 +1,4 @@ -{"name":"camera._finishWlan","state":"done"} +{ + "name": "camera._finishWlan", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_error.json b/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_error.json index 86c8ef0d782..d1e9fdc8fe6 100644 --- a/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/finishWlan/finish_wlan_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._finishWlan", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._finishWlan", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/getMetadata/metadata_error.json b/kotlin-multiplatform/src/commonTest/resources/getMetadata/metadata_error.json index a715d3b0f86..cd0069f5ac3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getMetadata/metadata_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/getMetadata/metadata_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._getMetadata", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._getMetadata", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_s.json b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_s.json index 8ae2b34137d..8939b73f673 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_s.json +++ b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_s.json @@ -1 +1,14 @@ -{"name":"camera._getMySetting","state":"done","results":{"options":{"fileFormat":{"type":"jpeg","width":5376,"height":2688},"whiteBalance":"auto"}}} +{ + "name": "camera._getMySetting", + "state": "done", + "results": { + "options": { + "fileFormat": { + "type": "jpeg", + "width": 5376, + "height": 2688 + }, + "whiteBalance": "auto" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_v.json b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_v.json index 34e949defc6..6df10edd95f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_v.json +++ b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_v.json @@ -1 +1,23 @@ -{"name":"camera._getMySetting","results":{"options":{"aperture":2.0,"_colorTemperature":5000,"exposureCompensation":0.0,"exposureDelay":-1,"exposureProgram":2,"fileFormat":{"height":0,"type":"jpeg","width":0},"_filter":"off","iso":0,"isoAutoHighLimit":-1,"shutterSpeed":0,"whiteBalance":"auto"}},"state":"done"} +{ + "name": "camera._getMySetting", + "results": { + "options": { + "aperture": 2.0, + "_colorTemperature": 5000, + "exposureCompensation": 0.0, + "exposureDelay": -1, + "exposureProgram": 2, + "fileFormat": { + "height": 0, + "type": "jpeg", + "width": 0 + }, + "_filter": "off", + "iso": 0, + "isoAutoHighLimit": -1, + "shutterSpeed": 0, + "whiteBalance": "auto" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_video_v.json b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_video_v.json index 1f21b4a1fed..4e33f9fd6a5 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_video_v.json +++ b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_done_video_v.json @@ -1,2 +1,24 @@ -{"name":"camera._getMySetting","results":{"options":{"aperture":2.0,"_colorTemperature":5000,"exposureCompensation":0.0,"exposureDelay":-1,"exposureProgram":2,"fileFormat":{"height":0,"type":"mp4","width":0},"iso":0,"isoAutoHighLimit":-1,"_maxRecordableTime":-1,"shutterSpeed":0,"whiteBalance":"auto"}},"state":"done"} +{ + "name": "camera._getMySetting", + "results": { + "options": { + "aperture": 2.0, + "_colorTemperature": 5000, + "exposureCompensation": 0.0, + "exposureDelay": -1, + "exposureProgram": 2, + "fileFormat": { + "height": 0, + "type": "mp4", + "width": 0 + }, + "iso": 0, + "isoAutoHighLimit": -1, + "_maxRecordableTime": -1, + "shutterSpeed": 0, + "whiteBalance": "auto" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_error.json b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_error.json index b3d23998737..68f5d5b077d 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/getMySetting/get_my_setting_error.json @@ -1 +1,8 @@ -{"name":"camera._getMySetting","state":"error","error":{"code":"missingParameter","message":"Any required parameter is not specified."}} +{ + "name": "camera._getMySetting", + "state": "error", + "error": { + "code": "missingParameter", + "message": "Any required parameter is not specified." + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_image.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_image.json index 8ea753a9155..cb62dba370c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_image.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_image.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"captureMode":"image"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "captureMode": "image" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_video.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_video.json index 39f299f76ec..3916d2b8fa9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_video.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_capture_mode_video.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"captureMode":"video"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "captureMode": "video" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_done.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_done.json index a45ea328cd9..76b8cbf68ae 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_done.json @@ -1 +1,10 @@ -{"name":"camera.getOptions","results":{"options":{"remainingPictures":2259,"remainingVideoSeconds":2578}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "remainingPictures": 2259, + "remainingVideoSeconds": 2578 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_error.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_error.json index a9dcb6403db..59f315ac015 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.getOptions", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.getOptions", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_hdr.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_hdr.json index eb0b772000c..e84636d4b70 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_hdr.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_hdr.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_filter":"hdr"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_filter": "hdr" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_off.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_off.json index b1064f2f729..48924707026 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_off.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_filter_off.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_filter":"off"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_filter": "off" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_s_done.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_s_done.json index 71fcab3e46d..4980687d76b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_s_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_s_done.json @@ -1 +1,12 @@ -{"name":"camera.getOptions","state":"done","results":{"options":{"dateTimeZone":"2022:11:28 09:33:53+09:00","offDelay":65535,"sleepDelay":65535,"_shutterVolume":0}}} +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "dateTimeZone": "2022:11:28 09:33:53+09:00", + "offDelay": 65535, + "sleepDelay": 65535, + "_shutterVolume": 0 + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_z1_done.json b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_z1_done.json index 104b980b6f6..659154bde91 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_z1_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/getOptions/get_options_init_z1_done.json @@ -1 +1,13 @@ -{"name":"camera.getOptions","results":{"options":{"dateTimeZone":"2022:11:25 17:17:43+09:00","_language":"en-US","offDelay":3600,"_shutterVolume":33,"sleepDelay":65535}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "dateTimeZone": "2022:11:25 17:17:43+09:00", + "_language": "en-US", + "offDelay": 3600, + "_shutterVolume": 33, + "sleepDelay": 65535 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getPluginLicense/license.html b/kotlin-multiplatform/src/commonTest/resources/getPluginLicense/license.html index 70c439bf2b9..3db013e5f13 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getPluginLicense/license.html +++ b/kotlin-multiplatform/src/commonTest/resources/getPluginLicense/license.html @@ -1,574 +1,576 @@ - - - RICOH THETA - Automatic Face Blur BETA - + + + RICOH THETA - Automatic Face Blur BETA + -
- -

Android Support Library v4

-

Copyright © The Android Open Source Project. All rights reserved.

-

http://developer.android.com/tools/extras/support-library.html

- -
+
+ +

Android Support Library v4

+

Copyright © The Android Open Source Project. All rights reserved.

+

http://developer.android.com/tools/extras/support-library.html +

+ +

- Apache License -
- Version 2.0, January 2004 -
- http://www.apache.org/licenses/ + Apache License +
+ Version 2.0, January 2004 +
+ http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

- "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document.

- "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License.

- "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity.

- "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License.

- "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files.

- "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types.

- "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below).

- "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof.

- "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution."

- "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work.

-

2. Grant of Copyright License.

-

- Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. -

+

2. Grant of Copyright License.

+

+ Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. +

-

3. Grant of Patent License.

-

- Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. -

+

3. Grant of Patent License.

+

+ Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. +

-

4. Redistribution.

-

- You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: -

+

4. Redistribution.

+

+ You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: +

    -
  • - You must give any other recipients of the Work or - Derivative Works a copy of this License; and -
  • -
  • - You must cause any modified files to carry prominent notices - stating that You changed the files; and -
  • -
  • - You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of | - the Derivative Works; and -
  • -
  • - If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. -
  • +
  • + You must give any other recipients of the Work or + Derivative Works a copy of this License; and +
  • +
  • + You must cause any modified files to carry prominent notices + stating that You changed the files; and +
  • +
  • + You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of | + the Derivative Works; and +
  • +
  • + If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. +

- You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License.

-

5. Submission of Contributions.

-

- Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. -

+

5. Submission of Contributions.

+

+ Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. +

-

6. Trademarks.

-

- This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. -

+

6. Trademarks.

+

+ This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. +

-

7. Disclaimer of Warranty.

-

- Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. -

+

7. Disclaimer of Warranty.

+

+ Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. +

-

8. Limitation of Liability.

-

- In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. -

+

8. Limitation of Liability.

+

+ In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. +

-

9. Accepting Warranty or Additional Liability.

-

- While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. -

+

9. Accepting Warranty or Additional Liability.

+

+ While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. +

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

- To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

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

  http://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.
-
-
- -

Android Support Library core utils

-

Copyright © The Android Open Source Project. All rights reserved.

-

http://developer.android.com/tools/extras/support-library.html

- -
+
+
+ +

Android Support Library core utils

+

Copyright © The Android Open Source Project. All rights reserved.

+

http://developer.android.com/tools/extras/support-library.html +

+ +

- Apache License -
- Version 2.0, January 2004 -
- http://www.apache.org/licenses/ + Apache License +
+ Version 2.0, January 2004 +
+ http://www.apache.org/licenses/

TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

1. Definitions.

- "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document.

- "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License.

- "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity.

- "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License.

- "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files.

- "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types.

- "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below).

- "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof.

- "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution."

- "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work.

-

2. Grant of Copyright License.

-

- Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. -

+

2. Grant of Copyright License.

+

+ Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. +

-

3. Grant of Patent License.

-

- Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. -

+

3. Grant of Patent License.

+

+ Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. +

-

4. Redistribution.

-

- You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: -

+

4. Redistribution.

+

+ You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: +

    -
  • - You must give any other recipients of the Work or - Derivative Works a copy of this License; and -
  • -
  • - You must cause any modified files to carry prominent notices - stating that You changed the files; and -
  • -
  • - You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of | - the Derivative Works; and -
  • -
  • - If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. -
  • +
  • + You must give any other recipients of the Work or + Derivative Works a copy of this License; and +
  • +
  • + You must cause any modified files to carry prominent notices + stating that You changed the files; and +
  • +
  • + You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of | + the Derivative Works; and +
  • +
  • + If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. +

- You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License.

-

5. Submission of Contributions.

-

- Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. -

+

5. Submission of Contributions.

+

+ Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. +

-

6. Trademarks.

-

- This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. -

+

6. Trademarks.

+

+ This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. +

-

7. Disclaimer of Warranty.

-

- Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. -

+

7. Disclaimer of Warranty.

+

+ Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. +

-

8. Limitation of Liability.

-

- In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. -

+

8. Limitation of Liability.

+

+ In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. +

-

9. Accepting Warranty or Additional Liability.

-

- While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. -

+

9. Accepting Warranty or Additional Liability.

+

+ While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. +

END OF TERMS AND CONDITIONS

APPENDIX: How to apply the Apache License to your work.

- To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives.

Copyright [yyyy] [name of copyright owner]

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

  http://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.
-
+
diff --git a/kotlin-multiplatform/src/commonTest/resources/getPluginOrders/get_plugin_orders_done_z1.json b/kotlin-multiplatform/src/commonTest/resources/getPluginOrders/get_plugin_orders_done_z1.json index 86ae7ca172b..dd6d45678de 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getPluginOrders/get_plugin_orders_done_z1.json +++ b/kotlin-multiplatform/src/commonTest/resources/getPluginOrders/get_plugin_orders_done_z1.json @@ -1,6 +1,11 @@ -{"name":"camera._getPluginOrders","results":{ - "pluginOrders":[ - "com.theta360.usbstorage", - "com.theta.remoteplayback", - "" - ]},"state":"done"} +{ + "name": "camera._getPluginOrders", + "results": { + "pluginOrders": [ + "com.theta360.usbstorage", + "com.theta.remoteplayback", + "" + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/getThetaLicense/license.html b/kotlin-multiplatform/src/commonTest/resources/getThetaLicense/license.html index 1895d203450..60e3e88b234 100644 --- a/kotlin-multiplatform/src/commonTest/resources/getThetaLicense/license.html +++ b/kotlin-multiplatform/src/commonTest/resources/getThetaLicense/license.html @@ -1,4 +1,4 @@ - - + + diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_other.json b/kotlin-multiplatform/src/commonTest/resources/info/info_other.json index 97674bd5a92..2a502ae7928 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_other.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_other.json @@ -5,21 +5,21 @@ "firmwareVersion": "01.90", "supportUrl": "https://theta360.com/en/support/", "apiLevel": [ - 1, - 2 + 1, + 2 ], "endpoints": { - "httpPort": 80, - "httpUpdatesPort": 80 + "httpPort": 80, + "httpUpdatesPort": 80 }, "gps": false, "gyro": false, "uptime": 942, "api": [ - "/osc/info", - "/osc/state", - "/osc/checkForUpdates", - "/osc/commands/execute", - "/osc/commands/status" + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" ] } diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_s.json b/kotlin-multiplatform/src/commonTest/resources/info/info_s.json index fe039e56db3..9d3f9a4da83 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_s.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_s.json @@ -5,21 +5,21 @@ "firmwareVersion": "01.90", "supportUrl": "https://theta360.com/en/support/", "apiLevel": [ - 1, - 2 + 1, + 2 ], "endpoints": { - "httpPort": 80, - "httpUpdatesPort": 80 + "httpPort": 80, + "httpUpdatesPort": 80 }, "gps": false, "gyro": false, "uptime": 942, "api": [ - "/osc/info", - "/osc/state", - "/osc/checkForUpdates", - "/osc/commands/execute", - "/osc/commands/status" + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" ] } diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_s_old.json b/kotlin-multiplatform/src/commonTest/resources/info/info_s_old.json index a0800358654..51dd833c6c4 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_s_old.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_s_old.json @@ -5,21 +5,21 @@ "firmwareVersion": "01.60", "supportUrl": "https://theta360.com/en/support/", "apiLevel": [ - 1, - 2 + 1, + 2 ], "endpoints": { - "httpPort": 80, - "httpUpdatesPort": 80 + "httpPort": 80, + "httpUpdatesPort": 80 }, "gps": false, "gyro": false, "uptime": 942, "api": [ - "/osc/info", - "/osc/state", - "/osc/checkForUpdates", - "/osc/commands/execute", - "/osc/commands/status" + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" ] } diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_sc.json b/kotlin-multiplatform/src/commonTest/resources/info/info_sc.json index 2d9fd059a35..c1377e90f7c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_sc.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_sc.json @@ -1 +1,25 @@ -{"manufacturer":"RICOH","model":"RICOH THETA SC","serialNumber":"30129704","firmwareVersion":"01.20","supportUrl":"https://theta360.com/en/support/","apiLevel":[1,2],"endpoints":{"httpPort":80,"httpUpdatesPort":80},"gps":false,"gyro":false,"uptime":37,"api":["/osc/info","/osc/state","/osc/checkForUpdates","/osc/commands/execute","/osc/commands/status"]} +{ + "manufacturer": "RICOH", + "model": "RICOH THETA SC", + "serialNumber": "30129704", + "firmwareVersion": "01.20", + "supportUrl": "https://theta360.com/en/support/", + "apiLevel": [ + 1, + 2 + ], + "endpoints": { + "httpPort": 80, + "httpUpdatesPort": 80 + }, + "gps": false, + "gyro": false, + "uptime": 37, + "api": [ + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" + ] +} diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_sc2.json b/kotlin-multiplatform/src/commonTest/resources/info/info_sc2.json index 47318d97a79..431a5eb0e1e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_sc2.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_sc2.json @@ -1 +1,26 @@ -{"manufacturer":"RICOH","model":"RICOH THETA SC2","serialNumber":"00118200","firmwareVersion":"01.82","supportUrl":"https://theta360.com/en/support/","gps":false,"gyro":true,"endpoints":{"httpPort":80,"httpUpdatesPort":80},"apiLevel":[2],"api":["/osc/info","/osc/state","/osc/checkForUpdates","/osc/commands/execute","/osc/commands/status"],"uptime":62,"_wlanMacAddress":"58:38:79:5e:2e:e7","_bluetoothMacAddress":"20:50:e7:73:5b:9b"} +{ + "manufacturer": "RICOH", + "model": "RICOH THETA SC2", + "serialNumber": "00118200", + "firmwareVersion": "01.82", + "supportUrl": "https://theta360.com/en/support/", + "gps": false, + "gyro": true, + "endpoints": { + "httpPort": 80, + "httpUpdatesPort": 80 + }, + "apiLevel": [ + 2 + ], + "api": [ + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" + ], + "uptime": 62, + "_wlanMacAddress": "58:38:79:5e:2e:e7", + "_bluetoothMacAddress": "20:50:e7:73:5b:9b" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_sc2_business.json b/kotlin-multiplatform/src/commonTest/resources/info/info_sc2_business.json index d68b2ab43a6..0043d0465f0 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_sc2_business.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_sc2_business.json @@ -1 +1,26 @@ -{"manufacturer":"RICOH","model":"RICOH THETA SC2","serialNumber":"40118200","firmwareVersion":"06.52","supportUrl":"https://theta360.com/en/support/","gps":false,"gyro":true,"endpoints":{"httpPort":80,"httpUpdatesPort":80},"apiLevel":[2],"api":["/osc/info","/osc/state","/osc/checkForUpdates","/osc/commands/execute","/osc/commands/status"],"uptime":119,"_wlanMacAddress":"58:38:79:5e:2e:e7","_bluetoothMacAddress":"20:50:e7:73:5b:9b"} +{ + "manufacturer": "RICOH", + "model": "RICOH THETA SC2", + "serialNumber": "40118200", + "firmwareVersion": "06.52", + "supportUrl": "https://theta360.com/en/support/", + "gps": false, + "gyro": true, + "endpoints": { + "httpPort": 80, + "httpUpdatesPort": 80 + }, + "apiLevel": [ + 2 + ], + "api": [ + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" + ], + "uptime": 119, + "_wlanMacAddress": "58:38:79:5e:2e:e7", + "_bluetoothMacAddress": "20:50:e7:73:5b:9b" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_x.json b/kotlin-multiplatform/src/commonTest/resources/info/info_x.json index 7a982a48765..d39eaf19433 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_x.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_x.json @@ -1,26 +1,26 @@ { - "api": [ - "/osc/info", - "/osc/state", - "/osc/checkForUpdates", - "/osc/commands/execute", - "/osc/commands/status" - ], - "apiLevel": [ - 2 - ], - "_bluetoothMacAddress": "58:38:79:03:0C:EB", - "endpoints": { - "httpPort": 80, - "httpUpdatesPort": 80 - }, - "firmwareVersion": "1.30.0", - "gps": true, - "gyro": true, - "manufacturer": "RICOH", - "model": "RICOH THETA X", - "serialNumber": "10010065", - "supportUrl": "https://theta360.com/en/support/", - "uptime": 142, - "_wlanMacAddress": "58:38:79:03:17:AA" + "api": [ + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" + ], + "apiLevel": [ + 2 + ], + "_bluetoothMacAddress": "58:38:79:03:0C:EB", + "endpoints": { + "httpPort": 80, + "httpUpdatesPort": 80 + }, + "firmwareVersion": "1.30.0", + "gps": true, + "gyro": true, + "manufacturer": "RICOH", + "model": "RICOH THETA X", + "serialNumber": "10010065", + "supportUrl": "https://theta360.com/en/support/", + "uptime": 142, + "_wlanMacAddress": "58:38:79:03:17:AA" } diff --git a/kotlin-multiplatform/src/commonTest/resources/info/info_z1.json b/kotlin-multiplatform/src/commonTest/resources/info/info_z1.json index 8158ca31170..f3dd88afc14 100644 --- a/kotlin-multiplatform/src/commonTest/resources/info/info_z1.json +++ b/kotlin-multiplatform/src/commonTest/resources/info/info_z1.json @@ -1,26 +1,26 @@ { - "api": [ - "/osc/info", - "/osc/state", - "/osc/checkForUpdates", - "/osc/commands/execute", - "/osc/commands/status" - ], - "apiLevel": [ - 2 - ], - "_bluetoothMacAddress": "58:38:79:03:0C:EB", - "endpoints": { - "httpPort": 80, - "httpUpdatesPort": 80 - }, - "firmwareVersion": "2.10.3", - "gps": false, - "gyro": true, - "manufacturer": "RICOH", - "model": "RICOH THETA Z1", - "serialNumber": "10010065", - "supportUrl": "https://theta360.com/en/support/", - "uptime": 142, - "_wlanMacAddress": "58:38:79:03:17:AA" + "api": [ + "/osc/info", + "/osc/state", + "/osc/checkForUpdates", + "/osc/commands/execute", + "/osc/commands/status" + ], + "apiLevel": [ + 2 + ], + "_bluetoothMacAddress": "58:38:79:03:0C:EB", + "endpoints": { + "httpPort": 80, + "httpUpdatesPort": 80 + }, + "firmwareVersion": "2.10.3", + "gps": false, + "gyro": true, + "manufacturer": "RICOH", + "model": "RICOH THETA Z1", + "serialNumber": "10010065", + "supportUrl": "https://theta360.com/en/support/", + "uptime": 142, + "_wlanMacAddress": "58:38:79:03:17:AA" } diff --git a/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_error.json b/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_error.json index efc9c216a34..679d7ad3f62 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._listAccessPoints", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._listAccessPoints", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_unknown.json b/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_unknown.json new file mode 100644 index 00000000000..9defff7c95a --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/listAccessPoints/list_access_points_unknown.json @@ -0,0 +1,15 @@ +{ + "name": "camera._listAccessPoints", + "results": { + "accessPoints": [ + { + "connectionPriority": 3, + "ipAddressAllocation": "dynamic", + "security": "abcd", + "ssid": "abcd", + "ssidStealth": true + } + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_all_50.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_all_50.json index 20e8cb5595c..26f3b56ffc1 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_all_50.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_all_50.json @@ -1 +1,685 @@ -{"name":"camera.listFiles","results":{"entries":[{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:15 14:00:15+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.24,"name":"R0013336.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":6460172,"_thumbSize":3899,"width":3840},{"dateTimeZone":"2022:11:14 17:24:38+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013335.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013335.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5400048,"_thumbSize":1463,"width":6720},{"dateTimeZone":"2022:11:14 11:57:13+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013334.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013334.JPG","previewUrl":"","_projectionType":"Equirectangular","size":7175239,"_thumbSize":1972,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:50:32+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330.MP4","height":960,"isProcessed":true,"name":"R0013330.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3947398,"_thumbSize":6379,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:48:01+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013328.MP4","height":960,"isProcessed":true,"name":"R0013328.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3645107,"_thumbSize":7646,"width":1920},{"dateTimeZone":"2022:11:09 10:47:53+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013327.JPG","height":3360,"isProcessed":true,"name":"R0013327.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5523808,"_thumbSize":1530,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:30:11+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013326.MP4","height":960,"isProcessed":true,"name":"R0013326.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3658192,"_thumbSize":8125,"width":1920},{"dateTimeZone":"2022:11:09 10:30:03+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013325.JPG","height":3360,"isProcessed":true,"name":"R0013325.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5317692,"_thumbSize":1528,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:28:23+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013324.MP4","height":960,"isProcessed":true,"name":"R0013324.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":5025143,"_thumbSize":8685,"width":1920},{"dateTimeZone":"2022:11:09 09:20:08+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013323.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013323.JPG","previewUrl":"","_projectionType":"Equirectangular","size":2213649,"_thumbSize":2805,"width":6720},{"dateTimeZone":"2022:11:09 09:19:44+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013322.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013322.JPG","previewUrl":"","_projectionType":"Equirectangular","size":2305770,"_thumbSize":2855,"width":6720},{"dateTimeZone":"2022:11:09 09:16:53+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013321.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013321.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8645736,"_thumbSize":7614,"width":6720},{"dateTimeZone":"2022:11:09 09:16:18+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013320.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013320.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8641966,"_thumbSize":7568,"width":6720},{"dateTimeZone":"2022:11:09 09:14:32+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013319.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013319.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8618982,"_thumbSize":7572,"width":6720},{"dateTimeZone":"2022:11:09 09:14:18+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013318.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013318.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8615835,"_thumbSize":7611,"width":6720},{"dateTimeZone":"2022:11:07 16:47:01+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013308.JPG","height":3360,"isProcessed":true,"name":"R0013308.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6197241,"_thumbSize":1842,"width":6720},{"dateTimeZone":"2022:11:07 16:46:37+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013307.JPG","height":3360,"isProcessed":true,"name":"R0013307.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6254905,"_thumbSize":1849,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 13:14:58+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013306.MP4","height":960,"isProcessed":true,"name":"R0013306.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3641631,"_thumbSize":6847,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 13:14:35+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013305.MP4","height":960,"isProcessed":true,"name":"R0013305.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":61,"size":86884007,"_thumbSize":7490,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 11:55:50+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013304.MP4","height":960,"isProcessed":true,"name":"R0013304.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":2087874,"_thumbSize":7051,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 11:54:46+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013303.MP4","height":960,"isProcessed":true,"name":"R0013303.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":2088430,"_thumbSize":5256,"width":1920},{"dateTimeZone":"2022:11:01 11:45:16+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013302.JPG","height":3360,"isProcessed":true,"name":"R0013302.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5444706,"_thumbSize":1461,"width":6720},{"dateTimeZone":"2022:11:01 11:44:39+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013301.JPG","height":3360,"isProcessed":true,"name":"R0013301.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5469358,"_thumbSize":1457,"width":6720},{"dateTimeZone":"2022:11:01 11:41:55+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013300.JPG","height":3360,"isProcessed":true,"name":"R0013300.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5496749,"_thumbSize":1457,"width":6720},{"dateTimeZone":"2022:11:01 11:40:50+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013299.JPG","height":3360,"isProcessed":true,"name":"R0013299.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5431532,"_thumbSize":1461,"width":6720},{"dateTimeZone":"2022:11:01 11:40:13+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013298.JPG","height":3360,"isProcessed":true,"name":"R0013298.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5464867,"_thumbSize":1463,"width":6720},{"dateTimeZone":"2022:11:01 11:39:38+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013297.JPG","height":3360,"isProcessed":true,"name":"R0013297.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5459539,"_thumbSize":1459,"width":6720},{"dateTimeZone":"2022:11:01 11:38:22+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013296.JPG","height":3360,"isProcessed":true,"name":"R0013296.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5412360,"_thumbSize":1459,"width":6720},{"dateTimeZone":"2022:11:01 11:18:03+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013295.JPG","height":3360,"isProcessed":true,"name":"R0013295.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5273237,"_thumbSize":1465,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:31 15:23:09+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013294.MP4","height":960,"isProcessed":true,"name":"R0013294.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":5721565,"_thumbSize":8159,"width":1920},{"dateTimeZone":"2022:10:31 15:23:00+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013293.JPG","height":3360,"isProcessed":true,"name":"R0013293.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6253201,"_thumbSize":1819,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:31 14:46:54+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013292.MP4","height":960,"isProcessed":true,"name":"R0013292.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":5921253,"_thumbSize":7637,"width":1920},{"dateTimeZone":"2022:10:31 14:46:45+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013291.JPG","height":3360,"isProcessed":true,"name":"R0013291.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5551933,"_thumbSize":1463,"width":6720},{"dateTimeZone":"2022:10:28 10:45:56+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013282.JPG","height":3360,"isProcessed":true,"name":"R0013282.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6128499,"_thumbSize":1712,"width":6720},{"dateTimeZone":"2022:10:26 10:37:01+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013275.JPG","height":3360,"isProcessed":true,"name":"R0013275.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5618969,"_thumbSize":1489,"width":6720},{"dateTimeZone":"2022:10:26 10:06:23+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013274.JPG","height":3360,"isProcessed":true,"name":"R0013274.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5417170,"_thumbSize":1485,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:25 09:11:11+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013261.MP4","height":1920,"isProcessed":true,"name":"R0013261.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":9196418,"_thumbSize":8307,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:24 17:06:11+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013257.MP4","height":1920,"isProcessed":true,"name":"R0013257.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":13288606,"_thumbSize":6525,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:24 15:37:31+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013251.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.24,"name":"R0013251.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":4,"size":21278461,"_thumbSize":6865,"width":3840},{"dateTimeZone":"2022:10:24 15:37:07+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013250.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013250.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5372830,"_thumbSize":1466,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:24 15:09:48+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013249.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.24,"name":"R0013249.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":5278796,"_thumbSize":7683,"width":3840},{"dateTimeZone":"2022:10:24 15:00:54+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013246.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF26B0EADB","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013246.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6103794,"_thumbSize":1684,"width":6720},{"dateTimeZone":"2022:10:24 15:00:06+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013238.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF26B0EADB","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013238.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6123923,"_thumbSize":1685,"width":6720},{"dateTimeZone":"2022:10:24 14:59:36+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013233.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF26B0EADB","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013233.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6080702,"_thumbSize":1683,"width":6720},{"dateTimeZone":"2022:10:24 14:43:36+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013073.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF26B0EADB","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013073.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5911788,"_thumbSize":1647,"width":6720},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:21 10:38:19+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013071.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.2401,"name":"R0013071.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":15741934,"_thumbSize":8974,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:18 14:32:40+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013060.MP4","height":1920,"isProcessed":true,"name":"R0013060.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":311,"size":1371209179,"_thumbSize":27951,"width":3840},{"dateTimeZone":"2022:10:18 13:07:42+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013051.JPG","height":3360,"isProcessed":true,"name":"R0013051.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5816513,"_thumbSize":1622,"width":6720},{"dateTimeZone":"2022:10:17 13:06:23+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013050.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013050.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6231342,"_thumbSize":1652,"width":6720},{"dateTimeZone":"2022:10:17 13:06:11+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013049.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013049.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6784053,"_thumbSize":2245,"width":6720}],"totalEntries":54},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [ + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:15 14:00:15+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.24, + "name": "R0013336.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 6460172, + "_thumbSize": 3899, + "width": 3840 + }, + { + "dateTimeZone": "2022:11:14 17:24:38+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013335.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013335.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5400048, + "_thumbSize": 1463, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:14 11:57:13+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013334.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013334.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 7175239, + "_thumbSize": 1972, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:50:32+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013330.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3947398, + "_thumbSize": 6379, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:48:01+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013328.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013328.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3645107, + "_thumbSize": 7646, + "width": 1920 + }, + { + "dateTimeZone": "2022:11:09 10:47:53+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013327.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013327.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5523808, + "_thumbSize": 1530, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:30:11+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013326.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013326.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3658192, + "_thumbSize": 8125, + "width": 1920 + }, + { + "dateTimeZone": "2022:11:09 10:30:03+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013325.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013325.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5317692, + "_thumbSize": 1528, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:28:23+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013324.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013324.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 5025143, + "_thumbSize": 8685, + "width": 1920 + }, + { + "dateTimeZone": "2022:11:09 09:20:08+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013323.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013323.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 2213649, + "_thumbSize": 2805, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:19:44+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013322.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013322.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 2305770, + "_thumbSize": 2855, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:16:53+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013321.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013321.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8645736, + "_thumbSize": 7614, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:16:18+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013320.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013320.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8641966, + "_thumbSize": 7568, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:14:32+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013319.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013319.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8618982, + "_thumbSize": 7572, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:14:18+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013318.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013318.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8615835, + "_thumbSize": 7611, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:07 16:47:01+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013308.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013308.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6197241, + "_thumbSize": 1842, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:07 16:46:37+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013307.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013307.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6254905, + "_thumbSize": 1849, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 13:14:58+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013306.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013306.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3641631, + "_thumbSize": 6847, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 13:14:35+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013305.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013305.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 61, + "size": 86884007, + "_thumbSize": 7490, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 11:55:50+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013304.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013304.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 2087874, + "_thumbSize": 7051, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 11:54:46+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013303.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013303.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 2088430, + "_thumbSize": 5256, + "width": 1920 + }, + { + "dateTimeZone": "2022:11:01 11:45:16+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013302.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013302.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5444706, + "_thumbSize": 1461, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:44:39+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013301.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013301.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5469358, + "_thumbSize": 1457, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:41:55+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013300.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013300.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5496749, + "_thumbSize": 1457, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:40:50+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013299.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013299.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5431532, + "_thumbSize": 1461, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:40:13+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013298.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013298.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5464867, + "_thumbSize": 1463, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:39:38+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013297.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013297.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5459539, + "_thumbSize": 1459, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:38:22+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013296.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013296.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5412360, + "_thumbSize": 1459, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:01 11:18:03+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013295.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013295.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5273237, + "_thumbSize": 1465, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:31 15:23:09+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013294.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013294.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 5721565, + "_thumbSize": 8159, + "width": 1920 + }, + { + "dateTimeZone": "2022:10:31 15:23:00+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013293.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013293.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6253201, + "_thumbSize": 1819, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:31 14:46:54+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013292.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013292.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 5921253, + "_thumbSize": 7637, + "width": 1920 + }, + { + "dateTimeZone": "2022:10:31 14:46:45+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013291.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013291.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5551933, + "_thumbSize": 1463, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:28 10:45:56+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013282.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013282.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6128499, + "_thumbSize": 1712, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:26 10:37:01+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013275.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013275.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5618969, + "_thumbSize": 1489, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:26 10:06:23+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013274.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013274.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5417170, + "_thumbSize": 1485, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:25 09:11:11+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013261.MP4", + "height": 1920, + "isProcessed": true, + "name": "R0013261.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 9196418, + "_thumbSize": 8307, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:24 17:06:11+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013257.MP4", + "height": 1920, + "isProcessed": true, + "name": "R0013257.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 13288606, + "_thumbSize": 6525, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:24 15:37:31+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013251.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.24, + "name": "R0013251.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 4, + "size": 21278461, + "_thumbSize": 6865, + "width": 3840 + }, + { + "dateTimeZone": "2022:10:24 15:37:07+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013250.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013250.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5372830, + "_thumbSize": 1466, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:24 15:09:48+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013249.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.24, + "name": "R0013249.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 5278796, + "_thumbSize": 7683, + "width": 3840 + }, + { + "dateTimeZone": "2022:10:24 15:00:54+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013246.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF26B0EADB", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013246.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6103794, + "_thumbSize": 1684, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:24 15:00:06+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013238.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF26B0EADB", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013238.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6123923, + "_thumbSize": 1685, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:24 14:59:36+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013233.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF26B0EADB", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013233.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6080702, + "_thumbSize": 1683, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:24 14:43:36+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013073.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF26B0EADB", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013073.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5911788, + "_thumbSize": 1647, + "width": 6720 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:21 10:38:19+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013071.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.2401, + "name": "R0013071.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 15741934, + "_thumbSize": 8974, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:18 14:32:40+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013060.MP4", + "height": 1920, + "isProcessed": true, + "name": "R0013060.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 311, + "size": 1371209179, + "_thumbSize": 27951, + "width": 3840 + }, + { + "dateTimeZone": "2022:10:18 13:07:42+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013051.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013051.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5816513, + "_thumbSize": 1622, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:17 13:06:23+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013050.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013050.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6231342, + "_thumbSize": 1652, + "width": 6720 + }, + { + "dateTimeZone": "2022:10:17 13:06:11+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013049.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013049.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6784053, + "_thumbSize": 2245, + "width": 6720 + } + ], + "totalEntries": 54 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_error.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_error.json index 906c536e898..504d1e2e313 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.listFiles", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.listFiles", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_image_10.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_image_10.json index 1da3b686a4e..3be7728bc8e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_image_10.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_image_10.json @@ -1 +1,145 @@ -{"name":"camera.listFiles","results":{"entries":[{"dateTimeZone":"2022:11:14 17:24:38+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013335.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013335.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5400048,"_thumbSize":1463,"width":6720},{"dateTimeZone":"2022:11:14 11:57:13+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013334.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013334.JPG","previewUrl":"","_projectionType":"Equirectangular","size":7175239,"_thumbSize":1972,"width":6720},{"dateTimeZone":"2022:11:09 10:47:53+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013327.JPG","height":3360,"isProcessed":true,"name":"R0013327.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5523808,"_thumbSize":1530,"width":6720},{"dateTimeZone":"2022:11:09 10:30:03+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013325.JPG","height":3360,"isProcessed":true,"name":"R0013325.JPG","previewUrl":"","_projectionType":"Equirectangular","size":5317692,"_thumbSize":1528,"width":6720},{"dateTimeZone":"2022:11:09 09:20:08+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013323.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013323.JPG","previewUrl":"","_projectionType":"Equirectangular","size":2213649,"_thumbSize":2805,"width":6720},{"dateTimeZone":"2022:11:09 09:19:44+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013322.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013322.JPG","previewUrl":"","_projectionType":"Equirectangular","size":2305770,"_thumbSize":2855,"width":6720},{"dateTimeZone":"2022:11:09 09:16:53+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013321.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013321.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8645736,"_thumbSize":7614,"width":6720},{"dateTimeZone":"2022:11:09 09:16:18+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013320.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013320.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8641966,"_thumbSize":7568,"width":6720},{"dateTimeZone":"2022:11:09 09:14:32+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013319.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.234,"name":"R0013319.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8618982,"_thumbSize":7572,"width":6720},{"dateTimeZone":"2022:11:09 09:14:18+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013318.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013318.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8615835,"_thumbSize":7611,"width":6720}],"totalEntries":37},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [ + { + "dateTimeZone": "2022:11:14 17:24:38+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013335.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013335.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5400048, + "_thumbSize": 1463, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:14 11:57:13+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013334.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013334.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 7175239, + "_thumbSize": 1972, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 10:47:53+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013327.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013327.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5523808, + "_thumbSize": 1530, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 10:30:03+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013325.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013325.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 5317692, + "_thumbSize": 1528, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:20:08+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013323.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013323.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 2213649, + "_thumbSize": 2805, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:19:44+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013322.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013322.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 2305770, + "_thumbSize": 2855, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:16:53+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013321.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013321.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8645736, + "_thumbSize": 7614, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:16:18+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013320.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013320.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8641966, + "_thumbSize": 7568, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:14:32+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013319.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.234, + "name": "R0013319.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8618982, + "_thumbSize": 7572, + "width": 6720 + }, + { + "dateTimeZone": "2022:11:09 09:14:18+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013318.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013318.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8615835, + "_thumbSize": 7611, + "width": 6720 + } + ], + "totalEntries": 37 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_v_video_dual-fish.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_v_video_dual-fish.json index 8691a713f22..440da448bb6 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_v_video_dual-fish.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_v_video_dual-fish.json @@ -1 +1,41 @@ -{"name":"camera.listFiles","results":{"entries":[{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:22 16:04:17+09:00","fileUrl":"http://192.168.1.1/files/150100525831424d420709bd9bf27400/100RICOH/R0010014.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.2401,"name":"R0010014.MP4","previewUrl":"","_projectionType":"Dual-Fisheye","_recordTime":3,"size":24955909,"_thumbSize":36174,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:02:06 13:24:02+09:00","fileUrl":"http://192.168.1.1/files/150100525831424d420709bd9bf27400/100RICOH/R0010002.MP4","height":960,"isProcessed":true,"lat":35.4856,"lng":134.24,"name":"R0010002.MP4","previewUrl":"","_projectionType":"Dual-Fisheye","_recordTime":3,"size":9160386,"_thumbSize":35648,"width":1920}],"totalEntries":2},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [ + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:22 16:04:17+09:00", + "fileUrl": "http://192.168.1.1/files/150100525831424d420709bd9bf27400/100RICOH/R0010014.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.2401, + "name": "R0010014.MP4", + "previewUrl": "", + "_projectionType": "Dual-Fisheye", + "_recordTime": 3, + "size": 24955909, + "_thumbSize": 36174, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:02:06 13:24:02+09:00", + "fileUrl": "http://192.168.1.1/files/150100525831424d420709bd9bf27400/100RICOH/R0010002.MP4", + "height": 960, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.24, + "name": "R0010002.MP4", + "previewUrl": "", + "_projectionType": "Dual-Fisheye", + "_recordTime": 3, + "size": 9160386, + "_thumbSize": 35648, + "width": 1920 + } + ], + "totalEntries": 2 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_video_10.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_video_10.json index 8758362f818..1e2f270cbd7 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_video_10.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_video_10.json @@ -1 +1,151 @@ -{"name":"camera.listFiles","results":{"entries":[{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:15 14:00:15+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4","height":1920,"isProcessed":true,"lat":35.4856,"lng":134.24,"name":"R0013336.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":6460172,"_thumbSize":3899,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:50:32+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330.MP4","height":960,"isProcessed":true,"name":"R0013330.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3947398,"_thumbSize":6379,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:48:01+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013328.MP4","height":960,"isProcessed":true,"name":"R0013328.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3645107,"_thumbSize":7646,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:30:11+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013326.MP4","height":960,"isProcessed":true,"name":"R0013326.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3658192,"_thumbSize":8125,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:09 10:28:23+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013324.MP4","height":960,"isProcessed":true,"name":"R0013324.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":5025143,"_thumbSize":8685,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 13:14:58+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013306.MP4","height":960,"isProcessed":true,"name":"R0013306.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":2,"size":3641631,"_thumbSize":6847,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 13:14:35+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013305.MP4","height":960,"isProcessed":true,"name":"R0013305.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":61,"size":86884007,"_thumbSize":7490,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 11:55:50+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013304.MP4","height":960,"isProcessed":true,"name":"R0013304.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":2087874,"_thumbSize":7051,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:11:01 11:54:46+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013303.MP4","height":960,"isProcessed":true,"name":"R0013303.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":1,"size":2088430,"_thumbSize":5256,"width":1920},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2022:10:31 15:23:09+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013294.MP4","height":960,"isProcessed":true,"name":"R0013294.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":3,"size":5721565,"_thumbSize":8159,"width":1920}],"totalEntries":17},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [ + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:15 14:00:15+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013336.MP4", + "height": 1920, + "isProcessed": true, + "lat": 35.4856, + "lng": 134.24, + "name": "R0013336.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 6460172, + "_thumbSize": 3899, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:50:32+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013330.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013330.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3947398, + "_thumbSize": 6379, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:48:01+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013328.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013328.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3645107, + "_thumbSize": 7646, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:30:11+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013326.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013326.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3658192, + "_thumbSize": 8125, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:09 10:28:23+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013324.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013324.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 5025143, + "_thumbSize": 8685, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 13:14:58+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013306.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013306.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 2, + "size": 3641631, + "_thumbSize": 6847, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 13:14:35+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013305.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013305.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 61, + "size": 86884007, + "_thumbSize": 7490, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 11:55:50+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013304.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013304.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 2087874, + "_thumbSize": 7051, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:11:01 11:54:46+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013303.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013303.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 1, + "size": 2088430, + "_thumbSize": 5256, + "width": 1920 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2022:10:31 15:23:09+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013294.MP4", + "height": 960, + "isProcessed": true, + "name": "R0013294.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": 3, + "size": 5721565, + "_thumbSize": 8159, + "width": 1920 + } + ], + "totalEntries": 17 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_continuousShootingGroupId.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_continuousShootingGroupId.json index a0ebb3755b5..36a1f47158e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_continuousShootingGroupId.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_continuousShootingGroupId.json @@ -1 +1,28 @@ -{"results":{"entries":[{"_continuousShootingGroupId":"-2162368964566516622","dateTimeZone":"2023:06:23 15:50:12+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010115.JPG","height":2752,"_imageDescription":"","isProcessed":true,"lat":35.48560555555556,"lng":134.24010277777776,"name":"R0010115.JPG","previewUrl":"","_projectionType":"Equirectangular","size":3481981,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":1888,"_uploaded":false,"width":5504}],"totalEntries":37},"name":"camera.listFiles","state":"done"} \ No newline at end of file +{ + "results": { + "entries": [ + { + "_continuousShootingGroupId": "-2162368964566516622", + "dateTimeZone": "2023:06:23 15:50:12+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010115.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "lat": 35.48560555555556, + "lng": 134.24010277777776, + "name": "R0010115.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 3481981, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 1888, + "_uploaded": false, + "width": 5504 + } + ], + "totalEntries": 37 + }, + "name": "camera.listFiles", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_image_5.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_image_5.json index 27bfa23bbc5..6d1ebd6beb9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_image_5.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_image_5.json @@ -1 +1,89 @@ -{"results":{"entries":[{"dateTimeZone":"2023:05:17 13:22:54+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010010.JPG","height":2752,"_imageDescription":"","isProcessed":true,"name":"R0010010.JPG","previewUrl":"","_projectionType":"Equirectangular","size":3985065,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":2247,"_uploaded":false,"width":5504},{"dateTimeZone":"2023:05:17 13:22:48+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010009.JPG","height":2752,"_imageDescription":"","isProcessed":true,"name":"R0010009.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4039235,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":2370,"_uploaded":false,"width":5504},{"dateTimeZone":"2023:05:17 13:22:41+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010008.JPG","height":2752,"_imageDescription":"","isProcessed":true,"name":"R0010008.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4030941,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":2390,"_uploaded":false,"width":5504},{"dateTimeZone":"2023:05:17 13:22:35+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010007.JPG","height":2752,"_imageDescription":"","isProcessed":true,"name":"R0010007.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4050252,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":2366,"_uploaded":false,"width":5504},{"dateTimeZone":"2023:05:17 13:22:28+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010006.JPG","height":2752,"_imageDescription":"","isProcessed":true,"name":"R0010006.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4144748,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":2438,"_uploaded":false,"width":5504}],"totalEntries":9},"name":"camera.listFiles","state":"done"} \ No newline at end of file +{ + "results": { + "entries": [ + { + "dateTimeZone": "2023:05:17 13:22:54+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010010.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010010.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 3985065, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 2247, + "_uploaded": false, + "width": 5504 + }, + { + "dateTimeZone": "2023:05:17 13:22:48+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010009.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010009.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4039235, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 2370, + "_uploaded": false, + "width": 5504 + }, + { + "dateTimeZone": "2023:05:17 13:22:41+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010008.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010008.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4030941, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 2390, + "_uploaded": false, + "width": 5504 + }, + { + "dateTimeZone": "2023:05:17 13:22:35+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010007.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010007.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4050252, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 2366, + "_uploaded": false, + "width": 5504 + }, + { + "dateTimeZone": "2023:05:17 13:22:28+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010006.JPG", + "height": 2752, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010006.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4144748, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 2438, + "_uploaded": false, + "width": 5504 + } + ], + "totalEntries": 9 + }, + "name": "camera.listFiles", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_video_5.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_video_5.json index a0b243a314f..720695554e8 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_video_5.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_x_video_5.json @@ -1 +1,104 @@ -{"results":{"entries":[{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:14 09:35:02+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010087.MP4","_frameRate":30,"height":1920,"_imageDescription":"","isProcessed":true,"name":"R0010087.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":"2","size":12056251,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":27985,"_uploaded":false,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:14 09:34:41+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010084.MP4","_frameRate":30,"height":1920,"_imageDescription":"","isProcessed":true,"name":"R0010084.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":"2","size":12629609,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":27465,"_uploaded":false,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:14 09:32:54+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010080.MP4","_frameRate":30,"height":1920,"_imageDescription":"","isProcessed":true,"name":"R0010080.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":"2","size":11830421,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":28885,"_uploaded":false,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:14 09:32:33+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010077.MP4","_frameRate":30,"height":1920,"_imageDescription":"","isProcessed":true,"name":"R0010077.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":"2","size":12472637,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":28855,"_uploaded":false,"width":3840},{"_codec":"H.264/MPEG-4 AVC","dateTimeZone":"2023:06:14 09:31:57+09:00","_favorite":false,"fileUrl":"http://192.168.1.1/files/100RICOH/R0010073.MP4","_frameRate":30,"height":1920,"_imageDescription":"","isProcessed":true,"name":"R0010073.MP4","previewUrl":"","_projectionType":"Equirectangular","_recordTime":"2","size":14690634,"_storageID":"412176649172527ab3d5edabb50a7d69","_thumbSize":28740,"_uploaded":false,"width":3840}],"totalEntries":19},"name":"camera.listFiles","state":"done"} \ No newline at end of file +{ + "results": { + "entries": [ + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:14 09:35:02+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010087.MP4", + "_frameRate": 30, + "height": 1920, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010087.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": "2", + "size": 12056251, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 27985, + "_uploaded": false, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:14 09:34:41+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010084.MP4", + "_frameRate": 30, + "height": 1920, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010084.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": "2", + "size": 12629609, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 27465, + "_uploaded": false, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:14 09:32:54+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010080.MP4", + "_frameRate": 30, + "height": 1920, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010080.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": "2", + "size": 11830421, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 28885, + "_uploaded": false, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:14 09:32:33+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010077.MP4", + "_frameRate": 30, + "height": 1920, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010077.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": "2", + "size": 12472637, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 28855, + "_uploaded": false, + "width": 3840 + }, + { + "_codec": "H.264/MPEG-4 AVC", + "dateTimeZone": "2023:06:14 09:31:57+09:00", + "_favorite": false, + "fileUrl": "http://192.168.1.1/files/100RICOH/R0010073.MP4", + "_frameRate": 30, + "height": 1920, + "_imageDescription": "", + "isProcessed": true, + "name": "R0010073.MP4", + "previewUrl": "", + "_projectionType": "Equirectangular", + "_recordTime": "2", + "size": 14690634, + "_storageID": "412176649172527ab3d5edabb50a7d69", + "_thumbSize": 28740, + "_uploaded": false, + "width": 3840 + } + ], + "totalEntries": 19 + }, + "name": "camera.listFiles", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_z1_group_id.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_z1_group_id.json index 642dd2035dd..de2cbc436c7 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_z1_group_id.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_z1_group_id.json @@ -1 +1,146 @@ -{"name":"camera.listFiles","results":{"entries":[{"_autoBracketGroupId":"B0FB64CF29AEFBE4","dateTimeZone":"2023:06:23 15:47:45+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013685.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013685.JPG","previewUrl":"","_projectionType":"Equirectangular","size":3255376,"_thumbSize":1450,"width":6720},{"_autoBracketGroupId":"B0FB64CF29AEFBE4","dateTimeZone":"2023:06:23 15:47:42+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013684.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013684.JPG","previewUrl":"","_projectionType":"Equirectangular","size":3550621,"_thumbSize":1453,"width":6720},{"dateTimeZone":"2023:06:23 15:46:36+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013683.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF29AEFB9C","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013683.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4337539,"_thumbSize":1467,"width":6720},{"dateTimeZone":"2023:06:23 15:46:31+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013682.JPG","height":3360,"_intervalCaptureGroupId":"B0FB64CF29AEFB9C","isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013682.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4446177,"_thumbSize":1459,"width":6720},{"_compositeShootingGroupId":"B0FB64CF29AEFB66","dateTimeZone":"2023:06:23 15:45:58+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013681.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013681.JPG","previewUrl":"","_projectionType":"Equirectangular","size":4505436,"_thumbSize":1471,"width":6720},{"dateTimeZone":"2023:05:11 15:26:16+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013567.JPG","height":3360,"isProcessed":true,"lat":35.4836,"lng":134.2341,"name":"R0013567.JPG","previewUrl":"","_projectionType":"Equirectangular","size":8281293,"_thumbSize":9735,"width":6720},{"dateTimeZone":"2023:04:10 17:19:12+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013556.JPG","height":3360,"isProcessed":true,"name":"R0013556.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6129799,"_thumbSize":5364,"width":6720},{"dateTimeZone":"2023:04:06 15:49:24+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013549.JPG","height":3360,"isProcessed":true,"name":"R0013549.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6835046,"_thumbSize":5618,"width":6720},{"dateTimeZone":"2023:04:06 14:53:38+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013546.JPG","height":3360,"isProcessed":true,"name":"R0013546.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6192100,"_thumbSize":5482,"width":6720},{"dateTimeZone":"2023:04:06 14:50:29+09:00","fileUrl":"http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013545.JPG","height":3360,"isProcessed":true,"name":"R0013545.JPG","previewUrl":"","_projectionType":"Equirectangular","size":6288249,"_thumbSize":5528,"width":6720}],"totalEntries":34},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [ + { + "_autoBracketGroupId": "B0FB64CF29AEFBE4", + "dateTimeZone": "2023:06:23 15:47:45+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013685.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013685.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 3255376, + "_thumbSize": 1450, + "width": 6720 + }, + { + "_autoBracketGroupId": "B0FB64CF29AEFBE4", + "dateTimeZone": "2023:06:23 15:47:42+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013684.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013684.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 3550621, + "_thumbSize": 1453, + "width": 6720 + }, + { + "dateTimeZone": "2023:06:23 15:46:36+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013683.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF29AEFB9C", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013683.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4337539, + "_thumbSize": 1467, + "width": 6720 + }, + { + "dateTimeZone": "2023:06:23 15:46:31+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013682.JPG", + "height": 3360, + "_intervalCaptureGroupId": "B0FB64CF29AEFB9C", + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013682.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4446177, + "_thumbSize": 1459, + "width": 6720 + }, + { + "_compositeShootingGroupId": "B0FB64CF29AEFB66", + "dateTimeZone": "2023:06:23 15:45:58+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013681.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013681.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 4505436, + "_thumbSize": 1471, + "width": 6720 + }, + { + "dateTimeZone": "2023:05:11 15:26:16+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013567.JPG", + "height": 3360, + "isProcessed": true, + "lat": 35.4836, + "lng": 134.2341, + "name": "R0013567.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 8281293, + "_thumbSize": 9735, + "width": 6720 + }, + { + "dateTimeZone": "2023:04:10 17:19:12+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013556.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013556.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6129799, + "_thumbSize": 5364, + "width": 6720 + }, + { + "dateTimeZone": "2023:04:06 15:49:24+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013549.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013549.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6835046, + "_thumbSize": 5618, + "width": 6720 + }, + { + "dateTimeZone": "2023:04:06 14:53:38+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013546.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013546.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6192100, + "_thumbSize": 5482, + "width": 6720 + }, + { + "dateTimeZone": "2023:04:06 14:50:29+09:00", + "fileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0013545.JPG", + "height": 3360, + "isProcessed": true, + "name": "R0013545.JPG", + "previewUrl": "", + "_projectionType": "Equirectangular", + "size": 6288249, + "_thumbSize": 5528, + "width": 6720 + } + ], + "totalEntries": 34 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_zero_entries.json b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_zero_entries.json index 1ab6cb9be5e..c6638942a01 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_zero_entries.json +++ b/kotlin-multiplatform/src/commonTest/resources/listFiles/listfiles_zero_entries.json @@ -1 +1,8 @@ -{"name":"camera.listFiles","results":{"entries":[],"totalEntries":36},"state":"done"} +{ + "name": "camera.listFiles", + "results": { + "entries": [], + "totalEntries": 36 + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/listPlugins/list_plugins_done.json b/kotlin-multiplatform/src/commonTest/resources/listPlugins/list_plugins_done.json index 149c8274406..bea51cbffb2 100644 --- a/kotlin-multiplatform/src/commonTest/resources/listPlugins/list_plugins_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/listPlugins/list_plugins_done.json @@ -1 +1,120 @@ -{"name":"camera._listPlugins","results":{"plugins":[{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta.remoteplayback","pluginName":"Remote Playback","running":false,"type":"system","version":"3.80.2","webServer":false},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.automaticfaceblur","pluginName":"Automatic Face Blur BETA","running":false,"type":"user","version":"1.6.0","webServer":true},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.cloudstreaming","pluginName":"Wireless Live Streaming","running":false,"type":"user","version":"1.1.3","webServer":true},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.clouduploadv2","pluginName":"File cloud upload V2","running":false,"type":"user","version":"1.0.0","webServer":true},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.pluginapplication","pluginName":"Plugin Application","running":false,"type":"user","version":"2.1.0","webServer":false},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.remotecontrol","pluginName":"Remote Control","running":false,"type":"system","version":"1.0.0","webServer":false},{"boot":true,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.timeshiftshooting","pluginName":"Time Shift Shooting","running":false,"type":"user","version":"1.0.2","webServer":true},{"boot":false,"bootOptions":"","exitStatus":"success","force":false,"foreground":false,"message":"","packageName":"com.theta360.usbstorage","pluginName":"USBデータ転送","running":false,"type":"system","version":"2.0.0","webServer":true}]},"state":"done"} +{ + "name": "camera._listPlugins", + "results": { + "plugins": [ + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta.remoteplayback", + "pluginName": "Remote Playback", + "running": false, + "type": "system", + "version": "3.80.2", + "webServer": false + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.automaticfaceblur", + "pluginName": "Automatic Face Blur BETA", + "running": false, + "type": "user", + "version": "1.6.0", + "webServer": true + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.cloudstreaming", + "pluginName": "Wireless Live Streaming", + "running": false, + "type": "user", + "version": "1.1.3", + "webServer": true + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.clouduploadv2", + "pluginName": "File cloud upload V2", + "running": false, + "type": "user", + "version": "1.0.0", + "webServer": true + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.pluginapplication", + "pluginName": "Plugin Application", + "running": false, + "type": "user", + "version": "2.1.0", + "webServer": false + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.remotecontrol", + "pluginName": "Remote Control", + "running": false, + "type": "system", + "version": "1.0.0", + "webServer": false + }, + { + "boot": true, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.timeshiftshooting", + "pluginName": "Time Shift Shooting", + "running": false, + "type": "user", + "version": "1.0.2", + "webServer": true + }, + { + "boot": false, + "bootOptions": "", + "exitStatus": "success", + "force": false, + "foreground": false, + "message": "", + "packageName": "com.theta360.usbstorage", + "pluginName": "USBデータ転送", + "running": false, + "type": "system", + "version": "2.0.0", + "webServer": true + } + ] + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_access_info.json b/kotlin-multiplatform/src/commonTest/resources/options/option_access_info.json new file mode 100644 index 00000000000..84d3b482b34 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_access_info.json @@ -0,0 +1,29 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_accessInfo": { + "ssid": "ssid_test", + "ipAddress": "192.168.1.2", + "subnetMask": "255.255.0.0", + "defaultGateway": "192.168.1.12", + "dns1": "192.168.1.55", + "dns2": "192.168.1.66", + "proxyURL": "http://192.168.1.3", + "frequency": "2.4", + "wlanSignalStrength": -60, + "wlanSignalLevel": 4, + "lteSignalStrength": 0, + "lteSignalLevel": 0, + "_dhcpLeaseAddress": [ + { + "ipAddress": "192.168.1.5", + "macAddress": "58:38:79:12:34:56", + "hostName": "Macbook-Pro" + } + ] + } + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_on.json index e234ebe36ef..d1a779fa7eb 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_on.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_aiAutoThumbnail":"ON"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_aiAutoThumbnail": "ON" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_support.json new file mode 100644 index 00000000000..db5f63d4dd9 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_ai_auto_thumbnail_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_aiAutoThumbnailSupport": [ + "ON", + "OFF", + "abc" + ] + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_2_1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_2_1.json index 2acc8d6e4a9..89d113a4bfd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_2_1.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_2_1.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"aperture":2.1}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "aperture": 2.1 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_support_X.json b/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_support_X.json new file mode 100644 index 00000000000..a4894839e77 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_aperture_support_X.json @@ -0,0 +1,11 @@ +{ + "results": { + "options": { + "apertureSupport": [ + 2.4 + ] + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_auto_bracket.json b/kotlin-multiplatform/src/commonTest/resources/options/option_auto_bracket.json index 545da84c46a..f3648ccc8aa 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_auto_bracket.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_auto_bracket.json @@ -1 +1,29 @@ -{"name":"camera.getOptions","results":{"options":{"_autoBracket":{"_bracketNumber":2,"_bracketParameters":[{"_colorTemperature":5000,"exposureCompensation":0.0,"exposureProgram":1,"iso":400,"shutterSpeed":0.004,"whiteBalance":"auto"},{"_colorTemperature":5000,"exposureCompensation":0.0,"exposureProgram":1,"iso":400,"shutterSpeed":0.004,"whiteBalance":"auto"}]}}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_autoBracket": { + "_bracketNumber": 2, + "_bracketParameters": [ + { + "_colorTemperature": 5000, + "exposureCompensation": 0.0, + "exposureProgram": 1, + "iso": 400, + "shutterSpeed": 0.004, + "whiteBalance": "auto" + }, + { + "_colorTemperature": 5000, + "exposureCompensation": 0.0, + "exposureProgram": 1, + "iso": 400, + "shutterSpeed": 0.004, + "whiteBalance": "auto" + } + ] + } + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_100000000.json b/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_100000000.json index 262001110ea..b40086f758f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_100000000.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_100000000.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_bitrate":"100000000"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_bitrate": "100000000" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_fine.json b/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_fine.json index 275f57e2910..1aaebc14a0b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_fine.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_bitrate_fine.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_bitrate":"Fine"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_bitrate": "Fine" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_power_on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_power_on.json index 7991a1a775e..367cee28b9a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_power_on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_power_on.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_bluetoothPower":"ON"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_bluetoothPower": "ON" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_role_central.json b/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_role_central.json index 1fb3ae415b0..c97a71083ea 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_role_central.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_bluetooth_role_central.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_bluetoothRole":"Central"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_bluetoothRole": "Central" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_burst_mode_off.json b/kotlin-multiplatform/src/commonTest/resources/options/option_burst_mode_off.json index c5866eacd5c..2f253c2ab5e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_burst_mode_off.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_burst_mode_off.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_burstMode":"OFF"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_burstMode": "OFF" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_burst_option_default.json b/kotlin-multiplatform/src/commonTest/resources/options/option_burst_option_default.json index 47d393bfb23..b6475fa072a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_burst_option_default.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_burst_option_default.json @@ -1 +1,16 @@ -{"name":"camera.getOptions","results":{"options":{"_burstOption":{"_burstBracketStep":0.0,"_burstCaptureNum":1,"_burstCompensation":0.0,"_burstEnableIsoControl":0,"_burstMaxExposureTime":15,"_burstOrder":0}}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_burstOption": { + "_burstBracketStep": 0.0, + "_burstCaptureNum": 1, + "_burstCompensation": 0.0, + "_burstEnableIsoControl": 0, + "_burstMaxExposureTime": 15, + "_burstOrder": 0 + } + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source.json index 7d1590f6fa2..ff7aa913eda 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source.json @@ -1 +1,9 @@ -{"results":{"options":{"_cameraControlSource":"camera"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_cameraControlSource": "camera" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source_support.json new file mode 100644 index 00000000000..73ce3518a11 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_control_source_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_cameraControlSourceSupport": [ + "camera", + "app", + "abc" + ] + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_A1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_A1.json new file mode 100644 index 00000000000..41fe31ce71c --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_A1.json @@ -0,0 +1,16 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_cameraLockConfig": { + "powerKey": "lock", + "shutterKey": "unlock", + "modeKey": "unlock", + "wlanKey": "unlock", + "fnKey": "unlock", + "panel": "lock" + } + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_unknown.json new file mode 100644 index 00000000000..757c94128d4 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_config_unknown.json @@ -0,0 +1,16 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_cameraLockConfig": { + "powerKey": "a", + "shutterKey": "b", + "modeKey": "c", + "wlanKey": "d", + "fnKey": "e", + "panel": "f" + } + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unknown.json new file mode 100644 index 00000000000..7033a01f179 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unknown.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_cameraLock": "abcde" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unlock.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unlock.json new file mode 100644 index 00000000000..864edd632c7 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_lock_unlock.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_cameraLock": "unlock" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_mode_capture.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_mode_capture.json index f3e2bfcffba..b1ea65c82a6 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_mode_capture.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_mode_capture.json @@ -1 +1,9 @@ -{"results":{"options":{"_cameraMode":"capture"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_cameraMode": "capture" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_saving.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_saving.json index a7046ec3015..f3d791d059d 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_saving.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_saving.json @@ -1 +1,9 @@ -{"results":{"options":{"_cameraPower":"powerSaving"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_cameraPower": "powerSaving" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_support.json new file mode 100644 index 00000000000..da5984dc795 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_support.json @@ -0,0 +1,15 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_cameraPowerSupport": [ + "on", + "off", + "powerSaving", + "silentMode", + "abc" + ] + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_unknown.json new file mode 100644 index 00000000000..463ee59e2f2 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_power_unknown.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_cameraPower": "other" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_unknown.json index 81576639d5b..463ee59e2f2 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_camera_unknown.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_camera_unknown.json @@ -1 +1,9 @@ -{"results":{"options":{"_cameraPower":"other"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_cameraPower": "other" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_interval_3600.json b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_interval_3600.json index 806f18071b7..91d89038ab1 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_interval_3600.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_interval_3600.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"captureInterval":3600}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "captureInterval": 3600 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_mode_image.json b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_mode_image.json index 8ea753a9155..cb62dba370c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_mode_image.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_mode_image.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"captureMode":"image"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "captureMode": "image" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_number_9999.json b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_number_9999.json index 69d62caa09b..dec9ed8973f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_capture_number_9999.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_capture_number_9999.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"captureNumber":9999}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "captureNumber": 9999 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_color_temperature_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_color_temperature_support.json new file mode 100644 index 00000000000..9aac30491b1 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_color_temperature_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_colorTemperatureSupport": { + "maxTemperature": 10000, + "minTemperature": 2500, + "stepSize": 100 + } + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_auto.json b/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_auto.json new file mode 100644 index 00000000000..75d03ec346c --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_auto.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_compassDirectionRef": "auto" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_unknown.json new file mode 100644 index 00000000000..65258687727 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_compass_direction_ref_unknown.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_compassDirectionRef": "abc" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_60.json b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_60.json index a4fbdfdf591..10d4ded9fc7 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_60.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_60.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_compositeShootingOutputInterval":60}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_compositeShootingOutputInterval": 60 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_support.json new file mode 100644 index 00000000000..6cbb3f3f81d --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_output_interval_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_compositeShootingOutputIntervalSupport": { + "maxInterval": 600, + "minInterval": 0, + "stepSize": 60 + } + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_600.json b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_600.json index 6f5e279bff8..792eabe9ea7 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_600.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_600.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_compositeShootingTime":600}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_compositeShootingTime": 600 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_support.json new file mode 100644 index 00000000000..3738cf722a0 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_composite_shooting_time_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_compositeShootingTimeSupport": { + "maxTime": 86400, + "minTime": 600, + "stepSize": 600 + } + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_continuous_number_20.json b/kotlin-multiplatform/src/commonTest/resources/options/option_continuous_number_20.json index 8db342b05b2..e3d9668f5fe 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_continuous_number_20.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_continuous_number_20.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"continuousNumber":20}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "continuousNumber": 20 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_2_0.json b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_2_0.json index 3bcb7d05726..e1de5e90dd3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_2_0.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_2_0.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"exposureCompensation":2.0}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "exposureCompensation": 2.0 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_unknown.json new file mode 100644 index 00000000000..55202aa4792 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_compensation_unknown.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "exposureCompensation": 100.0 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_1.json index 9c1ff58c124..73437997adf 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_1.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_1.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"exposureDelay":1}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "exposureDelay": 1 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_support.json new file mode 100644 index 00000000000..f84b6423a97 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_delay_support.json @@ -0,0 +1,21 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "exposureDelaySupport": [ + 0, + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 10 + ] + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_program_1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_program_1.json index 162d0e044f2..34b1a08e39b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_program_1.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_exposure_program_1.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"exposureProgram":1}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "exposureProgram": 1 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_face_detect_on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_face_detect_on.json index 2dd2dbf435f..257ebfaba3e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_face_detect_on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_face_detect_on.json @@ -1 +1,9 @@ -{"results":{"options":{"_faceDetect":"ON"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_faceDetect": "ON" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_unknown.json index a107312dbc9..23e11c1655d 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_unknown.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_unknown.json @@ -2,7 +2,13 @@ "name": "camera.getOptions", "results": { "options": { - "fileFormat": {"type": "mp1","width": 3840,"height": 1920, "_codec": "H.264/MPEG-4 AVC", "_frameRate": 60} + "fileFormat": { + "type": "mp1", + "width": 3840, + "height": 1920, + "_codec": "H.264/MPEG-4 AVC", + "_frameRate": 60 + } } }, "state": "done" diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_video4k_60f.json b/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_video4k_60f.json index 614a1393f70..7a1a4c39b00 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_video4k_60f.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_file_format_video4k_60f.json @@ -2,7 +2,13 @@ "name": "camera.getOptions", "results": { "options": { - "fileFormat": {"type": "mp4","width": 3840,"height": 1920, "_codec": "H.264/MPEG-4 AVC", "_frameRate": 60} + "fileFormat": { + "type": "mp4", + "width": 3840, + "height": 1920, + "_codec": "H.264/MPEG-4 AVC", + "_frameRate": 60 + } } }, "state": "done" diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_filter_hdr.json b/kotlin-multiplatform/src/commonTest/resources/options/option_filter_hdr.json index eb0b772000c..e84636d4b70 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_filter_hdr.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_filter_hdr.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_filter":"hdr"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_filter": "hdr" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_function_self_timer.json b/kotlin-multiplatform/src/commonTest/resources/options/option_function_self_timer.json index 14d90b94669..7ac73114e29 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_function_self_timer.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_function_self_timer.json @@ -1 +1,9 @@ -{"results":{"options":{"_function":"selfTimer"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_function": "selfTimer" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_gain_mute.json b/kotlin-multiplatform/src/commonTest/resources/options/option_gain_mute.json index 84411fcd4ce..a0e6bf60c3c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_gain_mute.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_gain_mute.json @@ -1 +1,9 @@ -{"results":{"options":{"_gain":"mute"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_gain": "mute" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_on.json index 232770e8c32..aaf7d437d00 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_on.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_gpsTagRecording":"on"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_gpsTagRecording": "on" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_support.json new file mode 100644 index 00000000000..790342bb765 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_gps_tag_recording_support.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_gpsTagRecordingSupport": [ + "on", + "off", + "abc" + ] + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_image_stitching.json b/kotlin-multiplatform/src/commonTest/resources/options/option_image_stitching.json index dd9f0702d09..3cc5efd3a06 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_image_stitching.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_image_stitching.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_imageStitching":"auto"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_imageStitching": "auto" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_iso_100.json b/kotlin-multiplatform/src/commonTest/resources/options/option_iso_100.json index 93d6c162a35..ea5c55b2c53 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_iso_100.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_iso_100.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"iso":100}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "iso": 100 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_iso_auto_high_limit_100.json b/kotlin-multiplatform/src/commonTest/resources/options/option_iso_auto_high_limit_100.json index a1497ad1713..e8f360840d4 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_iso_auto_high_limit_100.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_iso_auto_high_limit_100.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"isoAutoHighLimit":100}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "isoAutoHighLimit": 100 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_language_ja.json b/kotlin-multiplatform/src/commonTest/resources/options/option_language_ja.json index a54bb7403d5..34badb8658f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_language_ja.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_language_ja.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_language":"ja"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_language": "ja" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_latest_enabled_exposure_delay_time_1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_latest_enabled_exposure_delay_time_1.json index 1a14abeb6e2..7dfa26ee1fc 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_latest_enabled_exposure_delay_time_1.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_latest_enabled_exposure_delay_time_1.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_latestEnabledExposureDelayTime":1}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_latestEnabledExposureDelayTime": 1 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_max_recordable_time_1500.json b/kotlin-multiplatform/src/commonTest/resources/options/option_max_recordable_time_1500.json index 4420201c65a..6d76a04794c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_max_recordable_time_1500.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_max_recordable_time_1500.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_maxRecordableTime":1500}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_maxRecordableTime": 1500 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_on.json new file mode 100644 index 00000000000..170a2f5beb2 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_on.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_microphoneNoiseReduction": "on" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_unknown.json new file mode 100644 index 00000000000..dba264843fa --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_microphone_noise_reduction_unknown.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_microphoneNoiseReduction": "abc" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_normal.json b/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_normal.json new file mode 100644 index 00000000000..fac098a20f5 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_normal.json @@ -0,0 +1,12 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_mobileNetworkSetting": { + "roaming": "OFF", + "plan": "SORACOM" + } + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_unknown.json new file mode 100644 index 00000000000..b0af77a068d --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_mobile_network_setting_unknown.json @@ -0,0 +1,12 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_mobileNetworkSetting": { + "roaming": "abc", + "plan": "abc" + } + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_direct.json b/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_direct.json index 3507049798e..74a475f7a69 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_direct.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_direct.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_networkType":"AP"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_networkType": "AP" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_unknown.json new file mode 100644 index 00000000000..da765a63285 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_network_type_unknown.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_networkType": "aaa" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_600.json b/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_600.json index cfe76a1a15f..5d1cc5d63b2 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_600.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_600.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"offDelay":600}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "offDelay": 600 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_usb_600.json b/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_usb_600.json new file mode 100644 index 00000000000..05d6ca8ba38 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_off_delay_usb_600.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_offDelayUSB": 600 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_power_saving-on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_power_saving-on.json index 023749fbec2..678f8aca149 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_power_saving-on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_power_saving-on.json @@ -1 +1,9 @@ -{"results":{"options":{"_powerSaving":"ON"}},"name":"camera.getOptions","state":"done"} +{ + "results": { + "options": { + "_powerSaving": "ON" + } + }, + "name": "camera.getOptions", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_preset_room.json b/kotlin-multiplatform/src/commonTest/resources/options/option_preset_room.json index fdc7cbc3f39..db215986dc3 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_preset_room.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_preset_room.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","state":"done","results":{"options":{"_preset":"room"}}} +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_preset": "room" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_1024_512_30.json b/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_1024_512_30.json index df3ed8eb5a7..7a6b54ab9b8 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_1024_512_30.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_1024_512_30.json @@ -1 +1,13 @@ -{"name":"camera.getOptions","results":{"options":{"previewFormat":{"framerate":30,"height":512,"width":1024}}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "previewFormat": { + "framerate": 30, + "height": 512, + "width": 1024 + } + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_unknown.json new file mode 100644 index 00000000000..6eea824d207 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_preview_format_unknown.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "previewFormat": { + "framerate": -10, + "height": -1, + "width": -5 + } + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_X.json b/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_X.json index 2572b8953e8..347c6a278f0 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_X.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_X.json @@ -1 +1,13 @@ -{"results":{"options":{"_proxy":{"use":false,"url":"","port":8080}}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_proxy": { + "use": false, + "url": "", + "port": 8080 + } + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_Z1.json b/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_Z1.json index 4bdf25348e6..3005732c19c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_Z1.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_proxy_Z1.json @@ -1 +1,11 @@ -{"name":"camera.getOptions","results":{"options":{"_proxy":{"use":false}}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_proxy": { + "use": false + } + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_shooting_method_normal.json b/kotlin-multiplatform/src/commonTest/resources/options/option_shooting_method_normal.json index 4a8301e8a85..2e6f378942a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_shooting_method_normal.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_shooting_method_normal.json @@ -1 +1,9 @@ -{"results":{"options":{"_shootingMethod":"normal"}},"name":"camera.getOptions","state":"done"} +{ + "results": { + "options": { + "_shootingMethod": "normal" + } + }, + "name": "camera.getOptions", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_shutter_speed_10.json b/kotlin-multiplatform/src/commonTest/resources/options/option_shutter_speed_10.json index b6cc6f108b5..2f0a4edc16c 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_shutter_speed_10.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_shutter_speed_10.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"shutterSpeed":10}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "shutterSpeed": 10 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_sleep_delay_180.json b/kotlin-multiplatform/src/commonTest/resources/options/option_sleep_delay_180.json index 0a3f958e161..6089e2bf61a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_sleep_delay_180.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_sleep_delay_180.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"sleepDelay":180}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "sleepDelay": 180 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_time_shift.json b/kotlin-multiplatform/src/commonTest/resources/options/option_time_shift.json index 876aec810c3..bd667f907bd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_time_shift.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_time_shift.json @@ -1 +1,13 @@ -{"name":"camera.getOptions","results":{"options":{"_timeShift":{"firstInterval":5,"firstShooting":"front","secondInterval":5}}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_timeShift": { + "firstInterval": 5, + "firstShooting": "front", + "secondInterval": 5 + } + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_apply_auto.json b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_apply_auto.json index 460275e3b20..46b944dc56d 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_apply_auto.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_apply_auto.json @@ -1 +1,9 @@ -{"results":{"options":{"_topBottomCorrection":"ApplyAuto"}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_topBottomCorrection": "ApplyAuto" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_support.json b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_support.json new file mode 100644 index 00000000000..d4d3f28cf8d --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_support.json @@ -0,0 +1,25 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_topBottomCorrectionRotationSupport": { + "pitch": { + "maxPitch": "+90.0", + "minPitch": "-90.0", + "stepSize": "0.1" + }, + "roll": { + "maxRoll": "+180.0", + "minRoll": "-180.0", + "stepSize": "0.1" + }, + "yaw": { + "maxYaw": "+180.0", + "minYaw": "-180.0", + "stepSize": "0.1" + } + } + } + } +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_zero.json b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_zero.json index 777ceab48d9..919f3acc7fd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_zero.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_top_bottom_correction_rotation_zero.json @@ -1 +1,13 @@ -{"results":{"options":{"_topBottomCorrectionRotation":{"pitch":"0","roll":"0","yaw":"0"}}},"name":"camera.getOptions","state":"done"} \ No newline at end of file +{ + "results": { + "options": { + "_topBottomCorrectionRotation": { + "pitch": "0", + "roll": "0", + "yaw": "0" + } + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_msc.json b/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_msc.json new file mode 100644 index 00000000000..d64ae362cf3 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_msc.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_usbConnection": "MSC" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_unknown.json new file mode 100644 index 00000000000..fdd9d5c6a5c --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_usb_connection_unknown.json @@ -0,0 +1,9 @@ +{ + "results": { + "options": { + "_usbConnection": "abcde" + } + }, + "name": "camera.getOptions", + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_video_stitching-ondevice.json b/kotlin-multiplatform/src/commonTest/resources/options/option_video_stitching-ondevice.json index 9f00636b80c..8830af0a46b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_video_stitching-ondevice.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_video_stitching-ondevice.json @@ -1 +1,9 @@ -{"results":{"options":{"videoStitching":"ondevice"}},"name":"camera.getOptions","state":"done"} +{ + "results": { + "options": { + "videoStitching": "ondevice" + } + }, + "name": "camera.getOptions", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_visibility-reduction-on.json b/kotlin-multiplatform/src/commonTest/resources/options/option_visibility-reduction-on.json index 5ece3802b6a..a3de56cbade 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_visibility-reduction-on.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_visibility-reduction-on.json @@ -1 +1,9 @@ -{"results":{"options":{"_visibilityReduction":"ON"}},"name":"camera.getOptions","state":"done"} +{ + "results": { + "options": { + "_visibilityReduction": "ON" + } + }, + "name": "camera.getOptions", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_auto_strength_off.json b/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_auto_strength_off.json index 11a3799acf7..3b05527c6e9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_auto_strength_off.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_auto_strength_off.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_whiteBalanceAutoStrength":"OFF"}},"state":"done"} \ No newline at end of file +{ + "name": "camera.getOptions", + "results": { + "options": { + "_whiteBalanceAutoStrength": "OFF" + } + }, + "state": "done" +} \ No newline at end of file diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_daylight.json b/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_daylight.json index ce2f4bb9768..1fba3820ec2 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_daylight.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_white_balance_daylight.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"whiteBalance":"daylight"}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "whiteBalance": "daylight" + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_siso.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_siso.json new file mode 100644 index 00000000000..373609e3154 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_siso.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_wlanAntennaConfig": "SISO" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_unknown.json new file mode 100644 index 00000000000..43040fe5e33 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_antenna_config_unknown.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_wlanAntennaConfig": "abc" + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_5.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_5.json index 284b7bf998f..9724124a3fd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_5.json +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_5.json @@ -1 +1,9 @@ -{"name":"camera.getOptions","results":{"options":{"_wlanFrequency":5}},"state":"done"} +{ + "name": "camera.getOptions", + "results": { + "options": { + "_wlanFrequency": 5 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_cl_mode.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_cl_mode.json new file mode 100644 index 00000000000..1572065faa5 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_cl_mode.json @@ -0,0 +1,13 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_wlanFrequencyCLmode": { + "enable2.4GHz": true, + "enable5.2GHz": false, + "enable5.8GHz": false + } + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_support_with_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_support_with_unknown.json new file mode 100644 index 00000000000..8e0fad9b8ef --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_support_with_unknown.json @@ -0,0 +1,15 @@ +{ + "name": "camera.getOptions", + "state": "done", + "results": { + "options": { + "_wlanFrequencySupport": [ + 2.4, + 5.2, + 5.8, + 6.0, + 7.0 + ] + } + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_unknown.json b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_unknown.json new file mode 100644 index 00000000000..279067d4784 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/options/option_wlan_frequency_unknown.json @@ -0,0 +1,9 @@ +{ + "name": "camera.getOptions", + "results": { + "options": { + "_wlanFrequency": -100 + } + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/pluginControl/plugin_control_done.json b/kotlin-multiplatform/src/commonTest/resources/pluginControl/plugin_control_done.json index deb21d73cf6..3ce232c7129 100644 --- a/kotlin-multiplatform/src/commonTest/resources/pluginControl/plugin_control_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/pluginControl/plugin_control_done.json @@ -1 +1,4 @@ -{"name":"camera._pluginControl","state":"done"} +{ + "name": "camera._pluginControl", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_done.json b/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_done.json new file mode 100644 index 00000000000..c843c154bee --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_done.json @@ -0,0 +1,4 @@ +{ + "name": "camera._reboot", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_error.json b/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_error.json new file mode 100644 index 00000000000..1997470f862 --- /dev/null +++ b/kotlin-multiplatform/src/commonTest/resources/reboot/reboot_error.json @@ -0,0 +1,8 @@ +{ + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._reboot", + "state": "error" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/reset/reset_done.json b/kotlin-multiplatform/src/commonTest/resources/reset/reset_done.json index 84f65465dad..d758f023b68 100644 --- a/kotlin-multiplatform/src/commonTest/resources/reset/reset_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/reset/reset_done.json @@ -1 +1,4 @@ -{"name":"camera.reset","state":"done"} +{ + "name": "camera.reset", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/reset/reset_error.json b/kotlin-multiplatform/src/commonTest/resources/reset/reset_error.json index 54d5336c18c..3f1bd198147 100644 --- a/kotlin-multiplatform/src/commonTest/resources/reset/reset_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/reset/reset_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.reset", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.reset", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_done.json b/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_done.json index 90fb6b996a8..0bbc5a23762 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_done.json @@ -1 +1,4 @@ -{"name":"camera._setAccessPoint","state":"done"} +{ + "name": "camera._setAccessPoint", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_error.json b/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_error.json index ac228fe5a7e..6acfb905861 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/setAccessPoint/set_access_point_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._setAccessPoint", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._setAccessPoint", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/setBluetoothDevice/set_bluetooth_device_done.json b/kotlin-multiplatform/src/commonTest/resources/setBluetoothDevice/set_bluetooth_device_done.json index 82555857823..ef7ff2a969a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setBluetoothDevice/set_bluetooth_device_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/setBluetoothDevice/set_bluetooth_device_done.json @@ -1 +1,7 @@ -{"name":"camera._setBluetoothDevice","results":{"deviceName":"00155198"},"state":"done"} +{ + "name": "camera._setBluetoothDevice", + "results": { + "deviceName": "00155198" + }, + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/setMySetting/set_my_setting_done.json b/kotlin-multiplatform/src/commonTest/resources/setMySetting/set_my_setting_done.json index fae2b876ba3..cbaecd989db 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setMySetting/set_my_setting_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/setMySetting/set_my_setting_done.json @@ -1 +1,4 @@ -{"name":"camera._setMySetting","state":"done"} +{ + "name": "camera._setMySetting", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_done.json b/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_done.json index 1833f48e13f..aebc0303c83 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_done.json @@ -1 +1,4 @@ -{"name":"camera.setOptions","state":"done"} +{ + "name": "camera.setOptions", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_error.json b/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_error.json index 39cd56463a0..8b586712637 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/setOptions/set_options_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.setOptions", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.setOptions", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/setPlugin/set_plugin_done_v.json b/kotlin-multiplatform/src/commonTest/resources/setPlugin/set_plugin_done_v.json index fdff523681f..0f833e265f9 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setPlugin/set_plugin_done_v.json +++ b/kotlin-multiplatform/src/commonTest/resources/setPlugin/set_plugin_done_v.json @@ -1 +1,4 @@ -{"name":"camera._setPlugin","state":"done"} +{ + "name": "camera._setPlugin", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/setPluginOrders/set_plugin_orders_done.json b/kotlin-multiplatform/src/commonTest/resources/setPluginOrders/set_plugin_orders_done.json index d10ffdb9d7d..d84194ec807 100644 --- a/kotlin-multiplatform/src/commonTest/resources/setPluginOrders/set_plugin_orders_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/setPluginOrders/set_plugin_orders_done.json @@ -1 +1,4 @@ -{"name":"camera._setPluginOrders","state":"done"} +{ + "name": "camera._setPluginOrders", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_done.json b/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_done.json index c45100694cb..41b5a6afc3e 100644 --- a/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_done.json @@ -1 +1,8 @@ -{"name":"camera.startSession","state":"done","results":{ "sessionId":"SID_0001","timeout":180}} +{ + "name": "camera.startSession", + "state": "done", + "results": { + "sessionId": "SID_0001", + "timeout": 180 + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_error.json b/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_error.json index c58936ca7d0..bced1b6d20a 100644 --- a/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/startSession/start_session_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera.startSession", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera.startSession", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_s_api1.json b/kotlin-multiplatform/src/commonTest/resources/state/state_s_api1.json index c26c52214ea..09cc483933b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_s_api1.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_s_api1.json @@ -1 +1,15 @@ -{"fingerprint":"FIG_0002","state":{"sessionId":"SID_0001","batteryLevel":0.67,"storageChanged":false,"_captureStatus":"idle","_recordedTime":0,"_recordableTime":0,"_compositeShootingElapsedTime":0,"_latestFileUri":"","_batteryState":"disconnect","_apiVersion":1}} +{ + "fingerprint": "FIG_0002", + "state": { + "sessionId": "SID_0001", + "batteryLevel": 0.67, + "storageChanged": false, + "_captureStatus": "idle", + "_recordedTime": 0, + "_recordableTime": 0, + "_compositeShootingElapsedTime": 0, + "_latestFileUri": "", + "_batteryState": "disconnect", + "_apiVersion": 1 + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_s_api2.json b/kotlin-multiplatform/src/commonTest/resources/state/state_s_api2.json index b96cf317272..932fab2245b 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_s_api2.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_s_api2.json @@ -1 +1,14 @@ -{"fingerprint":"FIG_0002","state":{"batteryLevel":0.67,"storageUri":"http://192.168.1.1/files/744a60555344202010418a2304010a01/","_captureStatus":"idle","_recordedTime":0,"_recordableTime":0,"_compositeShootingElapsedTime":0,"_latestFileUrl":"","_batteryState":"disconnect","_apiVersion":2}} +{ + "fingerprint": "FIG_0002", + "state": { + "batteryLevel": 0.67, + "storageUri": "http://192.168.1.1/files/744a60555344202010418a2304010a01/", + "_captureStatus": "idle", + "_recordedTime": 0, + "_recordableTime": 0, + "_compositeShootingElapsedTime": 0, + "_latestFileUrl": "", + "_batteryState": "disconnect", + "_apiVersion": 2 + } +} diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_x.json b/kotlin-multiplatform/src/commonTest/resources/state/state_x.json index 8626eda0911..a0950d71942 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_x.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_x.json @@ -5,7 +5,9 @@ "_batteryInsert": true, "batteryLevel": 0.33, "_batteryState": "disconnect", - "_cameraError": ["HIGH_TEMPERATURE_WARNING"], + "_cameraError": [ + "HIGH_TEMPERATURE_WARNING" + ], "_captureStatus": "idle", "_capturedPictures": 0, "_currentMicrophone": "Internal", diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_x_electronic_compass_calibration.json b/kotlin-multiplatform/src/commonTest/resources/state/state_x_electronic_compass_calibration.json index 6b8f31dfe54..b4ba85490dd 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_x_electronic_compass_calibration.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_x_electronic_compass_calibration.json @@ -5,7 +5,9 @@ "_batteryInsert": true, "batteryLevel": 0.33, "_batteryState": "disconnect", - "_cameraError": ["ELECTRONIC_COMPASS_CALIBRATION"], + "_cameraError": [ + "ELECTRONIC_COMPASS_CALIBRATION" + ], "_captureStatus": "idle", "_capturedPictures": 0, "_currentMicrophone": "Internal", diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_x_gpsinfo.json b/kotlin-multiplatform/src/commonTest/resources/state/state_x_gpsinfo.json index ee9e5cef3f7..8a95a2e400f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_x_gpsinfo.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_x_gpsinfo.json @@ -8,7 +8,6 @@ "_batteryTemp": 30, "_boardTemp": 28, "_cameraError": [ - ], "_captureStatus": "idle", "_capturedPictures": 0, diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_x_unknown_camera_error.json b/kotlin-multiplatform/src/commonTest/resources/state/state_x_unknown_camera_error.json index 59b93729ac9..98513bf01d8 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_x_unknown_camera_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_x_unknown_camera_error.json @@ -5,7 +5,9 @@ "_batteryInsert": true, "batteryLevel": 0.33, "_batteryState": "disconnect", - "_cameraError": ["error_unknown"], + "_cameraError": [ + "error_unknown" + ], "_captureStatus": "idle", "_capturedPictures": 0, "_currentMicrophone": "Internal", diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_off.json b/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_off.json index 5c7ef348553..ffe5f2a76eb 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_off.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_off.json @@ -13,11 +13,9 @@ "_capturedPictures": 0, "_compositeShootingElapsedTime": 0, "_externalGpsInfo": { - }, "_function": "normal", "_internalGpsInfo": { - }, "_latestFileUrl": "", "_mySettingChanged": false, diff --git a/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_on.json b/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_on.json index 2138948abc4..875440e60fc 100644 --- a/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_on.json +++ b/kotlin-multiplatform/src/commonTest/resources/state/state_z1_gps_on.json @@ -23,7 +23,6 @@ }, "_function": "normal", "_internalGpsInfo": { - }, "_latestFileUrl": "http://192.168.1.1/files/150100524436344d4201375fda9dc400/100RICOH/R0010006.JPG", "_mySettingChanged": false, diff --git a/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_done.json b/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_done.json index 1714727f197..546e95e32c0 100644 --- a/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_done.json +++ b/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_done.json @@ -1 +1,4 @@ -{"name":"camera._stopSelfTimer","state":"done"} +{ + "name": "camera._stopSelfTimer", + "state": "done" +} diff --git a/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_error.json b/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_error.json index e13af9c59b2..1263ea8dd36 100644 --- a/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_error.json +++ b/kotlin-multiplatform/src/commonTest/resources/stopSelfTimer/stop_selftimer_error.json @@ -1,8 +1,8 @@ { - "error": { - "code": "UnitTest", - "message": "UnitTest error" - }, - "name": "camera._stopSelfTimer", - "state": "error" + "error": { + "code": "UnitTest", + "message": "UnitTest error" + }, + "name": "camera._stopSelfTimer", + "state": "error" } diff --git a/kotlin-multiplatform/src/commonTest/resources/updateFirmware/update_firmware.done.json b/kotlin-multiplatform/src/commonTest/resources/updateFirmware/update_firmware.done.json index 958de8f733a..f1bbb8afa6f 100644 --- a/kotlin-multiplatform/src/commonTest/resources/updateFirmware/update_firmware.done.json +++ b/kotlin-multiplatform/src/commonTest/resources/updateFirmware/update_firmware.done.json @@ -1 +1,4 @@ -{"name":"camera.dummyApiPath","state":"done"} +{ + "name": "camera.dummyApiPath", + "state": "done" +} diff --git a/kotlin-multiplatform/src/iosMain/kotlin/com/ricoh360/thetaclient/Platform.kt b/kotlin-multiplatform/src/iosMain/kotlin/com/ricoh360/thetaclient/Platform.kt index bae6192bae0..1744f5568c7 100644 --- a/kotlin-multiplatform/src/iosMain/kotlin/com/ricoh360/thetaclient/Platform.kt +++ b/kotlin-multiplatform/src/iosMain/kotlin/com/ricoh360/thetaclient/Platform.kt @@ -6,6 +6,9 @@ package com.ricoh360.thetaclient import kotlinx.cinterop.* import platform.Foundation.* import platform.UIKit.UIDevice +import kotlin.experimental.ExperimentalNativeApi +import kotlin.native.ref.WeakReference + /** * describe platform @@ -47,3 +50,6 @@ actual fun currentTimeMillis(): Long { val interval = NSDate().timeIntervalSince1970 return (interval / 1000.0).toLong() } + +@OptIn(ExperimentalNativeApi::class) +actual typealias WeakReference = WeakReference diff --git a/react-native/android/build.gradle b/react-native/android/build.gradle index 9add1ed654d..db81b0b49cc 100644 --- a/react-native/android/build.gradle +++ b/react-native/android/build.gradle @@ -34,6 +34,7 @@ def getExtOrIntegerDefault(name) { } android { + namespace = "com.ricoh360.thetaclientreactnative" compileSdkVersion getExtOrIntegerDefault('compileSdkVersion') defaultConfig { @@ -135,7 +136,7 @@ dependencies { implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3" - implementation "com.ricoh360.thetaclient:theta-client:1.12.1" + implementation "com.ricoh360.thetaclient:theta-client:1.13.0" // From node_modules } diff --git a/react-native/android/src/main/AndroidManifest.xml b/react-native/android/src/main/AndroidManifest.xml index a068fe92ab8..a2f47b6057d 100644 --- a/react-native/android/src/main/AndroidManifest.xml +++ b/react-native/android/src/main/AndroidManifest.xml @@ -1,4 +1,2 @@ - - + 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 aecc356f3ca..15f66a202b3 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 @@ -10,6 +10,7 @@ import com.ricoh360.thetaclient.DigestAuth import com.ricoh360.thetaclient.ThetaRepository.* import com.ricoh360.thetaclient.capture.* import com.ricoh360.thetaclient.websocket.CameraEvent +import kotlin.reflect.KClass const val KEY_NOTIFY_NAME = "name" const val KEY_NOTIFY_PARAMS = "params" @@ -23,10 +24,43 @@ const val KEY_STATE_EXTERNAL_GPS_INFO = "externalGpsInfo" const val KEY_STATE_INTERNAL_GPS_INFO = "internalGpsInfo" const val KEY_STATE_BOARD_TEMP = "boardTemp" const val KEY_STATE_BATTERY_TEMP = "batteryTemp" +const val KEY_SSID = "ssid" +const val KEY_SSID_STEALTH = "ssidStealth" +const val KEY_AUTH_MODE = "authMode" +const val KEY_PASSWORD = "password" +const val KEY_CONNECTION_PRIORITY = "connectionPriority" +const val KEY_IP_ADDRESS = "ipAddress" +const val KEY_SUBNET_MASK = "subnetMask" +const val KEY_DEFAULT_GATEWAY = "defaultGateway" +const val KEY_PROXY = "proxy" +const val KEY_MAC_ADDRESS = "macAddress" +const val KEY_HOST_NAME = "hostName" +const val KEY_DHCP_LEASE_ADDRESS = "dhcpLeaseAddress" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION = "topBottomCorrectionRotation" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH = "pitch" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL = "roll" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW = "yaw" +const val KEY_TOP_BOTTOM_CORRECTION_ROTATION_SUPPORT = "topBottomCorrectionRotationSupport" +const val KEY_MAX = "max" +const val KEY_MIN = "min" +const val KEY_STEP_SIZE = "stepSize" +const val KEY_GPS_TAG_RECORDING_SUPPORT = "gpsTagRecordingSupport" +const val KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL_SUPPORT = "compositeShootingOutputIntervalSupport" +const val KEY_COMPOSITE_SHOOTING_TIME_SUPPORT = "compositeShootingTimeSupport" +const val KEY_APERTURE_SUPPORT = "apertureSupport" +const val KEY_WLAN_FREQUENCY_CL_MODE = "wlanFrequencyClMode" +const val KEY_WLAN_FREQUENCY_CL_MODE_2_4 = "enable2_4" +const val KEY_WLAN_FREQUENCY_CL_MODE_5_2 = "enable5_2" +const val KEY_WLAN_FREQUENCY_CL_MODE_5_8 = "enable5_8" +const val KEY_DNS1 = "dns1" +const val KEY_DNS2 = "dns2" val optionItemNameToEnum: Map = mutableMapOf( + "accessInfo" to OptionNameEnum.AccessInfo, "aiAutoThumbnail" to OptionNameEnum.AiAutoThumbnail, + "aiAutoThumbnailSupport" to OptionNameEnum.AiAutoThumbnailSupport, "aperture" to OptionNameEnum.Aperture, + KEY_APERTURE_SUPPORT to OptionNameEnum.ApertureSupport, "autoBracket" to OptionNameEnum.AutoBracket, "bitrate" to OptionNameEnum.Bitrate, "bluetoothPower" to OptionNameEnum.BluetoothPower, @@ -34,26 +68,35 @@ val optionItemNameToEnum: Map = mutableMapOf( "burstMode" to OptionNameEnum.BurstMode, "burstOption" to OptionNameEnum.BurstOption, "cameraControlSource" to OptionNameEnum.CameraControlSource, + "cameraControlSourceSupport" to OptionNameEnum.CameraControlSourceSupport, + "cameraLock" to OptionNameEnum.CameraLock, + "cameraLockConfig" to OptionNameEnum.CameraLockConfig, "cameraMode" to OptionNameEnum.CameraMode, "cameraPower" to OptionNameEnum.CameraPower, + "cameraPowerSupport" to OptionNameEnum.CameraPowerSupport, "captureInterval" to OptionNameEnum.CaptureInterval, "captureMode" to OptionNameEnum.CaptureMode, "captureNumber" to OptionNameEnum.CaptureNumber, "colorTemperature" to OptionNameEnum.ColorTemperature, + "colorTemperatureSupport" to OptionNameEnum.ColorTemperatureSupport, + "compassDirectionRef" to OptionNameEnum.CompassDirectionRef, "compositeShootingOutputInterval" to OptionNameEnum.CompositeShootingOutputInterval, + KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL_SUPPORT to OptionNameEnum.CompositeShootingOutputIntervalSupport, "compositeShootingTime" to OptionNameEnum.CompositeShootingTime, + KEY_COMPOSITE_SHOOTING_TIME_SUPPORT to OptionNameEnum.CompositeShootingTimeSupport, "continuousNumber" to OptionNameEnum.ContinuousNumber, "dateTimeZone" to OptionNameEnum.DateTimeZone, "ethernetConfig" to OptionNameEnum.EthernetConfig, "exposureCompensation" to OptionNameEnum.ExposureCompensation, "exposureDelay" to OptionNameEnum.ExposureDelay, + "exposureDelaySupport" to OptionNameEnum.ExposureDelaySupport, "exposureProgram" to OptionNameEnum.ExposureProgram, "faceDetect" to OptionNameEnum.FaceDetect, "fileFormat" to OptionNameEnum.FileFormat, "filter" to OptionNameEnum.Filter, "function" to OptionNameEnum.Function, "gain" to OptionNameEnum.Gain, - "gpsInfo" to OptionNameEnum.GpsInfo, + KEY_GPS_INFO to OptionNameEnum.GpsInfo, "imageStitching" to OptionNameEnum.ImageStitching, "isGpsOn" to OptionNameEnum.IsGpsOn, "iso" to OptionNameEnum.Iso, @@ -61,13 +104,17 @@ val optionItemNameToEnum: Map = mutableMapOf( "language" to OptionNameEnum.Language, "latestEnabledExposureDelayTime" to OptionNameEnum.LatestEnabledExposureDelayTime, "maxRecordableTime" to OptionNameEnum.MaxRecordableTime, + "microphoneNoiseReduction" to OptionNameEnum.MicrophoneNoiseReduction, + "mobileNetworkSetting" to OptionNameEnum.MobileNetworkSetting, "networkType" to OptionNameEnum.NetworkType, "offDelay" to OptionNameEnum.OffDelay, - "password" to OptionNameEnum.Password, + KEY_GPS_TAG_RECORDING_SUPPORT to OptionNameEnum.GpsTagRecordingSupport, + "offDelayUsb" to OptionNameEnum.OffDelayUsb, + KEY_PASSWORD to OptionNameEnum.Password, "powerSaving" to OptionNameEnum.PowerSaving, "preset" to OptionNameEnum.Preset, "previewFormat" to OptionNameEnum.PreviewFormat, - "proxy" to OptionNameEnum.Proxy, + KEY_PROXY to OptionNameEnum.Proxy, "remainingPictures" to OptionNameEnum.RemainingPictures, "remainingVideoSeconds" to OptionNameEnum.RemainingVideoSeconds, "remainingSpace" to OptionNameEnum.RemainingSpace, @@ -76,13 +123,20 @@ val optionItemNameToEnum: Map = mutableMapOf( "shutterVolume" to OptionNameEnum.ShutterVolume, "sleepDelay" to OptionNameEnum.SleepDelay, "timeShift" to OptionNameEnum.TimeShift, + "topBottomCorrection" to OptionNameEnum.TopBottomCorrection, + KEY_TOP_BOTTOM_CORRECTION_ROTATION to OptionNameEnum.TopBottomCorrectionRotation, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_SUPPORT to OptionNameEnum.TopBottomCorrectionRotationSupport, "totalSpace" to OptionNameEnum.TotalSpace, + "usbConnection" to OptionNameEnum.UsbConnection, "username" to OptionNameEnum.Username, "videoStitching" to OptionNameEnum.VideoStitching, "visibilityReduction" to OptionNameEnum.VisibilityReduction, "whiteBalance" to OptionNameEnum.WhiteBalance, "whiteBalanceAutoStrength" to OptionNameEnum.WhiteBalanceAutoStrength, + "wlanAntennaConfig" to OptionNameEnum.WlanAntennaConfig, "wlanFrequency" to OptionNameEnum.WlanFrequency, + "wlanFrequencySupport" to OptionNameEnum.WlanFrequencySupport, + KEY_WLAN_FREQUENCY_CL_MODE to OptionNameEnum.WlanFrequencyClMode, ) val optionNameEnumToItemName: Map = optionItemNameToEnum.entries.associate { (key, value) -> value to key } @@ -154,7 +208,7 @@ fun setCaptureBuilderParams(optionMap: ReadableMap, builder: Capture.Builder optionMap.getString("exposureProgram")?.let { builder.setExposureProgram(ExposureProgramEnum.valueOf(it)) } - optionMap.getMap("gpsInfo")?.let { + optionMap.getMap(KEY_GPS_INFO)?.let { builder.setGpsInfo(toGpsInfo(map = it)) } optionMap.getString("gpsTagRecording")?.let { @@ -211,6 +265,27 @@ fun setTimeShiftCaptureBuilderParams(optionMap: ReadableMap, builder: TimeShiftC } } +fun setTimeShiftManualCaptureBuilderParams(optionMap: ReadableMap, builder: TimeShiftManualCapture.Builder) { + val interval = if (optionMap.hasKey("_capture_interval")) optionMap.getInt("_capture_interval") else null + interval?.let { + if (it >= 0) { + builder.setCheckStatusCommandInterval(it.toLong()) + } + } + + optionMap.getMap("timeShift")?.let { timeShiftMap -> + if (timeShiftMap.hasKey("isFrontFirst")) { + builder.setIsFrontFirst(timeShiftMap.getBoolean("isFrontFirst")) + } + timeShiftMap.getString("firstInterval")?.let { + builder.setFirstInterval(TimeShiftIntervalEnum.valueOf(it)) + } + timeShiftMap.getString("secondInterval")?.let { + builder.setSecondInterval(TimeShiftIntervalEnum.valueOf(it)) + } + } +} + fun setVideoCaptureBuilderParams(optionMap: ReadableMap, builder: VideoCapture.Builder) { val interval = if (optionMap.hasKey("_capture_interval")) optionMap.getInt("_capture_interval") else null @@ -307,14 +382,16 @@ fun setContinuousCaptureBuilderParams(optionMap: ReadableMap, builder: Continuou fun toGetOptionsParam(optionNames: ReadableArray): MutableList { val optionNameList = mutableListOf() for (index in 0..(optionNames.size() - 1)) { - val option = optionNames.getString(index) - optionNameList.add(OptionNameEnum.valueOf(option)) + optionNames.getString(index)?.let { + optionNameList.add(OptionNameEnum.valueOf(it)) + } } return optionNameList } fun toResult(options: Options): WritableMap { val result = Arguments.createMap() + val jsonResult = Arguments.createMap() val valueOptions = listOf( OptionNameEnum.CaptureInterval, @@ -332,8 +409,26 @@ fun toResult(options: Options): WritableMap { OptionNameEnum.ShutterVolume, OptionNameEnum.Username ) + val supportOptions = mapOf>( + OptionNameEnum.AiAutoThumbnailSupport to AiAutoThumbnailEnum::class, + OptionNameEnum.ApertureSupport to ApertureEnum::class, + OptionNameEnum.CameraControlSourceSupport to CameraControlSourceEnum::class, + OptionNameEnum.CameraPowerSupport to CameraPowerEnum::class, + OptionNameEnum.ExposureDelaySupport to ExposureDelayEnum::class, + OptionNameEnum.GpsTagRecordingSupport to GpsTagRecordingEnum::class, + OptionNameEnum.WlanFrequencySupport to WlanFrequencyEnum::class + ) + val intValueRangeSupportOptions = listOf( + OptionNameEnum.ColorTemperatureSupport, + OptionNameEnum.CompositeShootingOutputIntervalSupport, + OptionNameEnum.CompositeShootingTimeSupport + ) OptionNameEnum.values().forEach { name -> - if (name == OptionNameEnum.AutoBracket) { + if (name == OptionNameEnum.AccessInfo) { + options.accessInfo?.let { + result.putMap("accessInfo", toResult(accessInfo = it)) + } + } else if (name == OptionNameEnum.AutoBracket) { options.autoBracket?.let { result.putArray("autoBracket", toResult(autoBracket = it)) } @@ -353,9 +448,17 @@ fun toResult(options: Options): WritableMap { options.ethernetConfig?.let { result.putMap("ethernetConfig", toResult(ethernetConfig = it)) } + } else if (name == OptionNameEnum.CameraLockConfig) { + options.cameraLockConfig?.let { + result.putMap("cameraLockConfig", toResult(cameraLockConfig = it)) + } } else if (name == OptionNameEnum.GpsInfo) { options.gpsInfo?.let { - result.putMap("gpsInfo", toResult(gpsInfo = it)) + result.putMap(KEY_GPS_INFO, toResult(gpsInfo = it)) + } + } else if (name == OptionNameEnum.MobileNetworkSetting) { + options.mobileNetworkSetting?.let { + result.putMap("mobileNetworkSetting", toResult(mobileNetworkSetting = it)) } } else if (name == OptionNameEnum.OffDelay) { options.offDelay?.let { @@ -365,9 +468,17 @@ fun toResult(options: Options): WritableMap { result.putInt("offDelay", it.sec) } } + } else if (name == OptionNameEnum.OffDelayUsb) { + options.offDelayUsb?.let { + if (it is OffDelayUsbEnum) { + result.putString("offDelayUsb", it.name) + } else if (it is OffDelayUsbSec) { + result.putInt("offDelayUsb", it.sec) + } + } } else if (name == OptionNameEnum.Proxy) { options.proxy?.let { - result.putMap("proxy", toResult(proxy = it)) + result.putMap(KEY_PROXY, toResult(proxy = it)) } } else if (name == OptionNameEnum.SleepDelay) { options.sleepDelay?.let { @@ -381,13 +492,34 @@ fun toResult(options: Options): WritableMap { options.timeShift?.let { result.putMap("timeShift", toResult(timeShift = it)) } + } else if (name == OptionNameEnum.TopBottomCorrectionRotation) { + options.topBottomCorrectionRotation?.let { + result.putMap(KEY_TOP_BOTTOM_CORRECTION_ROTATION, toResult(rotation = it)) + } + } else if (name == OptionNameEnum.TopBottomCorrectionRotationSupport) { + options.topBottomCorrectionRotationSupport?.let { + val json = toJson(rotationSupport = it) + jsonResult.putString(KEY_TOP_BOTTOM_CORRECTION_ROTATION_SUPPORT, json) + } + } else if (name == OptionNameEnum.WlanFrequencyClMode) { + options.wlanFrequencyClMode?.let { + result.putMap(KEY_WLAN_FREQUENCY_CL_MODE, toResult(wlanFrequencyClMode = it)) + } } else if (valueOptions.contains(name)) { addOptionsValueToMap(options, name, result) + } else if (supportOptions.keys.contains(name)) { + addSupportOptionsValueToMap(options, name, result, supportOptions[name]!!) + } else if (intValueRangeSupportOptions.contains(name)) { + addValueRangeSupportOptionsValueToMap(options, name, result) } else { addOptionsEnumToMap(options, name, result) } } - return result + + val response = Arguments.createMap() + response.putMap("options", result) + response.putMap("json", jsonResult) + return response } fun > addOptionsEnumToMap(options: Options, name: OptionNameEnum, objects: WritableMap) { @@ -398,6 +530,53 @@ fun > addOptionsEnumToMap(options: Options, name: OptionNameEnum, ob } } +fun addSupportOptionsValueToMap( + options: Options, + name: OptionNameEnum, + objects: WritableMap, + valueClazz: KClass<*> +) { + val key = optionNameEnumToItemName[name] ?: return + options.getValue>(name)?.let { list -> + val array = Arguments.createArray() + for (item in list) { + when { + // for enum value + java.lang.Enum::class.java.isAssignableFrom(valueClazz.java) -> { + array.pushString(item.toString()) + } + } + } + objects.putArray(key, array) + } +} + +fun addValueRangeSupportOptionsValueToMap( + options: Options, + name: OptionNameEnum, + objects: WritableMap +) { + val key = optionNameEnumToItemName[name] ?: return + options.getValue>(name)?.let { value -> + val result = Arguments.createMap() + when (value.max) { + is Int -> { + result.putInt(KEY_MAX, value.max as Int) + result.putInt(KEY_MIN, value.min as Int) + result.putInt(KEY_STEP_SIZE, value.stepSize as Int) + } + + is Double -> { + result.putDouble(KEY_MAX, value.max as Double) + result.putDouble(KEY_MIN, value.min as Double) + result.putDouble(KEY_STEP_SIZE, value.stepSize as Double) + } + } + objects.putMap(key, result) + } +} + + fun addOptionsValueToMap(options: Options, name: OptionNameEnum, objects: WritableMap) { val key = optionNameEnumToItemName[name] ?: return options.getValue(name)?.let { value -> @@ -421,6 +600,45 @@ fun addOptionsValueToMap(options: Options, name: OptionNameEnum, objects: Wr } } +fun toResult(accessInfo: AccessInfo): WritableMap { + val result = Arguments.createMap() + result.putString(KEY_SSID, accessInfo.ssid) + result.putString(KEY_IP_ADDRESS, accessInfo.ipAddress) + result.putString(KEY_SUBNET_MASK, accessInfo.subnetMask) + result.putString(KEY_DEFAULT_GATEWAY, accessInfo.defaultGateway) + accessInfo.dns1?.let { + result.putString(KEY_DNS1, it) + } + accessInfo.dns2?.let { + result.putString(KEY_DNS2, it) + } + result.putString("proxyURL", accessInfo.proxyURL) + result.putString("frequency", accessInfo.frequency.name) + result.putInt("wlanSignalStrength", accessInfo.wlanSignalStrength) + result.putInt("wlanSignalLevel", accessInfo.wlanSignalLevel) + result.putInt("lteSignalStrength", accessInfo.lteSignalStrength) + result.putInt("lteSignalLevel", accessInfo.lteSignalLevel) + + accessInfo.dhcpLeaseAddress?.let { list -> + val array = Arguments.createArray() + list.forEach { + array.pushMap(toResult(dhcpLeaseAddress = it)) + } + if (array.size() > 0) { + result.putArray(KEY_DHCP_LEASE_ADDRESS, array) + } + } + return result +} + +fun toResult(dhcpLeaseAddress: DhcpLeaseAddress): WritableMap { + val result = Arguments.createMap() + result.putString(KEY_IP_ADDRESS, dhcpLeaseAddress.ipAddress) + result.putString(KEY_MAC_ADDRESS, dhcpLeaseAddress.macAddress) + result.putString(KEY_HOST_NAME, dhcpLeaseAddress.hostName) + return result +} + fun toResult(autoBracket: BracketSettingList): WritableArray { val resultList = Arguments.createArray() @@ -476,6 +694,29 @@ fun toResult(burstOption: BurstOption): WritableMap { return result } +fun toResult(cameraLockConfig: CameraLockConfig): WritableMap { + val result = Arguments.createMap() + cameraLockConfig.isPowerKeyLocked?.let { isPowerKeyLocked -> + result.putBoolean("isPowerKeyLocked", isPowerKeyLocked) + } + cameraLockConfig.isShutterKeyLocked?.let { isShutterKeyLocked -> + result.putBoolean("isShutterKeyLocked", isShutterKeyLocked) + } + cameraLockConfig.isModeKeyLocked?.let { isModeKeyLocked -> + result.putBoolean("isModeKeyLocked", isModeKeyLocked) + } + cameraLockConfig.isWlanKeyLocked?.let { isWlanKeyLocked -> + result.putBoolean("isWlanKeyLocked", isWlanKeyLocked) + } + cameraLockConfig.isFnKeyLocked?.let { isFnKeyLocked -> + result.putBoolean("isFnKeyLocked", isFnKeyLocked) + } + cameraLockConfig.isPanelLocked?.let { isPanelLocked -> + result.putBoolean("isPanelLocked", isPanelLocked) + } + return result +} + fun toResult(fileInfo: FileInfo): ReadableMap { val result = Arguments.createMap() result.putString("name", fileInfo.name) @@ -544,16 +785,22 @@ fun toResult(ethernetConfig: EthernetConfig): WritableMap { val result = Arguments.createMap() result.putBoolean("usingDhcp", ethernetConfig.usingDhcp) ethernetConfig.ipAddress?.let { ipAddress -> - result.putString("ipAddress", ipAddress) + result.putString(KEY_IP_ADDRESS, ipAddress) } ethernetConfig.subnetMask?.let { subnetMask -> - result.putString("subnetMask", subnetMask) + result.putString(KEY_SUBNET_MASK, subnetMask) } ethernetConfig.defaultGateway?.let { defaultGateway -> - result.putString("defaultGateway", defaultGateway) + result.putString(KEY_DEFAULT_GATEWAY, defaultGateway) + } + ethernetConfig.dns1?.let { dns1 -> + result.putString(KEY_DNS1, dns1) + } + ethernetConfig.dns2?.let { dns2 -> + result.putString(KEY_DNS2, dns2) } ethernetConfig.proxy?.let { proxy -> - result.putMap("proxy", toResult(proxy = proxy)) + result.putMap(KEY_PROXY, toResult(proxy = proxy)) } return result } @@ -588,7 +835,7 @@ fun toResult(proxy: Proxy): WritableMap { result.putString("userid", userid) } proxy.password?.let { password -> - result.putString("password", password) + result.putString(KEY_PASSWORD, password) } return result } @@ -607,6 +854,47 @@ fun toResult(timeShift: TimeShiftSetting): WritableMap { return result } +fun toResult(rotation: TopBottomCorrectionRotation): WritableMap { + val result = Arguments.createMap() + rotation.pitch?.let { value -> + result.putDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH, value.toDouble()) + } + rotation.roll?.let { value -> + result.putDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL, value.toDouble()) + } + rotation.yaw?.let { value -> + result.putDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW, value.toDouble()) + } + return result +} + +/** + * Avoided floating-point errors by converting to JSON string in the bridge, + * then to object in TypeScript. + */ +fun toJson(rotationSupport: TopBottomCorrectionRotationSupport): String { + val convertJson = """ + { + "pitch": { + "max": ${rotationSupport.pitch.max}, + "min": ${rotationSupport.pitch.min}, + "stepSize": ${rotationSupport.pitch.stepSize} + }, + "roll": { + "max": ${rotationSupport.roll.max}, + "min": ${rotationSupport.roll.min}, + "stepSize": ${rotationSupport.roll.stepSize} + }, + "yaw": { + "max": ${rotationSupport.yaw.max}, + "min": ${rotationSupport.yaw.min}, + "stepSize": ${rotationSupport.yaw.stepSize} + } + } + """.trimIndent() + return convertJson +} + fun toResult(state: ThetaState): WritableMap { val result = Arguments.createMap() state.fingerprint?.let { @@ -687,7 +975,7 @@ fun toResult(state: ThetaState): WritableMap { fun toResult(cameraEvent: CameraEvent): WritableMap { val result = Arguments.createMap() cameraEvent.options?.let { - result.putMap("options", toResult(it)) + result.putMap("options", toResult(options = it).getMap("options")) } cameraEvent.state?.let { result.putMap("state", toResult(it)) @@ -695,6 +983,25 @@ fun toResult(cameraEvent: CameraEvent): WritableMap { return result } +fun toResult(wlanFrequencyClMode: WlanFrequencyClMode): WritableMap { + val result = Arguments.createMap() + result.putBoolean(KEY_WLAN_FREQUENCY_CL_MODE_2_4, wlanFrequencyClMode.enable2_4) + result.putBoolean(KEY_WLAN_FREQUENCY_CL_MODE_5_2, wlanFrequencyClMode.enable5_2) + result.putBoolean(KEY_WLAN_FREQUENCY_CL_MODE_5_8, wlanFrequencyClMode.enable5_8) + return result +} + +fun toResult(mobileNetworkSetting: MobileNetworkSetting): WritableMap { + val result = Arguments.createMap() + mobileNetworkSetting.roaming?.name?.let { name -> + result.putString("roaming", name) + } + mobileNetworkSetting.plan?.name?.let { name -> + result.putString("plan", name) + } + return result +} + fun toSetOptionsParam(optionsMap: ReadableMap): Options { val result = Options() val iterator = optionsMap.keySetIterator() @@ -742,6 +1049,10 @@ fun setOptionValue(options: Options, name: OptionNameEnum, optionsMap: ReadableM options.setValue(name, optionsMap.getDouble(key).toLong()) } else if (boolOptions.contains(name)) { options.setValue(name, optionsMap.getBoolean(key)) + } else if (name == OptionNameEnum.AccessInfo) { + optionsMap.getMap(key)?.let { + options.setValue(name, toAccessInfo(map = it)) + } } else if (name == OptionNameEnum.AutoBracket) { optionsMap.getArray(key)?.let { options.setValue(name, toAutoBracket(list = it)) @@ -759,6 +1070,10 @@ fun setOptionValue(options: Options, name: OptionNameEnum, optionsMap: ReadableM optionsMap.getMap(key)?.let { options.setValue(name, toBurstOption(map = it)) } + } else if (name == OptionNameEnum.CameraLockConfig) { + optionsMap.getMap(key)?.let { + options.setValue(name, toCameraLockConfig(map = it) as Any) + } } else if (name == OptionNameEnum.EthernetConfig) { optionsMap.getMap(key)?.let { options.setValue(name, toEthernetConfig(map = it) as Any) @@ -767,10 +1082,18 @@ fun setOptionValue(options: Options, name: OptionNameEnum, optionsMap: ReadableM optionsMap.getMap(key)?.let { options.setValue(name, toGpsInfo(map = it)) } + } else if (name == OptionNameEnum.MobileNetworkSetting) { + optionsMap.getMap(key)?.let { + options.setValue(name, toMobileNetworkSetting(map = it)) + } } else if (name == OptionNameEnum.OffDelay) { toOffDelay(optionsMap)?.let { options.setValue(name, it) } + } else if (name == OptionNameEnum.OffDelayUsb) { + toOffDelayUsb(optionsMap)?.let { + options.setValue(name, it) + } } else if (name == OptionNameEnum.Proxy) { optionsMap.getMap(key)?.let { options.setValue(name, toProxy(map = it) as Any) @@ -783,6 +1106,14 @@ fun setOptionValue(options: Options, name: OptionNameEnum, optionsMap: ReadableM optionsMap.getMap(key)?.let { options.setValue(name, toTimeShift(map = it)) } + } else if (name == OptionNameEnum.TopBottomCorrectionRotation) { + optionsMap.getMap(key)?.let { + options.setValue(name, toTopBottomCorrectionRotation(map = it)) + } + } else if (name == OptionNameEnum.WlanFrequencyClMode) { + optionsMap.getMap(key)?.let { + options.setValue(name, toWlanFrequencyClMode(it)) + } } else { (optionsMap.getString(key))?.let { value -> getOptionValueEnum(name, value)?.let { @@ -800,9 +1131,11 @@ fun getOptionValueEnum(name: OptionNameEnum, valueName: String): Any? { OptionNameEnum.BluetoothRole -> BluetoothRoleEnum.values().find { it.name == valueName } OptionNameEnum.BurstMode -> BurstModeEnum.values().find { it.name == valueName } OptionNameEnum.CameraControlSource -> CameraControlSourceEnum.values().find { it.name == valueName } + OptionNameEnum.CameraLock -> CameraLockEnum.values().find { it.name == valueName } OptionNameEnum.CameraMode -> CameraModeEnum.values().find { it.name == valueName } OptionNameEnum.CameraPower -> CameraPowerEnum.values().find { it.name == valueName } OptionNameEnum.CaptureMode -> CaptureModeEnum.values().find { it.name == valueName } + OptionNameEnum.CompassDirectionRef -> CompassDirectionRefEnum.values().find { it.name == valueName } OptionNameEnum.ContinuousNumber -> ContinuousNumberEnum.values().find { it.name == valueName } OptionNameEnum.ExposureCompensation -> ExposureCompensationEnum.values().find { it.name == valueName } OptionNameEnum.ExposureDelay -> ExposureDelayEnum.values().find { it.name == valueName } @@ -818,18 +1151,23 @@ fun getOptionValueEnum(name: OptionNameEnum, valueName: String): Any? { OptionNameEnum.Language -> LanguageEnum.values().find { it.name == valueName } OptionNameEnum.LatestEnabledExposureDelayTime -> ExposureDelayEnum.values().find { it.name == valueName } OptionNameEnum.MaxRecordableTime -> MaxRecordableTimeEnum.values().find { it.name == valueName } + OptionNameEnum.MicrophoneNoiseReduction -> MicrophoneNoiseReductionEnum.values().find { it.name == valueName } OptionNameEnum.NetworkType -> NetworkTypeEnum.values().find { it.name == valueName } OptionNameEnum.OffDelay -> OffDelayEnum.values().find { it.name == valueName } + OptionNameEnum.OffDelayUsb -> OffDelayUsbEnum.entries.find { it.name == valueName } OptionNameEnum.PowerSaving -> PowerSavingEnum.values().find { it.name == valueName } OptionNameEnum.Preset -> PresetEnum.values().find { it.name == valueName } OptionNameEnum.PreviewFormat -> PreviewFormatEnum.values().find { it.name == valueName } OptionNameEnum.ShootingMethod -> ShootingMethodEnum.values().find { it.name == valueName } OptionNameEnum.ShutterSpeed -> ShutterSpeedEnum.values().find { it.name == valueName } OptionNameEnum.SleepDelay -> SleepDelayEnum.values().find { it.name == valueName } + OptionNameEnum.TopBottomCorrection -> TopBottomCorrectionOptionEnum.values().find { it.name == valueName } + OptionNameEnum.UsbConnection -> UsbConnectionEnum.entries.find { it.name == valueName } OptionNameEnum.VideoStitching -> VideoStitchingEnum.values().find { it.name == valueName } OptionNameEnum.VisibilityReduction -> VisibilityReductionEnum.values().find { it.name == valueName } OptionNameEnum.WhiteBalance -> WhiteBalanceEnum.values().find { it.name == valueName } OptionNameEnum.WhiteBalanceAutoStrength -> WhiteBalanceAutoStrengthEnum.values().find { it.name == valueName } + OptionNameEnum.WlanAntennaConfig -> WlanAntennaConfigEnum.values().find { it.name == valueName } OptionNameEnum.WlanFrequency -> WlanFrequencyEnum.values().find { it.name == valueName } else -> null } @@ -839,16 +1177,22 @@ fun toListAccessPointsResult(accessPointList: List): WritableArray val result = Arguments.createArray() accessPointList.forEach { val apinfo = Arguments.createMap() - apinfo.putString("ssid", it.ssid) - apinfo.putBoolean("ssidStealth", it.ssidStealth) - apinfo.putString("authMode", it.authMode.toString()) - apinfo.putInt("connectionPriority", it.connectionPriority) + apinfo.putString(KEY_SSID, it.ssid) + apinfo.putBoolean(KEY_SSID_STEALTH, it.ssidStealth) + apinfo.putString(KEY_AUTH_MODE, it.authMode.toString()) + apinfo.putInt(KEY_CONNECTION_PRIORITY, it.connectionPriority) apinfo.putBoolean("usingDhcp", it.usingDhcp) - apinfo.putString("ipAddress", it.ipAddress) - apinfo.putString("subnetMask", it.subnetMask) - apinfo.putString("defaultGateway", it.defaultGateway) + apinfo.putString(KEY_IP_ADDRESS, it.ipAddress) + apinfo.putString(KEY_SUBNET_MASK, it.subnetMask) + apinfo.putString(KEY_DEFAULT_GATEWAY, it.defaultGateway) + it.dns1?.let { dns1 -> + apinfo.putString(KEY_DNS1, dns1) + } + it.dns2?.let { dns2 -> + apinfo.putString(KEY_DNS2, dns2) + } it.proxy?.let { proxy -> - apinfo.putMap("proxy", toResult(proxy = proxy)) + apinfo.putMap(KEY_PROXY, toResult(proxy = proxy)) } result.pushMap(apinfo) @@ -856,6 +1200,46 @@ fun toListAccessPointsResult(accessPointList: List): WritableArray return result } +fun toAccessInfo(map: ReadableMap): AccessInfo { + val dhcpLeaseAddress = map.getArray(KEY_DHCP_LEASE_ADDRESS)?.let { array -> + val list = mutableListOf() + for (i in 0 until array.size()) { + array.getMap(i)?.let { map -> + toDhcpLeaseAddress(map)?.let { + list.add(it) + } + } + } + if (list.size > 0) list else null + } + + return AccessInfo( + ssid = map.getString(KEY_SSID) ?: "", + ipAddress = map.getString(KEY_IP_ADDRESS) ?: "", + subnetMask = map.getString(KEY_SUBNET_MASK) ?: "", + defaultGateway = map.getString(KEY_DEFAULT_GATEWAY) ?: "", + dns1 = map.getString(KEY_DNS1) ?: "", + dns2 = map.getString(KEY_DNS2) ?: "", + proxyURL = map.getString("proxyURL") ?: "", + frequency = map.getString("frequency")?.let { WlanFrequencyAccessInfoEnum.valueOf(it) } ?: WlanFrequencyAccessInfoEnum.INITIAL_VALUE, + wlanSignalStrength = map.getInt("wlanSignalStrength"), + wlanSignalLevel = map.getInt("wlanSignalLevel"), + lteSignalStrength = map.getInt("lteSignalStrength"), + lteSignalLevel = map.getInt("lteSignalLevel"), + dhcpLeaseAddress = dhcpLeaseAddress + ) +} + +fun toDhcpLeaseAddress(value: ReadableMap): DhcpLeaseAddress? { + val ipAddress = value.getString(KEY_IP_ADDRESS) + val macAddress = value.getString(KEY_MAC_ADDRESS) + val hostName = value.getString(KEY_HOST_NAME) + if (ipAddress == null || macAddress == null || hostName == null) { + return null + } + return DhcpLeaseAddress(ipAddress, macAddress, hostName) +} + fun toAutoBracket(list: ReadableArray): BracketSettingList { val autoBracket = BracketSettingList() for (i in 0 until list.size()) { @@ -876,14 +1260,29 @@ fun toAutoBracket(list: ReadableArray): BracketSettingList { return autoBracket } +fun toCameraLockConfig(map: ReadableMap?): CameraLockConfig? { + return map?.let { + CameraLockConfig( + isPowerKeyLocked = if (it.hasKey("isPowerKeyLocked")) it.getBoolean("isPowerKeyLocked") else null, + isShutterKeyLocked = if (it.hasKey("isShutterKeyLocked")) it.getBoolean("isShutterKeyLocked") else null, + isModeKeyLocked = if (it.hasKey("isModeKeyLocked")) it.getBoolean("isModeKeyLocked") else null, + isWlanKeyLocked = if (it.hasKey("isWlanKeyLocked")) it.getBoolean("isWlanKeyLocked") else null, + isFnKeyLocked = if (it.hasKey("isFnKeyLocked")) it.getBoolean("isFnKeyLocked") else null, + isPanelLocked = if (it.hasKey("isPanelLocked")) it.getBoolean("isPanelLocked") else null, + ) + } +} + fun toEthernetConfig(map: ReadableMap?): EthernetConfig? { return map?.let { EthernetConfig( usingDhcp = if (it.hasKey("usingDhcp")) it.getBoolean("usingDhcp") else true, - ipAddress = if (it.hasKey("ipAddress")) it.getString("ipAddress") else null, - subnetMask = if (it.hasKey("subnetMask")) it.getString("subnetMask") else null, - defaultGateway = if (it.hasKey("defaultGateway")) it.getString("defaultGateway") else null, - proxy = if (it.hasKey("proxy")) toProxy(map = it.getMap("proxy")) else null + ipAddress = if (it.hasKey(KEY_IP_ADDRESS)) it.getString(KEY_IP_ADDRESS) else null, + subnetMask = if (it.hasKey(KEY_SUBNET_MASK)) it.getString(KEY_SUBNET_MASK) else null, + defaultGateway = if (it.hasKey(KEY_DEFAULT_GATEWAY)) it.getString(KEY_DEFAULT_GATEWAY) else null, + dns1 = if (it.hasKey(KEY_DNS1)) it.getString(KEY_DNS1) else null, + dns2 = if (it.hasKey(KEY_DNS2)) it.getString(KEY_DNS2) else null, + proxy = if (it.hasKey(KEY_PROXY)) toProxy(map = it.getMap(KEY_PROXY)) else null ) } } @@ -892,10 +1291,10 @@ fun toProxy(map: ReadableMap?): Proxy? { return map?.let { Proxy( use = it.getBoolean("use"), - url = it.getString("url"), + url = if (it.hasKey("url")) it.getString("url") else null, port = if (it.hasKey("port")) it.getInt("port") else null, - userid = it.getString("userid"), - password = it.getString("password") + userid = if (it.hasKey("userid")) it.getString("userid") else null, + password = if (it.hasKey(KEY_PASSWORD)) it.getString(KEY_PASSWORD) else null ) } } @@ -925,6 +1324,20 @@ fun toTimeShift(map: ReadableMap): TimeShiftSetting { return timeShift } +fun toTopBottomCorrectionRotation(map: ReadableMap): TopBottomCorrectionRotation { + val pitch = map.getDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH)?.toFloat() ?: 0.0f + val roll = map.getDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL)?.toFloat() ?: 0.0f + val yaw = map.getDouble(KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW)?.toFloat() ?: 0.0f + return TopBottomCorrectionRotation(pitch = pitch, roll = roll, yaw = yaw) +} + +fun toMobileNetworkSetting(map: ReadableMap): MobileNetworkSetting { + return MobileNetworkSetting( + roaming = map.getString("roaming")?.let { RoamingEnum.valueOf(it) }, + plan = map.getString("plan")?.let { PlanEnum.valueOf(it) } + ) +} + fun configToTheta(objects: ReadableMap): Config { val config = Config() config.dateTime = objects.getString("dateTime") @@ -958,8 +1371,8 @@ fun digestAuthToTheta(objects: ReadableMap): DigestAuth? { val username = objects.getString("username") ?: run { return null } - val password = if (objects.hasKey("password")) { - objects.getString("password") + val password = if (objects.hasKey(KEY_PASSWORD)) { + objects.getString(KEY_PASSWORD) } else { null } @@ -994,6 +1407,26 @@ fun toOffDelay(objects: ReadableMap): OffDelay? { return null } +fun toOffDelayUsb(objects: ReadableMap): OffDelayUsb? { + if (!objects.hasKey("offDelayUsb")) { + return null + } + when (objects.getType("offDelayUsb")) { + ReadableType.String -> { + (objects.getString("offDelayUsb"))?.let { value -> + return getOptionValueEnum(OptionNameEnum.OffDelayUsb, value) as? OffDelayUsbEnum + } + } + + ReadableType.Number -> { + return OffDelayUsbSec(objects.getInt("offDelayUsb")) + } + + else -> {} + } + return null +} + fun toSleepDelay(objects: ReadableMap): SleepDelay? { if (!objects.hasKey("sleepDelay")) { return null @@ -1013,3 +1446,70 @@ fun toSleepDelay(objects: ReadableMap): SleepDelay? { } return null } + +data class SetAccessPointParams( + val ssid: String, + val ssidStealth: Boolean?, + val authMode: AuthModeEnum, + val password: String?, + val connectionPriority: Int?, + val dns1: String?, + val dns2: String?, + val proxy: Proxy?, +) + +fun toSetAccessPointParams(objects: ReadableMap): SetAccessPointParams { + val ssid = objects.getString(KEY_SSID) ?: throw IllegalArgumentException(KEY_SSID) + val ssidStealth = if (objects.hasKey(KEY_SSID_STEALTH)) { + objects.getBoolean(KEY_SSID_STEALTH) + } else { + null + } + val authMode = objects.getString(KEY_AUTH_MODE)?.let { AuthModeEnum.valueOf(it) } + ?: throw IllegalArgumentException(KEY_AUTH_MODE) + val password = objects.getString(KEY_PASSWORD) + val connectionPriority = if (objects.hasKey(KEY_CONNECTION_PRIORITY)) { + objects.getInt(KEY_CONNECTION_PRIORITY) + } else { + null + } + val dns1 = objects.getString(KEY_DNS1) + val dns2 = objects.getString(KEY_DNS2) + val proxy = if (objects.hasKey(KEY_PROXY)) toProxy(objects.getMap(KEY_PROXY)) else null + + return SetAccessPointParams( + ssid = ssid, + ssidStealth = ssidStealth, + authMode = authMode, + password = password, + connectionPriority = connectionPriority, + dns1 = dns1, + dns2 = dns2, + proxy = proxy, + ) +} + +data class SetAccessPointStaticallyParams( + val ipAddress: String, + val subnetMask: String, + val defaultGateway: String, +) + +fun toSetAccessPointStaticallyParams(objects: ReadableMap): SetAccessPointStaticallyParams { + val ipAddress = objects.getString(KEY_IP_ADDRESS) ?: throw IllegalArgumentException(KEY_IP_ADDRESS) + val subnetMask = objects.getString(KEY_SUBNET_MASK) ?: throw IllegalArgumentException(KEY_SUBNET_MASK) + val defaultGateway = objects.getString(KEY_DEFAULT_GATEWAY) ?: throw IllegalArgumentException(KEY_DEFAULT_GATEWAY) + + return SetAccessPointStaticallyParams( + ipAddress = ipAddress, + subnetMask = subnetMask, + defaultGateway = defaultGateway, + ) +} + +fun toWlanFrequencyClMode(objects: ReadableMap): WlanFrequencyClMode { + val enable2_4 = objects.getBoolean(KEY_WLAN_FREQUENCY_CL_MODE_2_4) + val enable5_2 = objects.getBoolean(KEY_WLAN_FREQUENCY_CL_MODE_5_2) + val enable5_8 = objects.getBoolean(KEY_WLAN_FREQUENCY_CL_MODE_5_8) + return WlanFrequencyClMode(enable2_4 = enable2_4, enable5_2 = enable5_2, enable5_8 = enable5_8) +} 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 d797bd3fc5a..9281ce58b6e 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 @@ -29,6 +29,9 @@ class ThetaClientReactNativeModule( var timeShiftCaptureBuilder: TimeShiftCapture.Builder? = null var timeShiftCapture: TimeShiftCapture? = null var timeShiftCapturing: TimeShiftCapturing? = null + var timeShiftManualCaptureBuilder: TimeShiftManualCapture.Builder? = null + var timeShiftManualCapture: TimeShiftManualCapture? = null + var timeShiftManualCapturing: TimeShiftManualCapturing? = null var videoCaptureBuilder: VideoCapture.Builder? = null var videoCapture: VideoCapture? = null var videoCapturing: VideoCapturing? = null @@ -88,6 +91,21 @@ class ThetaClientReactNativeModule( override fun getConstants(): MutableMap = hashMapOf("DEFAULT_EVENT_NAME" to EVENT_NAME) + + val apiLogListener = { message: String -> + val params = Arguments.createMap() + params.putString(KEY_NOTIFY_PARAM_MESSAGE, message) + sendNotifyEvent( + toNotify(NOTIFY_API_LOG, params) + ) + } + + @ReactMethod + fun setApiLogListener(enabled: Boolean, promise: Promise) { + com.ricoh360.thetaclient.setApiLogListener(if (enabled) apiLogListener else null) + promise.resolve(null) + } + /** * initialize Theta repository with endpoint * @param endpoint endpoint to connect THETA @@ -105,6 +123,9 @@ class ThetaClientReactNativeModule( timeShiftCaptureBuilder = null timeShiftCapture = null timeShiftCapturing = null + timeShiftManualCaptureBuilder = null + timeShiftManualCapture = null + timeShiftManualCapturing = null videoCaptureBuilder = null videoCapture = null videoCapturing = null @@ -302,7 +323,9 @@ class ThetaClientReactNativeModule( } val fileList = mutableListOf() for (index in 0..(fileUrls.size() - 1)) { - fileList.add(fileUrls.getString(index)) + fileUrls.getString(index)?.let { + fileList.add(it) + } } launch { try { @@ -451,6 +474,7 @@ class ThetaClientReactNativeModule( "data:image/jpeg;base64," + packet.first.copyOfRange(0, packet.second).toBase64() ) + param.putInt("dataSize", packet.second) reactApplicationContext .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) .emit(EVENT_NAME, param) @@ -690,6 +714,127 @@ class ThetaClientReactNativeModule( timeShiftCapturing?.cancelCapture() } + /** + * getTimeShiftManualCaptureBuilder - get manual time-shift builder from repository + */ + @ReactMethod + fun getTimeShiftManualCaptureBuilder(promise: Promise) { + val theta = theta + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + timeShiftManualCaptureBuilder = theta.getTimeShiftManualCaptureBuilder() + promise.resolve(true) + } + + /** + * buildTimeShiftManualCapture - build manual time-shift + * @param options option to execute manual time-shift + * @param promise Promise for buildTimeShiftManualCapture + */ + @ReactMethod + fun buildTimeShiftManualCapture(options: ReadableMap, promise: Promise) { + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + if (timeShiftManualCaptureBuilder == null) { + promise.reject(Exception("no timeShiftManualCaptureBuilder")) + return + } + timeShiftManualCapture = null + timeShiftManualCapturing = null + launch { + try { + timeShiftManualCaptureBuilder?.let { + setCaptureBuilderParams(optionMap = options, builder = it) + setTimeShiftManualCaptureBuilderParams(optionMap = options, builder = it) + timeShiftManualCapture = it.build() + } + promise.resolve(true) + } catch (t: Throwable) { + promise.reject(t) + } finally { + timeShiftManualCaptureBuilder = null + } + } + } + + /** + * startTimeShiftManualCapture - start manual time-shift + * @param promise promise for startTimeShiftManualCapture + */ + @ReactMethod + fun startTimeShiftManualCapture(promise: Promise) { + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + if (timeShiftManualCapture == null) { + promise.reject(Exception("no timeShiftManualCapture")) + return + } + class StartCaptureCallback : TimeShiftManualCapture.StartCaptureCallback { + override fun onCaptureCompleted(fileUrl: String?) { + promise.resolve(fileUrl) + timeShiftManualCapturing = null + } + + override fun onProgress(completion: Float) { + sendNotifyEvent( + toNotify(NOTIFY_TIMESHIFT_MANUAL_PROGRESS, toCaptureProgressNotifyParam(value = completion)) + ) + } + + override fun onCapturing(status: CapturingStatusEnum) { + super.onCapturing(status) + sendNotifyEvent( + toNotify(NOTIFY_TIMESHIFT_MANUAL_CAPTURING, toCapturingNotifyParam(status = status)) + ) + } + + override fun onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + promise.reject(exception) + timeShiftManualCapturing = null + } + + override fun onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + sendNotifyEvent( + toNotify( + NOTIFY_TIMESHIFT_MANUAL_STOP_ERROR, + toMessageNotifyParam(exception.message ?: exception.toString()) + ) + ) + } + } + timeShiftManualCapturing = timeShiftManualCapture?.startCapture(StartCaptureCallback()) + } + + /** + * startTimeShiftManualSecondCapture - start manual time-shift second capture + * @param promise promise for startTimeShiftManualSecondCapture + */ + @ReactMethod + fun startTimeShiftManualSecondCapture(promise: Promise) { + if (theta == null) { + throw Exception(messageNotInit) + } + timeShiftManualCapturing?.startSecondCapture() + } + + /** + * cancelTimeShiftManualCapture - stop manual time-shift + * @param promise promise for cancelTimeShiftManualCapture + */ + @ReactMethod + fun cancelTimeShiftManualCapture(promise: Promise) { + if (theta == null) { + throw Exception(messageNotInit) + } + timeShiftManualCapturing?.cancelCapture() + } + /** * getVideoCaptureBuilder - get video capture builder * @param promise promise to set result @@ -1548,6 +1693,27 @@ class ThetaClientReactNativeModule( } } + /** + * reboot - reboot THETA via repository + * @param promise promise to set result + */ + @ReactMethod + fun reboot(promise: Promise) { + val theta = theta + if (theta == null) { + promise.reject(Exception(messageNotInit)) + return + } + launch { + try { + theta.reboot() + promise.resolve(true) + } catch (t: Throwable) { + promise.reject(t) + } + } + } + /** * reset - reset THETA via repository * @param promise promise to set result @@ -1714,22 +1880,12 @@ class ThetaClientReactNativeModule( /** * 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 params parameters of setAccessPointDynamically * @param promise promise to set result */ @ReactMethod fun setAccessPointDynamically( - ssid: String, - ssidStealth: Boolean, - authMode: String, - password: String, - connectionPriority: Int, - proxy: ReadableMap?, + params: ReadableMap, promise: Promise ) { val theta = theta @@ -1739,13 +1895,14 @@ class ThetaClientReactNativeModule( } launch { try { + val accessPointParams = toSetAccessPointParams(params) theta.setAccessPointDynamically( - ssid, - ssidStealth, - ThetaRepository.AuthModeEnum.valueOf(authMode), - password, - connectionPriority, - toProxy(map = proxy) + accessPointParams.ssid, + accessPointParams.ssidStealth, + accessPointParams.authMode, + accessPointParams.password, + accessPointParams.connectionPriority, + accessPointParams.proxy, ) promise.resolve(true) } catch (t: Throwable) { @@ -1756,28 +1913,12 @@ class ThetaClientReactNativeModule( /** * 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 params parameters of setAccessPointStatically * @param promise promise to set result - * @param proxy Proxy information to be used for the access point. */ @ReactMethod fun setAccessPointStatically( - ssid: String, - ssidStealth: Boolean, - authMode: String, - password: String, - connectionPriority: Int, - ipAddress: String, - subnetMask: String, - defaultGateway: String, - proxy: ReadableMap?, + params: ReadableMap, promise: Promise ) { val theta = theta @@ -1787,16 +1928,20 @@ class ThetaClientReactNativeModule( } launch { try { + val accessPointParams = toSetAccessPointParams(params) + val staticallyParams = toSetAccessPointStaticallyParams(params) theta.setAccessPointStatically( - ssid, - ssidStealth, - ThetaRepository.AuthModeEnum.valueOf(authMode), - password, - connectionPriority, - ipAddress, - subnetMask, - defaultGateway, - toProxy(map = proxy) + accessPointParams.ssid, + accessPointParams.ssidStealth, + accessPointParams.authMode, + accessPointParams.password, + accessPointParams.connectionPriority, + staticallyParams.ipAddress, + staticallyParams.subnetMask, + staticallyParams.defaultGateway, + accessPointParams.dns1, + accessPointParams.dns2, + accessPointParams.proxy ) promise.resolve(true) } catch (t: Throwable) { @@ -1889,8 +2034,9 @@ class ThetaClientReactNativeModule( try { val optionNameList = mutableListOf() for (index in 0..(optionNames.size() - 1)) { - val option = optionNames.getString(index) - optionNameList.add(ThetaRepository.OptionNameEnum.valueOf(option)) + optionNames.getString(index)?.let { + optionNameList.add(ThetaRepository.OptionNameEnum.valueOf(it)) + } } val options = theta.getMySetting(optionNameList) promise.resolve(toResult(options = options)) @@ -2109,8 +2255,9 @@ class ThetaClientReactNativeModule( try { val pluginList = mutableListOf() for (index in 0..(plugins.size() - 1)) { - val plugin = plugins.getString(index) - pluginList.add(plugin) + plugins.getString(index)?.let { + pluginList.add(it) + } } theta.setPluginOrders(pluginList) promise.resolve(true) @@ -2195,6 +2342,9 @@ class ThetaClientReactNativeModule( const val NOTIFY_TIMESHIFT_PROGRESS = "TIME-SHIFT-PROGRESS" const val NOTIFY_TIMESHIFT_STOP_ERROR = "TIME-SHIFT-STOP-ERROR" const val NOTIFY_TIMESHIFT_CAPTURING = "TIME-SHIFT-CAPTURING" + const val NOTIFY_TIMESHIFT_MANUAL_PROGRESS = "TIME-SHIFT-MANUAL-PROGRESS" + const val NOTIFY_TIMESHIFT_MANUAL_STOP_ERROR = "TIME-SHIFT-MANUAL-STOP-ERROR" + const val NOTIFY_TIMESHIFT_MANUAL_CAPTURING = "TIME-SHIFT-MANUAL-CAPTURING" const val NOTIFY_VIDEO_CAPTURE_STOP_ERROR = "VIDEO-CAPTURE-STOP-ERROR" const val NOTIFY_VIDEO_CAPTURE_CAPTURING = "VIDEO-CAPTURE-CAPTURING" const val NOTIFY_VIDEO_CAPTURE_STARTED = "VIDEO-CAPTURE-STARTED" @@ -2217,5 +2367,6 @@ class ThetaClientReactNativeModule( const val NOTIFY_EVENT_WEBSOCKET_EVENT = "EVENT-WEBSOCKET-EVENT" const val NOTIFY_EVENT_WEBSOCKET_CLOSE = "EVENT-WEBSOCKET-CLOSE" const val NOTIFY_CONVERT_VIDEO_FORMATS_PROGRESS = "CONVERT-VIDEO-FORMATS-PROGRESS" + const val NOTIFY_API_LOG = "API-LOG" } } diff --git a/react-native/ios/ConvertUtil.swift b/react-native/ios/ConvertUtil.swift index 4d64cbf757d..eda5f52f48e 100644 --- a/react-native/ios/ConvertUtil.swift +++ b/react-native/ios/ConvertUtil.swift @@ -20,7 +20,10 @@ let KEY_REQUEST_TIMEOUT = "requestTimeout" let KEY_SOCKET_TIMEOUT = "socketTimeout" let KEY_THETA_MODEL = "thetaModel" let KEY_TIMESHIFT = "timeShift" +let KEY_AI_AUTO_THUMBNAIL_SUPPORT = "aiAutoThumbnailSupport" let KEY_APERTURE = "aperture" +let KEY_CAMERA_CONTROL_SOURCE_SUPPORT = "cameraControlSourceSupport" +let KEY_CAMERA_POWER_SUPPORT = "cameraPowerSupport" let KEY_CAPTURE_INTERVAL = "captureInterval" let KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL = "compositeShootingOutputInterval" let KEY_COLOR_TEMPERATURE = "colorTemperature" @@ -68,10 +71,41 @@ let KEY_STATE_BOARD_TEMP = "boardTemp" let KEY_STATE_BATTERY_TEMP = "batteryTemp" let KEY_PROXY = "proxy" let KEY_IP_ADDRESS = "ipAddress" +let KEY_MAC_ADDRESS = "macAddress" +let KEY_HOST_NAME = "hostName" let KEY_SUBNET_MASK = "subnetMask" let KEY_DEFAULT_GATEWAY = "defaultGateway" +let KEY_DNS1 = "dns1" +let KEY_DNS2 = "dns2" let KEY_OPTIONS = "options" let KEY_STATE = "state" +let KEY_SSID = "ssid" +let KEY_SSID_STEALTH = "ssidStealth" +let KEY_CONNECTION_PRIORITY = "connectionPriority" +let KEY_AUTH_MODE = "authMode" +let KEY_PASSWORD = "password" +let KEY_COLOR_TEMPERATURE_SUPPORT = "colorTemperatureSupport" +let KEY_WLAN_FREQUENCY_CL_MODE = "wlanFrequencyClMode" +let KEY_WLAN_FREQUENCY_CL_MODE_2_4 = "enable2_4" +let KEY_WLAN_FREQUENCY_CL_MODE_5_2 = "enable5_2" +let KEY_WLAN_FREQUENCY_CL_MODE_5_8 = "enable5_8" +let KEY_ROAMING = "roaming" +let KEY_PLAN = "plan" +let KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED = "isPowerKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED = "isShutterKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED = "isModeKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED = "isWlanKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED = "isFnKeyLocked" +let KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED = "isPanelLocked" +let KEY_DHCP_LEASE_ADDRESS = "dhcpLeaseAddress" +let KEY_TOP_BOTTOM_CORRECTION_ROTATION_SUPPORT = "topBottomCorrectionRotationSupport" +let KEY_MAX = "max" +let KEY_MIN = "min" +let KEY_STEP_SIZE = "stepSize" +let KEY_GPS_TAG_RECORDING_SUPPORT = "gpsTagRecordingSupport" +let KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL_SUPPORT = "compositeShootingOutputIntervalSupport" +let KEY_COMPOSITE_SHOOTING_TIME_SUPPORT = "compositeShootingTimeSupport" +let KEY_APERTURE_SUPPORT = "apertureSupport" public class ConvertUtil: NSObject {} @@ -79,67 +113,97 @@ public class ConvertUtil: NSObject {} let optionItemNameToEnum = [ // TODO: Add items when adding options + "accessInfo": ThetaRepository.OptionNameEnum.accessinfo, "aiAutoThumbnail": ThetaRepository.OptionNameEnum.aiautothumbnail, - "aperture": ThetaRepository.OptionNameEnum.aperture, - "autoBracket": ThetaRepository.OptionNameEnum.autobracket, + KEY_AI_AUTO_THUMBNAIL_SUPPORT: ThetaRepository.OptionNameEnum.aiautothumbnailsupport, + KEY_APERTURE: ThetaRepository.OptionNameEnum.aperture, + KEY_APERTURE_SUPPORT: ThetaRepository.OptionNameEnum.aperturesupport, + KEY_AUTO_BRACKET: ThetaRepository.OptionNameEnum.autobracket, "bitrate": ThetaRepository.OptionNameEnum.bitrate, "bluetoothPower": ThetaRepository.OptionNameEnum.bluetoothpower, "bluetoothRole": ThetaRepository.OptionNameEnum.bluetoothrole, - "burstMode": ThetaRepository.OptionNameEnum.burstmode, + KEY_BURST_MODE: ThetaRepository.OptionNameEnum.burstmode, "burstOption": ThetaRepository.OptionNameEnum.burstoption, "cameraControlSource": ThetaRepository.OptionNameEnum.cameracontrolsource, + KEY_CAMERA_CONTROL_SOURCE_SUPPORT: ThetaRepository.OptionNameEnum.cameracontrolsourcesupport, + "cameraLock": ThetaRepository.OptionNameEnum.cameralock, + "cameraLockConfig": ThetaRepository.OptionNameEnum.cameralockconfig, "cameraMode": ThetaRepository.OptionNameEnum.cameramode, "cameraPower": ThetaRepository.OptionNameEnum.camerapower, - "captureInterval": ThetaRepository.OptionNameEnum.captureinterval, + KEY_CAMERA_POWER_SUPPORT: ThetaRepository.OptionNameEnum.camerapowersupport, + KEY_CAPTURE_INTERVAL: ThetaRepository.OptionNameEnum.captureinterval, "captureMode": ThetaRepository.OptionNameEnum.capturemode, "captureNumber": ThetaRepository.OptionNameEnum.capturenumber, - "colorTemperature": ThetaRepository.OptionNameEnum.colortemperature, - "compositeShootingOutputInterval": ThetaRepository.OptionNameEnum - .compositeshootingoutputinterval, + KEY_COLOR_TEMPERATURE: ThetaRepository.OptionNameEnum.colortemperature, + KEY_COLOR_TEMPERATURE_SUPPORT: ThetaRepository.OptionNameEnum.colortemperaturesupport, + KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL_SUPPORT: ThetaRepository.OptionNameEnum.compositeshootingoutputintervalsupport, + "compassDirectionRef": ThetaRepository.OptionNameEnum.compassdirectionref, + KEY_COMPOSITE_SHOOTING_OUTPUT_INTERVAL: ThetaRepository.OptionNameEnum.compositeshootingoutputinterval, "compositeShootingTime": ThetaRepository.OptionNameEnum.compositeshootingtime, + KEY_COMPOSITE_SHOOTING_TIME_SUPPORT: ThetaRepository.OptionNameEnum.compositeshootingtimesupport, "continuousNumber": ThetaRepository.OptionNameEnum.continuousnumber, - "dateTimeZone": ThetaRepository.OptionNameEnum.datetimezone, + KEY_GPS_DATE_TIME_ZONE: ThetaRepository.OptionNameEnum.datetimezone, "ethernetConfig": ThetaRepository.OptionNameEnum.ethernetconfig, - "exposureCompensation": ThetaRepository.OptionNameEnum.exposurecompensation, - "exposureDelay": ThetaRepository.OptionNameEnum.exposuredelay, - "exposureProgram": ThetaRepository.OptionNameEnum.exposureprogram, + KEY_EXPOSURE_COMPENSATION: ThetaRepository.OptionNameEnum.exposurecompensation, + KEY_EXPOSURE_DELAY: ThetaRepository.OptionNameEnum.exposuredelay, + "exposureDelaySupport": ThetaRepository.OptionNameEnum.exposuredelaysupport, + KEY_EXPOSURE_PROGRAM: ThetaRepository.OptionNameEnum.exposureprogram, "faceDetect": ThetaRepository.OptionNameEnum.facedetect, - "fileFormat": ThetaRepository.OptionNameEnum.fileformat, - "filter": ThetaRepository.OptionNameEnum.filter, + KEY_FILE_FORMAT: ThetaRepository.OptionNameEnum.fileformat, + KEY_FILTER: ThetaRepository.OptionNameEnum.filter, "function": ThetaRepository.OptionNameEnum.function, "gain": ThetaRepository.OptionNameEnum.gain, - "gpsInfo": ThetaRepository.OptionNameEnum.gpsinfo, + KEY_GPS_INFO: ThetaRepository.OptionNameEnum.gpsinfo, + KEY_GPS_TAG_RECORDING_SUPPORT: ThetaRepository.OptionNameEnum.gpstagrecordingsupport, "imageStitching": ThetaRepository.OptionNameEnum.imagestitching, "isGpsOn": ThetaRepository.OptionNameEnum.isgpson, - "iso": ThetaRepository.OptionNameEnum.iso, - "isoAutoHighLimit": ThetaRepository.OptionNameEnum.isoautohighlimit, - "language": ThetaRepository.OptionNameEnum.language, + KEY_ISO: ThetaRepository.OptionNameEnum.iso, + KEY_ISO_AUTO_HIGH_LIMIT: ThetaRepository.OptionNameEnum.isoautohighlimit, + KEY_LANGUAGE: ThetaRepository.OptionNameEnum.language, "latestEnabledExposureDelayTime": ThetaRepository.OptionNameEnum.latestenabledexposuredelaytime, - "maxRecordableTime": ThetaRepository.OptionNameEnum.maxrecordabletime, + KEY_MAX_RECORDABLE_TIME: ThetaRepository.OptionNameEnum.maxrecordabletime, + "microphoneNoiseReduction": ThetaRepository.OptionNameEnum.microphonenoisereduction, + "mobileNetworkSetting": ThetaRepository.OptionNameEnum.mobilenetworksetting, "networkType": ThetaRepository.OptionNameEnum.networktype, - "offDelay": ThetaRepository.OptionNameEnum.offdelay, - "password": ThetaRepository.OptionNameEnum.password, + KEY_OFF_DELAY: ThetaRepository.OptionNameEnum.offdelay, + "offDelayUsb": ThetaRepository.OptionNameEnum.offdelayusb, + KEY_PROXY_PASSWORD: ThetaRepository.OptionNameEnum.password, "powerSaving": ThetaRepository.OptionNameEnum.powersaving, - "preset": ThetaRepository.OptionNameEnum.preset, + KEY_PRESET: ThetaRepository.OptionNameEnum.preset, "previewFormat": ThetaRepository.OptionNameEnum.previewformat, - "proxy": ThetaRepository.OptionNameEnum.proxy, + KEY_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, + KEY_SHUTTER_VOLUME: ThetaRepository.OptionNameEnum.shuttervolume, + KEY_SLEEP_DELAY: ThetaRepository.OptionNameEnum.sleepdelay, + KEY_TIMESHIFT: ThetaRepository.OptionNameEnum.timeshift, "topBottomCorrection": ThetaRepository.OptionNameEnum.topbottomcorrection, "topBottomCorrectionRotation": ThetaRepository.OptionNameEnum.topbottomcorrectionrotation, + KEY_TOP_BOTTOM_CORRECTION_ROTATION_SUPPORT: ThetaRepository.OptionNameEnum.topbottomcorrectionrotationsupport, "totalSpace": ThetaRepository.OptionNameEnum.totalspace, + "usbConnection": ThetaRepository.OptionNameEnum.usbconnection, "username": ThetaRepository.OptionNameEnum.username, "videoStitching": ThetaRepository.OptionNameEnum.videostitching, "visibilityReduction": ThetaRepository.OptionNameEnum.visibilityreduction, - "whiteBalance": ThetaRepository.OptionNameEnum.whitebalance, + KEY_WHITE_BALANCE: ThetaRepository.OptionNameEnum.whitebalance, "whiteBalanceAutoStrength": ThetaRepository.OptionNameEnum.whitebalanceautostrength, + "wlanAntennaConfig": ThetaRepository.OptionNameEnum.wlanantennaconfig, "wlanFrequency": ThetaRepository.OptionNameEnum.wlanfrequency, + "wlanFrequencySupport": ThetaRepository.OptionNameEnum.wlanfrequencysupport, + KEY_WLAN_FREQUENCY_CL_MODE: ThetaRepository.OptionNameEnum.wlanfrequencyclmode, +] + +let supportOptions: [ThetaRepository.OptionNameEnum: Any.Type] = [ + ThetaRepository.OptionNameEnum.aiautothumbnailsupport: ThetaRepository.AiAutoThumbnailEnum.self, + ThetaRepository.OptionNameEnum.aperturesupport: ThetaRepository.ApertureEnum.self, + ThetaRepository.OptionNameEnum.cameracontrolsourcesupport: ThetaRepository.CameraControlSourceEnum.self, + ThetaRepository.OptionNameEnum.camerapowersupport: ThetaRepository.CameraPowerEnum.self, + ThetaRepository.OptionNameEnum.exposuredelaysupport: ThetaRepository.ExposureDelayEnum.self, + ThetaRepository.OptionNameEnum.gpstagrecordingsupport: ThetaRepository.GpsTagRecordingEnum.self, + ThetaRepository.OptionNameEnum.wlanfrequencysupport: ThetaRepository.WlanFrequencyEnum.self, ] let optionNameEnumToItemName = { @@ -174,6 +238,10 @@ func convertSetOptionsParam(params: [String: Any]) -> ThetaRepository.Options { func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) { // TODO: Add items when adding options switch name { + case ThetaRepository.OptionNameEnum.accessinfo.name: + if let params = value as? [String: Any?] { + options.accessInfo = toAccessInfo(params: params) + } case ThetaRepository.OptionNameEnum.aiautothumbnail.name: options.aiAutoThumbnail = getEnumValue( values: ThetaRepository.AiAutoThumbnailEnum.values(), name: value as! String @@ -208,6 +276,14 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.cameraControlSource = getEnumValue( values: ThetaRepository.CameraControlSourceEnum.values(), name: value as! String )! + case ThetaRepository.OptionNameEnum.cameralock.name: + options.cameraLock = getEnumValue( + values: ThetaRepository.CameraLockEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.cameralockconfig.name: + if let params = value as? [String: Any] { + options.cameraLockConfig = toCameraLockConfig(params: params) + } case ThetaRepository.OptionNameEnum.cameramode.name: options.cameraMode = getEnumValue( values: ThetaRepository.CameraModeEnum.values(), name: value as! String @@ -226,6 +302,10 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.captureNumber = KotlinInt(integerLiteral: value as! Int) case ThetaRepository.OptionNameEnum.colortemperature.name: options.colorTemperature = KotlinInt(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.compassdirectionref.name: + options.compassDirectionRef = getEnumValue( + values: ThetaRepository.CompassDirectionRefEnum.values(), name: value as! String + )! case ThetaRepository.OptionNameEnum.compositeshootingoutputinterval.name: options.compositeShootingOutputInterval = KotlinInt(integerLiteral: value as! Int) case ThetaRepository.OptionNameEnum.compositeshootingtime.name: @@ -300,12 +380,22 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.maxRecordableTime = getEnumValue( values: ThetaRepository.MaxRecordableTimeEnum.values(), name: value as! String )! + case ThetaRepository.OptionNameEnum.microphonenoisereduction.name: + options.microphoneNoiseReduction = getEnumValue( + values: ThetaRepository.MicrophoneNoiseReductionEnum.values(), name: value as! String + )! + case ThetaRepository.OptionNameEnum.mobilenetworksetting.name: + if let params = value as? [String: Any] { + options.mobileNetworkSetting = toMobileNetworkSetting(params: params) + } case ThetaRepository.OptionNameEnum.networktype.name: options.networkType = getEnumValue( values: ThetaRepository.NetworkTypeEnum.values(), name: value as! String )! case ThetaRepository.OptionNameEnum.offdelay.name: options.offDelay = toOffDelay(value: value) + case ThetaRepository.OptionNameEnum.offdelayusb.name: + options.offDelayUsb = toOffDelayUsb(value: value) case ThetaRepository.OptionNameEnum.password.name: options.password = value as? String case ThetaRepository.OptionNameEnum.powersaving.name: @@ -352,6 +442,10 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.topBottomCorrectionRotation = toTopBottomCorrectionRotation(params: value as! [String: Any]) case ThetaRepository.OptionNameEnum.totalspace.name: options.totalSpace = KotlinLong(integerLiteral: value as! Int) + case ThetaRepository.OptionNameEnum.usbconnection.name: + options.usbConnection = getEnumValue( + values: ThetaRepository.UsbConnectionEnum.values(), name: value as! String + )! case ThetaRepository.OptionNameEnum.username.name: options.username = value as? String case ThetaRepository.OptionNameEnum.videostitching.name: @@ -370,16 +464,23 @@ func setOptionsValue(options: ThetaRepository.Options, name: String, value: Any) options.whiteBalanceAutoStrength = getEnumValue( values: ThetaRepository.WhiteBalanceAutoStrengthEnum.values(), name: value as! String )! + case ThetaRepository.OptionNameEnum.wlanantennaconfig.name: + options.wlanAntennaConfig = getEnumValue( + values: ThetaRepository.WlanAntennaConfigEnum.values(), name: value as! String + )! case ThetaRepository.OptionNameEnum.wlanfrequency.name: options.wlanFrequency = getEnumValue( values: ThetaRepository.WlanFrequencyEnum.values(), name: value as! String )! + case ThetaRepository.OptionNameEnum.wlanfrequencyclmode.name: + options.wlanFrequencyClMode = toWlanFrequencyClMode(params: value as! [String: Any]) default: break } } func convertResult(options: ThetaRepository.Options) -> [String: Any] { var result = [String: Any]() + var jsonResult = [String: Any]() let nameList = ThetaRepository.OptionNameEnum.values() for i in 0 ..< nameList.size { if let name = nameList.get(index: i), @@ -392,6 +493,10 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { result[key] = value as! Bool ? true : false } else if value is NSNumber || value is String { result[key] = value + } else if value is ThetaRepository.AccessInfo, + let accessInfo = value as? ThetaRepository.AccessInfo + { + result[key] = convertResult(accessInfo: accessInfo) } else if value is ThetaRepository.BracketSettingList, let autoBracket = value as? ThetaRepository.BracketSettingList { @@ -402,6 +507,10 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { let burstOption = value as? ThetaRepository.BurstOption { result[key] = convertResult(burstOption: burstOption) + } else if value is ThetaRepository.CameraLockConfig, + let config = value as? ThetaRepository.CameraLockConfig + { + result[key] = convertResult(cameraLockConfig: config) } else if value is ThetaRepository.EthernetConfig, let ethernetConfig = value as? ThetaRepository.EthernetConfig { @@ -409,6 +518,10 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { } else if value is ThetaRepository.GpsInfo { let gpsInfo = value as! ThetaRepository.GpsInfo result[key] = convertResult(gpsInfo: gpsInfo) + } else if value is ThetaRepository.MobileNetworkSetting, + let mobileNetworkSetting = value as? ThetaRepository.MobileNetworkSetting + { + result[key] = convertResult(mobileNetworkSetting: mobileNetworkSetting) } else if value is ThetaRepository.Proxy, let proxy = value as? ThetaRepository.Proxy { @@ -419,19 +532,38 @@ func convertResult(options: ThetaRepository.Options) -> [String: Any] { result[key] = convertResult(timeshift: timeshift) } else if value is ThetaRepository.TopBottomCorrectionRotation, let rotation = value as? ThetaRepository.TopBottomCorrectionRotation { result[key] = convertResult(rotation: rotation) + } else if value is ThetaRepository.TopBottomCorrectionRotationSupport, + let support = value as? ThetaRepository.TopBottomCorrectionRotationSupport + { + jsonResult[key] = convertJson(topBottomCorrectionRotationSupport: support) + } 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_ + } else if let enumValue = value as? [AnyObject], let enumType = supportOptions[name] { + result[key] = convertSupportResult(supportValueList: enumValue, enumType: enumType) + } else if let valueRange = value as? ThetaRepositoryValueRange { + result[key] = convertValueRangeSupportResult(valueRange: valueRange) } else if let offDelay = value as? ThetaRepository.OffDelaySec { - result[key] = - offDelay.sec == 0 ? ThetaRepository.OffDelayEnum.disable.name : offDelay.sec + result[key] = offDelay.sec_ == 0 ? ThetaRepository.OffDelayEnum.disable.name : offDelay.sec_ + } else if let offDelayUsb = value as? ThetaRepository.OffDelayUsbSec { + result[key] = offDelayUsb.sec_ == 0 ? ThetaRepository.OffDelayUsbEnum.disable.name : offDelayUsb.sec_ } else if let sleepDelay = value as? ThetaRepository.SleepDelaySec { - result[key] = - sleepDelay.sec == 0 - ? ThetaRepository.SleepDelayEnum.disable.name : sleepDelay.sec + result[key] = sleepDelay.sec_ == 0 ? ThetaRepository.SleepDelayEnum.disable.name : sleepDelay.sec_ + } else if value is ThetaRepository.WlanFrequencyClMode, let wlanFrequencyClMode = value as? ThetaRepository.WlanFrequencyClMode { + result[key] = convertResult(wlanFrequencyClMode: wlanFrequencyClMode) + } else if let enumValue = value as? [AnyObject], let enumType = supportOptions[name] { + result[key] = convertSupportResult(supportValueList: enumValue, enumType: enumType) } // TODO: Add class item when adding options } } } - return result + + var response = [String: Any]() + response[KEY_OPTIONS] = result + response["json"] = jsonResult + return response } // MARK: - Notify event @@ -440,45 +572,45 @@ func toNotify(name: String, params: [String: Any]?) -> [String: Any] { var result: [String: Any] = [ KEY_NOTIFY_NAME: name, ] - if let params = params { + if let params { result[KEY_NOTIFY_PARAMS] = params } return result } func toCaptureProgressNotifyParam(value: Float) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_COMPLETION: value, ] } func toEventWebSocketEventNotifyParam(value: CameraEvent) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_EVENT: convertResult(cameraEvent: value), ] } func toMessageNotifyParam(value: String) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_MESSAGE: value, ] } func toCapturingNotifyParam(value: CapturingStatusEnum) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_STATUS: value.name, ] } func toStartedNotifyParam(value: String) -> [String: Any] { - return [ + [ KEY_NOTIFY_PARAM_FILE_URL: value, ] } // MARK: - Capture builder -func setCaptureBuilderParams(params: [String: Any], builder: CaptureBuilder) { +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) { @@ -587,6 +719,26 @@ func setTimeShiftCaptureBuilderParams(params: [String: Any], builder: TimeShiftC } } +func setTimeShiftManualCaptureBuilderParams(params: [String: Any], builder: TimeShiftManualCapture.Builder) { + if let interval = params[KEY_TIMESHIFT_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 interval = params[KEY_TIMESHIFT_CAPTURE_INTERVAL] as? Int, interval >= 0 @@ -661,7 +813,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr if let autoBracket = params[KEY_AUTO_BRACKET] as? [[String: Any]] { try autoBracket.forEach { map in let aperture = { - if let name = map["aperture"] as? String { + if let name = map[KEY_APERTURE] as? String { return getEnumValue(values: ThetaRepository.ApertureEnum.values(), name: name) } else { return nil @@ -669,7 +821,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr }() let colorTemperature = { - if let value = map["colorTemperature"] as? Int { + if let value = map[KEY_COLOR_TEMPERATURE] as? Int { return toKotlinInt(value: value) } else { return nil @@ -677,7 +829,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr }() let exposureCompensation = { - if let name = map["exposureCompensation"] as? String { + if let name = map[KEY_EXPOSURE_COMPENSATION] as? String { return getEnumValue(values: ThetaRepository.ExposureCompensationEnum.values(), name: name) } else { return nil @@ -685,7 +837,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr }() let exposureProgram = { - if let name = map["exposureProgram"] as? String { + if let name = map[KEY_EXPOSURE_PROGRAM] as? String { return getEnumValue(values: ThetaRepository.ExposureProgramEnum.values(), name: name) } else { return nil @@ -693,7 +845,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr }() let iso = { - if let name = map["iso"] as? String { + if let name = map[KEY_ISO] as? String { return getEnumValue(values: ThetaRepository.IsoEnum.values(), name: name) } else { return nil @@ -709,7 +861,7 @@ func setMultiBracketCaptureBuilderParams(params: [String: Any], builder: MultiBr }() let whiteBalance = { - if let name = map["whiteBalance"] as? String { + if let name = map[KEY_WHITE_BALANCE] as? String { return getEnumValue(values: ThetaRepository.WhiteBalanceEnum.values(), name: name) } else { return nil @@ -757,7 +909,7 @@ func getEnumValue>(values: KotlinArray, name: String) -> } func convertKotlinBooleanToBool(value: Any?) -> Bool? { - guard let value = value else { return nil } + guard let value else { return nil } guard value is KotlinBoolean, let numVal = value as? NSNumber else { return false } return numVal.boolValue @@ -779,6 +931,42 @@ func toKotlinBoolean(value: Any?) -> KotlinBoolean? { // MARK: - Convert to react-native object +func convertResult(accessInfo: ThetaRepository.AccessInfo) -> [String: Any] { + var result: [String: Any] = [:] + result[KEY_SSID] = accessInfo.ssid + result[KEY_IP_ADDRESS] = accessInfo.ipAddress + result[KEY_SUBNET_MASK] = accessInfo.subnetMask + result[KEY_DEFAULT_GATEWAY] = accessInfo.defaultGateway + if let dns1 = accessInfo.dns1 { + result[KEY_DNS1] = dns1 + } + if let dns2 = accessInfo.dns2 { + result[KEY_DNS2] = dns2 + } + result["proxyURL"] = accessInfo.proxyURL + result["frequency"] = accessInfo.frequency.name + result["wlanSignalStrength"] = accessInfo.wlanSignalStrength + result["wlanSignalLevel"] = accessInfo.wlanSignalLevel + result["lteSignalStrength"] = accessInfo.lteSignalStrength + result["lteSignalLevel"] = accessInfo.lteSignalLevel + + if let list = accessInfo.dhcpLeaseAddress { + let array = list.map { item in convertResult(dhcpLeaseAddress: item) } + if !array.isEmpty { + result[KEY_DHCP_LEASE_ADDRESS] = array + } + } + return result +} + +func convertResult(dhcpLeaseAddress: ThetaRepository.DhcpLeaseAddress) -> [String: Any] { + [ + KEY_IP_ADDRESS: dhcpLeaseAddress.ipAddress, + KEY_MAC_ADDRESS: dhcpLeaseAddress.macAddress, + KEY_HOST_NAME: dhcpLeaseAddress.hostName, + ] +} + func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] { var resultList = [[String: Any]]() fileInfoList.forEach { fileInfo in @@ -786,11 +974,11 @@ func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] "name": fileInfo.name, "fileUrl": fileInfo.fileUrl, "size": fileInfo.size, - "dateTime": fileInfo.dateTime, + KEY_DATETIME: fileInfo.dateTime, "thumbnailUrl": fileInfo.thumbnailUrl, ] if let dateTimeZone = fileInfo.dateTimeZone { - item["dateTimeZone"] = dateTimeZone + item[KEY_GPS_DATE_TIME_ZONE] = dateTimeZone } if let lat = fileInfo.lat { item["lat"] = lat.floatValue @@ -849,7 +1037,7 @@ func convertResult(fileInfoList: [ThetaRepository.FileInfo]) -> [[String: Any]] } func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { - return [ + [ "manufacturer": thetaInfo.manufacturer, "model": thetaInfo.model, "serialNumber": thetaInfo.serialNumber, @@ -870,7 +1058,7 @@ func convertResult(thetaInfo: ThetaRepository.ThetaInfo) -> [String: Any?] { } func convertResult(cameraErrorList: [ThetaRepository.CameraErrorEnum]?) -> [String]? { - guard let cameraErrorList = cameraErrorList else { + guard let cameraErrorList else { return nil } var result: [String] = [] @@ -988,25 +1176,25 @@ func convertResult(autoBracket: ThetaRepository.BracketSettingList) -> [[String: var result: [String: Any] = [:] if let setting = bracketSetting as? ThetaRepository.BracketSetting { if let aperture = setting.aperture?.name { - result["aperture"] = aperture + result[KEY_APERTURE] = aperture } if let colorTemperature = setting.colorTemperature?.intValue { - result["colorTemperature"] = colorTemperature + result[KEY_COLOR_TEMPERATURE] = colorTemperature } if let exposureCompensation = setting.exposureCompensation?.name { - result["exposureCompensation"] = exposureCompensation + result[KEY_EXPOSURE_COMPENSATION] = exposureCompensation } if let exposureProgram = setting.exposureProgram?.name { - result["exposureProgram"] = exposureProgram + result[KEY_EXPOSURE_PROGRAM] = exposureProgram } if let iso = setting.iso?.name { - result["iso"] = iso + result[KEY_ISO] = iso } if let shutterSpeed = setting.shutterSpeed?.name { result["shutterSpeed"] = shutterSpeed } if let whiteBalance = setting.whiteBalance?.name { - result["whiteBalance"] = whiteBalance + result[KEY_WHITE_BALANCE] = whiteBalance } resultList.append(result) } @@ -1015,6 +1203,29 @@ func convertResult(autoBracket: ThetaRepository.BracketSettingList) -> [[String: return resultList } +func convertResult(cameraLockConfig: ThetaRepository.CameraLockConfig) -> [String: Any] { + var result: [String: Any] = [:] + if let isPowerKeyLocked = cameraLockConfig.isPowerKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED] = isPowerKeyLocked.boolValue + } + if let isShutterKeyLocked = cameraLockConfig.isShutterKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED] = isShutterKeyLocked.boolValue + } + if let isModeKeyLocked = cameraLockConfig.isModeKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED] = isModeKeyLocked.boolValue + } + if let isWlanKeyLocked = cameraLockConfig.isWlanKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED] = isWlanKeyLocked.boolValue + } + if let isFnKeyLocked = cameraLockConfig.isFnKeyLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED] = isFnKeyLocked.boolValue + } + if let isPanelLocked = cameraLockConfig.isPanelLocked { + result[KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED] = isPanelLocked.boolValue + } + return result +} + func convertResult(ethernetConfig: ThetaRepository.EthernetConfig) -> [String: Any] { var result: [String: Any] = ["usingDhcp": ethernetConfig.usingDhcp] if let ipAddress = ethernetConfig.ipAddress { @@ -1026,6 +1237,12 @@ func convertResult(ethernetConfig: ThetaRepository.EthernetConfig) -> [String: A if let defaultGateway = ethernetConfig.defaultGateway { result[KEY_DEFAULT_GATEWAY] = defaultGateway } + if let dns1 = ethernetConfig.dns1 { + result[KEY_DNS1] = dns1 + } + if let dns2 = ethernetConfig.dns2 { + result[KEY_DNS2] = dns2 + } if let proxy = ethernetConfig.proxy { result[KEY_PROXY] = convertResult(proxy: proxy) } @@ -1033,11 +1250,11 @@ func convertResult(ethernetConfig: ThetaRepository.EthernetConfig) -> [String: A } func convertResult(gpsInfo: ThetaRepository.GpsInfo) -> [String: Any] { - return [ - "latitude": gpsInfo.latitude, - "longitude": gpsInfo.longitude, - "altitude": gpsInfo.altitude, - "dateTimeZone": gpsInfo.dateTimeZone, + [ + KEY_GPS_LATITUDE: gpsInfo.latitude, + KEY_GPS_LONGITUDE: gpsInfo.longitude, + KEY_GPS_ALTITUDE: gpsInfo.altitude, + KEY_GPS_DATE_TIME_ZONE: gpsInfo.dateTimeZone, ] } @@ -1081,17 +1298,40 @@ func convertResult(timeshift: ThetaRepository.TimeShiftSetting) -> [String: Any] } func convertResult(rotation: ThetaRepository.TopBottomCorrectionRotation) -> [String: Any] { - return [ + [ KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH: rotation.pitch, KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL: rotation.roll, KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW: rotation.yaw, ] } +func convertJson(topBottomCorrectionRotationSupport: ThetaRepository.TopBottomCorrectionRotationSupport) -> String { + let convertJson = """ + { + "pitch": { + "max": \(topBottomCorrectionRotationSupport.pitch.max), + "min": \(topBottomCorrectionRotationSupport.pitch.min), + "stepSize": \(topBottomCorrectionRotationSupport.pitch.stepSize) + }, + "roll": { + "max": \(topBottomCorrectionRotationSupport.roll.max), + "min": \(topBottomCorrectionRotationSupport.roll.min), + "stepSize": \(topBottomCorrectionRotationSupport.roll.stepSize) + }, + "yaw": { + "max": \(topBottomCorrectionRotationSupport.yaw.max), + "min": \(topBottomCorrectionRotationSupport.yaw.min), + "stepSize": \(topBottomCorrectionRotationSupport.yaw.stepSize) + } + } + """ + return convertJson +} + func convertResult(exif: ThetaRepository.Exif) -> [String: Any] { var result = [String: Any]() result["exifVersion"] = exif.exifVersion - result["dateTime"] = exif.dateTime + result[KEY_DATETIME] = exif.dateTime if let imageWidth = exif.imageWidth { result["imageWidth"] = imageWidth } @@ -1119,7 +1359,7 @@ func convertResult(xmp: ThetaRepository.Xmp) -> [String: Any] { func convertResult(metadata: KotlinPair) -> [String: Any] { - return [ + [ "exif": convertResult(exif: metadata.first!), "xmp": convertResult(xmp: metadata.second!), ] @@ -1129,10 +1369,10 @@ func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: 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[KEY_SSID] = accessPoint.ssid + result[KEY_SSID_STEALTH] = accessPoint.ssidStealth + result[KEY_AUTH_MODE] = accessPoint.authMode.name + result[KEY_CONNECTION_PRIORITY] = accessPoint.connectionPriority result["usingDhcp"] = accessPoint.usingDhcp if let ipAddress = accessPoint.ipAddress { result[KEY_IP_ADDRESS] = ipAddress @@ -1143,6 +1383,12 @@ func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: if let defaultGateway = accessPoint.defaultGateway { result[KEY_DEFAULT_GATEWAY] = defaultGateway } + if let dns1 = accessPoint.dns1 { + result[KEY_DNS1] = dns1 + } + if let dns2 = accessPoint.dns2 { + result[KEY_DNS2] = dns2 + } if let proxy = accessPoint.proxy { result[KEY_PROXY] = convertResult(proxy: proxy) } @@ -1151,11 +1397,22 @@ func convertResult(accessPointList: [ThetaRepository.AccessPoint]) -> [[String: return resultList } +func convertResult(mobileNetworkSetting: ThetaRepository.MobileNetworkSetting) -> [String: Any] { + var result: [String: Any] = [:] + if let roaming = mobileNetworkSetting.roaming { + result[KEY_ROAMING] = roaming.name + } + if let plan = mobileNetworkSetting.plan { + result[KEY_PLAN] = plan.name + } + return result +} + func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[String: Any]] { var resultList = [[String: Any]]() pluginInfoList.forEach { pluginInfo in let item = [ - "name": pluginInfo.name, + KEY_NOTIFY_NAME: pluginInfo.name, "packageName": pluginInfo.packageName, "version": pluginInfo.version, "isPreInstalled": pluginInfo.isPreInstalled, @@ -1164,15 +1421,14 @@ func toPluginInfosResult(pluginInfoList: [ThetaRepository.PluginInfo]) -> [[Stri "isBoot": pluginInfo.isBoot, "hasWebServer": pluginInfo.hasWebServer, "exitStatus": pluginInfo.exitStatus, - "message": pluginInfo.message, + KEY_NOTIFY_PARAM_MESSAGE: pluginInfo.message, ] resultList.append(item) } return resultList } -func convertResult(cameraEvent: CameraEvent) -> [String: Any] -{ +func convertResult(cameraEvent: CameraEvent) -> [String: Any] { var result = [String: Any]() if let value = cameraEvent.options { result[KEY_OPTIONS] = convertResult(options: value) @@ -1184,8 +1440,88 @@ func convertResult(cameraEvent: CameraEvent) -> [String: Any] return result } +func convertResult(wlanFrequencyClMode: ThetaRepository.WlanFrequencyClMode) -> [String: Any] { + [ + KEY_WLAN_FREQUENCY_CL_MODE_2_4: wlanFrequencyClMode.enable2_4, + KEY_WLAN_FREQUENCY_CL_MODE_5_2: wlanFrequencyClMode.enable5_2, + KEY_WLAN_FREQUENCY_CL_MODE_5_8: wlanFrequencyClMode.enable5_8, + ] +} + +/** + * Convert support values + * + * @param supportValueList Support values + * @param enumType values type. e.g. ThetaRepository.WlanStandardInfoEnum.self , Int.self {class}.seff + */ +func convertSupportResult(supportValueList: [Any], enumType: Any.Type) -> [Any] { + var result = [Any]() + for item in supportValueList { + // for enum value + if let value = item as? KotlinEnum, + enumType is KotlinEnum.Type + { + result.append(value.name) + } + } + return result +} + +func convertValueRangeSupportResult(valueRange: ThetaRepositoryValueRange) -> [String: Any] { + var result: [String: Any] = [:] + result[KEY_MAX] = valueRange.max + result[KEY_MIN] = valueRange.min + result[KEY_STEP_SIZE] = valueRange.stepSize + return result +} + // MARK: - Convert to theta-client object +func toAccessInfo(params: [String: Any?]) -> ThetaRepository.AccessInfo { + let frequency = getEnumValue( + values: ThetaRepository.WlanFrequencyAccessInfoEnum.values(), name: params["frequency"] as! String + )! + + let dhcpLeaseAddress: [ThetaRepository.DhcpLeaseAddress]? = { + guard let list = params[KEY_DHCP_LEASE_ADDRESS] as? [[String: Any?]] else { + return nil + } + + let array = list.compactMap { toDhcpLeaseAddress(value: $0) } + return array.isEmpty ? nil : array + }() + + return ThetaRepository.AccessInfo( + ssid: params[KEY_SSID] as! String, + ipAddress: params[KEY_IP_ADDRESS] as! String, + subnetMask: params[KEY_SUBNET_MASK] as! String, + defaultGateway: params[KEY_DEFAULT_GATEWAY] as! String, + dns1: params[KEY_DNS1] as? String, + dns2: params[KEY_DNS2] as? String, + proxyURL: params["proxyURL"] as! String, + frequency: frequency, + wlanSignalStrength: params["wlanSignalStrength"] as! Int32, + wlanSignalLevel: params["wlanSignalLevel"] as! Int32, + lteSignalStrength: params["lteSignalStrength"] as! Int32, + lteSignalLevel: params["lteSignalLevel"] as! Int32, + dhcpLeaseAddress: dhcpLeaseAddress + ) +} + +func toDhcpLeaseAddress(value: [String: Any?]) -> ThetaRepository.DhcpLeaseAddress? { + guard let ipAddress = value[KEY_IP_ADDRESS] as? String, + let macAddress = value[KEY_MAC_ADDRESS] as? String, + let hostName = value[KEY_HOST_NAME] as? String + else { + return nil + } + return ThetaRepository.DhcpLeaseAddress( + ipAddress: ipAddress, + macAddress: macAddress, + hostName: hostName + ) +} + func toBitrate(value: Any) -> ThetaRepositoryBitrate? { if value is NSNumber, let intVal = value as? Int32 { return ThetaRepository.BitrateNumber(value: intVal) @@ -1212,6 +1548,18 @@ func toOffDelay(value: Any) -> ThetaRepositoryOffDelay? { } } +func toOffDelayUsb(value: Any) -> ThetaRepositoryOffDelayUsb? { + if value is NSNumber, let intVal = value as? Int32 { + return ThetaRepository.OffDelayUsbSec(sec: intVal) + } else if let name = value as? String, + let enumValue = getEnumValue(values: ThetaRepository.OffDelayUsbEnum.values(), name: name) + { + return enumValue + } else { + return nil + } +} + func toSleepDelay(value: Any) -> ThetaRepositorySleepDelay? { if value is NSNumber, let intVal = value as? Int32 { return ThetaRepository.SleepDelaySec(sec: intVal) @@ -1226,42 +1574,42 @@ func toSleepDelay(value: Any) -> ThetaRepositorySleepDelay? { func toBurstOption(params: [String: Any]) -> ThetaRepository.BurstOption { var burstCaptureNum: ThetaRepository.BurstCaptureNumEnum? = nil - if let name = params["burstCaptureNum"] as? String { + if let name = params[KEY_BURST_CAPTURE_NUM] as? String { burstCaptureNum = getEnumValue( values: ThetaRepository.BurstCaptureNumEnum.values(), name: name ) } var burstBracketStep: ThetaRepository.BurstBracketStepEnum? = nil - if let name = params["burstBracketStep"] as? String { + if let name = params[KEY_BURST_BRACKET_STEP] as? String { burstBracketStep = getEnumValue( values: ThetaRepository.BurstBracketStepEnum.values(), name: name ) } var burstCompensation: ThetaRepository.BurstCompensationEnum? = nil - if let name = params["burstCompensation"] as? String { + if let name = params[KEY_BURST_COMPENSATION] as? String { burstCompensation = getEnumValue( values: ThetaRepository.BurstCompensationEnum.values(), name: name ) } var burstMaxExposureTime: ThetaRepository.BurstMaxExposureTimeEnum? = nil - if let name = params["burstMaxExposureTime"] as? String { + if let name = params[KEY_BURST_MAX_EXPOSURE_TIME] as? String { burstMaxExposureTime = getEnumValue( values: ThetaRepository.BurstMaxExposureTimeEnum.values(), name: name ) } var burstEnableIsoControl: ThetaRepository.BurstEnableIsoControlEnum? = nil - if let name = params["burstEnableIsoControl"] as? String { + if let name = params[KEY_BURST_ENABLE_ISO_CONTROL] as? String { burstEnableIsoControl = getEnumValue( values: ThetaRepository.BurstEnableIsoControlEnum.values(), name: name ) } var burstOrder: ThetaRepository.BurstOrderEnum? = nil - if let name = params["burstOrder"] as? String { + if let name = params[KEY_BURST_ORDER] as? String { burstOrder = getEnumValue(values: ThetaRepository.BurstOrderEnum.values(), name: name) } @@ -1280,7 +1628,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis params.forEach { map in let aperture = { - if let name = map["aperture"] as? String { + if let name = map[KEY_APERTURE] as? String { return getEnumValue(values: ThetaRepository.ApertureEnum.values(), name: name) } else { return nil @@ -1288,7 +1636,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis }() let colorTemperature = { - if let value = map["colorTemperature"] as? Int { + if let value = map[KEY_COLOR_TEMPERATURE] as? Int { return toKotlinInt(value: value) } else { return nil @@ -1296,7 +1644,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis }() let exposureCompensation = { - if let name = map["exposureCompensation"] as? String { + if let name = map[KEY_EXPOSURE_COMPENSATION] as? String { return getEnumValue(values: ThetaRepository.ExposureCompensationEnum.values(), name: name) } else { return nil @@ -1304,7 +1652,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis }() let exposureProgram = { - if let name = map["exposureProgram"] as? String { + if let name = map[KEY_EXPOSURE_PROGRAM] as? String { return getEnumValue(values: ThetaRepository.ExposureProgramEnum.values(), name: name) } else { return nil @@ -1312,7 +1660,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis }() let iso = { - if let name = map["iso"] as? String { + if let name = map[KEY_ISO] as? String { return getEnumValue(values: ThetaRepository.IsoEnum.values(), name: name) } else { return nil @@ -1328,7 +1676,7 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis }() let whiteBalance = { - if let name = map["whiteBalance"] as? String { + if let name = map[KEY_WHITE_BALANCE] as? String { return getEnumValue(values: ThetaRepository.WhiteBalanceEnum.values(), name: name) } else { return nil @@ -1349,17 +1697,30 @@ func toAutoBracket(params: [[String: Any]]) -> ThetaRepository.BracketSettingLis return autoBracket } +func toCameraLockConfig(params: [String: Any]) -> ThetaRepository.CameraLockConfig { + ThetaRepository.CameraLockConfig( + isPowerKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_POWER_KEY_LOCKED]), + isShutterKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_SHUTTER_KEY_LOCKED]), + isModeKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_MODE_KEY_LOCKED]), + isWlanKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_WLAN_KEY_LOCKED]), + isFnKeyLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_FN_KEY_LOCKED]), + isPanelLocked: toKotlinBoolean(value: params[KEY_CAMERA_LOCK_CONFIG_IS_PANEL_LOCKED]) + ) +} + func toEthernetConfig(params: [String: Any]) -> ThetaRepository.EthernetConfig { var proxy: ThetaRepository.Proxy? = nil if let data = params[KEY_PROXY] as? [String: Any] { proxy = toProxy(params: data) } - + return ThetaRepository.EthernetConfig( usingDhcp: params["usingDhcp"] as? Bool ?? true, ipAddress: params[KEY_IP_ADDRESS] as? String, subnetMask: params[KEY_SUBNET_MASK] as? String, defaultGateway: params[KEY_DEFAULT_GATEWAY] as? String, + dns1: params[KEY_DNS1] as? String, + dns2: params[KEY_DNS2] as? String, proxy: proxy ) } @@ -1380,55 +1741,55 @@ func toGpsInfo(params: [String: Any]) -> ThetaRepository.GpsInfo? { } 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 + ThetaRepository.Proxy( + use: params[KEY_PROXY_USE] as? Bool ?? false, + url: params[KEY_PROXY_URL] as? String, + port: toKotlinInt(value: params[KEY_PROXY_PORT]), + userid: params[KEY_PROXY_USER_ID] as? String, + password: params[KEY_PROXY_PASSWORD] as? String ) } func toTimeShift(params: [String: Any]) -> ThetaRepository.TimeShiftSetting { var firstInterval: ThetaRepository.TimeShiftIntervalEnum? = nil - if let name = params["firstInterval"] as? String { + if let name = params[KEY_TIMESHIFT_FIRST_INTERVAL] as? String { firstInterval = getEnumValue( values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name ) } var secondInterval: ThetaRepository.TimeShiftIntervalEnum? = nil - if let name = params["secondInterval"] as? String { + if let name = params[KEY_TIMESHIFT_SECOND_INTERVAL] as? String { secondInterval = getEnumValue( values: ThetaRepository.TimeShiftIntervalEnum.values(), name: name ) } return ThetaRepository.TimeShiftSetting( - isFrontFirst: toKotlinBoolean(value: params["isFrontFirst"]), + isFrontFirst: toKotlinBoolean(value: params[KEY_TIMESHIFT_IS_FRONT_FIRST]), firstInterval: firstInterval, secondInterval: secondInterval ) } func toTopBottomCorrectionRotation(params: [String: Any]) -> ThetaRepository.TopBottomCorrectionRotation? { - guard let pitch = params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH] as? Double, - let roll = params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL] as? Double, - let yaw = params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW] as? Double else { return nil } + let pitch = Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_PITCH] as? Double ?? 0) + let roll = Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_ROLL] as? Double ?? 0) + let yaw = Float(params[KEY_TOP_BOTTOM_CORRECTION_ROTATION_YAW] as? Double ?? 0) return ThetaRepository.TopBottomCorrectionRotation( - pitch: Float(pitch), - roll: Float(roll), - yaw: Float(yaw) + pitch: pitch, + roll: roll, + yaw: yaw ) } func toDigestAuth(params: [String: String?]?) -> DigestAuth? { - guard let params = params, + guard let params, let username = params["username"] as? String else { return nil } - let password = params["password"] as? String + let password = params[KEY_PROXY_PASSWORD] as? String return DigestAuth(username: username, password: password) } @@ -1470,3 +1831,110 @@ func toTimeout(params: [String: Any]) -> ThetaRepository.Timeout? { socketTimeout: socketTimeout ) } + +struct SetAccessPointParams { + let ssid: String + let ssidStealth: KotlinBoolean? + let authMode: ThetaRepository.AuthModeEnum + let password: String? + let connectionPriority: KotlinInt? + let dns1: String? + let dns2: String? + let proxy: ThetaRepository.Proxy? +} + +func toSetAccessPointParams(params: [String: Any?]) throws -> SetAccessPointParams { + guard let ssid = params[KEY_SSID] as? String else { + throw ThetaClientError.invalidArgument(KEY_SSID) + } + let ssidStealth = toKotlinBoolean(value: params[KEY_SSID_STEALTH] as? Bool) + guard let authMode = params[KEY_AUTH_MODE] as? String else { + throw ThetaClientError.invalidArgument(KEY_AUTH_MODE) + } + guard + let authModeEnum = getEnumValue( + values: ThetaRepository.AuthModeEnum.values(), name: authMode + ) + else { + throw ThetaClientError.invalidArgument(KEY_AUTH_MODE) + } + let password = params[KEY_PASSWORD] as? String + let connectionPriority = toKotlinInt(value: params[KEY_CONNECTION_PRIORITY] as? Int) + let dns1 = params[KEY_DNS1] as? String + let dns2 = params[KEY_DNS2] as? String + let proxy = params[KEY_PROXY] + let proxyParam: ThetaRepository.Proxy? = { + if let proxy = proxy as? [String: Any] { + return toProxy(params: proxy) + } + return nil + }() + + return SetAccessPointParams( + ssid: ssid, + ssidStealth: ssidStealth, + authMode: authModeEnum, + password: password, + connectionPriority: connectionPriority, + dns1: dns1, + dns2: dns2, + proxy: proxyParam + ) +} + +struct SetAccessPointStaticallyParams { + let ipAddress: String + let subnetMask: String + let defaultGateway: String +} + +func toSetAccessPointStaticallyParams(params: [String: Any?]) throws -> SetAccessPointStaticallyParams { + guard let ipAddress = params[KEY_IP_ADDRESS] as? String else { + throw ThetaClientError.invalidArgument(KEY_IP_ADDRESS) + } + guard let subnetMask = params[KEY_SUBNET_MASK] as? String else { + throw ThetaClientError.invalidArgument(KEY_SUBNET_MASK) + } + guard let defaultGateway = params[KEY_DEFAULT_GATEWAY] as? String else { + throw ThetaClientError.invalidArgument(KEY_DEFAULT_GATEWAY) + } + + return SetAccessPointStaticallyParams( + ipAddress: ipAddress, + subnetMask: subnetMask, + defaultGateway: defaultGateway + ) +} + +func toWlanFrequencyClMode(params: [String: Any]) -> ThetaRepository.WlanFrequencyClMode? { + guard let enable2_4 = params[KEY_WLAN_FREQUENCY_CL_MODE_2_4] as? Bool, + let enable5_2 = params[KEY_WLAN_FREQUENCY_CL_MODE_5_2] as? Bool, + let enable5_8 = params[KEY_WLAN_FREQUENCY_CL_MODE_5_8] as? Bool + else { return nil } + return ThetaRepository.WlanFrequencyClMode( + enable2_4: enable2_4, + enable5_2: enable5_2, + enable5_8: enable5_8 + ) +} + +func toMobileNetworkSetting(params: [String: Any]) -> ThetaRepository.MobileNetworkSetting { + var roaming: ThetaRepository.RoamingEnum? = nil + if let name = params[KEY_ROAMING] as? String { + roaming = getEnumValue( + values: ThetaRepository.RoamingEnum.values(), name: name + ) + } + + var plan: ThetaRepository.PlanEnum? = nil + if let name = params[KEY_PLAN] as? String { + plan = getEnumValue( + values: ThetaRepository.PlanEnum.values(), name: name + ) + } + + return ThetaRepository.MobileNetworkSetting( + roaming: roaming, + plan: plan + ) +} diff --git a/react-native/ios/ThetaClientReactNative.m b/react-native/ios/ThetaClientReactNative.m index c52874db26a..813fb5f0803 100644 --- a/react-native/ios/ThetaClientReactNative.m +++ b/react-native/ios/ThetaClientReactNative.m @@ -2,6 +2,10 @@ @interface RCT_EXTERN_MODULE(ThetaClientReactNative, NSObject) +RCT_EXTERN_METHOD(setApiLogListener:(BOOL)enabled + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + RCT_EXTERN_METHOD(initialize:(NSString)endPoint withConfig:(NSDictionary*)config withTimeout:(NSDictionary*)timeout @@ -80,6 +84,22 @@ @interface RCT_EXTERN_MODULE(ThetaClientReactNative, NSObject) RCT_EXTERN_METHOD(cancelTimeShiftCapture:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(getTimeShiftManualCaptureBuilder:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(buildTimeShiftManualCapture:(NSDictionary*)options + withResolver:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(startTimeShiftManualCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(startTimeShiftManualSecondCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(cancelTimeShiftManualCapture:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + RCT_EXTERN_METHOD(getVideoCaptureBuilder:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -180,6 +200,9 @@ @interface RCT_EXTERN_MODULE(ThetaClientReactNative, NSObject) withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) +RCT_EXTERN_METHOD(reboot:(RCTPromiseResolveBlock)resolve + withRejecter:(RCTPromiseRejectBlock)reject) + RCT_EXTERN_METHOD(reset:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) @@ -204,24 +227,11 @@ @interface RCT_EXTERN_MODULE(ThetaClientReactNative, NSObject) 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 +RCT_EXTERN_METHOD(setAccessPointDynamically:(NSDictionary *)params 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 +RCT_EXTERN_METHOD(setAccessPointStatically:(NSDictionary *)params withResolver:(RCTPromiseResolveBlock)resolve withRejecter:(RCTPromiseRejectBlock)reject) diff --git a/react-native/ios/ThetaClientReactNative.swift b/react-native/ios/ThetaClientReactNative.swift index 1ad50850b54..c0cbe672bcd 100644 --- a/react-native/ios/ThetaClientReactNative.swift +++ b/react-native/ios/ThetaClientReactNative.swift @@ -10,6 +10,9 @@ 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_TIMESHIFT_MANUAL_CAPTURE = "No timeShiftManualCapture." +let MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURE_BUILDER = "no manual time-shift capture builder." +let MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURING = "no timeShiftManualCapturing." let MESSAGE_NO_VIDEO_CAPTURE = "No videoCapture." let MESSAGE_NO_VIDEO_CAPTURE_BUILDER = "no video capture builder." let MESSAGE_NO_VIDEO_CAPTURING = "no videoCapturing." @@ -32,6 +35,10 @@ let MESSAGE_NO_CONTINUOUS_CAPTURE = "No continuousCapture." let MESSAGE_NO_CONTINUOUS_CAPTURE_BUILDER = "no continuousCaptureBuilder." let MESSAGE_NO_EVENT_WEBSOCKET = "no eventWebSocket." +enum ThetaClientError: Error { + case invalidArgument(String) +} + @objc(ThetaClientReactNative) class ThetaClientReactNative: RCTEventEmitter { var thetaRepository: ThetaRepository? @@ -42,6 +49,9 @@ class ThetaClientReactNative: RCTEventEmitter { var timeShiftCaptureBuilder: TimeShiftCapture.Builder? var timeShiftCapture: TimeShiftCapture? var timeShiftCapturing: TimeShiftCapturing? + var timeShiftManualCaptureBuilder: TimeShiftManualCapture.Builder? + var timeShiftManualCapture: TimeShiftManualCapture? + var timeShiftManualCapturing: TimeShiftManualCapturing? var videoCaptureBuilder: VideoCapture.Builder? var videoCapture: VideoCapture? var videoCapturing: VideoCapturing? @@ -63,7 +73,7 @@ class ThetaClientReactNative: RCTEventEmitter { var continuousCaptureBuilder: ContinuousCapture.Builder? var continuousCapture: ContinuousCapture? var eventWebSocket: EventWebSocket? - + static let EVENT_FRAME = "ThetaFrameEvent" static let EVENT_NOTIFY = "ThetaNotify" @@ -71,6 +81,9 @@ class ThetaClientReactNative: RCTEventEmitter { static let NOTIFY_TIMESHIFT_PROGRESS = "TIME-SHIFT-PROGRESS" static let NOTIFY_TIMESHIFT_STOP_ERROR = "TIME-SHIFT-STOP-ERROR" static let NOTIFY_TIMESHIFT_CAPTURING = "TIME-SHIFT-CAPTURING" + static let NOTIFY_TIMESHIFT_MANUAL_PROGRESS = "TIME-SHIFT-MANUAL-PROGRESS" + static let NOTIFY_TIMESHIFT_MANUAL_STOP_ERROR = "TIME-SHIFT-MANUAL-STOP-ERROR" + static let NOTIFY_TIMESHIFT_MANUAL_CAPTURING = "TIME-SHIFT-MANUAL-CAPTURING" static let NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_PROGRESS = "SHOT-COUNT-SPECIFIED-INTERVAL-PROGRESS" static let NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_STOP_ERROR = "SHOT-COUNT-SPECIFIED-INTERVAL-STOP-ERROR" static let NOTIFY_SHOT_COUNT_SPECIFIED_INTERVAL_CAPTURING = "SHOT-COUNT-SPECIFIED-INTERVAL-CAPTURING" @@ -93,24 +106,46 @@ class ThetaClientReactNative: RCTEventEmitter { static let NOTIFY_EVENT_WEBSOCKET_EVENT = "EVENT-WEBSOCKET-EVENT" static let NOTIFY_EVENT_WEBSOCKET_CLOSE = "EVENT-WEBSOCKET-CLOSE" static let NOTIFY_CONVERT_VIDEO_FORMATS_PROGRESS = "CONVERT-VIDEO-FORMATS-PROGRESS" + static let NOTIFY_API_LOG = "API-LOG" @objc override func supportedEvents() -> [String]! { - return [ThetaClientReactNative.EVENT_FRAME, ThetaClientReactNative.EVENT_NOTIFY] + [ThetaClientReactNative.EVENT_FRAME, ThetaClientReactNative.EVENT_NOTIFY] } @objc override static func requiresMainQueueSetup() -> Bool { - return true + true } @objc override func constantsToExport() -> [AnyHashable: Any]! { - return [ + [ "DEFAULT_EVENT_NAME": ThetaClientReactNative.EVENT_FRAME, ] } + @objc(setApiLogListener:withResolver:withRejecter:) + func setApiLogListener(enabled: Bool, + resolve: @escaping RCTPromiseResolveBlock, + reject _: @escaping RCTPromiseRejectBlock) + { + Task { + if enabled { + try UtilKt.setApiLogListener { message in + self.sendEvent(withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_API_LOG, + params: [KEY_NOTIFY_PARAM_MESSAGE: message] + )) + } + } else { + try UtilKt.setApiLogListener(listener: nil) + } + resolve(nil) + } + } + @objc(initialize:withConfig:withTimeout:withResolver:withRejecter:) func initialize( endPoint: String?, @@ -125,6 +160,9 @@ class ThetaClientReactNative: RCTEventEmitter { timeShiftCaptureBuilder = nil timeShiftCapture = nil timeShiftCapturing = nil + timeShiftManualCaptureBuilder = nil + timeShiftManualCapture = nil + timeShiftManualCapturing = nil videoCaptureBuilder = nil videoCapture = nil videoCapturing = nil @@ -149,7 +187,7 @@ class ThetaClientReactNative: RCTEventEmitter { stopLivePreviewResolve = nil eventWebSocket?.stop(completionHandler: { _ in }) eventWebSocket = nil - + Task { let configParams: ThetaRepository.Config? = { if let config = config as? [String: Any] { @@ -234,7 +272,7 @@ class ThetaClientReactNative: RCTEventEmitter { return } - thetaRepository.getThetaLicense() { response, error in + thetaRepository.getThetaLicense { response, error in if let error { reject(ERROR_CODE_ERROR, error.localizedDescription, error) } else if let response { @@ -459,7 +497,7 @@ class ThetaClientReactNative: RCTEventEmitter { reject(ERROR_CODE_ERROR, MESSAGE_LIVE_PREVIEW_RUNNING, nil) return } - + class FrameHandler: KotlinSuspendFunction1 { let thetaClientReactNative: ThetaClientReactNative static let FrameInterval = CFTimeInterval(1.0 / 10.0) @@ -481,7 +519,7 @@ class ThetaClientReactNative: RCTEventEmitter { let dataUrl = "data:image/jpeg;base64," + encodeString thetaClientReactNative.sendEvent( withName: ThetaClientReactNative.EVENT_FRAME, - body: ["data": dataUrl] + body: ["data": dataUrl, "dataSize": nsData.count] ) } } @@ -495,17 +533,17 @@ class ThetaClientReactNative: RCTEventEmitter { return } let frameHandler = FrameHandler(self) - + previewing = true execAndResetStopLivePreviewResolve(false) - + thetaRepository.getLivePreview(frameHandler: frameHandler) { error in self.previewing = false if let error { reject(ERROR_CODE_ERROR, error.localizedDescription, error) } else { self.execAndResetStopLivePreviewResolve(true) - + resolve(true) } } @@ -522,7 +560,7 @@ class ThetaClientReactNative: RCTEventEmitter { } stopLivePreviewResolve = resolve } - + private func execAndResetStopLivePreviewResolve(_ flag: Bool) { stopLivePreviewResolve?(flag) stopLivePreviewResolve = nil @@ -723,7 +761,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -733,7 +771,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCaptureCompleted(fileUrl: String?) { callback(fileUrl, nil) } @@ -768,6 +806,167 @@ class ThetaClientReactNative: RCTEventEmitter { resolve(nil) } + @objc(getTimeShiftManualCaptureBuilder:withRejecter:) + func getTimeShiftManualCaptureBuilder( + resolve: RCTPromiseResolveBlock, + reject: RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + timeShiftManualCaptureBuilder = thetaRepository.getTimeShiftManualCaptureBuilder() + resolve(nil) + } + + @objc(buildTimeShiftManualCapture:withResolver:withRejecter:) + func buildTimeShiftManualCapture( + options: [AnyHashable: Any]?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftManualCaptureBuilder else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURE_BUILDER, nil) + return + } + + timeShiftManualCapture = nil + timeShiftManualCapturing = nil + if let options = options as? [String: Any] { + setCaptureBuilderParams(params: options, builder: timeShiftManualCaptureBuilder) + setTimeShiftManualCaptureBuilderParams(params: options, builder: timeShiftManualCaptureBuilder) + } + timeShiftManualCaptureBuilder.build { capture, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else if let capture { + self.timeShiftManualCapture = capture + self.timeShiftManualCaptureBuilder = nil + resolve(true) + } else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURE, nil) + } + } + } + + @objc(startTimeShiftManualCapture:withRejecter:) + func startTimeShiftManualCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftManualCapture else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURE, nil) + return + } + + class Callback: TimeShiftManualCaptureStartCaptureCallback { + 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 onCaptureFailed(exception: ThetaRepository.ThetaRepositoryException) { + callback(nil, exception.asError()) + client?.timeShiftManualCapturing = nil + } + + func onStopFailed(exception: ThetaRepository.ThetaRepositoryException) { + let error = exception.asError() + client?.sendEvent( + withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_TIMESHIFT_MANUAL_STOP_ERROR, + params: toMessageNotifyParam(value: error.localizedDescription) + ) + ) + } + + func onProgress(completion: Float) { + client?.sendEvent( + withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_TIMESHIFT_MANUAL_PROGRESS, + params: toCaptureProgressNotifyParam(value: completion) + ) + ) + } + + func onCapturing(status: CapturingStatusEnum) { + client?.sendEvent( + withName: ThetaClientReactNative.EVENT_NOTIFY, + body: toNotify( + name: ThetaClientReactNative.NOTIFY_TIMESHIFT_MANUAL_CAPTURING, + params: toCapturingNotifyParam(value: status) + ) + ) + } + + func onCaptureCompleted(fileUrl: String?) { + callback(fileUrl, nil) + client?.timeShiftManualCapturing = nil + } + } + + timeShiftManualCapturing = timeShiftManualCapture.startCapture( + callback: Callback( + { url, error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(url) + } + }, client: self + )) + } + + @objc(startTimeShiftManualSecondCapture:withRejecter:) + func startTimeShiftManualSecondCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftManualCapturing else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURING, nil) + return + } + + timeShiftManualCapturing.startSecondCapture() + resolve(nil) + } + + @objc(cancelTimeShiftManualCapture:withRejecter:) + func cancelTimeShiftManualCapture( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let _ = thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + guard let timeShiftManualCapturing else { + reject(ERROR_CODE_ERROR, MESSAGE_NO_TIMESHIFT_MANUAL_CAPTURING, nil) + return + } + timeShiftManualCapturing.cancelCapture() + resolve(nil) + } + @objc(getVideoCaptureBuilder:withRejecter:) func getVideoCaptureBuilder( resolve: RCTPromiseResolveBlock, @@ -855,7 +1054,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -995,7 +1194,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -1121,7 +1320,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -1131,7 +1330,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1571,7 +1770,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -1581,7 +1780,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1712,7 +1911,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCapturing(status: CapturingStatusEnum) { client?.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -1722,7 +1921,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onCaptureCompleted(fileUrls: [String]?) { callback(fileUrls, nil) } @@ -1766,6 +1965,25 @@ class ThetaClientReactNative: RCTEventEmitter { } } + @objc(reboot:withRejecter:) + func reboot( + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock + ) { + guard let thetaRepository else { + reject(ERROR_CODE_ERROR, MESSAGE_NOT_INIT, nil) + return + } + + thetaRepository.reboot { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } + } + } + @objc(reset:withRejecter:) func reset( resolve: @escaping RCTPromiseResolveBlock, @@ -1822,10 +2040,10 @@ class ThetaClientReactNative: RCTEventEmitter { } } } - + @objc( convertVideoFormats:withToLowResolution:withApplyTopBottomCorrection:withResolver: - withRejecter: + withRejecter: ) func convertVideoFormats( fileUrl: String, @@ -1924,21 +2142,11 @@ class ThetaClientReactNative: RCTEventEmitter { @objc( setAccessPointDynamically: - withSsidStealth: - withAuthMode: - withPassword: - withConnectionPriority: - withProxy: - withResolver: - withRejecter: + withResolver: + withRejecter: ) func setAccessPointDynamically( - ssid: String, - ssidStealth: Bool, - authMode: String, - password: String, - connectionPriority: Int, - proxy: [AnyHashable: Any]?, + params: [AnyHashable: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock ) { @@ -1946,60 +2154,34 @@ class ThetaClientReactNative: RCTEventEmitter { 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) + do { + let accessPointParams = try toSetAccessPointParams(params: params as? [String: Any] ?? [:]) + thetaRepository.setAccessPointDynamically( + ssid: accessPointParams.ssid, + ssidStealth: accessPointParams.ssidStealth, + authMode: accessPointParams.authMode, + password: accessPointParams.password, + connectionPriority: accessPointParams.connectionPriority, + proxy: accessPointParams.proxy + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } } + } catch { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) } } @objc( setAccessPointStatically: - withSsidStealth: - withAuthMode: - withPassword: - withConnectionPriority: - withIpAddress: - withSubnetMask: - withDefaultGateway: - withProxy: - withResolver: - withRejecter: + withResolver: + withRejecter: ) func setAccessPointStatically( - ssid: String, - ssidStealth: Bool, - authMode: String, - password: String?, - connectionPriority: Int, - ipAddress: String, - subnetMask: String, - defaultGateway: String, - proxy: [AnyHashable: Any]?, + params: [AnyHashable: Any], resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock ) { @@ -2007,37 +2189,30 @@ class ThetaClientReactNative: RCTEventEmitter { 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) + do { + let accessPointParams = try toSetAccessPointParams(params: params as? [String: Any] ?? [:]) + let staticallyParams = try toSetAccessPointStaticallyParams(params: params as? [String: Any] ?? [:]) + thetaRepository.setAccessPointStatically( + ssid: accessPointParams.ssid, + ssidStealth: accessPointParams.ssidStealth, + authMode: accessPointParams.authMode, + password: accessPointParams.password, + connectionPriority: accessPointParams.connectionPriority, + ipAddress: staticallyParams.ipAddress, + subnetMask: staticallyParams.subnetMask, + defaultGateway: staticallyParams.defaultGateway, + dns1: accessPointParams.dns1, + dns2: accessPointParams.dns2, + proxy: accessPointParams.proxy + ) { error in + if let error { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) + } else { + resolve(true) + } } + } catch { + reject(ERROR_CODE_ERROR, error.localizedDescription, error) } } @@ -2136,7 +2311,7 @@ class ThetaClientReactNative: RCTEventEmitter { } guard let options = options as? [String: Any], let captureMode = getEnumValue( - values: ThetaRepository.CaptureModeEnum.values(), name: captureMode + values: ThetaRepository.CaptureModeEnum.values(), name: captureMode ) else { reject(ERROR_CODE_ERROR, MESSAGE_NO_ARGUMENT, nil) @@ -2354,7 +2529,7 @@ class ThetaClientReactNative: RCTEventEmitter { } } } - + @objc(getEventWebSocket:withRejecter:) func getEventWebSocket( resolve: @escaping RCTPromiseResolveBlock, @@ -2369,7 +2544,7 @@ class ThetaClientReactNative: RCTEventEmitter { eventWebSocket = thetaRepository.getEventWebSocket() resolve(true) } - + @objc(eventWebSocketStart:withRejecter:) func eventWebSocketStart( resolve: @escaping RCTPromiseResolveBlock, @@ -2385,13 +2560,14 @@ class ThetaClientReactNative: RCTEventEmitter { } eventWebSocket.stop(completionHandler: { _ in }) - + class Callback: EventWebSocketCallback { let thetaClientReactNative: ThetaClientReactNative - + init(_ thetaClientReactNative: ThetaClientReactNative) { self.thetaClientReactNative = thetaClientReactNative } + func onClose() { thetaClientReactNative.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -2401,7 +2577,7 @@ class ThetaClientReactNative: RCTEventEmitter { ) ) } - + func onReceive(event: CameraEvent) { thetaClientReactNative.sendEvent( withName: ThetaClientReactNative.EVENT_NOTIFY, @@ -2420,7 +2596,7 @@ class ThetaClientReactNative: RCTEventEmitter { } } } - + @objc(eventWebSocketStop:withRejecter:) func eventWebSocketStop( resolve: @escaping RCTPromiseResolveBlock, diff --git a/react-native/package.json b/react-native/package.json index 02b0f6476ed..38ba0c265de 100644 --- a/react-native/package.json +++ b/react-native/package.json @@ -1,6 +1,6 @@ { "name": "theta-client-react-native", - "version": "1.12.1", + "version": "1.13.0", "description": "This library provides a way to control RICOH THETA using.", "main": "lib/commonjs/index", "module": "lib/module/index", diff --git a/react-native/src/__mocks__/react-native.ts b/react-native/src/__mocks__/react-native.ts index 453ed9b333d..f87fa1e0948 100644 --- a/react-native/src/__mocks__/react-native.ts +++ b/react-native/src/__mocks__/react-native.ts @@ -1,5 +1,6 @@ export const NativeModules = { ThetaClientReactNative: { + setApiLogListener: jest.fn(), initialize: jest.fn(), getThetaModel: jest.fn(), listFiles: jest.fn(), @@ -10,6 +11,11 @@ export const NativeModules = { buildTimeShiftCapture: jest.fn(), startTimeShiftCapture: jest.fn(), cancelTimeShiftCapture: jest.fn(), + getTimeShiftManualCaptureBuilder: jest.fn(), + buildTimeShiftManualCapture: jest.fn(), + startTimeShiftManualCapture: jest.fn(), + startTimeShiftManualSecondCapture: jest.fn(), + cancelTimeShiftManualCapture: jest.fn(), getVideoCaptureBuilder: jest.fn(), buildVideoCapture: jest.fn(), startVideoCapture: jest.fn(), diff --git a/react-native/src/__tests__/capture/continuous-capture.test.ts b/react-native/src/__tests__/capture/continuous-capture.test.ts index b8061e4540e..aeea8e9c366 100644 --- a/react-native/src/__tests__/capture/continuous-capture.test.ts +++ b/react-native/src/__tests__/capture/continuous-capture.test.ts @@ -259,7 +259,7 @@ describe('continuous shooting', () => { const options = { continuousNumber: ContinuousNumberEnum.MAX_10, }; - return options; + return { options: options }; }) ); diff --git a/react-native/src/__tests__/capture/time-shift-manual-capture.test.ts b/react-native/src/__tests__/capture/time-shift-manual-capture.test.ts new file mode 100644 index 00000000000..446864d5945 --- /dev/null +++ b/react-native/src/__tests__/capture/time-shift-manual-capture.test.ts @@ -0,0 +1,368 @@ +import { NativeModules } from 'react-native'; +import { + getTimeShiftManualCaptureBuilder, + initialize, +} from '../../theta-repository'; +import { + BaseNotify, + NotifyController, +} from '../../theta-repository/notify-controller'; +import { NativeEventEmitter_addListener } from '../../__mocks__/react-native'; +import { TimeShiftIntervalEnum } from '../../theta-repository/options'; +import { CapturingStatusEnum } from '../../capture'; + +describe('manual time shift capture', () => { + const thetaClient = NativeModules.ThetaClientReactNative; + + beforeEach(() => { + jest.clearAllMocks(); + NotifyController.instance.release(); + }); + + afterEach(() => { + thetaClient.initialize = jest.fn(); + thetaClient.buildTimeShiftManualCapture = jest.fn(); + thetaClient.startTimeShiftManualCapture = jest.fn(); + thetaClient.startTimeShiftManualSecondCapture = jest.fn(); + thetaClient.cancelTimeShiftManualCapture = jest.fn(); + NotifyController.instance.release(); + }); + + test('getTimeShiftManualCaptureBuilder', async () => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getTimeShiftManualCaptureBuilder(); + expect(builder.interval).toBeUndefined(); + expect(builder.options.timeShift).toBeUndefined(); + + builder.setCheckStatusCommandInterval(1); + builder.setIsFrontFirst(true); + builder.setFirstInterval(TimeShiftIntervalEnum.INTERVAL_1); + builder.setSecondInterval(TimeShiftIntervalEnum.INTERVAL_2); + + expect(builder.interval).toBe(1); + expect(builder.options.timeShift).toBeDefined(); + expect(builder.options.timeShift?.isFrontFirst).toBeTruthy(); + expect(builder.options.timeShift?.firstInterval).toBe( + TimeShiftIntervalEnum.INTERVAL_1 + ); + expect(builder.options.timeShift?.secondInterval).toBe( + TimeShiftIntervalEnum.INTERVAL_2 + ); + + let isCallBuild = false; + jest.mocked(thetaClient.buildTimeShiftManualCapture).mockImplementation( + jest.fn(async (options) => { + expect(options._capture_interval).toBe(1); + expect(options.timeShift?.isFrontFirst).toBeTruthy(); + expect(options.timeShift?.firstInterval).toBe( + TimeShiftIntervalEnum.INTERVAL_1 + ); + expect(options.timeShift?.secondInterval).toBe( + TimeShiftIntervalEnum.INTERVAL_2 + ); + isCallBuild = true; + }) + ); + + const capture = await builder.build(); + expect(capture).toBeDefined(); + expect(capture.notify).toBeDefined(); + expect(isCallBuild).toBeTruthy(); + }); + + test('build no interval', async () => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getTimeShiftManualCaptureBuilder(); + expect(builder.interval).toBeUndefined(); + + jest.mocked(thetaClient.buildTimeShiftManualCapture).mockImplementation( + jest.fn(async (options) => { + expect(options._capture_interval).toBe(-1); + expect(options.timeShift).toBeUndefined(); + }) + ); + + const capture = await builder.build(); + expect(capture).toBeDefined(); + }); + + test('startCapture', async () => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + await initialize(); + const builder = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildTimeShiftManualCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.JPG'; + jest.mocked(thetaClient.startTimeShiftManualCapture).mockImplementation( + jest.fn(async () => { + return testUrl; + }) + ); + + const capture = await builder.build(); + capture.startCapture().then((value) => { + expect(value).toBe(testUrl); + }); + capture.startSecondCapture(); + expect(thetaClient.startTimeShiftManualSecondCapture).toHaveBeenCalled(); + }); + + test('cancelCapture', (done) => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildTimeShiftManualCapture) + .mockImplementation(jest.fn(async () => {})); + jest.mocked(thetaClient.startTimeShiftManualCapture).mockImplementation( + jest.fn(async () => { + return null; + }) + ); + + builder.build().then((capture) => { + capture.startCapture().then((value) => { + expect(value).toBeUndefined(); + done(); + }); + capture.cancelCapture(); + expect(thetaClient.cancelTimeShiftManualCapture).toHaveBeenCalled(); + }); + }); + + test('exception', (done) => { + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn(() => { + return { + remove: jest.fn(), + }; + }) + ); + const builder = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildTimeShiftManualCapture) + .mockImplementation(jest.fn(async () => {})); + jest.mocked(thetaClient.startTimeShiftManualCapture).mockImplementation( + jest.fn(async () => { + throw 'error'; + }) + ); + + builder.build().then((capture) => { + capture + .startCapture() + .then(() => { + expect(true).toBeFalsy(); + }) + .catch((error) => { + expect(error).toBe('error'); + done(); + }); + }); + }); + + test('progress 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 = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildTimeShiftManualCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.JPG'; + + const sendProgress = (progress: number) => { + notifyCallback({ + name: 'TIME-SHIFT-MANUAL-PROGRESS', + params: { + completion: progress, + }, + }); + }; + + jest.mocked(thetaClient.startTimeShiftManualCapture).mockImplementation( + jest.fn(async () => { + sendProgress(0.5); + return testUrl; + }) + ); + + const capture = await builder.build(); + let isOnProgress = false; + const fileUrl = await capture.startCapture((completion) => { + expect(completion).toBe(0.5); + isOnProgress = 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(isOnProgress).toBeTruthy(); + done(0); + }, 1); + + return promise; + }); + + 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 = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildShotCountSpecifiedIntervalCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.JPG'; + + const sendStopError = (message: string) => { + notifyCallback({ + name: 'TIME-SHIFT-MANUAL-STOP-ERROR', + params: { + message, + }, + }); + }; + + jest.mocked(thetaClient.startTimeShiftManualCapture).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; + }); + + test('capturing 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 = getTimeShiftManualCaptureBuilder(); + jest + .mocked(thetaClient.buildShotCountSpecifiedIntervalCapture) + .mockImplementation(jest.fn(async () => {})); + const testUrl = 'http://192.168.1.1/files/100RICOH/R100.JPG'; + + const sendStatus = (status: CapturingStatusEnum) => { + notifyCallback({ + name: 'TIME-SHIFT-MANUAL-CAPTURING', + params: { + status, + }, + }); + }; + + jest.mocked(thetaClient.startTimeShiftManualCapture).mockImplementation( + jest.fn(async () => { + sendStatus(CapturingStatusEnum.SELF_TIMER_COUNTDOWN); + return testUrl; + }) + ); + + const capture = await builder.build(); + let isOnCapturing = false; + const fileUrl = await capture.startCapture( + undefined, + undefined, + (status) => { + expect(status).toBe(CapturingStatusEnum.SELF_TIMER_COUNTDOWN); + isOnCapturing = 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(isOnCapturing).toBeTruthy(); + done(0); + }, 1); + + return promise; + }); +}); diff --git a/react-native/src/__tests__/options/option-access-info.test.ts b/react-native/src/__tests__/options/option-access-info.test.ts new file mode 100644 index 00000000000..a42ab2f0f30 --- /dev/null +++ b/react-native/src/__tests__/options/option-access-info.test.ts @@ -0,0 +1,21 @@ +import { WlanFrequencyAccessInfoEnum } from '../../theta-repository/options/option-access-info'; + +describe('WlanFrequencyAccessInfoEnum', () => { + const data: string[][] = [ + [WlanFrequencyAccessInfoEnum.UNKNOWN, 'UNKNOWN'], + [WlanFrequencyAccessInfoEnum.GHZ_2_4, 'GHZ_2_4'], + [WlanFrequencyAccessInfoEnum.GHZ_5_2, 'GHZ_5_2'], + [WlanFrequencyAccessInfoEnum.GHZ_5_8, 'GHZ_5_8'], + [WlanFrequencyAccessInfoEnum.INITIAL_VALUE, 'INITIAL_VALUE'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(WlanFrequencyAccessInfoEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-ai-auto-thumbnail.test.tsx b/react-native/src/__tests__/options/option-ai-auto-thumbnail.test.tsx index 84a09a3ddc4..d4e28e97e6d 100644 --- a/react-native/src/__tests__/options/option-ai-auto-thumbnail.test.tsx +++ b/react-native/src/__tests__/options/option-ai-auto-thumbnail.test.tsx @@ -4,6 +4,7 @@ describe('AiAutoThumbnailEnum', () => { const data: [AiAutoThumbnailEnum, string][] = [ [AiAutoThumbnailEnum.ON, 'ON'], [AiAutoThumbnailEnum.OFF, 'OFF'], + [AiAutoThumbnailEnum.UNKNOWN, 'UNKNOWN'], ]; test('length', () => { diff --git a/react-native/src/__tests__/options/option-camera-control-source.test.tsx b/react-native/src/__tests__/options/option-camera-control-source.test.tsx index 5211a942146..192565cc6ba 100644 --- a/react-native/src/__tests__/options/option-camera-control-source.test.tsx +++ b/react-native/src/__tests__/options/option-camera-control-source.test.tsx @@ -2,6 +2,7 @@ import { CameraControlSourceEnum } from '../../theta-repository/options/option-c describe('CameraControlSourceEnum', () => { const data: string[][] = [ + [CameraControlSourceEnum.UNKNOWN, 'UNKNOWN'], [CameraControlSourceEnum.CAMERA, 'CAMERA'], [CameraControlSourceEnum.APP, 'APP'], ]; diff --git a/react-native/src/__tests__/options/option-camera-lock.test.tsx b/react-native/src/__tests__/options/option-camera-lock.test.tsx new file mode 100644 index 00000000000..ed881203092 --- /dev/null +++ b/react-native/src/__tests__/options/option-camera-lock.test.tsx @@ -0,0 +1,20 @@ +import { CameraLockEnum } from '../../theta-repository/options/option-camera-lock'; + +describe('CameraLockEnum', () => { + const data: string[][] = [ + [CameraLockEnum.UNKNOWN, 'UNKNOWN'], + [CameraLockEnum.UNLOCK, 'UNLOCK'], + [CameraLockEnum.BASIC_LOCK, 'BASIC_LOCK'], + [CameraLockEnum.CUSTOM_LOCK, 'CUSTOM_LOCK'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(CameraLockEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-camera-power.test.tsx b/react-native/src/__tests__/options/option-camera-power.test.tsx index 3613d0f1a16..2bc64b47c02 100644 --- a/react-native/src/__tests__/options/option-camera-power.test.tsx +++ b/react-native/src/__tests__/options/option-camera-power.test.tsx @@ -5,6 +5,7 @@ describe('CameraPowerEnum', () => { [CameraPowerEnum.UNKNOWN, 'UNKNOWN'], [CameraPowerEnum.ON, 'ON'], [CameraPowerEnum.OFF, 'OFF'], + [CameraPowerEnum.SLEEP, 'SLEEP'], [CameraPowerEnum.POWER_SAVING, 'POWER_SAVING'], [CameraPowerEnum.SILENT_MODE, 'SILENT_MODE'], ]; diff --git a/react-native/src/__tests__/options/option-compass-direction-ref.test.tsx b/react-native/src/__tests__/options/option-compass-direction-ref.test.tsx new file mode 100644 index 00000000000..edf5f96fe76 --- /dev/null +++ b/react-native/src/__tests__/options/option-compass-direction-ref.test.tsx @@ -0,0 +1,20 @@ +import { CompassDirectionRefEnum } from '../../theta-repository/options/option-compass-direction-ref'; + +describe('CompassDirectionRefEnum', () => { + const data: string[][] = [ + [CompassDirectionRefEnum.UNKNOWN, 'UNKNOWN'], + [CompassDirectionRefEnum.AUTO, 'AUTO'], + [CompassDirectionRefEnum.TRUE_NORTH, 'TRUE_NORTH'], + [CompassDirectionRefEnum.MAGNETIC, 'MAGNETIC'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(CompassDirectionRefEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-exposure-compensation.test.tsx b/react-native/src/__tests__/options/option-exposure-compensation.test.tsx new file mode 100644 index 00000000000..de912582d11 --- /dev/null +++ b/react-native/src/__tests__/options/option-exposure-compensation.test.tsx @@ -0,0 +1,42 @@ +import { ExposureCompensationEnum } from '../../theta-repository/options/option-exposure-compensation'; + +describe('ExposureCompensationEnum', () => { + const data: string[][] = [ + [ExposureCompensationEnum.UNKNOWN, 'UNKNOWN'], + [ExposureCompensationEnum.M_4_0, 'M4_0'], + [ExposureCompensationEnum.M_3_7, 'M3_7'], + [ExposureCompensationEnum.M_3_3, 'M3_3'], + [ExposureCompensationEnum.M_3_0, 'M3_0'], + [ExposureCompensationEnum.M_2_7, 'M2_7'], + [ExposureCompensationEnum.M_2_3, 'M2_3'], + [ExposureCompensationEnum.M_2_0, 'M2_0'], + [ExposureCompensationEnum.M_1_7, 'M1_7'], + [ExposureCompensationEnum.M_1_3, 'M1_3'], + [ExposureCompensationEnum.M_1_0, 'M1_0'], + [ExposureCompensationEnum.M_0_7, 'M0_7'], + [ExposureCompensationEnum.M_0_3, 'M0_3'], + [ExposureCompensationEnum.ZERO, 'ZERO'], + [ExposureCompensationEnum.P_0_3, 'P0_3'], + [ExposureCompensationEnum.P_0_7, 'P0_7'], + [ExposureCompensationEnum.P_1_0, 'P1_0'], + [ExposureCompensationEnum.P_1_3, 'P1_3'], + [ExposureCompensationEnum.P_1_7, 'P1_7'], + [ExposureCompensationEnum.P_2_0, 'P2_0'], + [ExposureCompensationEnum.P_2_3, 'P2_3'], + [ExposureCompensationEnum.P_2_7, 'P2_7'], + [ExposureCompensationEnum.P_3_0, 'P3_0'], + [ExposureCompensationEnum.P_3_3, 'P3_3'], + [ExposureCompensationEnum.P_3_7, 'P3_7'], + [ExposureCompensationEnum.P_4_0, 'P4_0'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(ExposureCompensationEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-file-format.test.tsx b/react-native/src/__tests__/options/option-file-format.test.tsx index a88d77ce639..e798b8f359e 100644 --- a/react-native/src/__tests__/options/option-file-format.test.tsx +++ b/react-native/src/__tests__/options/option-file-format.test.tsx @@ -51,6 +51,7 @@ describe('VideoFileFormatEnum', () => { [VideoFileFormatEnum.VIDEO_FULL_HD, 'VIDEO_FULL_HD'], [VideoFileFormatEnum.VIDEO_2K, 'VIDEO_2K'], [VideoFileFormatEnum.VIDEO_2K_NO_CODEC, 'VIDEO_2K_NO_CODEC'], + [VideoFileFormatEnum.VIDEO_2K_15F, 'VIDEO_2K_15F'], [VideoFileFormatEnum.VIDEO_2K_30F, 'VIDEO_2K_30F'], [VideoFileFormatEnum.VIDEO_2K_60F, 'VIDEO_2K_60F'], [VideoFileFormatEnum.VIDEO_2_7K_2752_2F, 'VIDEO_2_7K_2752_2F'], @@ -63,6 +64,8 @@ describe('VideoFileFormatEnum', () => { [VideoFileFormatEnum.VIDEO_3_6K_2F, 'VIDEO_3_6K_2F'], [VideoFileFormatEnum.VIDEO_4K, 'VIDEO_4K'], [VideoFileFormatEnum.VIDEO_4K_NO_CODEC, 'VIDEO_4K_NO_CODEC'], + [VideoFileFormatEnum.VIDEO_4K_2F, 'VIDEO_4K_2F'], + [VideoFileFormatEnum.VIDEO_4K_5F, 'VIDEO_4K_5F'], [VideoFileFormatEnum.VIDEO_4K_10F, 'VIDEO_4K_10F'], [VideoFileFormatEnum.VIDEO_4K_15F, 'VIDEO_4K_15F'], [VideoFileFormatEnum.VIDEO_4K_30F, 'VIDEO_4K_30F'], @@ -75,6 +78,17 @@ describe('VideoFileFormatEnum', () => { [VideoFileFormatEnum.VIDEO_7K_2F, 'VIDEO_7K_2F'], [VideoFileFormatEnum.VIDEO_7K_5F, 'VIDEO_7K_5F'], [VideoFileFormatEnum.VIDEO_7K_10F, 'VIDEO_7K_10F'], + [VideoFileFormatEnum.VIDEO_2K_30F_H265_HEVC, 'VIDEO_2K_30F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_4K_2F_H265_HEVC, 'VIDEO_4K_2F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_4K_5F_H265_HEVC, 'VIDEO_4K_5F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_4K_10F_H265_HEVC, 'VIDEO_4K_10F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_4K_30F_H265_HEVC, 'VIDEO_4K_30F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_5_7K_2F_H265_HEVC, 'VIDEO_5_7K_2F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_5_7K_5F_H265_HEVC, 'VIDEO_5_7K_5F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_5_7K_10F_H265_HEVC, 'VIDEO_5_7K_10F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_7K_2F_H265_HEVC, 'VIDEO_7K_2F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_7K_5F_H265_HEVC, 'VIDEO_7K_5F_H265_HEVC'], + [VideoFileFormatEnum.VIDEO_7K_10F_H265_HEVC, 'VIDEO_7K_10F_H265_HEVC'], ]; test('length', () => { @@ -101,6 +115,7 @@ describe('FileFormatEnum', () => { [FileFormatEnum.VIDEO_FULL_HD, 'VIDEO_FULL_HD'], [FileFormatEnum.VIDEO_2K, 'VIDEO_2K'], [FileFormatEnum.VIDEO_2K_NO_CODEC, 'VIDEO_2K_NO_CODEC'], + [FileFormatEnum.VIDEO_2K_15F, 'VIDEO_2K_15F'], [FileFormatEnum.VIDEO_2K_30F, 'VIDEO_2K_30F'], [FileFormatEnum.VIDEO_2K_60F, 'VIDEO_2K_60F'], [FileFormatEnum.VIDEO_2_7K_2752_2F, 'VIDEO_2_7K_2752_2F'], @@ -113,6 +128,8 @@ describe('FileFormatEnum', () => { [FileFormatEnum.VIDEO_3_6K_2F, 'VIDEO_3_6K_2F'], [FileFormatEnum.VIDEO_4K, 'VIDEO_4K'], [FileFormatEnum.VIDEO_4K_NO_CODEC, 'VIDEO_4K_NO_CODEC'], + [FileFormatEnum.VIDEO_4K_2F, 'VIDEO_4K_2F'], + [FileFormatEnum.VIDEO_4K_5F, 'VIDEO_4K_5F'], [FileFormatEnum.VIDEO_4K_10F, 'VIDEO_4K_10F'], [FileFormatEnum.VIDEO_4K_15F, 'VIDEO_4K_15F'], [FileFormatEnum.VIDEO_4K_30F, 'VIDEO_4K_30F'], @@ -125,6 +142,17 @@ describe('FileFormatEnum', () => { [FileFormatEnum.VIDEO_7K_2F, 'VIDEO_7K_2F'], [FileFormatEnum.VIDEO_7K_5F, 'VIDEO_7K_5F'], [FileFormatEnum.VIDEO_7K_10F, 'VIDEO_7K_10F'], + [FileFormatEnum.VIDEO_2K_30F_H265_HEVC, 'VIDEO_2K_30F_H265_HEVC'], + [FileFormatEnum.VIDEO_4K_2F_H265_HEVC, 'VIDEO_4K_2F_H265_HEVC'], + [FileFormatEnum.VIDEO_4K_5F_H265_HEVC, 'VIDEO_4K_5F_H265_HEVC'], + [FileFormatEnum.VIDEO_4K_10F_H265_HEVC, 'VIDEO_4K_10F_H265_HEVC'], + [FileFormatEnum.VIDEO_4K_30F_H265_HEVC, 'VIDEO_4K_30F_H265_HEVC'], + [FileFormatEnum.VIDEO_5_7K_2F_H265_HEVC, 'VIDEO_5_7K_2F_H265_HEVC'], + [FileFormatEnum.VIDEO_5_7K_5F_H265_HEVC, 'VIDEO_5_7K_5F_H265_HEVC'], + [FileFormatEnum.VIDEO_5_7K_10F_H265_HEVC, 'VIDEO_5_7K_10F_H265_HEVC'], + [FileFormatEnum.VIDEO_7K_2F_H265_HEVC, 'VIDEO_7K_2F_H265_HEVC'], + [FileFormatEnum.VIDEO_7K_5F_H265_HEVC, 'VIDEO_7K_5F_H265_HEVC'], + [FileFormatEnum.VIDEO_7K_10F_H265_HEVC, 'VIDEO_7K_10F_H265_HEVC'], ]; test('length', () => { diff --git a/react-native/src/__tests__/options/option-microphone-noise-reduction.test.tsx b/react-native/src/__tests__/options/option-microphone-noise-reduction.test.tsx new file mode 100644 index 00000000000..abc6ae1a81f --- /dev/null +++ b/react-native/src/__tests__/options/option-microphone-noise-reduction.test.tsx @@ -0,0 +1,19 @@ +import { MicrophoneNoiseReductionEnum } from '../../theta-repository/options/option-microphone-noise-reduction'; + +describe('MicrophoneNoiseReductionEnum', () => { + const data: string[][] = [ + [MicrophoneNoiseReductionEnum.UNKNOWN, 'UNKNOWN'], + [MicrophoneNoiseReductionEnum.ON, 'ON'], + [MicrophoneNoiseReductionEnum.OFF, 'OFF'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(MicrophoneNoiseReductionEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-mobile-network-setting.test.tsx b/react-native/src/__tests__/options/option-mobile-network-setting.test.tsx new file mode 100644 index 00000000000..5b39451330c --- /dev/null +++ b/react-native/src/__tests__/options/option-mobile-network-setting.test.tsx @@ -0,0 +1,37 @@ +import { PlanEnum, RoamingEnum } from '../../theta-repository/options'; + +describe('RoamingEnum', () => { + const data: string[][] = [ + [RoamingEnum.UNKNOWN, 'UNKNOWN'], + [RoamingEnum.OFF, 'OFF'], + [RoamingEnum.ON, 'ON'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(RoamingEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); + +describe('PlanEnum', () => { + const data: string[][] = [ + [PlanEnum.UNKNOWN, 'UNKNOWN'], + [PlanEnum.SORACOM, 'SORACOM'], + [PlanEnum.SORACOM_PLAN_DU, 'SORACOM_PLAN_DU'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(PlanEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-network-type.test.tsx b/react-native/src/__tests__/options/option-network-type.test.tsx index 2c21166608d..7961a14b485 100644 --- a/react-native/src/__tests__/options/option-network-type.test.tsx +++ b/react-native/src/__tests__/options/option-network-type.test.tsx @@ -2,10 +2,18 @@ import { NetworkTypeEnum } from '../../theta-repository/options/option-network-t describe('NetworkTypeEnum', () => { const data: string[][] = [ + [NetworkTypeEnum.UNKNOWN, 'UNKNOWN'], [NetworkTypeEnum.DIRECT, 'DIRECT'], [NetworkTypeEnum.CLIENT, 'CLIENT'], [NetworkTypeEnum.ETHERNET, 'ETHERNET'], [NetworkTypeEnum.OFF, 'OFF'], + [NetworkTypeEnum.LTE_D, 'LTE_D'], + [NetworkTypeEnum.LTE_DU, 'LTE_DU'], + [NetworkTypeEnum.LTE_01S, 'LTE_01S'], + [NetworkTypeEnum.LTE_X3, 'LTE_X3'], + [NetworkTypeEnum.LTE_P1, 'LTE_P1'], + [NetworkTypeEnum.LTE_K2, 'LTE_K2'], + [NetworkTypeEnum.LTE_K, 'LTE_K'], ]; test('length', () => { diff --git a/react-native/src/__tests__/options/option-off-delay-usb.test.tsx b/react-native/src/__tests__/options/option-off-delay-usb.test.tsx new file mode 100644 index 00000000000..347a1714cf1 --- /dev/null +++ b/react-native/src/__tests__/options/option-off-delay-usb.test.tsx @@ -0,0 +1,45 @@ +import { + OffDelayUsbEnum, + offDelayUsbToSeconds, +} from '../../theta-repository/options'; + +describe('OffDelayUsbEnum', () => { + const data: string[][] = [ + [OffDelayUsbEnum.DISABLE, 'DISABLE'], + [OffDelayUsbEnum.OFF_DELAY_10M, 'OFF_DELAY_10M'], + [OffDelayUsbEnum.OFF_DELAY_1H, 'OFF_DELAY_1H'], + [OffDelayUsbEnum.OFF_DELAY_2H, 'OFF_DELAY_2H'], + [OffDelayUsbEnum.OFF_DELAY_4H, 'OFF_DELAY_4H'], + [OffDelayUsbEnum.OFF_DELAY_8H, 'OFF_DELAY_8H'], + [OffDelayUsbEnum.OFF_DELAY_12H, 'OFF_DELAY_12H'], + [OffDelayUsbEnum.OFF_DELAY_18H, 'OFF_DELAY_18H'], + [OffDelayUsbEnum.OFF_DELAY_24H, 'OFF_DELAY_24H'], + [OffDelayUsbEnum.OFF_DELAY_2D, 'OFF_DELAY_2D'], + ]; + const timeData: [OffDelayUsbEnum, number][] = [ + [OffDelayUsbEnum.DISABLE, 0], + [OffDelayUsbEnum.OFF_DELAY_10M, 60 * 10], + [OffDelayUsbEnum.OFF_DELAY_1H, 60 * 60], + [OffDelayUsbEnum.OFF_DELAY_2H, 60 * 60 * 2], + [OffDelayUsbEnum.OFF_DELAY_4H, 60 * 60 * 4], + [OffDelayUsbEnum.OFF_DELAY_8H, 60 * 60 * 8], + [OffDelayUsbEnum.OFF_DELAY_12H, 60 * 60 * 12], + [OffDelayUsbEnum.OFF_DELAY_18H, 60 * 60 * 18], + [OffDelayUsbEnum.OFF_DELAY_24H, 60 * 60 * 24], + [OffDelayUsbEnum.OFF_DELAY_2D, 60 * 60 * 24 * 2], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(OffDelayUsbEnum).length); + expect(timeData.length).toBe(Object.keys(OffDelayUsbEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + timeData.forEach((item) => { + expect(offDelayUsbToSeconds(item[0])).toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-preview-format.test.tsx b/react-native/src/__tests__/options/option-preview-format.test.tsx index 5d140ee9c79..aa02baf132f 100644 --- a/react-native/src/__tests__/options/option-preview-format.test.tsx +++ b/react-native/src/__tests__/options/option-preview-format.test.tsx @@ -2,14 +2,17 @@ import { PreviewFormatEnum } from '../../theta-repository/options/option-preview describe('PreviewFormatEnum', () => { const data: string[][] = [ + [PreviewFormatEnum.UNKNOWN, 'UNKNOWN'], + [PreviewFormatEnum.W1920_H960_F30, 'W1920_H960_F30'], + [PreviewFormatEnum.W1920_H960_F8, 'W1920_H960_F8'], [PreviewFormatEnum.W1024_H512_F30, 'W1024_H512_F30'], [PreviewFormatEnum.W1024_H512_F15, 'W1024_H512_F15'], - [PreviewFormatEnum.W512_H512_F30, 'W512_H512_F30'], - [PreviewFormatEnum.W1920_H960_F8, 'W1920_H960_F8'], + [PreviewFormatEnum.W1024_H512_F10, 'W1024_H512_F10'], [PreviewFormatEnum.W1024_H512_F8, 'W1024_H512_F8'], [PreviewFormatEnum.W640_H320_F30, 'W640_H320_F30'], - [PreviewFormatEnum.W640_H320_F8, 'W640_H320_F8'], [PreviewFormatEnum.W640_H320_F10, 'W640_H320_F10'], + [PreviewFormatEnum.W640_H320_F8, 'W640_H320_F8'], + [PreviewFormatEnum.W512_H512_F30, 'W512_H512_F30'], [PreviewFormatEnum.W3840_H1920_F30, 'W3840_H1920_F30'], ]; diff --git a/react-native/src/__tests__/options/option-top-bottom-correction-rotation-support.test.tsx b/react-native/src/__tests__/options/option-top-bottom-correction-rotation-support.test.tsx new file mode 100644 index 00000000000..b148fc7b228 --- /dev/null +++ b/react-native/src/__tests__/options/option-top-bottom-correction-rotation-support.test.tsx @@ -0,0 +1,44 @@ +import { convertOptions } from '../../theta-repository/libs'; +import type { Options } from '../../theta-repository/options'; + +describe('convertOptions', () => { + test('getOption', () => { + const pitchData = { max: 10.0, min: -15.0, stepSize: 1.0 }; + const rollData = { max: 20.0, min: -5.0, stepSize: 0.5 }; + const yawData = { max: 30.0, min: -10.0, stepSize: 2.0 }; + + const jsonOptions = { + topBottomCorrectionRotationSupport: JSON.stringify({ + pitch: pitchData, + roll: rollData, + yaw: yawData, + }), + }; + + const emptyOptions: Options = {}; + const expectedOptions: Options = { + topBottomCorrectionRotationSupport: { + pitch: { + max: pitchData.max, + min: pitchData.min, + stepSize: pitchData.stepSize, + }, + roll: { + max: rollData.max, + min: rollData.min, + stepSize: rollData.stepSize, + }, + yaw: { + max: yawData.max, + min: yawData.min, + stepSize: yawData.stepSize, + }, + }, + }; + + const options = convertOptions(emptyOptions, jsonOptions); + expect(options.topBottomCorrectionRotationSupport).toEqual( + expectedOptions.topBottomCorrectionRotationSupport + ); + }); +}); diff --git a/react-native/src/__tests__/options/option-usb-connection.test.tsx b/react-native/src/__tests__/options/option-usb-connection.test.tsx new file mode 100644 index 00000000000..78b45affd4e --- /dev/null +++ b/react-native/src/__tests__/options/option-usb-connection.test.tsx @@ -0,0 +1,19 @@ +import { UsbConnectionEnum } from '../../theta-repository/options/option-usb-connection'; + +describe('UsbConnectionEnum', () => { + const data: string[][] = [ + [UsbConnectionEnum.UNKNOWN, 'UNKNOWN'], + [UsbConnectionEnum.MTP, 'MTP'], + [UsbConnectionEnum.MSC, 'MSC'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(UsbConnectionEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-wlan-antenna-config.test.tsx b/react-native/src/__tests__/options/option-wlan-antenna-config.test.tsx new file mode 100644 index 00000000000..ab22598c40d --- /dev/null +++ b/react-native/src/__tests__/options/option-wlan-antenna-config.test.tsx @@ -0,0 +1,19 @@ +import { WlanAntennaConfigEnum } from '../../theta-repository/options/option-wlan-antenna-config'; + +describe('WlanAntennaConfigEnum', () => { + const data: string[][] = [ + [WlanAntennaConfigEnum.UNKNOWN, 'UNKNOWN'], + [WlanAntennaConfigEnum.SISO, 'SISO'], + [WlanAntennaConfigEnum.MIMO, 'MIMO'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(WlanAntennaConfigEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0] ? item[0].toString() : '').toBe(item[1]); + }); + }); +}); diff --git a/react-native/src/__tests__/options/option-wlan-frequency.test.tsx b/react-native/src/__tests__/options/option-wlan-frequency.test.tsx index dd0d04d6a5b..a41d0f3fa7d 100644 --- a/react-native/src/__tests__/options/option-wlan-frequency.test.tsx +++ b/react-native/src/__tests__/options/option-wlan-frequency.test.tsx @@ -2,8 +2,11 @@ import { WlanFrequencyEnum } from '../../theta-repository/options/option-wlan-fr describe('WlanFrequencyEnum', () => { const data: string[][] = [ + [WlanFrequencyEnum.UNKNOWN, 'UNKNOWN'], [WlanFrequencyEnum.GHZ_2_4, 'GHZ_2_4'], [WlanFrequencyEnum.GHZ_5, 'GHZ_5'], + [WlanFrequencyEnum.GHZ_5_2, 'GHZ_5_2'], + [WlanFrequencyEnum.GHZ_5_8, 'GHZ_5_8'], ]; test('length', () => { diff --git a/react-native/src/__tests__/theta-repository/access-point.test.tsx b/react-native/src/__tests__/theta-repository/access-point.test.tsx new file mode 100644 index 00000000000..a47b6b7ab27 --- /dev/null +++ b/react-native/src/__tests__/theta-repository/access-point.test.tsx @@ -0,0 +1,21 @@ +import { AuthModeEnum } from '../../theta-repository'; + +describe('AuthModeEnum', () => { + const data: [AuthModeEnum, string][] = [ + [AuthModeEnum.UNKNOWN, 'UNKNOWN'], + [AuthModeEnum.NONE, 'NONE'], + [AuthModeEnum.WEP, 'WEP'], + [AuthModeEnum.WPA, 'WPA'], + [AuthModeEnum.WPA3, 'WPA3'], + ]; + + test('length', () => { + expect(data.length).toBe(Object.keys(AuthModeEnum).length); + }); + + test('data', () => { + data.forEach((item) => { + expect(item[0]).toBe(item[1]); + }); + }); +}); 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 40b9a9adf4d..abc2d45d740 100644 --- a/react-native/src/__tests__/theta-repository/list-files.test.ts +++ b/react-native/src/__tests__/theta-repository/list-files.test.ts @@ -25,7 +25,9 @@ describe('listFiles', () => { ]; const codecEnumArray: [CodecEnum, string][] = [ + [CodecEnum.UNKNOWN, 'UNKNOWN'], [CodecEnum.H264MP4AVC, 'H264MP4AVC'], + [CodecEnum.H265HEVC, 'H265HEVC'], ]; const projectionTypeEnumArray: [ProjectionTypeEnum, string][] = [ diff --git a/react-native/src/__tests__/theta-repository/set-api-log-listener.test.ts b/react-native/src/__tests__/theta-repository/set-api-log-listener.test.ts new file mode 100644 index 00000000000..4c4e813da1b --- /dev/null +++ b/react-native/src/__tests__/theta-repository/set-api-log-listener.test.ts @@ -0,0 +1,90 @@ +import { initialize, setApiLogListener } from '../../theta-repository'; +import { + BaseNotify, + NotifyController, +} from '../../theta-repository/notify-controller'; +import { NativeEventEmitter_addListener } from '../../__mocks__/react-native'; + +describe('setApiLogListener', () => { + beforeEach(() => { + jest.clearAllMocks(); + setApiLogListener(); + NotifyController.instance.release(); + }); + + afterEach(() => { + jest.clearAllMocks(); + setApiLogListener(); + NotifyController.instance.release(); + }); + + test('callback set before initialize', async () => { + let notifyCallback: (notify: BaseNotify) => void = () => { + expect(true).toBeFalsy(); + }; + jest.mocked(NativeEventEmitter_addListener).mockImplementation( + jest.fn((_, callback) => { + notifyCallback = callback; + return { + remove: jest.fn(), + }; + }) + ); + + let isCallCallback = false; + await setApiLogListener((message) => { + expect(message).toBe('log-message'); + isCallCallback = true; + }); + await initialize(); + notifyCallback({ + name: 'API-LOG', + params: { + message: 'log-message', + }, + }); + + expect(NotifyController.instance.existsNotify('API-LOG')).toBeTruthy(); + expect(isCallCallback).toBeTruthy(); + }); + + test('callback set after initialize', 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(); + expect(NotifyController.instance.existsNotify('API-LOG')).toBeFalsy(); + let isCallCallback = false; + await setApiLogListener((message) => { + expect(message).toBe('log-message'); + isCallCallback = true; + }); + notifyCallback({ + name: 'API-LOG', + params: { + message: 'log-message', + }, + }); + + expect(NotifyController.instance.existsNotify('API-LOG')).toBeTruthy(); + expect(isCallCallback).toBeTruthy(); + }); + + test('remove callback', async () => { + await setApiLogListener(() => {}); + await initialize(); + expect(NotifyController.instance.existsNotify('API-LOG')).toBeTruthy(); + + await setApiLogListener(); + expect(NotifyController.instance.existsNotify('API-LOG')).toBeFalsy(); + }); +}); diff --git a/react-native/src/__tests__/theta-repository/theta-model.test.ts b/react-native/src/__tests__/theta-repository/theta-model.test.ts index 67a9f6b7443..a7bd6063a28 100644 --- a/react-native/src/__tests__/theta-repository/theta-model.test.ts +++ b/react-native/src/__tests__/theta-repository/theta-model.test.ts @@ -12,6 +12,7 @@ describe('getThetaModel', () => { [ThetaModel.THETA_X, 'THETA_X'], [ThetaModel.THETA_SC2, 'THETA_SC2'], [ThetaModel.THETA_SC2_B, 'THETA_SC2_B'], + [ThetaModel.THETA_A1, 'THETA_A1'], ]; beforeEach(() => { diff --git a/react-native/src/__tests__/theta-repository/theta-state/capture-status.test.ts b/react-native/src/__tests__/theta-repository/theta-state/capture-status.test.ts index bbaa6f890f8..06c78c78d11 100644 --- a/react-native/src/__tests__/theta-repository/theta-state/capture-status.test.ts +++ b/react-native/src/__tests__/theta-repository/theta-state/capture-status.test.ts @@ -15,6 +15,7 @@ describe('CaptureStatusEnum', () => { 'RETROSPECTIVE_IMAGE_RECORDING', ], [CaptureStatusEnum.BURST_SHOOTING, 'BURST_SHOOTING'], + [CaptureStatusEnum.TIME_SHIFT_SHOOTING_IDLE, 'TIME_SHIFT_SHOOTING_IDLE'], ]; test('length', () => { diff --git a/react-native/src/capture/capture.ts b/react-native/src/capture/capture.ts index d9c543b9f48..bfc57870831 100644 --- a/react-native/src/capture/capture.ts +++ b/react-native/src/capture/capture.ts @@ -126,10 +126,21 @@ export abstract class CaptureBuilder> { /** Capturing status */ export const CapturingStatusEnum = { + /** The process is starting */ + STARTING: 'STARTING', /** Capture in progress */ CAPTURING: 'CAPTURING', /** Self-timer in progress */ SELF_TIMER_COUNTDOWN: 'SELF_TIMER_COUNTDOWN', + /** Performing timeShift shooting */ + TIME_SHIFT_SHOOTING: 'TIME_SHIFT_SHOOTING', + /** Performing second capture of manual timeShift shooting */ + TIME_SHIFT_SHOOTING_SECOND: 'TIME_SHIFT_SHOOTING_SECOND', + /** + * In the case of time-lag shooting by manual lens, + * set while waiting for the second shot to be taken after the first shot is completed. + */ + TIME_SHIFT_SHOOTING_IDLE: 'TIME_SHIFT_SHOOTING_IDLE', } as const; /** type definition of CapturingStatusEnum */ diff --git a/react-native/src/capture/continuous-capture.ts b/react-native/src/capture/continuous-capture.ts index dc329cba91d..6edae739c9b 100644 --- a/react-native/src/capture/continuous-capture.ts +++ b/react-native/src/capture/continuous-capture.ts @@ -78,10 +78,8 @@ export class ContinuousCapture { * @returns ContinuousNumberEnum */ async getContinuousNumber(): Promise { - return ( - (await getOptions([OptionNameEnum.ContinuousNumber])).continuousNumber ?? - ContinuousNumberEnum.UNSUPPORTED - ); + const options = await getOptions([OptionNameEnum.ContinuousNumber]); + return options?.continuousNumber ?? ContinuousNumberEnum.UNSUPPORTED; } } diff --git a/react-native/src/capture/index.ts b/react-native/src/capture/index.ts index d9ed88e599a..89b55e8eb07 100644 --- a/react-native/src/capture/index.ts +++ b/react-native/src/capture/index.ts @@ -2,6 +2,7 @@ export * from './capture'; export * from './photo-capture'; export * from './video-capture'; export * from './time-shift-capture'; +export * from './time-shift-manual-capture'; export * from './limitless-interval-capture'; export * from './shot-count-specified-interval-capture'; export * from './composite-interval-capture'; diff --git a/react-native/src/capture/time-shift-manual-capture.ts b/react-native/src/capture/time-shift-manual-capture.ts new file mode 100644 index 00000000000..635027961dc --- /dev/null +++ b/react-native/src/capture/time-shift-manual-capture.ts @@ -0,0 +1,203 @@ +import { CaptureBuilder, CapturingStatusEnum } from './capture'; +import { NativeModules } from 'react-native'; +import type { TimeShiftIntervalEnum } from '../theta-repository/options'; +import { + BaseNotify, + NotifyController, +} from '../theta-repository/notify-controller'; +const ThetaClientReactNative = NativeModules.ThetaClientReactNative; + +const NOTIFY_NAME = 'TIME-SHIFT-MANUAL-PROGRESS'; +const NOTIFY_STOP_ERROR = 'TIME-SHIFT-MANUAL-STOP-ERROR'; +const NOTIFY_CAPTURING = 'TIME-SHIFT-MANUAL-CAPTURING'; + +interface CaptureProgressNotify extends BaseNotify { + params?: { + completion: number; + }; +} + +interface CaptureStopErrorNotify extends BaseNotify { + params?: { + message: string; + }; +} + +interface CapturingNotify extends BaseNotify { + params?: { + status: CapturingStatusEnum; + }; +} + +/** + * TimeShiftManualCapture class + */ +export class TimeShiftManualCapture { + notify: NotifyController; + constructor(notify: NotifyController) { + this.notify = notify; + } + /** + * start manual time-shift + * + * Later, need to call startSecondCapture() or cancelCapture() + * + * @param onProgress the block for time-shift onProgress + * @param onStopFailed the block for error of cancelCapture + * @param onCapturing Called when change capture status + * @return promise of captured file url + */ + async startCapture( + onProgress?: (completion?: number) => void, + onStopFailed?: (error: any) => void, + onCapturing?: (status: CapturingStatusEnum) => void + ): Promise { + if (onProgress) { + this.notify.addNotify(NOTIFY_NAME, (event: CaptureProgressNotify) => { + onProgress(event.params?.completion); + }); + } + if (onStopFailed) { + this.notify.addNotify( + NOTIFY_STOP_ERROR, + (event: CaptureStopErrorNotify) => { + onStopFailed(event.params); + } + ); + } + if (onCapturing) { + this.notify.addNotify(NOTIFY_CAPTURING, (event: CapturingNotify) => { + if (event.params?.status) { + onCapturing(event.params.status); + } + }); + } + + return new Promise(async (resolve, reject) => { + await ThetaClientReactNative.startTimeShiftManualCapture() + .then((result?: string) => { + resolve(result ?? undefined); + }) + .catch((error: any) => { + reject(error); + }) + .finally(() => { + this.notify.removeNotify(NOTIFY_NAME); + this.notify.removeNotify(NOTIFY_STOP_ERROR); + this.notify.removeNotify(NOTIFY_CAPTURING); + }); + }); + } + + /** + * cancel manual time-shift + */ + async startSecondCapture(): Promise { + return await ThetaClientReactNative.startTimeShiftManualSecondCapture(); + } + + /** + * cancel manual time-shift + */ + async cancelCapture(): Promise { + return await ThetaClientReactNative.cancelTimeShiftManualCapture(); + } +} + +/** + * TimeShiftManualCaptureBuilder class + */ +export class TimeShiftManualCaptureBuilder extends CaptureBuilder { + interval?: number; + + /** construct TimeShiftManualCaptureBuilder instance */ + constructor() { + super(); + + this.interval = undefined; + } + + /** + * set interval of checking time-shift status command + * @param timeMillis interval + * @returns TimeShiftManualCaptureBuilder + */ + setCheckStatusCommandInterval( + timeMillis: number + ): TimeShiftManualCaptureBuilder { + this.interval = timeMillis; + return this; + } + + /** + * Set is front first. + * @param isFrontFirst is front first + * @returns TimeShiftManualCaptureBuilder + */ + setIsFrontFirst(isFrontFirst: boolean): TimeShiftManualCaptureBuilder { + this.checkAndInitTimeShiftSetting(); + if (this.options.timeShift != null) { + this.options.timeShift.isFrontFirst = isFrontFirst; + } + return this; + } + + /** + * set time (sec) before 1st lens shooting + * @param interval 1st interval + * @returns TimeShiftManualCaptureBuilder + */ + setFirstInterval( + interval: TimeShiftIntervalEnum + ): TimeShiftManualCaptureBuilder { + this.checkAndInitTimeShiftSetting(); + if (this.options.timeShift != null) { + this.options.timeShift.firstInterval = interval; + } + return this; + } + + /** + * set time (sec) from 1st lens shooting until start of 2nd lens shooting. + * @param interval 2nd interval + * @returns TimeShiftManualCaptureBuilder + */ + setSecondInterval( + interval: TimeShiftIntervalEnum + ): TimeShiftManualCaptureBuilder { + this.checkAndInitTimeShiftSetting(); + if (this.options.timeShift != null) { + this.options.timeShift.secondInterval = interval; + } + return this; + } + + private checkAndInitTimeShiftSetting() { + if (this.options.timeShift == null) { + this.options.timeShift = {}; + } + } + + /** + * Builds an instance of a TimeShiftManualCapture that has all the combined + * parameters of the Options that have been added to the Builder. + * + * @return promise of TimeShiftManualCapture instance + */ + build(): Promise { + 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.getTimeShiftManualCaptureBuilder(); + await ThetaClientReactNative.buildTimeShiftManualCapture(params); + resolve(new TimeShiftManualCapture(NotifyController.instance)); + } catch (error) { + reject(error); + } + }); + } +} diff --git a/react-native/src/theta-repository/access-point.ts b/react-native/src/theta-repository/access-point.ts index 66d0c12ffde..65e3c20a591 100644 --- a/react-native/src/theta-repository/access-point.ts +++ b/react-native/src/theta-repository/access-point.ts @@ -4,12 +4,16 @@ import type { Proxy } from './options'; * Enum for authentication mode. */ export const AuthModeEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** None of authentication */ NONE: 'NONE', /** Wep authentication */ WEP: 'WEP', /** WPA PSK or WPA2 PSK authentication */ WPA: 'WPA', + /** WPA3-SAE authentication */ + WPA3: 'WPA3', } as const; /** type definition of AuthModeEnum */ @@ -17,13 +21,13 @@ export type AuthModeEnum = (typeof AuthModeEnum)[keyof typeof AuthModeEnum]; /** Access point information. */ export type AccessPoint = { - /** SSID of the access point. */ + /** SSID */ ssid: string; - /** true if SSID stealth is enabled. */ + /** SSID stealth. Default is false. */ ssidStealth: boolean; /** Authentication mode. */ authMode: AuthModeEnum; - /** Connection priority 1 to 5. */ + /** Connection priority (1 to 5). Default is 1. */ connectionPriority: number; /** Using DHCP or not. */ usingDhcp: boolean; @@ -33,6 +37,10 @@ export type AccessPoint = { subnetMask?: string; /** Default Gateway. */ defaultGateway?: string; + /** Primary DNS server. */ + dns1?: string; + /** Secondary DNS server. */ + dns2?: string; /** Proxy information to be used when wired LAN is enabled. */ proxy?: Proxy; }; diff --git a/react-native/src/theta-repository/libs/convert-utils.ts b/react-native/src/theta-repository/libs/convert-utils.ts new file mode 100644 index 00000000000..1ab8a009bae --- /dev/null +++ b/react-native/src/theta-repository/libs/convert-utils.ts @@ -0,0 +1,27 @@ +import type { Options } from '../options'; + +export function convertOptions( + options: Options, + jsonOptions?: Record +): Options { + if (!jsonOptions) { + return options; + } + + const result = { ...options }; + + let jsonString = JSON.stringify(jsonOptions); + jsonString = jsonString + .replace(/\\"/g, '"') + .replace(/\\n/g, '') + .replace(/"{/g, '{') + .replace(/}"/g, '}'); + const parsedOptions = JSON.parse(jsonString) as Options; + + if (parsedOptions.topBottomCorrectionRotationSupport) { + result.topBottomCorrectionRotationSupport = + parsedOptions.topBottomCorrectionRotationSupport; + } + + return result; +} diff --git a/react-native/src/theta-repository/libs/index.ts b/react-native/src/theta-repository/libs/index.ts index 881f73cdb1c..6dff12f6f5b 100644 --- a/react-native/src/theta-repository/libs/index.ts +++ b/react-native/src/theta-repository/libs/index.ts @@ -1 +1,2 @@ export * from './convert-video-formats'; +export * from './convert-utils'; diff --git a/react-native/src/theta-repository/notify-controller.ts b/react-native/src/theta-repository/notify-controller.ts index 7161f986e29..c688e9d476a 100644 --- a/react-native/src/theta-repository/notify-controller.ts +++ b/react-native/src/theta-repository/notify-controller.ts @@ -15,19 +15,27 @@ export class NotifyController { return NotifyController._instance; } + eventEmitter?: NativeEventEmitter = undefined; eventListener?: EmitterSubscription = undefined; notifyList = new Map void) | undefined>(); init() { - this.release(); - this.eventListener = this.addNotifyListener((notify: BaseNotify) => { - this.notifyList.get(notify.name)?.(notify as BaseNotify); - }); + this.notifyList.clear(); + if (this.eventListener == null) { + this.eventListener = this.addNotifyListener((notify: BaseNotify) => { + this.notifyList.get(notify.name)?.(notify as BaseNotify); + }); + } + } + + isInit() { + return this.eventListener != null; } release() { this.eventListener?.remove(); this.eventListener = undefined; + this.eventEmitter = undefined; this.notifyList.clear(); } @@ -46,9 +54,11 @@ export class NotifyController { private addNotifyListener( callback: (event: BaseNotify) => void ): EmitterSubscription { - const eventEmitter = new NativeEventEmitter( - NativeModules.ThetaClientReactNative - ); - return eventEmitter.addListener('ThetaNotify', callback); + if (this.eventEmitter == null) { + this.eventEmitter = new NativeEventEmitter( + NativeModules.ThetaClientReactNative + ); + } + return this.eventEmitter.addListener('ThetaNotify', callback); } } diff --git a/react-native/src/theta-repository/options/index.ts b/react-native/src/theta-repository/options/index.ts index 781aa3a04d1..c542143c99a 100644 --- a/react-native/src/theta-repository/options/index.ts +++ b/react-native/src/theta-repository/options/index.ts @@ -1,23 +1,34 @@ export * from './options'; +export * from './option-access-info'; export * from './option-ai-auto-thumbnail'; +export * from './option-aperture'; export * from './option-auto-bracket'; export * from './option-bitrate'; export * from './option-bluetooth-role'; export * from './option-burst-mode'; export * from './option-burst-option'; export * from './option-camera-control-source'; +export * from './option-camera-lock-config'; +export * from './option-camera-lock'; export * from './option-camera-mode'; export * from './option-camera-power'; export * from './option-capture-mode'; +export * from './option-compass-direction-ref'; export * from './option-continuous-number'; +export * from './option-ethernet-config'; +export * from './option-exposure-compensation'; export * from './option-face-detect'; export * from './option-file-format'; export * from './option-filter'; export * from './option-function'; export * from './option-gain'; +export * from './option-gps-tag-recording'; export * from './option-image-stitching'; export * from './option-max-recordable-time'; +export * from './option-microphone-noise-reduction'; +export * from './option-mobile-network-setting'; export * from './option-network-type'; +export * from './option-off-delay-usb'; export * from './option-off-delay'; export * from './option-power-saving'; export * from './option-preset'; @@ -27,10 +38,13 @@ export * from './option-shooting-method'; export * from './option-shutter-speed'; export * from './option-sleep-delay'; export * from './option-time-shift'; -export * from './option-top-bottom-correction'; +export * from './option-top-bottom-correction-rotation-support'; export * from './option-top-bottom-correction-rotation'; +export * from './option-top-bottom-correction'; +export * from './option-usb-connection'; export * from './option-video-stitching'; export * from './option-visibility-reduction'; export * from './option-white-balance-auto-strength'; +export * from './option-wlan-antenna-config'; +export * from './option-wlan-frequency-cl-mode'; export * from './option-wlan-frequency'; -export * from './option-ethernet-config'; diff --git a/react-native/src/theta-repository/options/option-access-info.ts b/react-native/src/theta-repository/options/option-access-info.ts new file mode 100644 index 00000000000..956e8bd5098 --- /dev/null +++ b/react-native/src/theta-repository/options/option-access-info.ts @@ -0,0 +1,108 @@ +/** + * Connected network information. + */ +export type AccessInfo = { + /** + * SSID of the wireless LAN access point that THETA connects to + */ + ssid: string; + + /** + * IP address of access point + */ + ipAddress: string; + + /** + * subnet mask of access point + */ + subnetMask: string; + + /** + * default gateway of access point + */ + defaultGateway: string; + + /** + * Primary DNS server + */ + dns1?: string; + + /** + * Secondary DNS server + */ + dns2?: string; + + /** + * proxy URL of access point + */ + proxyURL: string; + + /** + * Radio frequency. + * @see WlanFrequencyAccessInfoEnum + */ + frequency: WlanFrequencyAccessInfoEnum; + + /** + * WLAN signal strength. + */ + wlanSignalStrength: number; + + /** + * WLAN signal level. + */ + wlanSignalLevel: number; + + /** + * LTE signal strength. + */ + lteSignalStrength: number; + + /** + * LTE signal level. + */ + lteSignalLevel: number; + + /** + * client devices information + */ + dhcpLeaseAddress?: DhcpLeaseAddress[]; +}; + +/** WLAN standards of the access points. */ +export const WlanFrequencyAccessInfoEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** 2.4GHz */ + GHZ_2_4: 'GHZ_2_4', + /** 5.2GHz */ + GHZ_5_2: 'GHZ_5_2', + /** 5.8GHz */ + GHZ_5_8: 'GHZ_5_8', + /** Initial value */ + INITIAL_VALUE: 'INITIAL_VALUE', +} as const; + +/** type definition of WlanFrequencyAccessInfoEnum */ +export type WlanFrequencyAccessInfoEnum = + (typeof WlanFrequencyAccessInfoEnum)[keyof typeof WlanFrequencyAccessInfoEnum]; + +/** + * client devices information + */ +export interface DhcpLeaseAddress { + /** + * IP address of client device + */ + ipAddress: string; + + /** + * MAC address of client device + */ + macAddress: string; + + /** + * host name of client device + */ + hostName: string; +} diff --git a/react-native/src/theta-repository/options/option-ai-auto-thumbnail.ts b/react-native/src/theta-repository/options/option-ai-auto-thumbnail.ts index a578a40b0f9..fb758ff2019 100644 --- a/react-native/src/theta-repository/options/option-ai-auto-thumbnail.ts +++ b/react-native/src/theta-repository/options/option-ai-auto-thumbnail.ts @@ -1,5 +1,7 @@ /** AI auto thumbnail setting. */ export const AiAutoThumbnailEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** ON */ ON: 'ON', /** OFF */ diff --git a/react-native/src/theta-repository/options/option-aperture.ts b/react-native/src/theta-repository/options/option-aperture.ts new file mode 100644 index 00000000000..b635cb6eec9 --- /dev/null +++ b/react-native/src/theta-repository/options/option-aperture.ts @@ -0,0 +1,18 @@ +/** Aperture value. */ +export const ApertureEnum = { + /** AUTO(0) */ + APERTURE_AUTO: 'APERTURE_AUTO', + /** 2.0F RICOH THETA V or prior */ + APERTURE_2_0: 'APERTURE_2_0', + /** 2.1F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ + APERTURE_2_1: 'APERTURE_2_1', + /** 2.4F RICOH THETA X or later */ + APERTURE_2_4: 'APERTURE_2_4', + /** 3.5F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ + APERTURE_3_5: 'APERTURE_3_5', + /** 5.6F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ + APERTURE_5_6: 'APERTURE_5_6', +} as const; + +/** type definition of ApertureEnum */ +export type ApertureEnum = (typeof ApertureEnum)[keyof typeof ApertureEnum]; diff --git a/react-native/src/theta-repository/options/option-auto-bracket.ts b/react-native/src/theta-repository/options/option-auto-bracket.ts index 14176161e23..ae4d0d3077e 100644 --- a/react-native/src/theta-repository/options/option-auto-bracket.ts +++ b/react-native/src/theta-repository/options/option-auto-bracket.ts @@ -1,11 +1,7 @@ -import type { - ApertureEnum, - ExposureCompensationEnum, - ExposureProgramEnum, - IsoEnum, - WhiteBalanceEnum, -} from './options'; +import type { ExposureProgramEnum, IsoEnum, WhiteBalanceEnum } from './options'; +import type { ApertureEnum } from './option-aperture'; import type { ShutterSpeedEnum } from './option-shutter-speed'; +import type { ExposureCompensationEnum } from './option-exposure-compensation'; /** * Multi bracket shooting setting diff --git a/react-native/src/theta-repository/options/option-camera-control-source.ts b/react-native/src/theta-repository/options/option-camera-control-source.ts index 0d36d434746..901d7b58d29 100644 --- a/react-native/src/theta-repository/options/option-camera-control-source.ts +++ b/react-native/src/theta-repository/options/option-camera-control-source.ts @@ -6,6 +6,9 @@ * For RICOH THETA X */ export const CameraControlSourceEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** * Operation is possible with the camera. Locks the smartphone * application UI (supported app only). diff --git a/react-native/src/theta-repository/options/option-camera-lock-config.ts b/react-native/src/theta-repository/options/option-camera-lock-config.ts new file mode 100644 index 00000000000..5cd8c221ba1 --- /dev/null +++ b/react-native/src/theta-repository/options/option-camera-lock-config.ts @@ -0,0 +1,25 @@ +/** + * Camera Lock Config + * + * - It is possible to enable/disable the function for each HW key. + * - It is possible to enable/disable the operation of the panel. + * - When all supported buttons/components are in the "unlock" state, it will be the same as the normal setting. + * - For THETA models, if there are no wlanKey, fnKey, or panel, it will return "lock". If there are no supported buttons/components, setting to "unlock" or "lock" will not return an error and will not perform any action. + */ +export type CameraLockConfig = { + /** power key locked or unlocked. */ + isPowerKeyLocked?: boolean; + /** Shutter key locked or unlocked. */ + isShutterKeyLocked?: boolean; + /** mode key locked or unlocked. */ + isModeKeyLocked?: boolean; + /** wlan key locked or unlocked. */ + isWlanKeyLocked?: boolean; + /** fn key locked or unlocked. */ + isFnKeyLocked?: boolean; + /** + * panel locked or unlocked. + * Fixed to true. Does not cause an error when false, but it is not reflected either. + */ + isPanelLocked?: boolean; +}; diff --git a/react-native/src/theta-repository/options/option-camera-lock.ts b/react-native/src/theta-repository/options/option-camera-lock.ts new file mode 100644 index 00000000000..0265b0946a0 --- /dev/null +++ b/react-native/src/theta-repository/options/option-camera-lock.ts @@ -0,0 +1,22 @@ +/** + * Control camera lock/unlock + */ +export const CameraLockEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** Camera is unlocked */ + UNLOCK: 'UNLOCK', + /** + * Camera basic lock state + * (Mode button, WLAN button, and Fn button presses are inhibited) + */ + BASIC_LOCK: 'BASIC_LOCK', + /** + * Lock according to the parameters set in _cameraLockConfig. + */ + CUSTOM_LOCK: 'CUSTOM_LOCK', +} as const; + +/** type definition of CameraLockEnum */ +export type CameraLockEnum = + (typeof CameraLockEnum)[keyof typeof CameraLockEnum]; diff --git a/react-native/src/theta-repository/options/option-camera-power.ts b/react-native/src/theta-repository/options/option-camera-power.ts index a69503129a1..fbf56e35db2 100644 --- a/react-native/src/theta-repository/options/option-camera-power.ts +++ b/react-native/src/theta-repository/options/option-camera-power.ts @@ -1,4 +1,5 @@ /** + * Camera power state. * _cameraPower is the power status of camera. * * For RICOH THETA X v2.61.0 or later @@ -10,6 +11,8 @@ export const CameraPowerEnum = { ON: 'ON', /** Power OFF */ OFF: 'OFF', + /** Sleep */ + SLEEP: 'SLEEP', /** * Power on, power saving mode. Camera is closed. * Unavailable parameter when plugin is running. In this case, invalidParameterValue error will be returned. diff --git a/react-native/src/theta-repository/options/option-compass-direction-ref.ts b/react-native/src/theta-repository/options/option-compass-direction-ref.ts new file mode 100644 index 00000000000..96aeee9f806 --- /dev/null +++ b/react-native/src/theta-repository/options/option-compass-direction-ref.ts @@ -0,0 +1,25 @@ +/** + * _compassDirectionRef + */ +export const CompassDirectionRefEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** + * If GPS positioning is available, record in true north; + * if GPS is off or not available, record in magnetic north. + */ + AUTO: 'AUTO', + /** + * If the azimuth is set to true north, GPS is turned off, or positioning is not possible, + * the azimuth information is not recorded (positioning information is required for conversion). + */ + TRUE_NORTH: 'TRUE_NORTH', + /** + * Do not set azimuth to true north, set azimuth to magnetic north + */ + MAGNETIC: 'MAGNETIC', +} as const; + +/** type definition of CompassDirectionRefEnum */ +export type CompassDirectionRefEnum = + (typeof CompassDirectionRefEnum)[keyof typeof CompassDirectionRefEnum]; diff --git a/react-native/src/theta-repository/options/option-ethernet-config.ts b/react-native/src/theta-repository/options/option-ethernet-config.ts index 68f33da5811..d6c29679b28 100644 --- a/react-native/src/theta-repository/options/option-ethernet-config.ts +++ b/react-native/src/theta-repository/options/option-ethernet-config.ts @@ -8,13 +8,17 @@ import type { Proxy } from './option-proxy'; */ export type EthernetConfig = { /** Using DHCP or not */ - usingDhcp?: boolean; - /** (optional) IPv4 for IP address */ + usingDhcp: boolean; + /** (optional) IPv4 for IP address. Do not specify this property when usingDhcp is true. */ ipAddress?: string; - /** (optional) IPv4 for subnet mask */ + /** (optional) IPv4 for subnet mask. Do not specify this property when usingDhcp is true. */ subnetMask?: string; - /** (optional) IPv4 for default gateway */ + /** (optional) IPv4 for default gateway. Do not specify this property when usingDhcp is true. */ defaultGateway?: string; + /** (optional) IPv4 for Primary DNS server. Do not specify this property when usingDhcp is true. */ + dns1?: string; + /** (optional) IPv4 for Secondary DNS server. Do not specify this property when usingDhcp is true. */ + dns2?: string; /** * (optional) refer to _proxy for detail * diff --git a/react-native/src/theta-repository/options/option-exposure-compensation.ts b/react-native/src/theta-repository/options/option-exposure-compensation.ts new file mode 100644 index 00000000000..cd446f1827c --- /dev/null +++ b/react-native/src/theta-repository/options/option-exposure-compensation.ts @@ -0,0 +1,59 @@ +/** Exposure compensation (EV). */ +export const ExposureCompensationEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** -4.0 */ + M_4_0: 'M4_0', + /** -3.7 */ + M_3_7: 'M3_7', + /** -3.3 */ + M_3_3: 'M3_3', + /** -3.0 */ + M_3_0: 'M3_0', + /** -2.7 */ + M_2_7: 'M2_7', + /** -2.3 */ + M_2_3: 'M2_3', + /** -2.0 */ + M_2_0: 'M2_0', + /** -1.7 */ + M_1_7: 'M1_7', + /** -1.3 */ + M_1_3: 'M1_3', + /** -1.0 */ + M_1_0: 'M1_0', + /** -0.7 */ + M_0_7: 'M0_7', + /** -0.3 */ + M_0_3: 'M0_3', + /** 0 */ + ZERO: 'ZERO', + /** 0.3 */ + P_0_3: 'P0_3', + /** 0.7 */ + P_0_7: 'P0_7', + /** 1.0 */ + P_1_0: 'P1_0', + /** 1.3 */ + P_1_3: 'P1_3', + /** 1.7 */ + P_1_7: 'P1_7', + /** 2.0 */ + P_2_0: 'P2_0', + /** 2.3 */ + P_2_3: 'P2_3', + /** 2.7 */ + P_2_7: 'P2_7', + /** 3.0 */ + P_3_0: 'P3_0', + /** 3.3 */ + P_3_3: 'P3_3', + /** 3.7 */ + P_3_7: 'P3_7', + /** 4.0 */ + P_4_0: 'P4_0', +} as const; + +/** type definition of ExposureCompensationEnum */ +export type ExposureCompensationEnum = + (typeof ExposureCompensationEnum)[keyof typeof ExposureCompensationEnum]; diff --git a/react-native/src/theta-repository/options/option-file-format.ts b/react-native/src/theta-repository/options/option-file-format.ts index 25b538049eb..38602bf529c 100644 --- a/react-native/src/theta-repository/options/option-file-format.ts +++ b/react-native/src/theta-repository/options/option-file-format.ts @@ -44,6 +44,8 @@ export const VideoFileFormatEnum = { VIDEO_4K: 'VIDEO_4K', /** mp4 3840 x 1920 no codec */ VIDEO_4K_NO_CODEC: 'VIDEO_4K_NO_CODEC', + /** mp4 1920 x 960 15fps */ + VIDEO_2K_15F: 'VIDEO_2K_15F', /** mp4 1920 x 960 30fps */ VIDEO_2K_30F: 'VIDEO_2K_30F', /** mp4 1920 x 960 60fps */ @@ -64,6 +66,10 @@ export const VideoFileFormatEnum = { VIDEO_3_6K_1F: 'VIDEO_3_6K_1F', /** mp4 3648 x 3648 2fps */ VIDEO_3_6K_2F: 'VIDEO_3_6K_2F', + /** mp4 3840 x 1920 2fps */ + VIDEO_4K_2F: 'VIDEO_4K_2F', + /** mp4 3840 x 1920 5fps */ + VIDEO_4K_5F: 'VIDEO_4K_5F', /** mp4 3840 x 1920 10fps */ VIDEO_4K_10F: 'VIDEO_4K_10F', /** mp4 3840 x 1920 15fps */ @@ -88,6 +94,29 @@ export const VideoFileFormatEnum = { VIDEO_7K_5F: 'VIDEO_7K_5F', /** mp4 7680 x 3840 10fps */ VIDEO_7K_10F: 'VIDEO_7K_10F', + + /** mp4 1920 x 960 30fps H.265/HEVC */ + VIDEO_2K_30F_H265_HEVC: 'VIDEO_2K_30F_H265_HEVC', + /** mp4 3840 x 1920 2fps H.265/HEVC */ + VIDEO_4K_2F_H265_HEVC: 'VIDEO_4K_2F_H265_HEVC', + /** mp4 3840 x 1920 5fps H.265/HEVC */ + VIDEO_4K_5F_H265_HEVC: 'VIDEO_4K_5F_H265_HEVC', + /** mp4 3840 x 1920 10fps H.265/HEVC */ + VIDEO_4K_10F_H265_HEVC: 'VIDEO_4K_10F_H265_HEVC', + /** mp4 3840 x 1920 30fps H.265/HEVC */ + VIDEO_4K_30F_H265_HEVC: 'VIDEO_4K_30F_H265_HEVC', + /** mp4 5760 x 2880 2fps H.265/HEVC */ + VIDEO_5_7K_2F_H265_HEVC: 'VIDEO_5_7K_2F_H265_HEVC', + /** mp4 5760 x 2880 5fps H.265/HEVC */ + VIDEO_5_7K_5F_H265_HEVC: 'VIDEO_5_7K_5F_H265_HEVC', + /** mp4 5760 x 2880 10fps H.265/HEVC */ + VIDEO_5_7K_10F_H265_HEVC: 'VIDEO_5_7K_10F_H265_HEVC', + /** mp4 7680 x 3840 2fps H.265/HEVC */ + VIDEO_7K_2F_H265_HEVC: 'VIDEO_7K_2F_H265_HEVC', + /** mp4 7680 x 3840 5fps H.265/HEVC */ + VIDEO_7K_5F_H265_HEVC: 'VIDEO_7K_5F_H265_HEVC', + /** mp4 7680 x 3840 10fps H.265/HEVC */ + VIDEO_7K_10F_H265_HEVC: 'VIDEO_7K_10F_H265_HEVC', } as const; /** type definition of PhotoFileFormatEnum */ diff --git a/react-native/src/theta-repository/options/option-gps-tag-recording.ts b/react-native/src/theta-repository/options/option-gps-tag-recording.ts new file mode 100644 index 00000000000..13459166f53 --- /dev/null +++ b/react-native/src/theta-repository/options/option-gps-tag-recording.ts @@ -0,0 +1,13 @@ +/** Turns position information assigning ON/OFF. */ +export const GpsTagRecordingEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** Position information assigning ON. */ + ON: 'ON', + /** Position information assigning OFF. */ + OFF: 'OFF', +} as const; + +/** type definition of GpsTagRecordingEnum */ +export type GpsTagRecordingEnum = + (typeof GpsTagRecordingEnum)[keyof typeof GpsTagRecordingEnum]; diff --git a/react-native/src/theta-repository/options/option-microphone-noise-reduction.ts b/react-native/src/theta-repository/options/option-microphone-noise-reduction.ts new file mode 100644 index 00000000000..4ca63b2a21d --- /dev/null +++ b/react-native/src/theta-repository/options/option-microphone-noise-reduction.ts @@ -0,0 +1,18 @@ +/** + * Built-in microphone noise reduction. + * + * 1) Video: Ignore changes during recording + * 2) Live Streaming: Ignore changes during delivery + */ +export const MicrophoneNoiseReductionEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** ON */ + ON: 'ON', + /** OFF */ + OFF: 'OFF', +} as const; + +/** type definition of MicrophoneNoiseReductionEnum */ +export type MicrophoneNoiseReductionEnum = + (typeof MicrophoneNoiseReductionEnum)[keyof typeof MicrophoneNoiseReductionEnum]; diff --git a/react-native/src/theta-repository/options/option-mobile-network-setting.ts b/react-native/src/theta-repository/options/option-mobile-network-setting.ts new file mode 100644 index 00000000000..79f57af5faa --- /dev/null +++ b/react-native/src/theta-repository/options/option-mobile-network-setting.ts @@ -0,0 +1,38 @@ +/** + * Mobile Network Settings + */ +export type MobileNetworkSetting = { + /** + * @see RoamingEnum + */ + roaming?: RoamingEnum; + + /** + * @see PlanEnum + */ + plan?: PlanEnum; +}; + +/** + * Roaming of MobileNetworkSetting + */ +export const RoamingEnum = { + UNKNOWN: 'UNKNOWN', + OFF: 'OFF', + ON: 'ON', +} as const; + +/** Type definition of RoamingEnum */ +export type RoamingEnum = (typeof RoamingEnum)[keyof typeof RoamingEnum]; + +/** + * Plan of MobileNetworkSetting + */ +export const PlanEnum = { + UNKNOWN: 'UNKNOWN', + SORACOM: 'SORACOM', + SORACOM_PLAN_DU: 'SORACOM_PLAN_DU', +} as const; + +/** Type definition of PlanEnum */ +export type PlanEnum = (typeof PlanEnum)[keyof typeof PlanEnum]; diff --git a/react-native/src/theta-repository/options/option-network-type.ts b/react-native/src/theta-repository/options/option-network-type.ts index f5f8dbd5f63..1065734c627 100644 --- a/react-native/src/theta-repository/options/option-network-type.ts +++ b/react-native/src/theta-repository/options/option-network-type.ts @@ -2,9 +2,11 @@ * Network type of the camera. * Can be acquired by camera.getOptions and set by camera.setOptions. * - * For Theta X, Z1 and V. + * For Theta A1, X, Z1 and V. */ export const NetworkTypeEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** Direct mode */ DIRECT: 'DIRECT', /** Client mode via WLAN */ @@ -13,6 +15,20 @@ export const NetworkTypeEnum = { ETHERNET: 'ETHERNET', /** Network is off. This value can be gotten only by plugin. */ OFF: 'OFF', + /** LTE plan-D */ + LTE_D: 'LTE_D', + /** LTE plan-DU */ + LTE_DU: 'LTE_DU', + /** LTE plan01s */ + LTE_01S: 'LTE_01S', + /** LTE planX3 */ + LTE_X3: 'LTE_X3', + /** LTE planP1 */ + LTE_P1: 'LTE_P1', + /** LTE plan-K2 */ + LTE_K2: 'LTE_K2', + /** LTE plan-K */ + LTE_K: 'LTE_K', } as const; /** Type definition of NetworkTypeEnum */ diff --git a/react-native/src/theta-repository/options/option-off-delay-usb.ts b/react-native/src/theta-repository/options/option-off-delay-usb.ts new file mode 100644 index 00000000000..e6b54b8029e --- /dev/null +++ b/react-native/src/theta-repository/options/option-off-delay-usb.ts @@ -0,0 +1,57 @@ +/** + * Auto power off time with USB power supply. + * + * For RICOH THETA A1 + */ +export const OffDelayUsbEnum = { + /** Do not turn power off. */ + DISABLE: 'DISABLE', + /** Power off after 10 minutes.(600sec) */ + OFF_DELAY_10M: 'OFF_DELAY_10M', + /** Power off after 1 hour.(3,600sec) */ + OFF_DELAY_1H: 'OFF_DELAY_1H', + /** Power off after 2 hour.(7,200sec) */ + OFF_DELAY_2H: 'OFF_DELAY_2H', + /** Power off after 4 hour.(14,400sec) */ + OFF_DELAY_4H: 'OFF_DELAY_4H', + /** Power off after 8 hour.(28,800sec) */ + OFF_DELAY_8H: 'OFF_DELAY_8H', + /** Power off after 12 hour.(43,200sec) */ + OFF_DELAY_12H: 'OFF_DELAY_12H', + /** Power off after 18 hour.(64,800sec) */ + OFF_DELAY_18H: 'OFF_DELAY_18H', + /** Power off after 24 hour.(86,400sec) */ + OFF_DELAY_24H: 'OFF_DELAY_24H', + /** Power off after 2 days.(172,800sec) */ + OFF_DELAY_2D: 'OFF_DELAY_2D', +} as const; + +/** type definition of OffDelayEnum */ +export type OffDelayUsbEnum = + | (typeof OffDelayUsbEnum)[keyof typeof OffDelayUsbEnum] + | number; + +/** + * Get time(seconds) from OffDelayEnum + * @param offDelayUsb enum of OffDelayUsb + * @returns time(seconds) + */ +export function offDelayUsbToSeconds(offDelayUsb: OffDelayUsbEnum): number { + if (typeof offDelayUsb === 'number') { + return offDelayUsb; + } + + const table: Record = { + DISABLE: 0, + OFF_DELAY_10M: 600, + OFF_DELAY_1H: 3600, + OFF_DELAY_2H: 7200, + OFF_DELAY_4H: 14400, + OFF_DELAY_8H: 28800, + OFF_DELAY_12H: 43200, + OFF_DELAY_18H: 64800, + OFF_DELAY_24H: 86400, + OFF_DELAY_2D: 172800, + }; + return table[offDelayUsb]; +} diff --git a/react-native/src/theta-repository/options/option-preview-format.ts b/react-native/src/theta-repository/options/option-preview-format.ts index f8df494b19c..fb71c0808e3 100644 --- a/react-native/src/theta-repository/options/option-preview-format.ts +++ b/react-native/src/theta-repository/options/option-preview-format.ts @@ -2,23 +2,29 @@ * Format of live view */ export const PreviewFormatEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** width-height-framerate */ + /** For Theta X firmware v2.71.1 or later */ + W1920_H960_F30: 'W1920_H960_F30', + /** For Theta Z1 and V */ + W1920_H960_F8: 'W1920_H960_F8', /** For Theta X, Z1, V and SC2 */ W1024_H512_F30: 'W1024_H512_F30', /** For Theta X. This value can't set. */ W1024_H512_F15: 'W1024_H512_F15', - /** For Theta X */ - W512_H512_F30: 'W512_H512_F30', - /** For Theta Z1 and V */ - W1920_H960_F8: 'W1920_H960_F8', + /** For Theta A1 */ + W1024_H512_F10: 'W1024_H512_F10', /** For Theta Z1 and V */ W1024_H512_F8: 'W1024_H512_F8', /** For Theta Z1 and V */ W640_H320_F30: 'W640_H320_F30', - /** For Theta Z1 and V */ - W640_H320_F8: 'W640_H320_F8', /** For Theta S and SC */ W640_H320_F10: 'W640_H320_F10', + /** For Theta Z1 and V */ + W640_H320_F8: 'W640_H320_F8', + /** For Theta X */ + W512_H512_F30: 'W512_H512_F30', /** For Theta X */ W3840_H1920_F30: 'W3840_H1920_F30', } as const; diff --git a/react-native/src/theta-repository/options/option-top-bottom-correction-rotation-support.ts b/react-native/src/theta-repository/options/option-top-bottom-correction-rotation-support.ts new file mode 100644 index 00000000000..a51c3581f6d --- /dev/null +++ b/react-native/src/theta-repository/options/option-top-bottom-correction-rotation-support.ts @@ -0,0 +1,19 @@ +import type { ValueRange } from './options'; + +/** + * Supported TopBottomCorrectionRotation + */ +export type TopBottomCorrectionRotationSupport = { + /** + * Supported pitch + */ + pitch: ValueRange; + /** + * Supported roll + */ + roll: ValueRange; + /** + * Supported yaw + */ + yaw: ValueRange; +}; diff --git a/react-native/src/theta-repository/options/option-usb-connection.ts b/react-native/src/theta-repository/options/option-usb-connection.ts new file mode 100644 index 00000000000..62cf8885445 --- /dev/null +++ b/react-native/src/theta-repository/options/option-usb-connection.ts @@ -0,0 +1,18 @@ +/** + * USB connection of the camera. + * + * Default value is "MTP". + * After switching the setting value, reconnect the camera to USB to enable it. + */ +export const UsbConnectionEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** MTP */ + MTP: 'MTP', + /** MSC */ + MSC: 'MSC', +} as const; + +/** type definition of UsbConnectionEnum */ +export type UsbConnectionEnum = + (typeof UsbConnectionEnum)[keyof typeof UsbConnectionEnum]; diff --git a/react-native/src/theta-repository/options/option-wlan-antenna-config.ts b/react-native/src/theta-repository/options/option-wlan-antenna-config.ts new file mode 100644 index 00000000000..ededa89c5c3 --- /dev/null +++ b/react-native/src/theta-repository/options/option-wlan-antenna-config.ts @@ -0,0 +1,15 @@ +/** + * Configure SISO or MIMO for Wireless LAN. + */ +export const WlanAntennaConfigEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', + /** SISO */ + SISO: 'SISO', + /** MIMO */ + MIMO: 'MIMO', +} as const; + +/** type definition of WlanAntennaConfigEnum */ +export type WlanAntennaConfigEnum = + (typeof WlanAntennaConfigEnum)[keyof typeof WlanAntennaConfigEnum]; diff --git a/react-native/src/theta-repository/options/option-wlan-frequency-cl-mode.ts b/react-native/src/theta-repository/options/option-wlan-frequency-cl-mode.ts new file mode 100644 index 00000000000..94309ce288f --- /dev/null +++ b/react-native/src/theta-repository/options/option-wlan-frequency-cl-mode.ts @@ -0,0 +1,19 @@ +/** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies. + * + * For RICOH THETA A1 + */ +export type WlanFrequencyClMode = { + /** + * 2.4GHz + */ + enable2_4: boolean; + /** + * 5.2GHz + */ + enable5_2: boolean; + /** + * 5.8GHz + */ + enable5_8: boolean; +}; diff --git a/react-native/src/theta-repository/options/option-wlan-frequency.ts b/react-native/src/theta-repository/options/option-wlan-frequency.ts index a29f47c00f3..37212da28de 100644 --- a/react-native/src/theta-repository/options/option-wlan-frequency.ts +++ b/react-native/src/theta-repository/options/option-wlan-frequency.ts @@ -2,13 +2,19 @@ * Wireless LAN frequency of the camera. * Can be acquired by camera.getOptions and set by camera.setOptions. * - * For Theta X, Z1 amd V. + * For Theta A1, X, Z1 amd V. */ export const WlanFrequencyEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** 2.4GHz */ GHZ_2_4: 'GHZ_2_4', /** 5GHz */ GHZ_5: 'GHZ_5', + /** 5.2GHz */ + GHZ_5_2: 'GHZ_5_2', + /** 5.8GHz */ + GHZ_5_8: 'GHZ_5_8', } as const; /** type definition of WlanFrequency */ diff --git a/react-native/src/theta-repository/options/options.ts b/react-native/src/theta-repository/options/options.ts index 470f5e458dd..54d4d85eb2c 100644 --- a/react-native/src/theta-repository/options/options.ts +++ b/react-native/src/theta-repository/options/options.ts @@ -1,57 +1,52 @@ +import type { AccessInfo } from './option-access-info'; import type { AiAutoThumbnailEnum } from './option-ai-auto-thumbnail'; +import type { ApertureEnum } from './option-aperture'; import type { BitrateEnum } from './option-bitrate'; import type { BluetoothRoleEnum } from './option-bluetooth-role'; import type { BracketSetting } from './option-auto-bracket'; import type { BurstModeEnum } from './option-burst-mode'; import type { BurstOption } from './option-burst-option'; import type { CameraControlSourceEnum } from './option-camera-control-source'; +import type { CameraLockConfig } from './option-camera-lock-config'; +import type { CameraLockEnum } from './option-camera-lock'; import type { CameraModeEnum } from './option-camera-mode'; +import type { CameraPowerEnum } from './option-camera-power'; import type { CaptureModeEnum } from './option-capture-mode'; +import type { CompassDirectionRefEnum } from './option-compass-direction-ref'; import type { ContinuousNumberEnum } from './option-continuous-number'; +import type { EthernetConfig } from './option-ethernet-config'; +import type { ExposureCompensationEnum } from './option-exposure-compensation'; import type { FaceDetectEnum } from './option-face-detect'; +import type { FileFormatEnum } from './option-file-format'; import type { FilterEnum } from './option-filter'; -import type { ShootingFunctionEnum } from './option-function'; import type { GainEnum } from './option-gain'; +import type { GpsTagRecordingEnum } from './option-gps-tag-recording'; import type { ImageStitchingEnum } from './option-image-stitching'; import type { MaxRecordableTimeEnum } from './option-max-recordable-time'; +import type { MicrophoneNoiseReductionEnum } from './option-microphone-noise-reduction'; +import type { MobileNetworkSetting } from './option-mobile-network-setting'; import type { NetworkTypeEnum } from './option-network-type'; +import type { OffDelayEnum } from './option-off-delay'; +import type { OffDelayUsbEnum } from './option-off-delay-usb'; import type { PowerSavingEnum } from './option-power-saving'; import type { PresetEnum } from './option-preset'; import type { PreviewFormatEnum } from './option-preview-format'; import type { Proxy } from './option-proxy'; +import type { ShootingFunctionEnum } from './option-function'; import type { ShootingMethodEnum } from './option-shooting-method'; import type { ShutterSpeedEnum } from './option-shutter-speed'; +import type { SleepDelayEnum } from './option-sleep-delay'; import type { TimeShift } from './option-time-shift'; import type { TopBottomCorrectionOptionEnum } from './option-top-bottom-correction'; import type { TopBottomCorrectionRotation } from './option-top-bottom-correction-rotation'; +import type { TopBottomCorrectionRotationSupport } from './option-top-bottom-correction-rotation-support'; +import type { UsbConnectionEnum } from './option-usb-connection'; import type { VideoStitchingEnum } from './option-video-stitching'; import type { VisibilityReductionEnum } from './option-visibility-reduction'; import type { WhiteBalanceAutoStrengthEnum } from './option-white-balance-auto-strength'; +import type { WlanAntennaConfigEnum } from './option-wlan-antenna-config'; +import type { WlanFrequencyClMode } from './option-wlan-frequency-cl-mode'; import type { WlanFrequencyEnum } from './option-wlan-frequency'; -import type { OffDelayEnum } from './option-off-delay'; -import type { SleepDelayEnum } from './option-sleep-delay'; -import type { EthernetConfig } from './option-ethernet-config'; -import type { FileFormatEnum } from './option-file-format'; -import type { CameraPowerEnum } from './option-camera-power'; - -/** Aperture value. */ -export const ApertureEnum = { - /** AUTO(0) */ - APERTURE_AUTO: 'APERTURE_AUTO', - /** 2.0F RICOH THETA V or prior */ - APERTURE_2_0: 'APERTURE_2_0', - /** 2.1F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ - APERTURE_2_1: 'APERTURE_2_1', - /** 2.4F RICOH THETA X or later */ - APERTURE_2_4: 'APERTURE_2_4', - /** 3.5F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ - APERTURE_3_5: 'APERTURE_3_5', - /** 5.6F RICOH THETA Z1 and the exposure program (exposureProgram) is set to Manual or Aperture Priority */ - APERTURE_5_6: 'APERTURE_5_6', -} as const; - -/** type definition of ApertureEnum */ -export type ApertureEnum = (typeof ApertureEnum)[keyof typeof ApertureEnum]; /** BluetoothPower value. */ export const BluetoothPowerEnum = { @@ -65,40 +60,6 @@ export const BluetoothPowerEnum = { export type BluetoothPowerEnum = (typeof BluetoothPowerEnum)[keyof typeof BluetoothPowerEnum]; -/** Exposure compensation (EV). */ -export const ExposureCompensationEnum = { - /** -2.0 */ - M_2_0: 'M2_0', - /** -1.7 */ - M_1_7: 'M1_7', - /** -1.3 */ - M_1_3: 'M1_3', - /** -1.0 */ - M_1_0: 'M1_0', - /** -0.7 */ - M_0_7: 'M0_7', - /** -0.3 */ - M_0_3: 'M0_3', - /** 0 */ - ZERO: 'ZERO', - /** 0.3 */ - P_0_3: 'P0_3', - /** 0.7 */ - P_0_7: 'P0_7', - /** 1.0 */ - P_1_0: 'P1_0', - /** 1.3 */ - P_1_3: 'P1_3', - /** 1.7 */ - P_1_7: 'P1_7', - /** 2.0 */ - P_2_0: 'P2_0', -} as const; - -/** type definition of ExposureCompensationEnum */ -export type ExposureCompensationEnum = - (typeof ExposureCompensationEnum)[keyof typeof ExposureCompensationEnum]; - /** Operating time (sec.) of the self-timer. */ export const ExposureDelayEnum = { /** Disable self-timer. */ @@ -159,18 +120,6 @@ export type GpsInfo = { dateTimeZone: string; }; -/** Turns position information assigning ON/OFF. */ -export const GpsTagRecordingEnum = { - /** Position information assigning ON. */ - ON: 'ON', - /** Position information assigning OFF. */ - OFF: 'OFF', -} as const; - -/** type definition of GpsTagRecordingEnum */ -export type GpsTagRecordingEnum = - (typeof GpsTagRecordingEnum)[keyof typeof GpsTagRecordingEnum]; - /** ISO sensitivity. */ export const IsoEnum = { /** AUTO (0) */ @@ -329,10 +278,16 @@ export type WhiteBalanceEnum = /** Camera setting options name. */ export const OptionNameEnum = { + /** _accessInfo */ + AccessInfo: 'AccessInfo', /** _aiAutoThumbnail */ AiAutoThumbnail: 'AiAutoThumbnail', + /** _aiAutoThumbnailSupport */ + AiAutoThumbnailSupport: 'AiAutoThumbnailSupport', /** aperture */ Aperture: 'Aperture', + /** apertureSupport */ + ApertureSupport: 'ApertureSupport', /** autoBracket */ AutoBracket: 'AutoBracket', /** _bitrate*/ @@ -347,10 +302,18 @@ export const OptionNameEnum = { BurstOption: 'BurstOption', /** _cameraControlSource */ CameraControlSource: 'CameraControlSource', + /** _cameraControlSourceSupport */ + CameraControlSourceSupport: 'CameraControlSourceSupport', + /** _cameraLock */ + CameraLock: 'CameraLock', + /** _cameraLockConfig */ + CameraLockConfig: 'CameraLockConfig', /** cameraMode */ CameraMode: 'CameraMode', /** cameraPower */ CameraPower: 'CameraPower', + /** _cameraPowerSupport */ + CameraPowerSupport: 'CameraPowerSupport', /** captureInterval */ CaptureInterval: 'CaptureInterval', /** captureMode */ @@ -359,10 +322,19 @@ export const OptionNameEnum = { CaptureNumber: 'CaptureNumber', /** colorTemperature */ ColorTemperature: 'ColorTemperature', + /** _colorTemperatureSupport */ + ColorTemperatureSupport: 'ColorTemperatureSupport', + /** _compassDirectionRef */ + CompassDirectionRef: 'CompassDirectionRef', /** _compositeShootingOutputInterval */ CompositeShootingOutputInterval: 'CompositeShootingOutputInterval', + /** _compositeShootingOutputIntervalSupport */ + CompositeShootingOutputIntervalSupport: + 'CompositeShootingOutputIntervalSupport', /** _compositeShootingTime */ CompositeShootingTime: 'CompositeShootingTime', + /** _compositeShootingTimeSupport */ + CompositeShootingTimeSupport: 'CompositeShootingTimeSupport', /** continuousNumber */ ContinuousNumber: 'ContinuousNumber', /** dateTimeZone */ @@ -373,6 +345,8 @@ export const OptionNameEnum = { ExposureCompensation: 'ExposureCompensation', /** exposureDelay */ ExposureDelay: 'ExposureDelay', + /** exposureDelaySupport */ + ExposureDelaySupport: 'ExposureDelaySupport', /** exposureProgram */ ExposureProgram: 'ExposureProgram', /** _faceDetect */ @@ -387,6 +361,8 @@ export const OptionNameEnum = { Gain: 'Gain', /** gpsInfo */ GpsInfo: 'GpsInfo', + /** _gpsTagRecordingSupport */ + GpsTagRecordingSupport: 'GpsTagRecordingSupport', /** imageStitching */ ImageStitching: 'ImageStitching', /** isGpsOn */ @@ -401,10 +377,16 @@ export const OptionNameEnum = { LatestEnabledExposureDelayTime: 'LatestEnabledExposureDelayTime', /** maxRecordableTime */ MaxRecordableTime: 'MaxRecordableTime', + /** microphoneNoiseReduction */ + MicrophoneNoiseReduction: 'MicrophoneNoiseReduction', + /** _mobileNetworkSetting */ + MobileNetworkSetting: 'MobileNetworkSetting', /** networkType */ NetworkType: 'NetworkType', /** offDelay */ OffDelay: 'OffDelay', + /** _offDelayUSB */ + OffDelayUsb: 'OffDelayUsb', /** password */ Password: 'Password', /** powerSaving */ @@ -433,10 +415,14 @@ export const OptionNameEnum = { TopBottomCorrection: 'TopBottomCorrection', /** topBottomCorrectionRotation */ TopBottomCorrectionRotation: 'TopBottomCorrectionRotation', + /** topBottomCorrectionRotationSupport */ + TopBottomCorrectionRotationSupport: 'TopBottomCorrectionRotationSupport', /** totalSpace */ TotalSpace: 'TotalSpace', /** shutterVolume */ ShutterVolume: 'ShutterVolume', + /** _usbConnection */ + UsbConnection: 'UsbConnection', /** username */ Username: 'Username', /** videoStitching */ @@ -447,8 +433,14 @@ export const OptionNameEnum = { WhiteBalance: 'WhiteBalance', /** _whiteBalanceAutoStrength */ WhiteBalanceAutoStrength: 'WhiteBalanceAutoStrength', + /** _wlanAntennaConfig */ + WlanAntennaConfig: 'WlanAntennaConfig', /** _wlanFrequency */ WlanFrequency: 'WlanFrequency', + /** _wlanFrequencySupport */ + WlanFrequencySupport: 'WlanFrequencySupport', + /** _wlanFrequencyCLmode */ + WlanFrequencyClMode: 'WlanFrequencyClMode', } as const; /** type definition of OptionNameEnum */ @@ -457,10 +449,16 @@ export type OptionNameEnum = /** camera setting options */ export type Options = { + /** Connected network information. */ + accessInfo?: AccessInfo; /** AI auto thumbnail setting. */ aiAutoThumbnail?: AiAutoThumbnailEnum; + /** Supported AI auto thumbnail setting. */ + aiAutoThumbnailSupport?: AiAutoThumbnailEnum[]; /** Aperture value. */ aperture?: ApertureEnum; + /** Supported aperture value. */ + apertureSupport?: ApertureEnum[]; /** */ autoBracket?: BracketSetting[]; /** Bitrate */ @@ -475,10 +473,18 @@ export type Options = { burstOption?: BurstOption; /** camera control source. */ cameraControlSource?: CameraControlSourceEnum; + /** Supported Camera Control Source. */ + cameraControlSourceSupport?: CameraControlSourceEnum[]; + /** Camera lock. */ + cameraLock?: CameraLockEnum; + /** Camera lock config. */ + cameraLockConfig?: CameraLockConfig; /** Camera mode. */ cameraMode?: CameraModeEnum; /** Camera power state */ cameraPower?: CameraPowerEnum; + /** Supported Camera Power. */ + cameraPowerSupport?: CameraPowerEnum[]; /** * Shooting interval (sec.) for interval shooting. * @@ -520,6 +526,10 @@ export type Options = { captureNumber?: number; /** Color temperature of the camera (Kelvin). */ colorTemperature?: number; + /** supported color temperature. */ + colorTemperatureSupport?: ValueRange; + /** _compassDirectionRef */ + compassDirectionRef?: CompassDirectionRefEnum; /** * In-progress save interval for interval composite shooting (sec). * @@ -531,6 +541,10 @@ export type Options = { * RICOH THETA S firmware v01.82 or later */ compositeShootingOutputInterval?: number; + /** + * Supported in-progress save interval for interval composite shooting (sec). + */ + compositeShootingOutputIntervalSupport?: ValueRange; /** * Shooting time for interval composite shooting (sec). * @@ -542,6 +556,10 @@ export type Options = { * RICOH THETA S firmware v01.82 or later */ compositeShootingTime?: number; + /** + * Supported shooting time for interval composite shooting (sec). + */ + compositeShootingTimeSupport?: ValueRange; /** Number of shots for continuous shooting. */ continuousNumber?: ContinuousNumberEnum; /** Current system time of RICOH THETA. Setting another options will result in an error. */ @@ -552,6 +570,8 @@ export type Options = { exposureCompensation?: ExposureCompensationEnum; /** Operating time (sec.) of the self-timer. */ exposureDelay?: ExposureDelayEnum; + /** Supported operating time (sec.) of the self-timer. */ + exposureDelaySupport?: ExposureDelayEnum[]; /** Exposure program. The exposure settings that take priority can be selected. */ exposureProgram?: ExposureProgramEnum; /** Face detection */ @@ -566,6 +586,11 @@ export type Options = { gain?: GainEnum; /** GPS location information. */ gpsInfo?: GpsInfo; + /** + * Supported GpsTagRecording + * For THETA X + */ + gpsTagRecordingSupport?: GpsTagRecordingEnum[]; /** Still image stitching setting during shooting. */ imageStitching?: ImageStitchingEnum; /** Turns position information assigning ON/OFF. */ @@ -580,10 +605,20 @@ export type Options = { latestEnabledExposureDelayTime?: ExposureDelayEnum; /** Maximum recordable time (in seconds) of the camera. */ maxRecordableTime?: MaxRecordableTimeEnum; + /** _microphoneNoiseReduction */ + microphoneNoiseReduction?: MicrophoneNoiseReductionEnum; + /** _mobileNetworkSetting */ + mobileNetworkSetting?: MobileNetworkSetting; /** Network type of the camera */ networkType?: NetworkTypeEnum; /** Length of standby time before the camera automatically powers OFF. */ offDelay?: OffDelayEnum; + /** + * Auto power off time with USB power supply. + * + * For RICOH THETA A1 + */ + offDelayUsb?: OffDelayUsbEnum; /** Password used for digest authentication when _networkType is set to client mode. */ password?: String; /** Power saving mode */ @@ -622,8 +657,12 @@ export type Options = { * Enabled only for _topBottomCorrection Manual. */ topBottomCorrectionRotation?: TopBottomCorrectionRotation; + /** Supported TopBottomCorrectionRotation */ + topBottomCorrectionRotationSupport?: TopBottomCorrectionRotationSupport; /** Total storage space (byte). */ totalSpace?: number; + /** _usbConnection */ + usbConnection?: UsbConnectionEnum; /** User name used for digest authentication when _networkType is set to client mode. */ username?: String; /** Video stitching during shooting. */ @@ -635,7 +674,30 @@ export type Options = { /** White balance auto strength. */ whiteBalanceAutoStrength?: WhiteBalanceAutoStrengthEnum; /** WLAN frequency */ + wlanAntennaConfig?: WlanAntennaConfigEnum; + /** WlanAntennaConfig */ wlanFrequency?: WlanFrequencyEnum; + /** + * Supported WlanFrequency + * + * For RICOH THETA X, Z1 and V. + */ + wlanFrequencySupport?: WlanFrequencyEnum[]; + /** + * Whether the camera's WLAN CL mode uses 2.4 GHz, 5.2 GHz, or 5.8 GHz frequencies + * + * For RICOH THETA A1 + */ + wlanFrequencyClMode?: WlanFrequencyClMode; /** GPS setting used in only capturing */ _gpsTagRecording?: GpsTagRecordingEnum; }; + +export type ValueRange = { + /** maximum value */ + max: number; + /** minimum value */ + min: number; + /** step size */ + stepSize: number; +}; diff --git a/react-native/src/theta-repository/theta-files.ts b/react-native/src/theta-repository/theta-files.ts index 2f975a8da05..789d78b6459 100644 --- a/react-native/src/theta-repository/theta-files.ts +++ b/react-native/src/theta-repository/theta-files.ts @@ -84,8 +84,12 @@ export type StorageEnum = (typeof StorageEnum)[keyof typeof StorageEnum]; /** Video codec */ export const CodecEnum = { + /** Undefined value */ + UNKNOWN: 'UNKNOWN', /** codec H.264/MPEG-4 AVC */ H264MP4AVC: 'H264MP4AVC', + /** codec H.265/HEVC */ + H265HEVC: 'H265HEVC', } as const; /** type definition of CodecEnum */ diff --git a/react-native/src/theta-repository/theta-info.ts b/react-native/src/theta-repository/theta-info.ts index 1de150a3ebd..3b44fdd0257 100644 --- a/react-native/src/theta-repository/theta-info.ts +++ b/react-native/src/theta-repository/theta-info.ts @@ -14,6 +14,8 @@ export const ThetaModel = { THETA_SC2: 'THETA_SC2', /** THETA SC2 for business */ THETA_SC2_B: 'THETA_SC2_B', + /** THETA A1 */ + THETA_A1: 'THETA_A1', } as const; /** type definition of ThetaModel */ @@ -27,7 +29,13 @@ export type ThetaInfo = { model: string; /** Theta serial number */ serialNumber: string; - /** MAC address of wireless LAN (RICOH THETA V firmware v2.11.1 or later) */ + /** MAC address of wireless LAN + * (RICOH THETA V firmware v2.11.1 or later) + * + * For THETA X, firmware versions v2.63.0 and earlier display `the communication MAC address`, + * while v2.71.1 and later diplay `the physical MAC address`. + * For other than THETA X, `the physical MAC address` is displayed. + */ wlanMacAddress: string | null; /** MAC address of Bluetooth (RICOH THETA V firmware v2.11.1 or later) */ bluetoothMacAddress: string | null; diff --git a/react-native/src/theta-repository/theta-repository.ts b/react-native/src/theta-repository/theta-repository.ts index 51076d742e7..36dbe2c96cb 100644 --- a/react-native/src/theta-repository/theta-repository.ts +++ b/react-native/src/theta-repository/theta-repository.ts @@ -22,6 +22,7 @@ import { PhotoCaptureBuilder, VideoCaptureBuilder, TimeShiftCaptureBuilder, + TimeShiftManualCaptureBuilder, LimitlessIntervalCaptureBuilder, ShotCountSpecifiedIntervalCaptureBuilder, CompositeIntervalCaptureBuilder, @@ -33,9 +34,36 @@ import type { ThetaConfig } from './theta-config'; import type { ThetaTimeout } from './theta-timeout'; import { NotifyController } from './notify-controller'; import { EventWebSocket } from './event-websocket'; -import { convertVideoFormatsImpl } from './libs'; +import { convertOptions, convertVideoFormatsImpl } from './libs'; const ThetaClientReactNative = NativeModules.ThetaClientReactNative; +const NOTIFY_API_LOG = 'API-LOG'; +let apiLogListener: ((message: string) => void) | undefined; + +async function setNotifyApiLogListener(listener?: (message: string) => void) { + if (listener) { + NotifyController.instance.addNotify(NOTIFY_API_LOG, (event) => { + listener(event.params.message); + }); + } else { + NotifyController.instance.removeNotify(NOTIFY_API_LOG); + } + await ThetaClientReactNative.setApiLogListener(listener != null); +} + +/** + * Set up a log listener for THETA API calls + * + * @param listener Called when there is a THETA API request and response; if undefined, unregister. + */ +export async function setApiLogListener(listener?: (message: string) => void) { + if (!NotifyController.instance.isInit()) { + NotifyController.instance.init(); + } + apiLogListener = listener; + await setNotifyApiLogListener(apiLogListener); +} + /** * initialize theta client sdk * @@ -45,12 +73,15 @@ const ThetaClientReactNative = NativeModules.ThetaClientReactNative; * @param {ThetaTimeout} timeout Timeout of HTTP call. * @return promise of boolean result **/ -export function initialize( +export async function initialize( endPoint: string = 'http://192.168.1.1', config?: ThetaConfig, timeout?: ThetaTimeout ): Promise { NotifyController.instance.init(); + if (apiLogListener) { + await setNotifyApiLogListener(apiLogListener); + } return ThetaClientReactNative.initialize(endPoint, config, timeout); } @@ -65,6 +96,19 @@ export function isInitialized(): Promise { return ThetaClientReactNative.isInitialized(); } +/** + * Turn off/on (reboot) the camera. + * Supported models are THETA A1 only. + * + * Error when connecting in CL mode for Japan-bound models only + * + * @function reboot + * @return promise + */ +export function reboot(): Promise { + return ThetaClientReactNative.reboot(); +} + /** * Reset all device settings and capture settings. * After reset, the camera will be restarted. @@ -96,6 +140,16 @@ export function getTimeShiftCaptureBuilder(): TimeShiftCaptureBuilder { return new TimeShiftCaptureBuilder(); } +/** + * Get TimeShiftManualCaptureBuilder for manual time-shift. + * + * @function getTimeShiftManualCaptureBuilder + * @return created TimeShiftManualCaptureBuilder + */ +export function getTimeShiftManualCaptureBuilder(): TimeShiftManualCaptureBuilder { + return new TimeShiftManualCaptureBuilder(); +} + /** * Get VideoCapture.Builder for capture video. * @@ -415,8 +469,13 @@ export function deleteAllVideoFiles(): Promise { * @param {OptionNameEnum[]} optionNames List of OptionNameEnum. * @return promise of Options acquired */ -export function getOptions(optionNames: OptionNameEnum[]): Promise { - return ThetaClientReactNative.getOptions(optionNames); +export async function getOptions( + optionNames: OptionNameEnum[] +): Promise { + const response = await ThetaClientReactNative.getOptions(optionNames); + const { options, json } = response; + const result = convertOptions(options, json); + return result; } /** @@ -456,29 +515,39 @@ export function listAccessPoints(): Promise { * * @function setAccessPointDynamically * @param {string} ssid SSID of the access point. - * @param {boolean} ssidStealth true if SSID stealth is enabled. - * @param {AuthModeEnum} authMode Authentication mode. - * @param {string} password Password. If authMode is "NONE", pass empty String. - * @param {number} connectionPriority Connection priority 1 to 5. - * @param {Proxy} proxy Proxy information to be used for the access point. + * @param {} params - Optional parameters for additional configuration. + * @param {boolean} params.ssidStealth true if SSID stealth is enabled. + * @param {AuthModeEnum} params.authMode Authentication mode. + * @param {string} params.password Password. Not set if authMode is “NONE”. + * @param {number} params.connectionPriority Connection priority 1 to 5. + * @param {Proxy} params.proxy Proxy information to be used for the access point. * @return promise of boolean result */ export function setAccessPointDynamically( ssid: string, - ssidStealth: boolean = false, - authMode: AuthModeEnum = AuthModeEnum.NONE, - password: string = '', - connectionPriority: number = 1, - proxy?: Proxy + params?: { + ssidStealth?: boolean; + authMode?: AuthModeEnum; + password?: string; + connectionPriority?: number; + proxy?: Proxy; + } ): Promise { - return ThetaClientReactNative.setAccessPointDynamically( + const { + ssidStealth, + authMode = AuthModeEnum.NONE, + password, + connectionPriority, + proxy, + } = params ?? {}; + return ThetaClientReactNative.setAccessPointDynamically({ ssid, ssidStealth, authMode, password, connectionPriority, - proxy - ); + proxy, + }); } /** @@ -486,28 +555,44 @@ export function setAccessPointDynamically( * * @function setAccessPointStatically * @param {string} ssid SSID of the access point. - * @param {boolean} ssidStealth True if SSID stealth is enabled. - * @param {AuthModeEnum} authMode Authentication mode. - * @param {string} password Password. If authMode is "NONE", pass empty String. - * @param {number} connectionPriority Connection priority 1 to 5. * @param {string} ipAddress IP address assigns to Theta. * @param {string} subnetMask Subnet mask. * @param {string} defaultGateway Default gateway. - * @param {Proxy} proxy Proxy information to be used for the access point. + * @param {} params - Optional parameters for additional configuration. + * @param {boolean} params.ssidStealth True if SSID stealth is enabled. + * @param {AuthModeEnum} params.authMode Authentication mode. + * @param {string} params.password Password. Not set if authMode is “NONE”. + * @param {number} params.connectionPriority Connection priority 1 to 5. + * @param {string} params.dns1 Primary DNS server. + * @param {string} params.dns2 Secondary DNS server. + * @param {Proxy} params.proxy Proxy information to be used for the access point. * @return promise of boolean result */ export function setAccessPointStatically( ssid: string, - ssidStealth: boolean = false, - authMode: AuthModeEnum = AuthModeEnum.NONE, - password: string = '', - connectionPriority: number = 1, ipAddress: string, subnetMask: string, defaultGateway: string, - proxy?: Proxy + params?: { + ssidStealth?: boolean; + authMode: AuthModeEnum; + password?: string; + connectionPriority?: number; + dns1?: string; + dns2?: string; + proxy?: Proxy; + } ): Promise { - return ThetaClientReactNative.setAccessPointStatically( + const { + ssidStealth, + authMode = AuthModeEnum.NONE, + password, + connectionPriority, + dns1, + dns2, + proxy, + } = params ?? {}; + return ThetaClientReactNative.setAccessPointStatically({ ssid, ssidStealth, authMode, @@ -516,8 +601,10 @@ export function setAccessPointStatically( ipAddress, subnetMask, defaultGateway, - proxy - ); + dns1, + dns2, + proxy, + }); } /** diff --git a/react-native/src/theta-repository/theta-state/capture-status.ts b/react-native/src/theta-repository/theta-state/capture-status.ts index 4fa92d8963c..5f8f7c35bc4 100644 --- a/react-native/src/theta-repository/theta-state/capture-status.ts +++ b/react-native/src/theta-repository/theta-state/capture-status.ts @@ -20,6 +20,11 @@ export const CaptureStatusEnum = { RETROSPECTIVE_IMAGE_RECORDING: 'RETROSPECTIVE_IMAGE_RECORDING', /** Performing burst shooting */ BURST_SHOOTING: 'BURST_SHOOTING', + /** + * In the case of time-lag shooting by manual lens, + * set while waiting for the second shot to be taken after the first shot is completed. + */ + TIME_SHIFT_SHOOTING_IDLE: 'TIME_SHIFT_SHOOTING_IDLE', } as const; /** type definition of CaptureStatusEnum */ diff --git a/react-native/theta-client-react-native.podspec b/react-native/theta-client-react-native.podspec index 6711b07e1c5..76cfa4fe1ce 100644 --- a/react-native/theta-client-react-native.podspec +++ b/react-native/theta-client-react-native.podspec @@ -17,7 +17,7 @@ Pod::Spec.new do |s| s.source_files = "ios/**/*.{h,m,mm,swift}" s.dependency "React-Core" - s.dependency "THETAClient", "1.12.1" + s.dependency "THETAClient", "1.13.0" # Don't install the dependencies when we run `pod install` in the old architecture. if ENV['RCT_NEW_ARCH_ENABLED'] == '1' then diff --git a/react-native/verification-tool/src/App.tsx b/react-native/verification-tool/src/App.tsx index e1a0e48c46b..ee4f57a9722 100644 --- a/react-native/verification-tool/src/App.tsx +++ b/react-native/verification-tool/src/App.tsx @@ -17,13 +17,16 @@ 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'; +import TimeShiftManualCaptureScreen from './screen/time-shift-manual-capture-screen/time-shift-manual-capture-screen'; import LimitlessIntervalCaptureScreen from './screen/limitless-interval-capture-screen/limitless-interval-capture-screen'; import ShotCountSpecifiedIntervalCaptureScreen from './screen/shot-count-specified-interval-capture-screen/shot-count-specified-interval-capture-screen'; import CompositeIntervalCaptureScreen from './screen/composite-interval-capture-screen/composite-interval-capture-screen'; import BurstCaptureScreen from './screen/burst-capture-screen/burst-capture-screen'; import ContinuousCaptureScreen from './screen/continuous-capture-screen/continuous-capture-screen'; import MultiBracketCaptureScreen from './screen/multi-bracket-capture-screen/multi-bracket-capture-screen'; -import type { FileInfo } from './modules/theta-client'; +import { setApiLogListener, type FileInfo } from './modules/theta-client'; +import { Button } from 'react-native'; +import LogPopupView from './components/log-popup-view/log-popup-view'; export type RootStackParamList = { menu: undefined; @@ -39,6 +42,7 @@ export type RootStackParamList = { videoCapture: undefined; limitlessIntervalCapture: undefined; timeShiftCapture: undefined; + timeShiftManualCapture: undefined; shotCountSpecifiedIntervalCapture: undefined; compositeIntervalCapture: undefined; burstCapture: undefined; @@ -63,101 +67,155 @@ const screenOptions = { } as NativeStackNavigationOptions; const App = () => { + const [log, setLog] = React.useState(''); + const [isShowLog, setShowLog] = React.useState(false); + const headerRight = () => ( + <> +