commit c1ddd61e9bf2f3944e7f560a87c10f388f454e71
Author: jiangdongguo <765067602@qq.com>
Date: Fri Sep 29 17:36:05 2017 +0800
Initial commit
diff --git a/.gradle/2.14.1/taskArtifacts/cache.properties b/.gradle/2.14.1/taskArtifacts/cache.properties
new file mode 100644
index 0000000..a2f0313
--- /dev/null
+++ b/.gradle/2.14.1/taskArtifacts/cache.properties
@@ -0,0 +1 @@
+#Fri Sep 29 14:50:34 CST 2017
diff --git a/.gradle/2.14.1/taskArtifacts/cache.properties.lock b/.gradle/2.14.1/taskArtifacts/cache.properties.lock
new file mode 100644
index 0000000..3089714
Binary files /dev/null and b/.gradle/2.14.1/taskArtifacts/cache.properties.lock differ
diff --git a/.gradle/2.14.1/taskArtifacts/fileHashes.bin b/.gradle/2.14.1/taskArtifacts/fileHashes.bin
new file mode 100644
index 0000000..e07f308
Binary files /dev/null and b/.gradle/2.14.1/taskArtifacts/fileHashes.bin differ
diff --git a/.gradle/2.14.1/taskArtifacts/fileSnapshots.bin b/.gradle/2.14.1/taskArtifacts/fileSnapshots.bin
new file mode 100644
index 0000000..65ed922
Binary files /dev/null and b/.gradle/2.14.1/taskArtifacts/fileSnapshots.bin differ
diff --git a/.gradle/2.14.1/taskArtifacts/fileSnapshotsToTreeSnapshotsIndex.bin b/.gradle/2.14.1/taskArtifacts/fileSnapshotsToTreeSnapshotsIndex.bin
new file mode 100644
index 0000000..0c7e314
Binary files /dev/null and b/.gradle/2.14.1/taskArtifacts/fileSnapshotsToTreeSnapshotsIndex.bin differ
diff --git a/.gradle/2.14.1/taskArtifacts/taskArtifacts.bin b/.gradle/2.14.1/taskArtifacts/taskArtifacts.bin
new file mode 100644
index 0000000..59c5caa
Binary files /dev/null and b/.gradle/2.14.1/taskArtifacts/taskArtifacts.bin differ
diff --git a/.gradle/3.3/taskArtifacts/fileHashes.bin b/.gradle/3.3/taskArtifacts/fileHashes.bin
new file mode 100644
index 0000000..d911fa5
Binary files /dev/null and b/.gradle/3.3/taskArtifacts/fileHashes.bin differ
diff --git a/.gradle/3.3/taskArtifacts/fileSnapshots.bin b/.gradle/3.3/taskArtifacts/fileSnapshots.bin
new file mode 100644
index 0000000..fc48224
Binary files /dev/null and b/.gradle/3.3/taskArtifacts/fileSnapshots.bin differ
diff --git a/.gradle/3.3/taskArtifacts/taskArtifacts.bin b/.gradle/3.3/taskArtifacts/taskArtifacts.bin
new file mode 100644
index 0000000..3d16c11
Binary files /dev/null and b/.gradle/3.3/taskArtifacts/taskArtifacts.bin differ
diff --git a/.gradle/3.3/taskArtifacts/taskArtifacts.lock b/.gradle/3.3/taskArtifacts/taskArtifacts.lock
new file mode 100644
index 0000000..c9156c0
Binary files /dev/null and b/.gradle/3.3/taskArtifacts/taskArtifacts.lock differ
diff --git a/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin
new file mode 100644
index 0000000..98f72eb
Binary files /dev/null and b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin differ
diff --git a/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock
new file mode 100644
index 0000000..8dae71e
Binary files /dev/null and b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock differ
diff --git a/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin
new file mode 100644
index 0000000..0423367
Binary files /dev/null and b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin differ
diff --git a/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock
new file mode 100644
index 0000000..20d36ac
Binary files /dev/null and b/.gradle/3.3/tasks/_app_compileDebugJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock differ
diff --git a/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin
new file mode 100644
index 0000000..a07a7a9
Binary files /dev/null and b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.bin differ
diff --git a/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock
new file mode 100644
index 0000000..9a9df5f
Binary files /dev/null and b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localClassSetAnalysis/localClassSetAnalysis.lock differ
diff --git a/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin
new file mode 100644
index 0000000..66bf0f4
Binary files /dev/null and b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.bin differ
diff --git a/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock
new file mode 100644
index 0000000..1d4f344
Binary files /dev/null and b/.gradle/3.3/tasks/_libusbcamera_compileReleaseJavaWithJavac/localJarClasspathSnapshot/localJarClasspathSnapshot.lock differ
diff --git a/.idea/compiler.xml b/.idea/compiler.xml
new file mode 100644
index 0000000..96cc43e
--- /dev/null
+++ b/.idea/compiler.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/copyright/profiles_settings.xml b/.idea/copyright/profiles_settings.xml
new file mode 100644
index 0000000..e7bedf3
--- /dev/null
+++ b/.idea/copyright/profiles_settings.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
new file mode 100644
index 0000000..11c42be
--- /dev/null
+++ b/.idea/gradle.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/animated_vector_drawable_25_3_1.xml b/.idea/libraries/animated_vector_drawable_25_3_1.xml
new file mode 100644
index 0000000..8d7505d
--- /dev/null
+++ b/.idea/libraries/animated_vector_drawable_25_3_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/appcompat_v7_25_3_1.xml b/.idea/libraries/appcompat_v7_25_3_1.xml
new file mode 100644
index 0000000..038e5c7
--- /dev/null
+++ b/.idea/libraries/appcompat_v7_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/auto_common_0_8.xml b/.idea/libraries/auto_common_0_8.xml
new file mode 100644
index 0000000..67f5310
--- /dev/null
+++ b/.idea/libraries/auto_common_0_8.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/butterknife_8_8_1.xml b/.idea/libraries/butterknife_8_8_1.xml
new file mode 100644
index 0000000..4bb46d5
--- /dev/null
+++ b/.idea/libraries/butterknife_8_8_1.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/butterknife_annotations_8_8_1.xml b/.idea/libraries/butterknife_annotations_8_8_1.xml
new file mode 100644
index 0000000..e2d12da
--- /dev/null
+++ b/.idea/libraries/butterknife_annotations_8_8_1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/butterknife_compiler_8_8_1.xml b/.idea/libraries/butterknife_compiler_8_8_1.xml
new file mode 100644
index 0000000..b63b0f2
--- /dev/null
+++ b/.idea/libraries/butterknife_compiler_8_8_1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/common_1_5_20.xml b/.idea/libraries/common_1_5_20.xml
new file mode 100644
index 0000000..b9b9846
--- /dev/null
+++ b/.idea/libraries/common_1_5_20.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/espresso_core_2_2_2.xml b/.idea/libraries/espresso_core_2_2_2.xml
new file mode 100644
index 0000000..5df88bb
--- /dev/null
+++ b/.idea/libraries/espresso_core_2_2_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/espresso_idling_resource_2_2_2.xml b/.idea/libraries/espresso_idling_resource_2_2_2.xml
new file mode 100644
index 0000000..1b2b924
--- /dev/null
+++ b/.idea/libraries/espresso_idling_resource_2_2_2.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml b/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml
new file mode 100644
index 0000000..e94c107
--- /dev/null
+++ b/.idea/libraries/exposed_instrumentation_api_publish_0_5.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/guava_19_0.xml b/.idea/libraries/guava_19_0.xml
new file mode 100644
index 0000000..3c0bbce
--- /dev/null
+++ b/.idea/libraries/guava_19_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/hamcrest_core_1_3.xml b/.idea/libraries/hamcrest_core_1_3.xml
new file mode 100644
index 0000000..157e3f3
--- /dev/null
+++ b/.idea/libraries/hamcrest_core_1_3.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/hamcrest_integration_1_3.xml b/.idea/libraries/hamcrest_integration_1_3.xml
new file mode 100644
index 0000000..58b2c4b
--- /dev/null
+++ b/.idea/libraries/hamcrest_integration_1_3.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/hamcrest_library_1_3.xml b/.idea/libraries/hamcrest_library_1_3.xml
new file mode 100644
index 0000000..676cc63
--- /dev/null
+++ b/.idea/libraries/hamcrest_library_1_3.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/javapoet_1_9_0.xml b/.idea/libraries/javapoet_1_9_0.xml
new file mode 100644
index 0000000..efa3453
--- /dev/null
+++ b/.idea/libraries/javapoet_1_9_0.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/javawriter_2_1_1.xml b/.idea/libraries/javawriter_2_1_1.xml
new file mode 100644
index 0000000..a66fefb
--- /dev/null
+++ b/.idea/libraries/javawriter_2_1_1.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/javax_annotation_api_1_2.xml b/.idea/libraries/javax_annotation_api_1_2.xml
new file mode 100644
index 0000000..811e73f
--- /dev/null
+++ b/.idea/libraries/javax_annotation_api_1_2.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/javax_inject_1.xml b/.idea/libraries/javax_inject_1.xml
new file mode 100644
index 0000000..0d1d5fc
--- /dev/null
+++ b/.idea/libraries/javax_inject_1.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/jsr305_2_0_1.xml b/.idea/libraries/jsr305_2_0_1.xml
new file mode 100644
index 0000000..cdf9878
--- /dev/null
+++ b/.idea/libraries/jsr305_2_0_1.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/junit_4_12.xml b/.idea/libraries/junit_4_12.xml
new file mode 100644
index 0000000..305df30
--- /dev/null
+++ b/.idea/libraries/junit_4_12.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/recyclerview_v7_25_3_1.xml b/.idea/libraries/recyclerview_v7_25_3_1.xml
new file mode 100644
index 0000000..a2e5c1e
--- /dev/null
+++ b/.idea/libraries/recyclerview_v7_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/rules_0_5.xml b/.idea/libraries/rules_0_5.xml
new file mode 100644
index 0000000..4935ce1
--- /dev/null
+++ b/.idea/libraries/rules_0_5.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/runner_0_5.xml b/.idea/libraries/runner_0_5.xml
new file mode 100644
index 0000000..40993f6
--- /dev/null
+++ b/.idea/libraries/runner_0_5.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_annotations_25_3_1.xml b/.idea/libraries/support_annotations_25_3_1.xml
new file mode 100644
index 0000000..21d2ecc
--- /dev/null
+++ b/.idea/libraries/support_annotations_25_3_1.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_compat_25_3_1.xml b/.idea/libraries/support_compat_25_3_1.xml
new file mode 100644
index 0000000..553fd37
--- /dev/null
+++ b/.idea/libraries/support_compat_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_core_ui_25_3_1.xml b/.idea/libraries/support_core_ui_25_3_1.xml
new file mode 100644
index 0000000..b42a139
--- /dev/null
+++ b/.idea/libraries/support_core_ui_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_core_utils_25_3_1.xml b/.idea/libraries/support_core_utils_25_3_1.xml
new file mode 100644
index 0000000..049c378
--- /dev/null
+++ b/.idea/libraries/support_core_utils_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_fragment_25_3_1.xml b/.idea/libraries/support_fragment_25_3_1.xml
new file mode 100644
index 0000000..ed1f097
--- /dev/null
+++ b/.idea/libraries/support_fragment_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_media_compat_25_3_1.xml b/.idea/libraries/support_media_compat_25_3_1.xml
new file mode 100644
index 0000000..527d3ea
--- /dev/null
+++ b/.idea/libraries/support_media_compat_25_3_1.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_v4_25_3_1.xml b/.idea/libraries/support_v4_25_3_1.xml
new file mode 100644
index 0000000..26ee12f
--- /dev/null
+++ b/.idea/libraries/support_v4_25_3_1.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/libraries/support_vector_drawable_25_3_1.xml b/.idea/libraries/support_vector_drawable_25_3_1.xml
new file mode 100644
index 0000000..404781e
--- /dev/null
+++ b/.idea/libraries/support_vector_drawable_25_3_1.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
new file mode 100644
index 0000000..7158618
--- /dev/null
+++ b/.idea/misc.xml
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1.8
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
new file mode 100644
index 0000000..ca1f180
--- /dev/null
+++ b/.idea/modules.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml
new file mode 100644
index 0000000..7f68460
--- /dev/null
+++ b/.idea/runConfigurations.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/AndroidUSBCamera.iml b/AndroidUSBCamera.iml
new file mode 100644
index 0000000..70245ff
--- /dev/null
+++ b/AndroidUSBCamera.iml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/.gitignore b/app/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/app/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/app/app.iml b/app/app.iml
new file mode 100644
index 0000000..e03e0af
--- /dev/null
+++ b/app/app.iml
@@ -0,0 +1,136 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle b/app/build.gradle
new file mode 100644
index 0000000..318544d
--- /dev/null
+++ b/app/build.gradle
@@ -0,0 +1,32 @@
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "26.0.1"
+ defaultConfig {
+ applicationId "com.jiangdg.usbcamera"
+ minSdkVersion 15
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ compile fileTree(include: ['*.jar'], dir: 'libs')
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.1'
+ testCompile 'junit:junit:4.12'
+ compile project(':libusbcamera')
+ compile 'com.jakewharton:butterknife:8.8.1'
+ compile 'com.jakewharton:butterknife-compiler:8.8.1'
+}
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
new file mode 100644
index 0000000..492aa79
--- /dev/null
+++ b/app/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in E:\Environment\android-sdk-windows/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/app/src/androidTest/java/com/jiangdg/usbcamera/ExampleInstrumentedTest.java b/app/src/androidTest/java/com/jiangdg/usbcamera/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..fb81ad9
--- /dev/null
+++ b/app/src/androidTest/java/com/jiangdg/usbcamera/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.jiangdg.usbcamera;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.jiangdg.androidusbcamera", appContext.getPackageName());
+ }
+}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d2146dd
--- /dev/null
+++ b/app/src/main/AndroidManifest.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java b/app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java
new file mode 100644
index 0000000..768242c
--- /dev/null
+++ b/app/src/main/java/com/jiangdg/usbcamera/application/MyApplication.java
@@ -0,0 +1,22 @@
+package com.jiangdg.usbcamera.application;
+
+import android.app.Application;
+
+import com.jiangdg.usbcamera.utils.CrashHandler;
+
+/**全局类
+ *
+ * Created by jianddongguo on 2017/7/20.
+ */
+
+public class MyApplication extends Application {
+ private CrashHandler mCrashHandler;
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mCrashHandler = CrashHandler.getInstance();
+ mCrashHandler.init(getApplicationContext(), getClass());
+
+ }
+}
diff --git a/app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java b/app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java
new file mode 100644
index 0000000..1165523
--- /dev/null
+++ b/app/src/main/java/com/jiangdg/usbcamera/utils/CrashHandler.java
@@ -0,0 +1,190 @@
+package com.jiangdg.usbcamera.utils;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.os.Build;
+import android.os.Environment;
+import android.os.Looper;
+import android.widget.Toast;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.lang.Thread.UncaughtExceptionHandler;
+import java.lang.reflect.Field;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
+ *
+ * @author user
+ *
+ */
+public class CrashHandler implements UncaughtExceptionHandler {
+
+ public static final String TAG = "CrashHandler";
+
+ public static final String PROGRAM_BROKEN_ACTION = "com.teligen.wccp.PROGRAM_BROKEN";
+
+ // 系统默认的UncaughtException处理类
+ private UncaughtExceptionHandler mDefaultHandler;
+ // CrashHandler实例
+ private static CrashHandler instance = new CrashHandler();
+ // 程序的Context对象
+ private Context mContext;
+ // 程序的主Activity的class
+ private Class> mainActivityClass;
+ // 用来存储设备信息和异常信息
+ private Map infos = new HashMap();
+
+ /** 保证只有一个CrashHandler实例 */
+ private CrashHandler() {
+ }
+
+ /** 获取CrashHandler实例 ,单例模式 */
+ public static CrashHandler getInstance() {
+ return instance;
+ }
+
+ public void init(Context context, Class> activityClass) {
+ mContext = context;
+ this.setMainActivityClass(activityClass);
+ // 获取系统默认的UncaughtException处理器
+ mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
+ // 设置该CrashHandler为程序的默认处理器
+ Thread.setDefaultUncaughtExceptionHandler(this);
+ }
+
+ /**
+ * 当UncaughtException发生时会转入该函数来处理
+ */
+ @Override
+ public void uncaughtException(Thread thread, Throwable ex) {
+ if (!handleException(ex) && mDefaultHandler != null) {
+ // 如果用户没有处理则让系统默认的异常处理器来处理
+ mDefaultHandler.uncaughtException(thread, ex);
+ } else {
+ //重启应用,释放资源
+ System.out.println("uncaughtException--->" + ex.getMessage());
+// Log.e(TAG, ex.getMessage());
+ logError(ex);
+ try {
+ Thread.sleep(3000);
+ } catch (InterruptedException e) {
+// Log.e("debug", "error:", e);
+ }
+// AppManagerUtils.removeAllActivities();
+// AppManagerUtils.restartApp(mContext,mContext.getPackageName());
+// AppManagerUtils.releaseAppResource();
+ }
+ }
+
+ /**
+ * 自定义错误处理,收集错误信息 发送错误报告等操作均在此完成.
+ *
+ * @param ex
+ * @return true:如果处理了该异常信息;否则返回false.
+ */
+ private boolean handleException(Throwable ex) {
+ if (ex == null) {
+ return false;
+ }
+ new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Looper.prepare();
+ Toast.makeText(mContext.getApplicationContext(),
+ "程序异常退出,即将重启...", Toast.LENGTH_LONG).show();
+ Looper.loop();
+ }
+ }).start();
+ // 收集设备参数信息
+ collectDeviceInfo(mContext.getApplicationContext());
+ // 保存日志文件
+ logError(ex);
+ return true;
+ }
+
+ /**
+ * 收集设备参数信息
+ *
+ * @param ctx
+ */
+ public void collectDeviceInfo(Context ctx) {
+ try {
+ PackageManager pm = ctx.getPackageManager();
+ PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(),
+ PackageManager.GET_ACTIVITIES);
+ if (pi != null) {
+ String versionName = pi.versionName == null ? "null"
+ : pi.versionName;
+ String versionCode = pi.versionCode + "";
+ infos.put("versionName", versionName);
+ infos.put("versionCode", versionCode);
+ }
+ } catch (NameNotFoundException e) {
+// Log.e(TAG, "收集包信息出现错误", e);
+ }
+ Field[] fields = Build.class.getDeclaredFields();
+ for (Field field : fields) {
+ try {
+ field.setAccessible(true);
+ infos.put(field.getName(), field.get(null).toString());
+ } catch (Exception e) {
+// Log.e(TAG, "收集系统信息出现错误", e);
+ }
+ }
+ }
+
+ /**
+ * 保存错误信息到文件中
+ *
+ * @param ex
+ * @return 返回文件名称,便于将文件传送到服务器
+ */
+ private void logError(Throwable ex) {
+
+ StringBuffer sb = new StringBuffer();
+// for (Map.Entry entry : infos.entrySet()) {
+// String key = entry.getKey();
+// String value = entry.getValue();
+// sb.append(key + "=" + value + "\n");
+// }
+ int num = ex.getStackTrace().length;
+ for (int i=0;i getMainActivityClass() {
+ return mainActivityClass;
+ }
+
+ public void setMainActivityClass(Class> mainActivityClass) {
+ this.mainActivityClass = mainActivityClass;
+ }
+}
diff --git a/app/src/main/java/com/jiangdg/usbcamera/view/BaseActivity.java b/app/src/main/java/com/jiangdg/usbcamera/view/BaseActivity.java
new file mode 100644
index 0000000..c61b4ab
--- /dev/null
+++ b/app/src/main/java/com/jiangdg/usbcamera/view/BaseActivity.java
@@ -0,0 +1,198 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.jiangdg.usbcamera.view;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.v7.app.AppCompatActivity;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.serenegiant.dialog.MessageDialogFragment;
+import com.serenegiant.utils.BuildCheck;
+import com.serenegiant.utils.HandlerThreadHandler;
+import com.serenegiant.utils.PermissionCheck;
+
+/**
+ * Created by saki on 2016/11/18.
+ *
+ */
+public class BaseActivity extends AppCompatActivity {
+
+ private static boolean DEBUG = false;
+ private static final String TAG = BaseActivity.class.getSimpleName();
+
+ // 处理UI的Handler
+ private final Handler mUIHandler = new Handler(Looper.getMainLooper());
+ private final Thread mUiThread = mUIHandler.getLooper().getThread();
+ // 工作线程Handler
+ private Handler mWorkerHandler;
+ private long mWorkerThreadID = -1;
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // 创建工作线程
+ if (mWorkerHandler == null) {
+ mWorkerHandler = HandlerThreadHandler.createHandler(TAG);
+ mWorkerThreadID = mWorkerHandler.getLooper().getThread().getId();
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ clearToast();
+ super.onPause();
+ }
+
+ @Override
+ protected synchronized void onDestroy() {
+ // 释放线程资源
+ if (mWorkerHandler != null) {
+ try {
+ mWorkerHandler.getLooper().quit();
+ } catch (final Exception e) {
+ //
+ }
+ mWorkerHandler = null;
+ }
+ super.onDestroy();
+ }
+
+//================================================================================
+ /**
+ *
+ * 子线程中更新UI,duration为延迟多久执行
+ *
+ */
+ public final void runOnUiThread(final Runnable task, final long duration) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ if ((duration > 0) || Thread.currentThread() != mUiThread) {
+ mUIHandler.postDelayed(task, duration);
+ } else {
+ try {
+ task.run();
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ /**
+ * 移除更新UI task
+ * @param task
+ */
+ public final void removeFromUiThread(final Runnable task) {
+ if (task == null) return;
+ mUIHandler.removeCallbacks(task);
+ }
+
+ /**
+ * 工作子线程中执行的任务
+ */
+ protected final synchronized void queueEvent(final Runnable task, final long delayMillis) {
+ if ((task == null) || (mWorkerHandler == null)) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ if (delayMillis > 0) {
+ mWorkerHandler.postDelayed(task, delayMillis);
+ } else if (mWorkerThreadID == Thread.currentThread().getId()) {
+ task.run();
+ } else {
+ mWorkerHandler.post(task);
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ protected final synchronized void removeEvent(final Runnable task) {
+ if (task == null) return;
+ try {
+ mWorkerHandler.removeCallbacks(task);
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+//================================================================================
+ private Toast mToast;
+
+ protected void showToast(@StringRes final int msg, final Object... args) {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = new ShowToastTask(msg, args);
+ runOnUiThread(mShowToastTask, 0);
+ }
+
+
+ protected void clearToast() {
+ removeFromUiThread(mShowToastTask);
+ mShowToastTask = null;
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+
+ private ShowToastTask mShowToastTask;
+ private final class ShowToastTask implements Runnable {
+ final int msg;
+ final Object args;
+ private ShowToastTask(@StringRes final int msg, final Object... args) {
+ this.msg = msg;
+ this.args = args;
+ }
+
+ @Override
+ public void run() {
+ try {
+ if (mToast != null) {
+ mToast.cancel();
+ mToast = null;
+ }
+ if (args != null) {
+ final String _msg = getString(msg, args);
+ mToast = Toast.makeText(BaseActivity.this, _msg, Toast.LENGTH_SHORT);
+ } else {
+ mToast = Toast.makeText(BaseActivity.this, msg, Toast.LENGTH_SHORT);
+ }
+ mToast.show();
+ } catch (final Exception e) {
+ // ignore
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/jiangdg/usbcamera/view/MainActivity.java b/app/src/main/java/com/jiangdg/usbcamera/view/MainActivity.java
new file mode 100644
index 0000000..6bc9447
--- /dev/null
+++ b/app/src/main/java/com/jiangdg/usbcamera/view/MainActivity.java
@@ -0,0 +1,250 @@
+package com.jiangdg.usbcamera.view;
+
+import android.hardware.usb.UsbDevice;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ImageButton;
+import android.widget.Toast;
+
+import com.jiangdg.usbcamera.R;
+import com.serenegiant.usb.CameraDialog;
+import com.serenegiant.usb.USBMonitor;
+import com.serenegiant.usb.USBMonitor.OnDeviceConnectListener;
+import com.serenegiant.usb.USBMonitor.UsbControlBlock;
+import com.serenegiant.usb.UVCCamera;
+
+import butterknife.BindView;
+import butterknife.ButterKnife;
+import butterknife.OnClick;
+
+public class MainActivity extends BaseActivity implements CameraDialog.CameraDialogParent {
+ private static final boolean DEBUG = true;
+ private static final String TAG = "MainActivity";
+ @BindView(R.id.camera_surface_view)
+ public SurfaceView mUVCCameraView;
+
+ private final Object mSync = new Object();
+ // USB和USB Camera访问管理类
+ private USBMonitor mUSBMonitor;
+ private UVCCamera mUVCCamera;
+
+ private Surface mPreviewSurface;
+ private boolean isActive, isPreview;
+
+ private final SurfaceHolder.Callback mSurfaceViewCallback = new SurfaceHolder.Callback() {
+ @Override
+ public void surfaceCreated(final SurfaceHolder holder) {
+ if (DEBUG) Log.v(TAG, "surfaceCreated:");
+ }
+
+ @Override
+ public void surfaceChanged(final SurfaceHolder holder, final int format, final int width, final int height) {
+ if ((width == 0) || (height == 0)) return;
+ if (DEBUG) Log.v(TAG, "surfaceChanged:");
+ mPreviewSurface = holder.getSurface();
+ synchronized (mSync) {
+ if (isActive && !isPreview && (mUVCCamera != null)) {
+ mUVCCamera.setPreviewDisplay(mPreviewSurface);
+ mUVCCamera.startPreview();
+ isPreview = true;
+ }
+ }
+ }
+
+ @Override
+ public void surfaceDestroyed(final SurfaceHolder holder) {
+ if (DEBUG) Log.v(TAG, "surfaceDestroyed:");
+ synchronized (mSync) {
+ if (mUVCCamera != null) {
+ mUVCCamera.stopPreview();
+ }
+ isPreview = false;
+ }
+ mPreviewSurface = null;
+ }
+ };
+
+ @Override
+ protected void onCreate(final Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_main);
+ // 绑定Activity
+ ButterKnife.bind(this);
+ mUVCCameraView.getHolder().addCallback(mSurfaceViewCallback);
+
+ // 初始化USBMonitor
+ // 注册USB设备监听器
+ mUSBMonitor = new USBMonitor(this, new OnDeviceConnectListener() {
+ @Override
+ public void onAttach(final UsbDevice device) {
+ if (DEBUG) Log.v(TAG, "onAttach:");
+ Toast.makeText(MainActivity.this, "检测到USB设备", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onConnect(final UsbDevice device, final UsbControlBlock ctrlBlock, final boolean createNew) {
+ if (DEBUG) Log.v(TAG, "onConnect:");
+ Toast.makeText(MainActivity.this, "成功连接到USB设备", Toast.LENGTH_SHORT).show();
+ synchronized (mSync) {
+ if (mUVCCamera != null) {
+ mUVCCamera.destroy();
+ }
+ isActive = isPreview = false;
+ }
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mSync) {
+ final UVCCamera camera = new UVCCamera();
+ camera.open(ctrlBlock);
+ if (DEBUG) Log.i(TAG, "supportedSize:" + camera.getSupportedSize());
+ try {
+ camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.FRAME_FORMAT_MJPEG);
+ } catch (final IllegalArgumentException e) {
+ try {
+ // fallback to YUV mode
+ camera.setPreviewSize(UVCCamera.DEFAULT_PREVIEW_WIDTH, UVCCamera.DEFAULT_PREVIEW_HEIGHT, UVCCamera.DEFAULT_PREVIEW_MODE);
+ } catch (final IllegalArgumentException e1) {
+ camera.destroy();
+ return;
+ }
+ }
+ mPreviewSurface = mUVCCameraView.getHolder().getSurface();
+ if (mPreviewSurface != null) {
+ isActive = true;
+ camera.setPreviewDisplay(mPreviewSurface);
+ camera.startPreview();
+ isPreview = true;
+ }
+ synchronized (mSync) {
+ mUVCCamera = camera;
+ }
+ }
+ }
+ }, 0);
+ }
+
+ @Override
+ public void onDisconnect(final UsbDevice device, final UsbControlBlock ctrlBlock) {
+ if (DEBUG) Log.v(TAG, "onDisconnect:");
+ Toast.makeText(MainActivity.this, "与USB设备断开连接", Toast.LENGTH_SHORT).show();
+ // XXX you should check whether the comming device equal to camera device that currently using
+ queueEvent(new Runnable() {
+ @Override
+ public void run() {
+ synchronized (mSync) {
+ if (mUVCCamera != null) {
+ mUVCCamera.close();
+ if (mPreviewSurface != null) {
+ mPreviewSurface.release();
+ mPreviewSurface = null;
+ }
+ isActive = isPreview = false;
+ }
+ }
+ }
+ }, 0);
+ }
+
+ @Override
+ public void onDettach(final UsbDevice device) {
+ if (DEBUG) Log.v(TAG, "onDettach:");
+ Toast.makeText(MainActivity.this, "未检测到USB设备", Toast.LENGTH_SHORT).show();
+ }
+
+ @Override
+ public void onCancel(final UsbDevice device) {
+ }
+ });
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (DEBUG) Log.v(TAG, "onStart:");
+ synchronized (mSync) {
+ // 注册
+ if (mUSBMonitor != null) {
+ mUSBMonitor.register();
+ }
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ if (DEBUG) Log.v(TAG, "onStop:");
+ synchronized (mSync) {
+ // 注销
+ if (mUSBMonitor != null) {
+ mUSBMonitor.unregister();
+ }
+ }
+ super.onStop();
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (DEBUG) Log.v(TAG, "onDestroy:");
+ synchronized (mSync) {
+ isActive = isPreview = false;
+ if (mUVCCamera != null) {
+ mUVCCamera.destroy();
+ mUVCCamera = null;
+ }
+ // 释放资源
+ if (mUSBMonitor != null) {
+ mUSBMonitor.destroy();
+ mUSBMonitor = null;
+ }
+ }
+ mUVCCameraView = null;
+ super.onDestroy();
+ }
+
+ @OnClick({R.id.camera_surface_view})
+ public void onViewClicked(View view){
+ int vId= view.getId();
+ switch (vId){
+ case R.id.camera_surface_view:
+ if (mUVCCamera == null) {
+ // XXX calling CameraDialog.showDialog is necessary at only first time(only when app has no permission).
+ // 当APP访问USB设备没有被授权时,弹出对话框
+ CameraDialog.showDialog(MainActivity.this);
+ } else {
+ synchronized (mSync) {
+ mUVCCamera.destroy();
+ mUVCCamera = null;
+ isActive = isPreview = false;
+ }
+ }
+ break;
+ }
+ }
+
+
+ /**
+ * to access from CameraDialog
+ * @return
+ */
+ @Override
+ public USBMonitor getUSBMonitor() {
+ return mUSBMonitor;
+ }
+
+ @Override
+ public void onDialogResult(boolean canceled) {
+ if (canceled) {
+ runOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ // FIXME
+ }
+ }, 0);
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..7aff3d8
--- /dev/null
+++ b/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000..cde69bc
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000..c133a0c
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..bfa42f0
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..324e72c
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..aee44e1
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-w820dp/dimens.xml b/app/src/main/res/values-w820dp/dimens.xml
new file mode 100644
index 0000000..63fc816
--- /dev/null
+++ b/app/src/main/res/values-w820dp/dimens.xml
@@ -0,0 +1,6 @@
+
+
+ 64dp
+
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
new file mode 100644
index 0000000..3ab3e9c
--- /dev/null
+++ b/app/src/main/res/values/colors.xml
@@ -0,0 +1,6 @@
+
+
+ #3F51B5
+ #303F9F
+ #FF4081
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..47c8224
--- /dev/null
+++ b/app/src/main/res/values/dimens.xml
@@ -0,0 +1,5 @@
+
+
+ 16dp
+ 16dp
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..60b5af8
--- /dev/null
+++ b/app/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ AndroidUSBCamera
+
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..5885930
--- /dev/null
+++ b/app/src/main/res/values/styles.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
diff --git a/app/src/test/java/com/jiangdg/usbcamera/ExampleUnitTest.java b/app/src/test/java/com/jiangdg/usbcamera/ExampleUnitTest.java
new file mode 100644
index 0000000..3fac274
--- /dev/null
+++ b/app/src/test/java/com/jiangdg/usbcamera/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.jiangdg.usbcamera;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..c8f7571
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,31 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+buildscript {
+ repositories {
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:2.3.1'
+ }
+}
+
+allprojects {
+ repositories {
+ maven { url 'http://raw.github.com/saki4510t/libcommon/master/repository/' }
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
+
+ext {
+ supportLibVersion = '25.3.1' // variable that can be referenced to keep support libs consistent
+ commonLibVersion= '1.5.20'
+ versionBuildTool = '25.0.2'
+ versionCompiler = 25
+ versionTarget = 23
+ versionNameString = '1.0.0'
+ javaSourceCompatibility = JavaVersion.VERSION_1_7
+ javaTargetCompatibility = JavaVersion.VERSION_1_7
+}
diff --git a/build/android-profile/profile-2017-09-29-17-33-27-872.rawproto b/build/android-profile/profile-2017-09-29-17-33-27-872.rawproto
new file mode 100644
index 0000000..e8583e7
Binary files /dev/null and b/build/android-profile/profile-2017-09-29-17-33-27-872.rawproto differ
diff --git a/build/generated/mockable-android-25.jar b/build/generated/mockable-android-25.jar
new file mode 100644
index 0000000..c4ed2a8
Binary files /dev/null and b/build/generated/mockable-android-25.jar differ
diff --git a/gradle.properties b/gradle.properties
new file mode 100644
index 0000000..aac7c9b
--- /dev/null
+++ b/gradle.properties
@@ -0,0 +1,17 @@
+# Project-wide Gradle settings.
+
+# IDE (e.g. Android Studio) users:
+# Gradle settings configured through the IDE *will override*
+# any settings specified in this file.
+
+# For more details on how to configure your build environment visit
+# http://www.gradle.org/docs/current/userguide/build_environment.html
+
+# Specifies the JVM arguments used for the daemon process.
+# The setting is particularly useful for tweaking memory settings.
+org.gradle.jvmargs=-Xmx1536m
+
+# When configured, Gradle will run in incubating parallel mode.
+# This option should only be used with decoupled projects. More details, visit
+# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
+# org.gradle.parallel=true
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..13372ae
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..9a778d6
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Mon Dec 28 10:00:20 PST 2015
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
diff --git a/gradlew b/gradlew
new file mode 100644
index 0000000..9d82f78
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,160 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644
index 0000000..8a0b282
--- /dev/null
+++ b/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/libusbcamera/.gitignore b/libusbcamera/.gitignore
new file mode 100644
index 0000000..796b96d
--- /dev/null
+++ b/libusbcamera/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/libusbcamera/build.gradle b/libusbcamera/build.gradle
new file mode 100644
index 0000000..fc0ad3a
--- /dev/null
+++ b/libusbcamera/build.gradle
@@ -0,0 +1,35 @@
+apply plugin: 'com.android.library'
+
+android {
+ compileSdkVersion 25
+ buildToolsVersion "26.0.1"
+
+ defaultConfig {
+ minSdkVersion 15
+ targetSdkVersion 25
+ versionCode 1
+ versionName "1.0"
+
+ testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
+
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+
+dependencies {
+ compile fileTree(dir: 'libs', include: ['*.jar'])
+ androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
+ exclude group: 'com.android.support', module: 'support-annotations'
+ })
+ compile 'com.android.support:appcompat-v7:25.3.1'
+ testCompile 'junit:junit:4.12'
+ compile("com.serenegiant:common:${commonLibVersion}") {
+ exclude module: 'support-v4'
+ }
+}
diff --git a/libusbcamera/libusbcamera.iml b/libusbcamera/libusbcamera.iml
new file mode 100644
index 0000000..3ce8d33
--- /dev/null
+++ b/libusbcamera/libusbcamera.iml
@@ -0,0 +1,131 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ generateDebugSources
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libusbcamera/proguard-rules.pro b/libusbcamera/proguard-rules.pro
new file mode 100644
index 0000000..492aa79
--- /dev/null
+++ b/libusbcamera/proguard-rules.pro
@@ -0,0 +1,17 @@
+# Add project specific ProGuard rules here.
+# By default, the flags in this file are appended to flags specified
+# in E:\Environment\android-sdk-windows/tools/proguard/proguard-android.txt
+# You can edit the include path and order by changing the proguardFiles
+# directive in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# Add any project specific keep options here:
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
diff --git a/libusbcamera/src/androidTest/java/com/jiangdg/libusbcamera/ExampleInstrumentedTest.java b/libusbcamera/src/androidTest/java/com/jiangdg/libusbcamera/ExampleInstrumentedTest.java
new file mode 100644
index 0000000..4f535e1
--- /dev/null
+++ b/libusbcamera/src/androidTest/java/com/jiangdg/libusbcamera/ExampleInstrumentedTest.java
@@ -0,0 +1,26 @@
+package com.jiangdg.libusbcamera;
+
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.runner.AndroidJUnit4;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static org.junit.Assert.*;
+
+/**
+ * Instrumentation test, which will execute on an Android device.
+ *
+ * @see Testing documentation
+ */
+@RunWith(AndroidJUnit4.class)
+public class ExampleInstrumentedTest {
+ @Test
+ public void useAppContext() throws Exception {
+ // Context of the app under test.
+ Context appContext = InstrumentationRegistry.getTargetContext();
+
+ assertEquals("com.jiangdg.libusbcamera.test", appContext.getPackageName());
+ }
+}
diff --git a/libusbcamera/src/main/AndroidManifest.xml b/libusbcamera/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..072143f
--- /dev/null
+++ b/libusbcamera/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/CameraDialog.java b/libusbcamera/src/main/java/com/serenegiant/usb/CameraDialog.java
new file mode 100644
index 0000000..2fcc9fa
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/CameraDialog.java
@@ -0,0 +1,237 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.hardware.usb.UsbDevice;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.BaseAdapter;
+import android.widget.Button;
+import android.widget.CheckedTextView;
+import android.widget.Spinner;
+
+import com.jiangdg.libusbcamera.R;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class CameraDialog extends DialogFragment {
+ private static final String TAG = CameraDialog.class.getSimpleName();
+
+ public interface CameraDialogParent {
+ public USBMonitor getUSBMonitor();
+ public void onDialogResult(boolean canceled);
+ }
+
+ /**
+ * Helper method
+ * @param parent FragmentActivity
+ * @return
+ */
+ public static CameraDialog showDialog(final Activity parent/* add parameters here if you need */) {
+ CameraDialog dialog = newInstance(/* add parameters here if you need */);
+ try {
+ dialog.show(parent.getFragmentManager(), TAG);
+ } catch (final IllegalStateException e) {
+ dialog = null;
+ }
+ return dialog;
+ }
+
+ public static CameraDialog newInstance(/* add parameters here if you need */) {
+ final CameraDialog dialog = new CameraDialog();
+ final Bundle args = new Bundle();
+ // add parameters here if you need
+ dialog.setArguments(args);
+ return dialog;
+ }
+
+ protected USBMonitor mUSBMonitor;
+ private Spinner mSpinner;
+ private DeviceListAdapter mDeviceListAdapter;
+
+ public CameraDialog(/* no arguments */) {
+ // Fragment need default constructor
+ }
+
+ @SuppressWarnings("deprecation")
+ @Override
+ public void onAttach(final Activity activity) {
+ super.onAttach(activity);
+ if (mUSBMonitor == null)
+ try {
+ mUSBMonitor = ((CameraDialogParent)activity).getUSBMonitor();
+ } catch (final ClassCastException e) {
+ } catch (final NullPointerException e) {
+ }
+ if (mUSBMonitor == null) {
+ throw new ClassCastException(activity.toString() + " must implement CameraDialogParent#getUSBController");
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState == null)
+ savedInstanceState = getArguments();
+ }
+
+ @Override
+ public void onSaveInstanceState(final Bundle saveInstanceState) {
+ final Bundle args = getArguments();
+ if (args != null)
+ saveInstanceState.putAll(args);
+ super.onSaveInstanceState(saveInstanceState);
+ }
+
+ @Override
+ public Dialog onCreateDialog(final Bundle savedInstanceState) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
+ builder.setView(initView());
+ builder.setTitle(R.string.select);
+ builder.setPositiveButton(android.R.string.ok, mOnDialogClickListener);
+ builder.setNegativeButton(android.R.string.cancel , mOnDialogClickListener);
+ builder.setNeutralButton(R.string.refresh, null);
+ final Dialog dialog = builder.create();
+ dialog.setCancelable(true);
+ dialog.setCanceledOnTouchOutside(true);
+ return dialog;
+ }
+
+ /**
+ * create view that this fragment shows
+ * @return
+ */
+ private final View initView() {
+ final View rootView = getActivity().getLayoutInflater().inflate(R.layout.dialog_camera, null);
+ mSpinner = (Spinner)rootView.findViewById(R.id.spinner1);
+ final View empty = rootView.findViewById(android.R.id.empty);
+ mSpinner.setEmptyView(empty);
+ return rootView;
+ }
+
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateDevices();
+ final Button button = (Button)getDialog().findViewById(android.R.id.button3);
+ if (button != null) {
+ button.setOnClickListener(mOnClickListener);
+ }
+ }
+
+ private final OnClickListener mOnClickListener = new OnClickListener() {
+ @Override
+ public void onClick(final View v) {
+ switch (v.getId()) {
+ case android.R.id.button3:
+ updateDevices();
+ break;
+ }
+ }
+ };
+
+ private final DialogInterface.OnClickListener mOnDialogClickListener = new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(final DialogInterface dialog, final int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ final Object item = mSpinner.getSelectedItem();
+ if (item instanceof UsbDevice) {
+ mUSBMonitor.requestPermission((UsbDevice)item);
+ ((CameraDialogParent)getActivity()).onDialogResult(false);
+ }
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ ((CameraDialogParent)getActivity()).onDialogResult(true);
+ break;
+ }
+ }
+ };
+
+ @Override
+ public void onCancel(final DialogInterface dialog) {
+ ((CameraDialogParent)getActivity()).onDialogResult(true);
+ super.onCancel(dialog);
+ }
+
+ public void updateDevices() {
+// mUSBMonitor.dumpDevices();
+ final List filter = DeviceFilter.getDeviceFilters(getActivity(), R.xml.device_filter);
+ mDeviceListAdapter = new DeviceListAdapter(getActivity(), mUSBMonitor.getDeviceList(filter.get(0)));
+ mSpinner.setAdapter(mDeviceListAdapter);
+ }
+
+ private static final class DeviceListAdapter extends BaseAdapter {
+
+ private final LayoutInflater mInflater;
+ private final List mList;
+
+ public DeviceListAdapter(final Context context, final Listlist) {
+ mInflater = LayoutInflater.from(context);
+ mList = list != null ? list : new ArrayList();
+ }
+
+ @Override
+ public int getCount() {
+ return mList.size();
+ }
+
+ @Override
+ public UsbDevice getItem(final int position) {
+ if ((position >= 0) && (position < mList.size()))
+ return mList.get(position);
+ else
+ return null;
+ }
+
+ @Override
+ public long getItemId(final int position) {
+ return position;
+ }
+
+ @Override
+ public View getView(final int position, View convertView, final ViewGroup parent) {
+ if (convertView == null) {
+ convertView = mInflater.inflate(R.layout.listitem_device, parent, false);
+ }
+ if (convertView instanceof CheckedTextView) {
+ final UsbDevice device = getItem(position);
+ ((CheckedTextView)convertView).setText(
+ String.format("UVC Camera:(%x:%x:%s)", device.getVendorId(), device.getProductId(), device.getDeviceName()));
+ }
+ return convertView;
+ }
+ }
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/DeviceFilter.java b/libusbcamera/src/main/java/com/serenegiant/usb/DeviceFilter.java
new file mode 100644
index 0000000..2e94263
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/DeviceFilter.java
@@ -0,0 +1,527 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.content.Context;
+import android.content.res.Resources.NotFoundException;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public final class DeviceFilter {
+
+ private static final String TAG = "DeviceFilter";
+
+ // USB Vendor ID (or -1 for unspecified)
+ public final int mVendorId;
+ // USB Product ID (or -1 for unspecified)
+ public final int mProductId;
+ // USB device or interface class (or -1 for unspecified)
+ public final int mClass;
+ // USB device subclass (or -1 for unspecified)
+ public final int mSubclass;
+ // USB device protocol (or -1 for unspecified)
+ public final int mProtocol;
+ // USB device manufacturer name string (or null for unspecified)
+ public final String mManufacturerName;
+ // USB device product name string (or null for unspecified)
+ public final String mProductName;
+ // USB device serial number string (or null for unspecified)
+ public final String mSerialNumber;
+ // set true if specific device(s) should exclude
+ public final boolean isExclude;
+
+ public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
+ final int protocol, final String manufacturer, final String product, final String serialNum) {
+ this(vid, pid, clasz, subclass, protocol, manufacturer, product, serialNum, false);
+ }
+
+ public DeviceFilter(final int vid, final int pid, final int clasz, final int subclass,
+ final int protocol, final String manufacturer, final String product, final String serialNum, final boolean isExclude) {
+ mVendorId = vid;
+ mProductId = pid;
+ mClass = clasz;
+ mSubclass = subclass;
+ mProtocol = protocol;
+ mManufacturerName = manufacturer;
+ mProductName = product;
+ mSerialNumber = serialNum;
+ this.isExclude = isExclude;
+/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
+ mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
+ }
+
+ public DeviceFilter(final UsbDevice device) {
+ this(device, false);
+ }
+
+ public DeviceFilter(final UsbDevice device, final boolean isExclude) {
+ mVendorId = device.getVendorId();
+ mProductId = device.getProductId();
+ mClass = device.getDeviceClass();
+ mSubclass = device.getDeviceSubclass();
+ mProtocol = device.getDeviceProtocol();
+ mManufacturerName = null; // device.getManufacturerName();
+ mProductName = null; // device.getProductName();
+ mSerialNumber = null; // device.getSerialNumber();
+ this.isExclude = isExclude;
+/* Log.i(TAG, String.format("vendorId=0x%04x,productId=0x%04x,class=0x%02x,subclass=0x%02x,protocol=0x%02x",
+ mVendorId, mProductId, mClass, mSubclass, mProtocol)); */
+ }
+
+ /**
+ * 指定したxmlリソースからDeviceFilterリストを生成する
+ * @param context
+ * @param deviceFilterXmlId
+ * @return
+ */
+ public static List getDeviceFilters(final Context context, final int deviceFilterXmlId) {
+ final XmlPullParser parser = context.getResources().getXml(deviceFilterXmlId);
+ final List deviceFilters = new ArrayList();
+ try {
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ if (eventType == XmlPullParser.START_TAG) {
+ final DeviceFilter deviceFilter = readEntryOne(context, parser);
+ if (deviceFilter != null) {
+ deviceFilters.add(deviceFilter);
+ }
+ }
+ eventType = parser.next();
+ }
+ } catch (final XmlPullParserException e) {
+ Log.d(TAG, "XmlPullParserException", e);
+ } catch (final IOException e) {
+ Log.d(TAG, "IOException", e);
+ }
+
+ return Collections.unmodifiableList(deviceFilters);
+ }
+
+ /**
+ * read as integer values with default value from xml(w/o exception throws)
+ * resource integer id is also resolved into integer
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final int getAttributeInteger(final Context context, final XmlPullParser parser, final String namespace, final String name, final int defaultValue) {
+ int result = defaultValue;
+ try {
+ String v = parser.getAttributeValue(namespace, name);
+ if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
+ final String r = v.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0) {
+ result = context.getResources().getInteger(resId);
+ }
+ } else {
+ int radix = 10;
+ if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
+ (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ v = v.substring(2);
+ }
+ result = Integer.parseInt(v, radix);
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * read as boolean values with default value from xml(w/o exception throws)
+ * resource boolean id is also resolved into boolean
+ * if the value is zero, return false, if the value is non-zero integer, return true
+ * @param context
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final boolean getAttributeBoolean(final Context context, final XmlPullParser parser, final String namespace, final String name, final boolean defaultValue) {
+ boolean result = defaultValue;
+ try {
+ String v = parser.getAttributeValue(namespace, name);
+ if ("TRUE".equalsIgnoreCase(v)) {
+ result = true;
+ } else if ("FALSE".equalsIgnoreCase(v)) {
+ result = false;
+ } else if (!TextUtils.isEmpty(v) && v.startsWith("@")) {
+ final String r = v.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0) {
+ result = context.getResources().getBoolean(resId);
+ }
+ } else {
+ int radix = 10;
+ if (v != null && v.length() > 2 && v.charAt(0) == '0' &&
+ (v.charAt(1) == 'x' || v.charAt(1) == 'X')) {
+ // allow hex values starting with 0x or 0X
+ radix = 16;
+ v = v.substring(2);
+ }
+ final int val = Integer.parseInt(v, radix);
+ result = val != 0;
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ /**
+ * read as String attribute with default value from xml(w/o exception throws)
+ * resource string id is also resolved into string
+ * @param parser
+ * @param namespace
+ * @param name
+ * @param defaultValue
+ * @return
+ */
+ private static final String getAttributeString(final Context context, final XmlPullParser parser, final String namespace, final String name, final String defaultValue) {
+ String result = defaultValue;
+ try {
+ result = parser.getAttributeValue(namespace, name);
+ if (result == null)
+ result = defaultValue;
+ if (!TextUtils.isEmpty(result) && result.startsWith("@")) {
+ final String r = result.substring(1);
+ final int resId = context.getResources().getIdentifier(r, null, context.getPackageName());
+ if (resId > 0)
+ result = context.getResources().getString(resId);
+ }
+ } catch (final NotFoundException e) {
+ result = defaultValue;
+ } catch (final NumberFormatException e) {
+ result = defaultValue;
+ } catch (final NullPointerException e) {
+ result = defaultValue;
+ }
+ return result;
+ }
+
+ public static DeviceFilter readEntryOne(final Context context, final XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int vendorId = -1;
+ int productId = -1;
+ int deviceClass = -1;
+ int deviceSubclass = -1;
+ int deviceProtocol = -1;
+ boolean exclude = false;
+ String manufacturerName = null;
+ String productName = null;
+ String serialNumber = null;
+ boolean hasValue = false;
+
+ String tag;
+ int eventType = parser.getEventType();
+ while (eventType != XmlPullParser.END_DOCUMENT) {
+ tag = parser.getName();
+ if (!TextUtils.isEmpty(tag) && (tag.equalsIgnoreCase("usb-device"))) {
+ if (eventType == XmlPullParser.START_TAG) {
+ hasValue = true;
+ vendorId = getAttributeInteger(context, parser, null, "vendor-id", -1);
+ if (vendorId == -1) {
+ vendorId = getAttributeInteger(context, parser, null, "vendorId", -1);
+ if (vendorId == -1)
+ vendorId = getAttributeInteger(context, parser, null, "venderId", -1);
+ }
+ productId = getAttributeInteger(context, parser, null, "product-id", -1);
+ if (productId == -1)
+ productId = getAttributeInteger(context, parser, null, "productId", -1);
+ deviceClass = getAttributeInteger(context, parser, null, "class", -1);
+ deviceSubclass = getAttributeInteger(context, parser, null, "subclass", -1);
+ deviceProtocol = getAttributeInteger(context, parser, null, "protocol", -1);
+ manufacturerName = getAttributeString(context, parser, null, "manufacturer-name", null);
+ if (TextUtils.isEmpty(manufacturerName))
+ manufacturerName = getAttributeString(context, parser, null, "manufacture", null);
+ productName = getAttributeString(context, parser, null, "product-name", null);
+ if (TextUtils.isEmpty(productName))
+ productName = getAttributeString(context, parser, null, "product", null);
+ serialNumber = getAttributeString(context, parser, null, "serial-number", null);
+ if (TextUtils.isEmpty(serialNumber))
+ serialNumber = getAttributeString(context, parser, null, "serial", null);
+ exclude = getAttributeBoolean(context, parser, null, "exclude", false);
+ } else if (eventType == XmlPullParser.END_TAG) {
+ if (hasValue) {
+ return new DeviceFilter(vendorId, productId, deviceClass,
+ deviceSubclass, deviceProtocol, manufacturerName, productName,
+ serialNumber, exclude);
+ }
+ }
+ }
+ eventType = parser.next();
+ }
+ return null;
+ }
+
+/* public void write(XmlSerializer serializer) throws IOException {
+ serializer.startTag(null, "usb-device");
+ if (mVendorId != -1) {
+ serializer
+ .attribute(null, "vendor-id", Integer.toString(mVendorId));
+ }
+ if (mProductId != -1) {
+ serializer.attribute(null, "product-id",
+ Integer.toString(mProductId));
+ }
+ if (mClass != -1) {
+ serializer.attribute(null, "class", Integer.toString(mClass));
+ }
+ if (mSubclass != -1) {
+ serializer.attribute(null, "subclass", Integer.toString(mSubclass));
+ }
+ if (mProtocol != -1) {
+ serializer.attribute(null, "protocol", Integer.toString(mProtocol));
+ }
+ if (mManufacturerName != null) {
+ serializer.attribute(null, "manufacturer-name", mManufacturerName);
+ }
+ if (mProductName != null) {
+ serializer.attribute(null, "product-name", mProductName);
+ }
+ if (mSerialNumber != null) {
+ serializer.attribute(null, "serial-number", mSerialNumber);
+ }
+ serializer.attribute(null, "serial-number", Boolean.toString(isExclude));
+ serializer.endTag(null, "usb-device");
+ } */
+
+ /**
+ * 指定したクラス・サブクラス・プロトコルがこのDeviceFilterとマッチするかどうかを返す
+ * mExcludeフラグは別途#isExcludeか自前でチェックすること
+ * @param clasz
+ * @param subclass
+ * @param protocol
+ * @return
+ */
+ private boolean matches(final int clasz, final int subclass, final int protocol) {
+ return ((mClass == -1 || clasz == mClass)
+ && (mSubclass == -1 || subclass == mSubclass) && (mProtocol == -1 || protocol == mProtocol));
+ }
+
+ /**
+ * 指定したUsbDeviceがこのDeviceFilterにマッチするかどうかを返す
+ * mExcludeフラグは別途#isExcludeか自前でチェックすること
+ * @param device
+ * @return
+ */
+ public boolean matches(final UsbDevice device) {
+ if (mVendorId != -1 && device.getVendorId() != mVendorId) {
+ return false;
+ }
+ if (mProductId != -1 && device.getProductId() != mProductId) {
+ return false;
+ }
+/* if (mManufacturerName != null && device.getManufacturerName() == null)
+ return false;
+ if (mProductName != null && device.getProductName() == null)
+ return false;
+ if (mSerialNumber != null && device.getSerialNumber() == null)
+ return false;
+ if (mManufacturerName != null && device.getManufacturerName() != null
+ && !mManufacturerName.equals(device.getManufacturerName()))
+ return false;
+ if (mProductName != null && device.getProductName() != null
+ && !mProductName.equals(device.getProductName()))
+ return false;
+ if (mSerialNumber != null && device.getSerialNumber() != null
+ && !mSerialNumber.equals(device.getSerialNumber()))
+ return false; */
+
+ // check device class/subclass/protocol
+ if (matches(device.getDeviceClass(), device.getDeviceSubclass(), device.getDeviceProtocol())) {
+ return true;
+ }
+
+ // if device doesn't match, check the interfaces
+ final int count = device.getInterfaceCount();
+ for (int i = 0; i < count; i++) {
+ final UsbInterface intf = device.getInterface(i);
+ if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), intf.getInterfaceProtocol())) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * このDeviceFilterに一致してかつmExcludeがtrueならtrueを返す
+ * @param device
+ * @return
+ */
+ public boolean isExclude(final UsbDevice device) {
+ return isExclude && matches(device);
+ }
+
+ /**
+ * これって要らんかも, equalsでできる気が
+ * @param f
+ * @return
+ */
+ public boolean matches(final DeviceFilter f) {
+ if (isExclude != f.isExclude) {
+ return false;
+ }
+ if (mVendorId != -1 && f.mVendorId != mVendorId) {
+ return false;
+ }
+ if (mProductId != -1 && f.mProductId != mProductId) {
+ return false;
+ }
+ if (f.mManufacturerName != null && mManufacturerName == null) {
+ return false;
+ }
+ if (f.mProductName != null && mProductName == null) {
+ return false;
+ }
+ if (f.mSerialNumber != null && mSerialNumber == null) {
+ return false;
+ }
+ if (mManufacturerName != null && f.mManufacturerName != null
+ && !mManufacturerName.equals(f.mManufacturerName)) {
+ return false;
+ }
+ if (mProductName != null && f.mProductName != null
+ && !mProductName.equals(f.mProductName)) {
+ return false;
+ }
+ if (mSerialNumber != null && f.mSerialNumber != null
+ && !mSerialNumber.equals(f.mSerialNumber)) {
+ return false;
+ }
+
+ // check device class/subclass/protocol
+ return matches(f.mClass, f.mSubclass, f.mProtocol);
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ // can't compare if we have wildcard strings
+ if (mVendorId == -1 || mProductId == -1 || mClass == -1
+ || mSubclass == -1 || mProtocol == -1) {
+ return false;
+ }
+ if (obj instanceof DeviceFilter) {
+ final DeviceFilter filter = (DeviceFilter) obj;
+
+ if (filter.mVendorId != mVendorId
+ || filter.mProductId != mProductId
+ || filter.mClass != mClass || filter.mSubclass != mSubclass
+ || filter.mProtocol != mProtocol) {
+ return false;
+ }
+ if ((filter.mManufacturerName != null && mManufacturerName == null)
+ || (filter.mManufacturerName == null && mManufacturerName != null)
+ || (filter.mProductName != null && mProductName == null)
+ || (filter.mProductName == null && mProductName != null)
+ || (filter.mSerialNumber != null && mSerialNumber == null)
+ || (filter.mSerialNumber == null && mSerialNumber != null)) {
+ return false;
+ }
+ if ((filter.mManufacturerName != null && mManufacturerName != null && !mManufacturerName
+ .equals(filter.mManufacturerName))
+ || (filter.mProductName != null && mProductName != null && !mProductName
+ .equals(filter.mProductName))
+ || (filter.mSerialNumber != null && mSerialNumber != null && !mSerialNumber
+ .equals(filter.mSerialNumber))) {
+ return false;
+ }
+ return (filter.isExclude != isExclude);
+ }
+ if (obj instanceof UsbDevice) {
+ final UsbDevice device = (UsbDevice) obj;
+ if (isExclude
+ || (device.getVendorId() != mVendorId)
+ || (device.getProductId() != mProductId)
+ || (device.getDeviceClass() != mClass)
+ || (device.getDeviceSubclass() != mSubclass)
+ || (device.getDeviceProtocol() != mProtocol) ) {
+ return false;
+ }
+/* if ((mManufacturerName != null && device.getManufacturerName() == null)
+ || (mManufacturerName == null && device
+ .getManufacturerName() != null)
+ || (mProductName != null && device.getProductName() == null)
+ || (mProductName == null && device.getProductName() != null)
+ || (mSerialNumber != null && device.getSerialNumber() == null)
+ || (mSerialNumber == null && device.getSerialNumber() != null)) {
+ return (false);
+ } */
+/* if ((device.getManufacturerName() != null && !mManufacturerName
+ .equals(device.getManufacturerName()))
+ || (device.getProductName() != null && !mProductName
+ .equals(device.getProductName()))
+ || (device.getSerialNumber() != null && !mSerialNumber
+ .equals(device.getSerialNumber()))) {
+ return (false);
+ } */
+ return true;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return (((mVendorId << 16) | mProductId) ^ ((mClass << 16)
+ | (mSubclass << 8) | mProtocol));
+ }
+
+ @Override
+ public String toString() {
+ return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId="
+ + mProductId + ",mClass=" + mClass + ",mSubclass=" + mSubclass
+ + ",mProtocol=" + mProtocol
+ + ",mManufacturerName=" + mManufacturerName
+ + ",mProductName=" + mProductName
+ + ",mSerialNumber=" + mSerialNumber
+ + ",isExclude=" + isExclude
+ + "]";
+ }
+
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/IButtonCallback.java b/libusbcamera/src/main/java/com/serenegiant/usb/IButtonCallback.java
new file mode 100644
index 0000000..4025085
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/IButtonCallback.java
@@ -0,0 +1,5 @@
+package com.serenegiant.usb;
+
+public interface IButtonCallback {
+ void onButton(int button, int state);
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/IFrameCallback.java b/libusbcamera/src/main/java/com/serenegiant/usb/IFrameCallback.java
new file mode 100644
index 0000000..f6aee75
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/IFrameCallback.java
@@ -0,0 +1,46 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import java.nio.ByteBuffer;
+
+/**
+ * Callback interface for UVCCamera class
+ * If you need frame data as ByteBuffer, you can use this callback interface with UVCCamera#setFrameCallback
+ */
+public interface IFrameCallback {
+ /**
+ * This method is called from native library via JNI on the same thread as UVCCamera#startCapture.
+ * You can use both UVCCamera#startCapture and #setFrameCallback
+ * but it is better to use either for better performance.
+ * You can also pass pixel format type to UVCCamera#setFrameCallback for this method.
+ * Some frames may drops if this method takes a time.
+ * When you use some color format like NV21, this library never execute color space conversion,
+ * just execute pixel format conversion. If you want to get same result as on screen, please try to
+ * consider to get images via texture(SurfaceTexture) and read pixel buffer from it using OpenGL|ES2/3
+ * instead of using IFrameCallback(this way is much efficient in most case than using IFrameCallback).
+ * @param frame this is direct ByteBuffer from JNI layer and you should handle it's byte order and limitation.
+ */
+ public void onFrame(ByteBuffer frame);
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/IStatusCallback.java b/libusbcamera/src/main/java/com/serenegiant/usb/IStatusCallback.java
new file mode 100644
index 0000000..ad74320
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/IStatusCallback.java
@@ -0,0 +1,7 @@
+package com.serenegiant.usb;
+
+import java.nio.ByteBuffer;
+
+public interface IStatusCallback {
+ void onStatus(int statusClass, int event, int selector, int statusAttribute, ByteBuffer data);
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/Size.java b/libusbcamera/src/main/java/com/serenegiant/usb/Size.java
new file mode 100644
index 0000000..963a805
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/Size.java
@@ -0,0 +1,302 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+public class Size implements Parcelable {
+ //
+ /**
+ * native側のuvc_raw_format_tの値, こっちは主にlibuvc用
+ * 9999 is still image
+ */
+ public int type;
+ /**
+ * native側のraw_frame_tの値, androusb用,
+ * libuvcは対応していない
+ */
+ public int frame_type;
+ public int index;
+ public int width;
+ public int height;
+ public int frameIntervalType;
+ public int frameIntervalIndex;
+ public int[] intervals;
+ // ここ以下はframeIntervalTypeとintervalsから#updateFrameRateで計算する
+ public float[] fps;
+ private String frameRates;
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ frameIntervalType = -1;
+ frameIntervalIndex = 0;
+ intervals = null;
+ updateFrameRate();
+ }
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ * @param _min_intervals
+ * @param _max_intervals
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int _min_intervals, final int _max_intervals, final int _step) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ frameIntervalType = 0;
+ frameIntervalIndex = 0;
+ intervals = new int[3];
+ intervals[0] = _min_intervals;
+ intervals[1] = _max_intervals;
+ intervals[2] = _step;
+ updateFrameRate();
+ }
+
+ /**
+ * コンストラクタ
+ * @param _type native側のraw_format_tの値, ただし9999は静止画
+ * @param _frame_type native側のraw_frame_tの値
+ * @param _index
+ * @param _width
+ * @param _height
+ * @param _intervals
+ */
+ public Size(final int _type, final int _frame_type, final int _index, final int _width, final int _height, final int[] _intervals) {
+ type = _type;
+ frame_type = _frame_type;
+ index = _index;
+ width = _width;
+ height = _height;
+ final int n = _intervals != null ? _intervals.length : -1;
+ if (n > 0) {
+ frameIntervalType = n;
+ intervals = new int[n];
+ System.arraycopy(_intervals, 0, intervals, 0, n);
+ } else {
+ frameIntervalType = -1;
+ intervals = null;
+ }
+ frameIntervalIndex = 0;
+ updateFrameRate();
+ }
+
+ /**
+ * コピーコンストラクタ
+ * @param other
+ */
+ public Size(final Size other) {
+ type = other.type;
+ frame_type = other.frame_type;
+ index = other.index;
+ width = other.width;
+ height = other.height;
+ frameIntervalType = other.frameIntervalType;
+ frameIntervalIndex = other.frameIntervalIndex;
+ final int n = other.intervals != null ? other.intervals.length : -1;
+ if (n > 0) {
+ intervals = new int[n];
+ System.arraycopy(other.intervals, 0, intervals, 0, n);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+
+ private Size(final Parcel source) {
+ // 読み取り順はwriteToParcelでの書き込み順と同じでないとダメ
+ type = source.readInt();
+ frame_type = source.readInt();
+ index = source.readInt();
+ width = source.readInt();
+ height = source.readInt();
+ frameIntervalType = source.readInt();
+ frameIntervalIndex = source.readInt();
+ if (frameIntervalType >= 0) {
+ if (frameIntervalType > 0) {
+ intervals = new int[frameIntervalType];
+ } else {
+ intervals = new int[3];
+ }
+ source.readIntArray(intervals);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+
+ public Size set(final Size other) {
+ if (other != null) {
+ type = other.type;
+ frame_type = other.frame_type;
+ index = other.index;
+ width = other.width;
+ height = other.height;
+ frameIntervalType = other.frameIntervalType;
+ frameIntervalIndex = other.frameIntervalIndex;
+ final int n = other.intervals != null ? other.intervals.length : -1;
+ if (n > 0) {
+ intervals = new int[n];
+ System.arraycopy(other.intervals, 0, intervals, 0, n);
+ } else {
+ intervals = null;
+ }
+ updateFrameRate();
+ }
+ return this;
+ }
+
+ public float getCurrentFrameRate() throws IllegalStateException {
+ final int n = fps != null ? fps.length : 0;
+ if ((frameIntervalIndex >= 0) && (frameIntervalIndex < n)) {
+ return fps[frameIntervalIndex];
+ }
+ throw new IllegalStateException("unknown frame rate or not ready");
+ }
+
+ public void setCurrentFrameRate(final float frameRate) {
+ // 一番近いのを選ぶ
+ int index = -1;
+ final int n = fps != null ? fps.length : 0;
+ for (int i = 0; i < n; i++) {
+ if (fps[i] <= frameRate) {
+ index = i;
+ break;
+ }
+ }
+ frameIntervalIndex = index;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(final Parcel dest, final int flags) {
+ dest.writeInt(type);
+ dest.writeInt(frame_type);
+ dest.writeInt(index);
+ dest.writeInt(width);
+ dest.writeInt(height);
+ dest.writeInt(frameIntervalType);
+ dest.writeInt(frameIntervalIndex);
+ if (intervals != null) {
+ dest.writeIntArray(intervals);
+ }
+ }
+
+ public void updateFrameRate() {
+ final int n = frameIntervalType;
+ if (n > 0) {
+ fps = new float[n];
+ for (int i = 0; i < n; i++) {
+ final float _fps = fps[i] = 10000000.0f / intervals[i];
+ }
+ } else if (n == 0) {
+ try {
+ final int min = Math.min(intervals[0], intervals[1]);
+ final int max = Math.max(intervals[0], intervals[1]);
+ final int step = intervals[2];
+ if (step > 0) {
+ int m = 0;
+ for (int i = min; i <= max; i+= step) { m++; }
+ fps = new float[m];
+ m = 0;
+ for (int i = min; i <= max; i+= step) {
+ final float _fps = fps[m++] = 10000000.0f / i;
+ }
+ } else {
+ final float max_fps = 10000000.0f / min;
+ int m = 0;
+ for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) { m++; }
+ fps = new float[m];
+ m = 0;
+ for (float fps = 10000000.0f / min; fps <= max_fps; fps += 1.0f) {
+ this.fps[m++] = fps;
+ }
+ }
+ } catch (final Exception e) {
+ // ignore, なんでかminとmaxが0になってるんちゃうかな
+ fps = null;
+ }
+ }
+ final int m = fps != null ? fps.length : 0;
+ final StringBuilder sb = new StringBuilder();
+ sb.append("[");
+ for (int i = 0; i < m; i++) {
+ sb.append(String.format(Locale.US, "%4.1f", fps[i]));
+ if (i < m-1) {
+ sb.append(",");
+ }
+ }
+ sb.append("]");
+ frameRates = sb.toString();
+ if (frameIntervalIndex > m) {
+ frameIntervalIndex = 0;
+ }
+ }
+
+ @Override
+ public String toString() {
+ float frame_rate = 0.0f;
+ try {
+ frame_rate = getCurrentFrameRate();
+ } catch (final Exception e) {
+ }
+ return String.format(Locale.US, "Size(%dx%d@%4.1f,type:%d,frame:%d,index:%d,%s)", width, height, frame_rate, type, frame_type, index, frameRates);
+ }
+
+ public static final Creator CREATOR = new Creator() {
+ @Override
+ public Size createFromParcel(final Parcel source) {
+ return new Size(source);
+ }
+ @Override
+ public Size[] newArray(final int size) {
+ return new Size[size];
+ }
+ };
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/USBMonitor.java b/libusbcamera/src/main/java/com/serenegiant/usb/USBMonitor.java
new file mode 100644
index 0000000..fe46a6c
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/USBMonitor.java
@@ -0,0 +1,1346 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.annotation.SuppressLint;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbDeviceConnection;
+import android.hardware.usb.UsbInterface;
+import android.hardware.usb.UsbManager;
+import android.os.Handler;
+import android.text.TextUtils;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.serenegiant.utils.BuildCheck;
+import com.serenegiant.utils.HandlerThreadHandler;
+
+import java.io.UnsupportedEncodingException;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+public final class USBMonitor {
+
+ private static final boolean DEBUG = false; // TODO set false on production
+ private static final String TAG = "USBMonitor";
+
+ private static final String ACTION_USB_PERMISSION_BASE = "com.serenegiant.USB_PERMISSION.";
+ private final String ACTION_USB_PERMISSION = ACTION_USB_PERMISSION_BASE + hashCode();
+
+ public static final String ACTION_USB_DEVICE_ATTACHED = "android.hardware.usb.action.USB_DEVICE_ATTACHED";
+
+ /**
+ * openしているUsbControlBlock
+ */
+ private final ConcurrentHashMap mCtrlBlocks = new ConcurrentHashMap();
+ private final SparseArray> mHasPermissions = new SparseArray>();
+
+ private final WeakReference mWeakContext;
+ private final UsbManager mUsbManager;
+ private final OnDeviceConnectListener mOnDeviceConnectListener;
+ private PendingIntent mPermissionIntent = null;
+ private List mDeviceFilters = new ArrayList();
+
+ /**
+ * コールバックをワーカースレッドで呼び出すためのハンドラー
+ */
+ private final Handler mAsyncHandler;
+ private volatile boolean destroyed;
+ /**
+ * USB機器の状態変更時のコールバックリスナー
+ */
+ public interface OnDeviceConnectListener {
+ /**
+ * called when device attached
+ * @param device
+ */
+ public void onAttach(UsbDevice device);
+ /**
+ * called when device dettach(after onDisconnect)
+ * @param device
+ */
+ public void onDettach(UsbDevice device);
+ /**
+ * called after device opend
+ * @param device
+ * @param ctrlBlock
+ * @param createNew
+ */
+ public void onConnect(UsbDevice device, UsbControlBlock ctrlBlock, boolean createNew);
+ /**
+ * called when USB device removed or its power off (this callback is called after device closing)
+ * @param device
+ * @param ctrlBlock
+ */
+ public void onDisconnect(UsbDevice device, UsbControlBlock ctrlBlock);
+ /**
+ * called when canceled or could not get permission from user
+ * @param device
+ */
+ public void onCancel(UsbDevice device);
+ }
+
+ public USBMonitor(final Context context, final OnDeviceConnectListener listener) {
+ if (DEBUG) Log.v(TAG, "USBMonitor:Constructor");
+ if (listener == null)
+ throw new IllegalArgumentException("OnDeviceConnectListener should not null.");
+ mWeakContext = new WeakReference(context);
+ mUsbManager = (UsbManager)context.getSystemService(Context.USB_SERVICE);
+ mOnDeviceConnectListener = listener;
+ mAsyncHandler = HandlerThreadHandler.createHandler(TAG);
+ destroyed = false;
+ if (DEBUG) Log.v(TAG, "USBMonitor:mUsbManager=" + mUsbManager);
+ }
+
+ /**
+ * Release all related resources,
+ * never reuse again
+ */
+ public void destroy() {
+ if (DEBUG) Log.i(TAG, "destroy:");
+ unregister();
+ if (!destroyed) {
+ destroyed = true;
+ // モニターしているUSB機器を全てcloseする
+ final Set keys = mCtrlBlocks.keySet();
+ if (keys != null) {
+ UsbControlBlock ctrlBlock;
+ try {
+ for (final UsbDevice key: keys) {
+ ctrlBlock = mCtrlBlocks.remove(key);
+ if (ctrlBlock != null) {
+ ctrlBlock.close();
+ }
+ }
+ } catch (final Exception e) {
+ Log.e(TAG, "destroy:", e);
+ }
+ }
+ mCtrlBlocks.clear();
+ try {
+ mAsyncHandler.getLooper().quit();
+ } catch (final Exception e) {
+ Log.e(TAG, "destroy:", e);
+ }
+ }
+ }
+
+ /**
+ * register BroadcastReceiver to monitor USB events
+ * @throws IllegalStateException
+ */
+ public synchronized void register() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ if (mPermissionIntent == null) {
+ if (DEBUG) Log.i(TAG, "register:");
+ final Context context = mWeakContext.get();
+ if (context != null) {
+ mPermissionIntent = PendingIntent.getBroadcast(context, 0, new Intent(ACTION_USB_PERMISSION), 0);
+ final IntentFilter filter = new IntentFilter(ACTION_USB_PERMISSION);
+ // ACTION_USB_DEVICE_ATTACHED never comes on some devices so it should not be added here
+ filter.addAction(UsbManager.ACTION_USB_DEVICE_DETACHED);
+ context.registerReceiver(mUsbReceiver, filter);
+ }
+ // start connection check
+ mDeviceCounts = 0;
+ mAsyncHandler.postDelayed(mDeviceCheckRunnable, 1000);
+ }
+ }
+
+ /**
+ * unregister BroadcastReceiver
+ * @throws IllegalStateException
+ */
+ public synchronized void unregister() throws IllegalStateException {
+ // 接続チェック用Runnableを削除
+ mDeviceCounts = 0;
+ if (!destroyed) {
+ mAsyncHandler.removeCallbacks(mDeviceCheckRunnable);
+ }
+ if (mPermissionIntent != null) {
+// if (DEBUG) Log.i(TAG, "unregister:");
+ final Context context = mWeakContext.get();
+ try {
+ if (context != null) {
+ context.unregisterReceiver(mUsbReceiver);
+ }
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ }
+ mPermissionIntent = null;
+ }
+ }
+
+ public synchronized boolean isRegistered() {
+ return !destroyed && (mPermissionIntent != null);
+ }
+
+ /**
+ * set device filter
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void setDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.clear();
+ mDeviceFilters.add(filter);
+ }
+
+ /**
+ * デバイスフィルターを追加
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void addDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.add(filter);
+ }
+
+ /**
+ * デバイスフィルターを削除
+ * @param filter
+ * @throws IllegalStateException
+ */
+ public void removeDeviceFilter(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.remove(filter);
+ }
+
+ /**
+ * set device filters
+ * @param filters
+ * @throws IllegalStateException
+ */
+ public void setDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.clear();
+ mDeviceFilters.addAll(filters);
+ }
+
+ /**
+ * add device filters
+ * @param filters
+ * @throws IllegalStateException
+ */
+ public void addDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.addAll(filters);
+ }
+
+ /**
+ * remove device filters
+ * @param filters
+ */
+ public void removeDeviceFilter(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ mDeviceFilters.removeAll(filters);
+ }
+
+ /**
+ * return the number of connected USB devices that matched device filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceCount() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return getDeviceList().size();
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return getDeviceList(mDeviceFilters);
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @param filters
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList(final List filters) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ final HashMap deviceList = mUsbManager.getDeviceList();
+ final List result = new ArrayList();
+ if (deviceList != null) {
+ if ((filters == null) || filters.isEmpty()) {
+ result.addAll(deviceList.values());
+ } else {
+ for (final UsbDevice device: deviceList.values() ) {
+ for (final DeviceFilter filter: filters) {
+ if ((filter != null) && filter.matches(device)) {
+ // when filter matches
+ if (!filter.isExclude) {
+ result.add(device);
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * return device list, return empty list if no device matched
+ * @param filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public List getDeviceList(final DeviceFilter filter) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ final HashMap deviceList = mUsbManager.getDeviceList();
+ final List result = new ArrayList();
+ if (deviceList != null) {
+ for (final UsbDevice device: deviceList.values() ) {
+ if ((filter == null) || (filter.matches(device) && !filter.isExclude)) {
+ result.add(device);
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * get USB device list, without filter
+ * @return
+ * @throws IllegalStateException
+ */
+ public Iterator getDevices() throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ Iterator iterator = null;
+ final HashMap list = mUsbManager.getDeviceList();
+ if (list != null)
+ iterator = list.values().iterator();
+ return iterator;
+ }
+
+ /**
+ * output device list to LogCat
+ */
+ public final void dumpDevices() {
+ final HashMap list = mUsbManager.getDeviceList();
+ if (list != null) {
+ final Set keys = list.keySet();
+ if (keys != null && keys.size() > 0) {
+ final StringBuilder sb = new StringBuilder();
+ for (final String key: keys) {
+ final UsbDevice device = list.get(key);
+ final int num_interface = device != null ? device.getInterfaceCount() : 0;
+ sb.setLength(0);
+ for (int i = 0; i < num_interface; i++) {
+ sb.append(String.format(Locale.US, "interface%d:%s", i, device.getInterface(i).toString()));
+ }
+ Log.i(TAG, "key=" + key + ":" + device + ":" + sb.toString());
+ }
+ } else {
+ Log.i(TAG, "no device");
+ }
+ } else {
+ Log.i(TAG, "no device");
+ }
+ }
+
+ /**
+ * return whether the specific Usb device has permission
+ * @param device
+ * @return true: 指定したUsbDeviceにパーミッションがある
+ * @throws IllegalStateException
+ */
+ public final boolean hasPermission(final UsbDevice device) throws IllegalStateException {
+ if (destroyed) throw new IllegalStateException("already destroyed");
+ return updatePermission(device, device != null && mUsbManager.hasPermission(device));
+ }
+
+ /**
+ * 内部で保持しているパーミッション状態を更新
+ * @param device
+ * @param hasPermission
+ * @return hasPermission
+ */
+ private boolean updatePermission(final UsbDevice device, final boolean hasPermission) {
+ final int deviceKey = getDeviceKey(device, true);
+ synchronized (mHasPermissions) {
+ if (hasPermission) {
+ if (mHasPermissions.get(deviceKey) == null) {
+ mHasPermissions.put(deviceKey, new WeakReference(device));
+ }
+ } else {
+ mHasPermissions.remove(deviceKey);
+ }
+ }
+ return hasPermission;
+ }
+
+ /**
+ * request permission to access to USB device
+ * @param device
+ * @return true if fail to request permission
+ */
+ public synchronized boolean requestPermission(final UsbDevice device) {
+// if (DEBUG) Log.v(TAG, "requestPermission:device=" + device);
+ boolean result = false;
+ if (isRegistered()) {
+ if (device != null) {
+ if (mUsbManager.hasPermission(device)) {
+ // call onConnect if app already has permission
+ processConnect(device);
+ } else {
+ try {
+ // パーミッションがなければ要求する
+ mUsbManager.requestPermission(device, mPermissionIntent);
+ } catch (final Exception e) {
+ // Android5.1.xのGALAXY系でandroid.permission.sec.MDM_APP_MGMTという意味不明の例外生成するみたい
+ Log.w(TAG, e);
+ processCancel(device);
+ result = true;
+ }
+ }
+ } else {
+ processCancel(device);
+ result = true;
+ }
+ } else {
+ processCancel(device);
+ result = true;
+ }
+ return result;
+ }
+
+ /**
+ * 指定したUsbDeviceをopenする
+ * @param device
+ * @return
+ * @throws SecurityException パーミッションがなければSecurityExceptionを投げる
+ */
+ public UsbControlBlock openDevice(final UsbDevice device) throws SecurityException {
+ if (hasPermission(device)) {
+ UsbControlBlock result = mCtrlBlocks.get(device);
+ if (result == null) {
+ result = new UsbControlBlock(USBMonitor.this, device); // この中でopenDeviceする
+ mCtrlBlocks.put(device, result);
+ }
+ return result;
+ } else {
+ throw new SecurityException("has no permission");
+ }
+ }
+
+ /**
+ * BroadcastReceiver for USB permission
+ */
+ private final BroadcastReceiver mUsbReceiver = new BroadcastReceiver() {
+
+ @Override
+ public void onReceive(final Context context, final Intent intent) {
+ if (destroyed) return;
+ final String action = intent.getAction();
+ if (ACTION_USB_PERMISSION.equals(action)) {
+ // when received the result of requesting USB permission
+ synchronized (USBMonitor.this) {
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)) {
+ if (device != null) {
+ // get permission, call onConnect
+ processConnect(device);
+ }
+ } else {
+ // failed to get permission
+ processCancel(device);
+ }
+ }
+ } else if (UsbManager.ACTION_USB_DEVICE_ATTACHED.equals(action)) {
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ updatePermission(device, hasPermission(device));
+ processAttach(device);
+ } else if (UsbManager.ACTION_USB_DEVICE_DETACHED.equals(action)) {
+ // when device removed
+ final UsbDevice device = intent.getParcelableExtra(UsbManager.EXTRA_DEVICE);
+ if (device != null) {
+ UsbControlBlock ctrlBlock = mCtrlBlocks.remove(device);
+ if (ctrlBlock != null) {
+ // cleanup
+ ctrlBlock.close();
+ }
+ mDeviceCounts = 0;
+ processDettach(device);
+ }
+ }
+ }
+ };
+
+ /** number of connected & detected devices */
+ private volatile int mDeviceCounts = 0;
+ /**
+ * periodically check connected devices and if it changed, call onAttach
+ */
+ private final Runnable mDeviceCheckRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (destroyed) return;
+ final List devices = getDeviceList();
+ final int n = devices.size();
+ final int hasPermissionCounts;
+ final int m;
+ synchronized (mHasPermissions) {
+ hasPermissionCounts = mHasPermissions.size();
+ mHasPermissions.clear();
+ for (final UsbDevice device: devices) {
+ hasPermission(device);
+ }
+ m = mHasPermissions.size();
+ }
+ if ((n > mDeviceCounts) || (m > hasPermissionCounts)) {
+ mDeviceCounts = n;
+ if (mOnDeviceConnectListener != null) {
+ for (int i = 0; i < n; i++) {
+ final UsbDevice device = devices.get(i);
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onAttach(device);
+ }
+ });
+ }
+ }
+ }
+ mAsyncHandler.postDelayed(this, 2000); // confirm every 2 seconds
+ }
+ };
+
+ /**
+ * open specific USB device
+ * @param device
+ */
+ private final void processConnect(final UsbDevice device) {
+ if (destroyed) return;
+ updatePermission(device, true);
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ if (DEBUG) Log.v(TAG, "processConnect:device=" + device);
+ UsbControlBlock ctrlBlock;
+ final boolean createNew;
+ ctrlBlock = mCtrlBlocks.get(device);
+ if (ctrlBlock == null) {
+ ctrlBlock = new UsbControlBlock(USBMonitor.this, device);
+ mCtrlBlocks.put(device, ctrlBlock);
+ createNew = true;
+ } else {
+ createNew = false;
+ }
+ if (mOnDeviceConnectListener != null) {
+ mOnDeviceConnectListener.onConnect(device, ctrlBlock, createNew);
+ }
+ }
+ });
+ }
+
+ private final void processCancel(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) Log.v(TAG, "processCancel:");
+ updatePermission(device, false);
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onCancel(device);
+ }
+ });
+ }
+ }
+
+ private final void processAttach(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) Log.v(TAG, "processAttach:");
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onAttach(device);
+ }
+ });
+ }
+ }
+
+ private final void processDettach(final UsbDevice device) {
+ if (destroyed) return;
+ if (DEBUG) Log.v(TAG, "processDettach:");
+ if (mOnDeviceConnectListener != null) {
+ mAsyncHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mOnDeviceConnectListener.onDettach(device);
+ }
+ });
+ }
+ }
+
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * 同種の製品だと同じキー名になるので注意
+ * @param device nullなら空文字列を返す
+ * @return
+ */
+ public static final String getDeviceKeyName(final UsbDevice device) {
+ return getDeviceKeyName(device, null, false);
+ }
+
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。
+ * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device
+ * @param useNewAPI
+ * @return
+ */
+ public static final String getDeviceKeyName(final UsbDevice device, final boolean useNewAPI) {
+ return getDeviceKeyName(device, null, useNewAPI);
+ }
+ /**
+ * USB機器毎の設定保存用にデバイスキー名を生成する。この機器名をHashMapのキーにする
+ * UsbDeviceがopenしている時のみ有効
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * serialがnullや空文字でなければserialを含めたデバイスキー名を生成する
+ * useNewAPI=trueでAPIレベルを満たしていればマニュファクチャ名, バージョン, コンフィギュレーションカウントも使う
+ * @param device nullなら空文字列を返す
+ * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
+ * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
+ * @return
+ */
+ @SuppressLint("NewApi")
+ public static final String getDeviceKeyName(final UsbDevice device, final String serial, final boolean useNewAPI) {
+ if (device == null) return "";
+ final StringBuilder sb = new StringBuilder();
+ sb.append(device.getVendorId()); sb.append("#"); // API >= 12
+ sb.append(device.getProductId()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceClass()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceSubclass()); sb.append("#"); // API >= 12
+ sb.append(device.getDeviceProtocol()); // API >= 12
+ if (!TextUtils.isEmpty(serial)) {
+ sb.append("#"); sb.append(serial);
+ }
+ if (useNewAPI && BuildCheck.isAndroid5()) {
+ sb.append("#");
+ if (TextUtils.isEmpty(serial)) {
+ sb.append(device.getSerialNumber()); sb.append("#"); // API >= 21
+ }
+ sb.append(device.getManufacturerName()); sb.append("#"); // API >= 21
+ sb.append(device.getConfigurationCount()); sb.append("#"); // API >= 21
+ if (BuildCheck.isMarshmallow()) {
+ sb.append(device.getVersion()); sb.append("#"); // API >= 23
+ }
+ }
+// if (DEBUG) Log.v(TAG, "getDeviceKeyName:" + sb.toString());
+ return sb.toString();
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * ベンダーID, プロダクトID, デバイスクラス, デバイスサブクラス, デバイスプロトコルから生成
+ * 同種の製品だと同じデバイスキーになるので注意
+ * @param device nullなら0を返す
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device) {
+ return device != null ? getDeviceKeyName(device, null, false).hashCode() : 0;
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * useNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device
+ * @param useNewAPI
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device, final boolean useNewAPI) {
+ return device != null ? getDeviceKeyName(device, null, useNewAPI).hashCode() : 0;
+ }
+
+ /**
+ * デバイスキーを整数として取得
+ * getDeviceKeyNameで得られる文字列のhasCodeを取得
+ * serialがnullでuseNewAPI=falseで同種の製品だと同じデバイスキーになるので注意
+ * @param device nullなら0を返す
+ * @param serial UsbDeviceConnection#getSerialで取得したシリアル番号を渡す, nullでuseNewAPI=trueでAPI>=21なら内部で取得
+ * @param useNewAPI API>=21またはAPI>=23のみで使用可能なメソッドも使用する(ただし機器によってはnullが返ってくるので有効かどうかは機器による)
+ * @return
+ */
+ public static final int getDeviceKey(final UsbDevice device, final String serial, final boolean useNewAPI) {
+ return device != null ? getDeviceKeyName(device, serial, useNewAPI).hashCode() : 0;
+ }
+
+ public static class UsbDeviceInfo {
+ public String usb_version;
+ public String manufacturer;
+ public String product;
+ public String version;
+ public String serial;
+
+ private void clear() {
+ usb_version = manufacturer = product = version = serial = null;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("UsbDevice:usb_version=%s,manufacturer=%s,product=%s,version=%s,serial=%s",
+ usb_version != null ? usb_version : "",
+ manufacturer != null ? manufacturer : "",
+ product != null ? product : "",
+ version != null ? version : "",
+ serial != null ? serial : "");
+ }
+ }
+
+ private static final int USB_DIR_OUT = 0;
+ private static final int USB_DIR_IN = 0x80;
+ private static final int USB_TYPE_MASK = (0x03 << 5);
+ private static final int USB_TYPE_STANDARD = (0x00 << 5);
+ private static final int USB_TYPE_CLASS = (0x01 << 5);
+ private static final int USB_TYPE_VENDOR = (0x02 << 5);
+ private static final int USB_TYPE_RESERVED = (0x03 << 5);
+ private static final int USB_RECIP_MASK = 0x1f;
+ private static final int USB_RECIP_DEVICE = 0x00;
+ private static final int USB_RECIP_INTERFACE = 0x01;
+ private static final int USB_RECIP_ENDPOINT = 0x02;
+ private static final int USB_RECIP_OTHER = 0x03;
+ private static final int USB_RECIP_PORT = 0x04;
+ private static final int USB_RECIP_RPIPE = 0x05;
+ private static final int USB_REQ_GET_STATUS = 0x00;
+ private static final int USB_REQ_CLEAR_FEATURE = 0x01;
+ private static final int USB_REQ_SET_FEATURE = 0x03;
+ private static final int USB_REQ_SET_ADDRESS = 0x05;
+ private static final int USB_REQ_GET_DESCRIPTOR = 0x06;
+ private static final int USB_REQ_SET_DESCRIPTOR = 0x07;
+ private static final int USB_REQ_GET_CONFIGURATION = 0x08;
+ private static final int USB_REQ_SET_CONFIGURATION = 0x09;
+ private static final int USB_REQ_GET_INTERFACE = 0x0A;
+ private static final int USB_REQ_SET_INTERFACE = 0x0B;
+ private static final int USB_REQ_SYNCH_FRAME = 0x0C;
+ private static final int USB_REQ_SET_SEL = 0x30;
+ private static final int USB_REQ_SET_ISOCH_DELAY = 0x31;
+ private static final int USB_REQ_SET_ENCRYPTION = 0x0D;
+ private static final int USB_REQ_GET_ENCRYPTION = 0x0E;
+ private static final int USB_REQ_RPIPE_ABORT = 0x0E;
+ private static final int USB_REQ_SET_HANDSHAKE = 0x0F;
+ private static final int USB_REQ_RPIPE_RESET = 0x0F;
+ private static final int USB_REQ_GET_HANDSHAKE = 0x10;
+ private static final int USB_REQ_SET_CONNECTION = 0x11;
+ private static final int USB_REQ_SET_SECURITY_DATA = 0x12;
+ private static final int USB_REQ_GET_SECURITY_DATA = 0x13;
+ private static final int USB_REQ_SET_WUSB_DATA = 0x14;
+ private static final int USB_REQ_LOOPBACK_DATA_WRITE = 0x15;
+ private static final int USB_REQ_LOOPBACK_DATA_READ = 0x16;
+ private static final int USB_REQ_SET_INTERFACE_DS = 0x17;
+
+ private static final int USB_REQ_STANDARD_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x10
+ private static final int USB_REQ_STANDARD_DEVICE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE); // 0x90
+ private static final int USB_REQ_STANDARD_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x11
+ private static final int USB_REQ_STANDARD_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_INTERFACE); // 0x91
+ private static final int USB_REQ_STANDARD_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x12
+ private static final int USB_REQ_STANDARD_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT); // 0x92
+
+ private static final int USB_REQ_CS_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x20
+ private static final int USB_REQ_CS_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xa0
+ private static final int USB_REQ_CS_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x21
+ private static final int USB_REQ_CS_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xa1
+ private static final int USB_REQ_CS_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x22
+ private static final int USB_REQ_CS_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xa2
+
+ private static final int USB_REQ_VENDER_DEVICE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0x40
+ private static final int USB_REQ_VENDER_DEVICE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_DEVICE); // 0xc0
+ private static final int USB_REQ_VENDER_INTERFACE_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0x41
+ private static final int USB_REQ_VENDER_INTERFACE_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE); // 0xc1
+ private static final int USB_REQ_VENDER_ENDPOINT_SET = (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0x42
+ private static final int USB_REQ_VENDER_ENDPOINT_GET = (USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT); // 0xc2
+
+ private static final int USB_DT_DEVICE = 0x01;
+ private static final int USB_DT_CONFIG = 0x02;
+ private static final int USB_DT_STRING = 0x03;
+ private static final int USB_DT_INTERFACE = 0x04;
+ private static final int USB_DT_ENDPOINT = 0x05;
+ private static final int USB_DT_DEVICE_QUALIFIER = 0x06;
+ private static final int USB_DT_OTHER_SPEED_CONFIG = 0x07;
+ private static final int USB_DT_INTERFACE_POWER = 0x08;
+ private static final int USB_DT_OTG = 0x09;
+ private static final int USB_DT_DEBUG = 0x0a;
+ private static final int USB_DT_INTERFACE_ASSOCIATION = 0x0b;
+ private static final int USB_DT_SECURITY = 0x0c;
+ private static final int USB_DT_KEY = 0x0d;
+ private static final int USB_DT_ENCRYPTION_TYPE = 0x0e;
+ private static final int USB_DT_BOS = 0x0f;
+ private static final int USB_DT_DEVICE_CAPABILITY = 0x10;
+ private static final int USB_DT_WIRELESS_ENDPOINT_COMP = 0x11;
+ private static final int USB_DT_WIRE_ADAPTER = 0x21;
+ private static final int USB_DT_RPIPE = 0x22;
+ private static final int USB_DT_CS_RADIO_CONTROL = 0x23;
+ private static final int USB_DT_PIPE_USAGE = 0x24;
+ private static final int USB_DT_SS_ENDPOINT_COMP = 0x30;
+ private static final int USB_DT_CS_DEVICE = (USB_TYPE_CLASS | USB_DT_DEVICE);
+ private static final int USB_DT_CS_CONFIG = (USB_TYPE_CLASS | USB_DT_CONFIG);
+ private static final int USB_DT_CS_STRING = (USB_TYPE_CLASS | USB_DT_STRING);
+ private static final int USB_DT_CS_INTERFACE = (USB_TYPE_CLASS | USB_DT_INTERFACE);
+ private static final int USB_DT_CS_ENDPOINT = (USB_TYPE_CLASS | USB_DT_ENDPOINT);
+ private static final int USB_DT_DEVICE_SIZE = 18;
+
+ /**
+ * 指定したIDのStringディスクリプタから文字列を取得する。取得できなければnull
+ * @param connection
+ * @param id
+ * @param languageCount
+ * @param languages
+ * @return
+ */
+ private static String getString(final UsbDeviceConnection connection, final int id, final int languageCount, final byte[] languages) {
+ final byte[] work = new byte[256];
+ String result = null;
+ for (int i = 1; i <= languageCount; i++) {
+ int ret = connection.controlTransfer(
+ USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
+ USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | id, languages[i], work, 256, 0);
+ if ((ret > 2) && (work[0] == ret) && (work[1] == USB_DT_STRING)) {
+ // skip first two bytes(bLength & bDescriptorType), and copy the rest to the string
+ try {
+ result = new String(work, 2, ret - 2, "UTF-16LE");
+ if (!"Љ".equals(result)) { // 変なゴミが返ってくる時がある
+ break;
+ } else {
+ result = null;
+ }
+ } catch (final UnsupportedEncodingException e) {
+ // ignore
+ }
+ }
+ }
+ return result;
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * @param device
+ * @return
+ */
+ public UsbDeviceInfo getDeviceInfo(final UsbDevice device) {
+ return updateDeviceInfo(mUsbManager, device, null);
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * #updateDeviceInfo(final UsbManager, final UsbDevice, final UsbDeviceInfo)のヘルパーメソッド
+ * @param context
+ * @param device
+ * @return
+ */
+ public static UsbDeviceInfo getDeviceInfo(final Context context, final UsbDevice device) {
+ return updateDeviceInfo((UsbManager)context.getSystemService(Context.USB_SERVICE), device, new UsbDeviceInfo());
+ }
+
+ /**
+ * ベンダー名・製品名・バージョン・シリアルを取得する
+ * @param manager
+ * @param device
+ * @param _info
+ * @return
+ */
+ public static UsbDeviceInfo updateDeviceInfo(final UsbManager manager, final UsbDevice device, final UsbDeviceInfo _info) {
+ final UsbDeviceInfo info = _info != null ? _info : new UsbDeviceInfo();
+ info.clear();
+
+ if (device != null) {
+ if (BuildCheck.isLollipop()) {
+ info.manufacturer = device.getManufacturerName();
+ info.product = device.getProductName();
+ info.serial = device.getSerialNumber();
+ }
+ if (BuildCheck.isMarshmallow()) {
+ info.usb_version = device.getVersion();
+ }
+ if ((manager != null) && manager.hasPermission(device)) {
+ final UsbDeviceConnection connection = manager.openDevice(device);
+ final byte[] desc = connection.getRawDescriptors();
+
+ if (TextUtils.isEmpty(info.usb_version)) {
+ info.usb_version = String.format("%x.%02x", ((int)desc[3] & 0xff), ((int)desc[2] & 0xff));
+ }
+ if (TextUtils.isEmpty(info.version)) {
+ info.version = String.format("%x.%02x", ((int)desc[13] & 0xff), ((int)desc[12] & 0xff));
+ }
+ if (TextUtils.isEmpty(info.serial)) {
+ info.serial = connection.getSerial();
+ }
+
+ final byte[] languages = new byte[256];
+ int languageCount = 0;
+ // controlTransfer(int requestType, int request, int value, int index, byte[] buffer, int length, int timeout)
+ try {
+ int result = connection.controlTransfer(
+ USB_REQ_STANDARD_DEVICE_GET, // USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE
+ USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) | 0, 0, languages, 256, 0);
+ if (result > 0) {
+ languageCount = (result - 2) / 2;
+ }
+ if (languageCount > 0) {
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = getString(connection, desc[14], languageCount, languages);
+ }
+ if (TextUtils.isEmpty(info.product)) {
+ info.product = getString(connection, desc[15], languageCount, languages);
+ }
+ if (TextUtils.isEmpty(info.serial)) {
+ info.serial = getString(connection, desc[16], languageCount, languages);
+ }
+ }
+ } finally {
+ connection.close();
+ }
+ }
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = USBVendorId.vendorName(device.getVendorId());
+ }
+ if (TextUtils.isEmpty(info.manufacturer)) {
+ info.manufacturer = String.format("%04x", device.getVendorId());
+ }
+ if (TextUtils.isEmpty(info.product)) {
+ info.product = String.format("%04x", device.getProductId());
+ }
+ }
+ return info;
+ }
+
+ /**
+ * control class
+ * never reuse the instance when it closed
+ */
+ public static final class UsbControlBlock implements Cloneable {
+ private final WeakReference mWeakMonitor;
+ private final WeakReference mWeakDevice;
+ protected UsbDeviceConnection mConnection;
+ protected final UsbDeviceInfo mInfo;
+ private final int mBusNum;
+ private final int mDevNum;
+ private final SparseArray> mInterfaces = new SparseArray>();
+
+ /**
+ * this class needs permission to access USB device before constructing
+ * @param monitor
+ * @param device
+ */
+ private UsbControlBlock(final USBMonitor monitor, final UsbDevice device) {
+ if (DEBUG) Log.i(TAG, "UsbControlBlock:constructor");
+ mWeakMonitor = new WeakReference(monitor);
+ mWeakDevice = new WeakReference(device);
+ mConnection = monitor.mUsbManager.openDevice(device);
+ mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
+ final String name = device.getDeviceName();
+ final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
+ int busnum = 0;
+ int devnum = 0;
+ if (v != null) {
+ busnum = Integer.parseInt(v[v.length-2]);
+ devnum = Integer.parseInt(v[v.length-1]);
+ }
+ mBusNum = busnum;
+ mDevNum = devnum;
+// if (DEBUG) {
+ if (mConnection != null) {
+ final int desc = mConnection.getFileDescriptor();
+ final byte[] rawDesc = mConnection.getRawDescriptors();
+ Log.i(TAG, String.format(Locale.US, "name=%s,desc=%d,busnum=%d,devnum=%d,rawDesc=", name, desc, busnum, devnum) + rawDesc);
+ } else {
+ Log.e(TAG, "could not connect to device " + name);
+ }
+// }
+ }
+
+ /**
+ * copy constructor
+ * @param src
+ * @throws IllegalStateException
+ */
+ private UsbControlBlock(final UsbControlBlock src) throws IllegalStateException {
+ final USBMonitor monitor = src.getUSBMonitor();
+ final UsbDevice device = src.getDevice();
+ if (device == null) {
+ throw new IllegalStateException("device may already be removed");
+ }
+ mConnection = monitor.mUsbManager.openDevice(device);
+ if (mConnection == null) {
+ throw new IllegalStateException("device may already be removed or have no permission");
+ }
+ mInfo = updateDeviceInfo(monitor.mUsbManager, device, null);
+ mWeakMonitor = new WeakReference(monitor);
+ mWeakDevice = new WeakReference(device);
+ mBusNum = src.mBusNum;
+ mDevNum = src.mDevNum;
+ // FIXME USBMonitor.mCtrlBlocksに追加する(今はHashMapなので追加すると置き換わってしまうのでだめ, ListかHashMapにListをぶら下げる?)
+ }
+
+ /**
+ * duplicate by clone
+ * need permission
+ * USBMonitor never handle cloned UsbControlBlock, you should release it after using it.
+ * @return
+ * @throws CloneNotSupportedException
+ */
+ @Override
+ public UsbControlBlock clone() throws CloneNotSupportedException {
+ final UsbControlBlock ctrlblock;
+ try {
+ ctrlblock = new UsbControlBlock(this);
+ } catch (final IllegalStateException e) {
+ throw new CloneNotSupportedException(e.getMessage());
+ }
+ return ctrlblock;
+ }
+
+ public USBMonitor getUSBMonitor() {
+ return mWeakMonitor.get();
+ }
+
+ public final UsbDevice getDevice() {
+ return mWeakDevice.get();
+ }
+
+ /**
+ * get device name
+ * @return
+ */
+ public String getDeviceName() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getDeviceName() : "";
+ }
+
+ /**
+ * get device id
+ * @return
+ */
+ public int getDeviceId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getDeviceId() : 0;
+ }
+
+ /**
+ * get device key string
+ * @return same value if the devices has same vendor id, product id, device class, device subclass and device protocol
+ */
+ public String getDeviceKeyName() {
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get());
+ }
+
+ /**
+ * get device key string
+ * @param useNewAPI if true, try to use serial number
+ * @return
+ * @throws IllegalStateException
+ */
+ public String getDeviceKeyName(final boolean useNewAPI) throws IllegalStateException {
+ if (useNewAPI) checkConnection();
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, useNewAPI);
+ }
+
+ /**
+ * get device key
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceKey() throws IllegalStateException {
+ checkConnection();
+ return USBMonitor.getDeviceKey(mWeakDevice.get());
+ }
+
+ /**
+ * get device key
+ * @param useNewAPI if true, try to use serial number
+ * @return
+ * @throws IllegalStateException
+ */
+ public int getDeviceKey(final boolean useNewAPI) throws IllegalStateException {
+ if (useNewAPI) checkConnection();
+ return USBMonitor.getDeviceKey(mWeakDevice.get(), mInfo.serial, useNewAPI);
+ }
+
+ /**
+ * get device key string
+ * if device has serial number, use it
+ * @return
+ */
+ public String getDeviceKeyNameWithSerial() {
+ return USBMonitor.getDeviceKeyName(mWeakDevice.get(), mInfo.serial, false);
+ }
+
+ /**
+ * get device key
+ * if device has serial number, use it
+ * @return
+ */
+ public int getDeviceKeyWithSerial() {
+ return getDeviceKeyNameWithSerial().hashCode();
+ }
+
+ /**
+ * get UsbDeviceConnection
+ * @return
+ */
+ public synchronized UsbDeviceConnection getConnection() {
+ return mConnection;
+ }
+
+ /**
+ * get file descriptor to access USB device
+ * @return
+ * @throws IllegalStateException
+ */
+ public synchronized int getFileDescriptor() throws IllegalStateException {
+ checkConnection();
+ return mConnection.getFileDescriptor();
+ }
+
+ /**
+ * get raw descriptor for the USB device
+ * @return
+ * @throws IllegalStateException
+ */
+ public synchronized byte[] getRawDescriptors() throws IllegalStateException {
+ checkConnection();
+ return mConnection.getRawDescriptors();
+ }
+
+ /**
+ * get vendor id
+ * @return
+ */
+ public int getVenderId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getVendorId() : 0;
+ }
+
+ /**
+ * get product id
+ * @return
+ */
+ public int getProductId() {
+ final UsbDevice device = mWeakDevice.get();
+ return device != null ? device.getProductId() : 0;
+ }
+
+ /**
+ * get version string of USB
+ * @return
+ */
+ public String getUsbVersion() {
+ return mInfo.usb_version;
+ }
+
+ /**
+ * get manufacture
+ * @return
+ */
+ public String getManufacture() {
+ return mInfo.manufacturer;
+ }
+
+ /**
+ * get product name
+ * @return
+ */
+ public String getProductName() {
+ return mInfo.product;
+ }
+
+ /**
+ * get version
+ * @return
+ */
+ public String getVersion() {
+ return mInfo.version;
+ }
+
+ /**
+ * get serial number
+ * @return
+ */
+ public String getSerial() {
+ return mInfo.serial;
+ }
+
+ public int getBusNum() {
+ return mBusNum;
+ }
+
+ public int getDevNum() {
+ return mDevNum;
+ }
+
+ /**
+ * get interface
+ * @param interface_id
+ * @throws IllegalStateException
+ */
+ public synchronized UsbInterface getInterface(final int interface_id) throws IllegalStateException {
+ return getInterface(interface_id, 0);
+ }
+
+ /**
+ * get interface
+ * @param interface_id
+ * @param altsetting
+ * @return
+ * @throws IllegalStateException
+ */
+ public synchronized UsbInterface getInterface(final int interface_id, final int altsetting) throws IllegalStateException {
+ checkConnection();
+ SparseArray intfs = mInterfaces.get(interface_id);
+ if (intfs == null) {
+ intfs = new SparseArray();
+ mInterfaces.put(interface_id, intfs);
+ }
+ UsbInterface intf = intfs.get(altsetting);
+ if (intf == null) {
+ final UsbDevice device = mWeakDevice.get();
+ final int n = device.getInterfaceCount();
+ for (int i = 0; i < n; i++) {
+ final UsbInterface temp = device.getInterface(i);
+ if ((temp.getId() == interface_id) && (temp.getAlternateSetting() == altsetting)) {
+ intf = temp;
+ break;
+ }
+ }
+ if (intf != null) {
+ intfs.append(altsetting, intf);
+ }
+ }
+ return intf;
+ }
+
+ /**
+ * open specific interface
+ * @param intf
+ */
+ public synchronized void claimInterface(final UsbInterface intf) {
+ claimInterface(intf, true);
+ }
+
+ public synchronized void claimInterface(final UsbInterface intf, final boolean force) {
+ checkConnection();
+ mConnection.claimInterface(intf, force);
+ }
+
+ /**
+ * close interface
+ * @param intf
+ * @throws IllegalStateException
+ */
+ public synchronized void releaseInterface(final UsbInterface intf) throws IllegalStateException {
+ checkConnection();
+ final SparseArray intfs = mInterfaces.get(intf.getId());
+ if (intfs != null) {
+ final int index = intfs.indexOfValue(intf);
+ intfs.removeAt(index);
+ if (intfs.size() == 0) {
+ mInterfaces.remove(intf.getId());
+ }
+ }
+ mConnection.releaseInterface(intf);
+ }
+
+ /**
+ * Close device
+ * This also close interfaces if they are opened in Java side
+ */
+ public synchronized void close() {
+ if (DEBUG) Log.i(TAG, "UsbControlBlock#close:");
+
+ if (mConnection != null) {
+ final int n = mInterfaces.size();
+ for (int i = 0; i < n; i++) {
+ final SparseArray intfs = mInterfaces.valueAt(i);
+ if (intfs != null) {
+ final int m = intfs.size();
+ for (int j = 0; j < m; j++) {
+ final UsbInterface intf = intfs.valueAt(j);
+ mConnection.releaseInterface(intf);
+ }
+ intfs.clear();
+ }
+ }
+ mInterfaces.clear();
+ mConnection.close();
+ mConnection = null;
+ final USBMonitor monitor = mWeakMonitor.get();
+ if (monitor != null) {
+ if (monitor.mOnDeviceConnectListener != null) {
+ monitor.mOnDeviceConnectListener.onDisconnect(mWeakDevice.get(), UsbControlBlock.this);
+ }
+ monitor.mCtrlBlocks.remove(getDevice());
+ }
+ }
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (o == null) return false;
+ if (o instanceof UsbControlBlock) {
+ final UsbDevice device = ((UsbControlBlock) o).getDevice();
+ return device == null ? mWeakDevice.get() == null
+ : device.equals(mWeakDevice.get());
+ } else if (o instanceof UsbDevice) {
+ return o.equals(mWeakDevice.get());
+ }
+ return super.equals(o);
+ }
+
+// @Override
+// protected void finalize() throws Throwable {
+/// close();
+// super.finalize();
+// }
+
+ private synchronized void checkConnection() throws IllegalStateException {
+ if (mConnection == null) {
+ throw new IllegalStateException("already closed");
+ }
+ }
+ }
+
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/USBVendorId.java b/libusbcamera/src/main/java/com/serenegiant/usb/USBVendorId.java
new file mode 100644
index 0000000..d354b66
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/USBVendorId.java
@@ -0,0 +1,859 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.util.SparseArray;
+
+public class USBVendorId {
+ private static final SparseArray IDS = new SparseArray();
+
+ public static String vendorName(final int vendor_id) {
+ return IDS.get(vendor_id);
+ }
+
+ static {
+ IDS.put(10006, "YUEN DA ELECTRONIC PRODUCTS FACTORY");
+ IDS.put(10013, "Gionee Communication Equipment Co., Ltd. ShenZhen");
+ IDS.put(10022, "Universal Electronics Inc. (dba: TVIEW)");
+ IDS.put(1003, "Atmel Corporation");
+ IDS.put(1006, "Mitsumi");
+ IDS.put(1008, "HP Inc.");
+ IDS.put(10112, "M31 Technology Corp.");
+ IDS.put(10113, "Liteconn Co., Ltd.");
+ IDS.put(10121, "Suzhou WEIJU Electronics Technology Co., Ltd.");
+ IDS.put(10144, "Mondokey Limited");
+ IDS.put(10149, "Advantest Corporation");
+ IDS.put(10150, "iRobot Corporation");
+ IDS.put(1020, "Elitegroup Computer Systems");
+ IDS.put(1021, "Xilinx Inc.");
+ IDS.put(10226, "Sibridge Tech.");
+ IDS.put(1026, "ALi Corporation");
+ IDS.put(1027, "Future Technology Devices International Limited");
+ IDS.put(10275, "Dongguan Jiumutong Industry Co., Ltd.");
+ IDS.put(10289, "Power Integrations");
+ IDS.put(10291, "Oculus VR, Inc.");
+ IDS.put(10300, "HIGH TEK HARNESS ENTERPRISE CO., LTD.");
+ IDS.put(10316, "Full in Hope Co., Ltd.");
+ IDS.put(1032, "Quanta Computer Inc.");
+ IDS.put(10329, "Viconn Technology (HK) Co., Ltd.");
+ IDS.put(1033, "NEC Corporation");
+ IDS.put(1035, "Weltrend Semiconductor");
+ IDS.put(1037, "VIA Technologies, Inc.");
+ IDS.put(10374, "Seeed Technology Co., Ltd.");
+ IDS.put(10375, "Specwerkz");
+ IDS.put(1038, "MCCI Corporation");
+ IDS.put(10398, "Esselte Leitz GmbH & Co. KG");
+ IDS.put(10406, "E-SEEK Inc.");
+ IDS.put(1041, "BUFFALO INC.");
+ IDS.put(10423, "Pleora Technologies Inc.");
+ IDS.put(10431, "Vitetech Int'l Co., Ltd.");
+ IDS.put(1044, "Giga-Byte Technology Co., Ltd.");
+ IDS.put(10446, "Changzhou Shi Wujin Miqi East Electronic Co., Ltd.");
+ IDS.put(10457, "Shenzhen Ourconn Technology Co., Ltd.");
+ IDS.put(10458, "G.SKILL Int'l Enterprice Co., Ltd.");
+ IDS.put(1046, "Nuvoton Technology Corp.");
+ IDS.put(10466, "Surplus Electronic Technology Co., Ltd.");
+ IDS.put(10470, "BIAMP SYSTEMS");
+ IDS.put(10509, "IBCONN Technologies (Shenzhen) Co., Ltd.");
+ IDS.put(10510, "Fugoo Inc.");
+ IDS.put(10519, "Pan Xin Precision Electronics Co., Ltd.");
+ IDS.put(10530, "Dongguan Digi-in Digital Technology Co., Ltd.");
+ IDS.put(1054, "Creative Labs");
+ IDS.put(10540, "GENUSION, Inc.");
+ IDS.put(10544, "Ineda Systems Inc.");
+ IDS.put(10545, "Jolla Ltd.");
+ IDS.put(10546, "Peraso Technologies, Inc.");
+ IDS.put(10549, "Nanjing Magewell Electronics Co., Ltd.");
+ IDS.put(10560, "Shenzhen Yiwanda Electronics Co., Ltd.");
+ IDS.put(1057, "Nokia Corporation");
+ IDS.put(10575, "Dollar Connection Ltd.");
+ IDS.put(10595, "BIO-key International, Inc.");
+ IDS.put(1060, "Microchip-SMSC");
+ IDS.put(10603, "Xacti Corporation");
+ IDS.put(10615, "Shenzhen Zowee Technology Co., Ltd.");
+ IDS.put(10643, "ADPlaus Technology Limited");
+ IDS.put(10646, "Unwired Technology");
+ IDS.put(1065, "Cirrus Logic Inc.");
+ IDS.put(10657, "Union Electric Plug & Connector Corp.");
+ IDS.put(10674, "Canova Tech");
+ IDS.put(10685, "Silicon Works");
+ IDS.put(10695, "HANRICO ANFU ELECTRONICS CO., LTD.");
+ IDS.put(10700, "Kodak Alaris");
+ IDS.put(10702, "JGR Optics Inc.");
+ IDS.put(10703, "Richtek Technology Corporation");
+ IDS.put(10705, "Binatone Electronics Int. Ltd.");
+ IDS.put(1071, "Molex Inc.");
+ IDS.put(10715, "Shenzhen iBoard Technology Co., Ltd.");
+ IDS.put(10719, "SMIT(HK) Limited");
+ IDS.put(1072, "Fujitsu Component Limited");
+ IDS.put(10725, "Dongguan Kechenda Electronic Technology Co., Ltd.");
+ IDS.put(10726, "Fengshun Peiying Electro-Acoustic Co., Ltd.");
+ IDS.put(10744, "MD ELEKTRONIK GmbH");
+ IDS.put(10749, "Bad Elf, LLC");
+ IDS.put(10770, "Vreo Limited");
+ IDS.put(10772, "Kanex");
+ IDS.put(10781, "Oxford Nanopore Technologies");
+ IDS.put(10782, "Obsidian Technology");
+ IDS.put(10783, "Lucent Trans Electronics Co., Ltd.");
+ IDS.put(10784, "GUOGUANG GROUP CO., LTD.");
+ IDS.put(10788, "CNPLUS");
+ IDS.put(10789, "Fourstar Group");
+ IDS.put(10790, "Tragant International Co., Ltd.");
+ IDS.put(10791, "DongGuan LianGang Optoelectronic Technology Co., Ltd.");
+ IDS.put(10797, "Atrust Computer Corp.");
+ IDS.put(10798, "VIA Alliance Semiconductor Co., Ltd.");
+ IDS.put(10799, "BSUN Electronics Co., Ltd.");
+ IDS.put(1080, "Advanced Micro Devices");
+ IDS.put(10807, "RTD Embedded Technologies, Inc.");
+ IDS.put(10816, "Shenzhen Choseal Industrial Co., Ltd.");
+ IDS.put(10817, "Canyon Semiconductor");
+ IDS.put(10818, "Spectra7 Microsystems Corp.");
+ IDS.put(10821, "Meizu Technology Co., Ltd.");
+ IDS.put(10822, "Hubei Yingtong Telecommunication Cable Inc.");
+ IDS.put(10829, "Wilder Technologies");
+ IDS.put(10837, "Diodes Inc.");
+ IDS.put(10846, "DuPont");
+ IDS.put(1085, "Lexmark International Inc.");
+ IDS.put(10852, "Zhejiang Songcheng Electronics Co., Ltd.");
+ IDS.put(10859, "VSN Mobil");
+ IDS.put(10875, "Bellwether Electronic Corp.");
+ IDS.put(10878, "VAIO Corporation");
+ IDS.put(10879, "Perixx Computer GmbH");
+ IDS.put(10885, "HANK ELECTRONICS CO., LTD");
+ IDS.put(10892, "Sonnet Technologies, Inc.");
+ IDS.put(10893, "Keysight Technologies Inc.");
+ IDS.put(10895, "Manutronics Vietnam Joint Stock Company");
+ IDS.put(10900, "G2 Touch Co., Ltd.");
+ IDS.put(10902, "Micromax Informatics Ltd");
+ IDS.put(10910, "SEIKO SOLUTIONS Inc.");
+ IDS.put(10912, "Casco Products Corp.");
+ IDS.put(10922, "Virtium Technology, Inc.");
+ IDS.put(10923, "Field and Company LLC, dba Leef USA");
+ IDS.put(10928, "GM Global Technology Operations LLC");
+ IDS.put(10931, "Key Asic Inc.");
+ IDS.put(10943, "Revolabs, Inc.");
+ IDS.put(10945, "Lattice Semiconductor Corp");
+ IDS.put(10947, "Foshan Nanhai Saga Audio Equipment Co., Ltd.");
+ IDS.put(10957, "Silergy Corp.");
+ IDS.put(10963, "Shenzhen Hali-Power Industrial Co., Ltd.");
+ IDS.put(10971, "I-PEX (Dai-ichi Seiko)");
+ IDS.put(10973, "SEE-PLUS INDUSTRIAL LTD.");
+ IDS.put(10990, "Adapt-IP Company");
+ IDS.put(10997, "Libratone A/S");
+ IDS.put(10999, "Shenzhen Hazens Automotive Electronics (SZ) Co., Ltd.");
+ IDS.put(11000, "Jiangsu Toppower Automotive Electronics Co., Ltd.");
+ IDS.put(11001, "Drapho Electronics Technology Co., Ltd.");
+ IDS.put(1102, "Alps Electric Co., Ltd.");
+ IDS.put(11022, "Le Shi Zhi Xin Electronic Technology (Tian Jin) Limited");
+ IDS.put(11024, "Cardiac Insight, Inc.");
+ IDS.put(11028, "EverPro Technologies Company, Ltd.");
+ IDS.put(11029, "Rosenberger Hochfrequenztechnik");
+ IDS.put(11035, "Dongguan City Sanji Electronics Co., Ltd.");
+ IDS.put(11037, "Lintes Technology Co., Ltd.");
+ IDS.put(11039, "KinnexA, Inc.");
+ IDS.put(11042, "Metra Electronics Corp.");
+ IDS.put(11044, "KeepKey, LLC");
+ IDS.put(11047, "FluxData Incorporated");
+ IDS.put(1105, "Texas Instruments");
+ IDS.put(11061, "Assem Technology Co., Ltd.");
+ IDS.put(11062, "Dongguan City Jianghan Electronics Co., Ltd.");
+ IDS.put(11063, "Huizhou Desay SV Automotive Co., Ltd.");
+ IDS.put(11064, "Ningbo Rixing Electronics Co., Ltd.");
+ IDS.put(11069, "GuangDong YuanFeng Automotive Electroics Co., Ltd.");
+ IDS.put(11080, "Sounding Audio Industrial Limited");
+ IDS.put(11082, "Yueqing Huaxin Electronic Co., Ltd.");
+ IDS.put(11098, "Universal Audio, Inc.");
+ IDS.put(11111, "Lifesize, Inc.");
+ IDS.put(11123, "Pioneer DJ Corporation");
+ IDS.put(11124, "Embedded Intelligence, Inc.");
+ IDS.put(11125, "New Matter");
+ IDS.put(11126, "Shanghai Wingtech Electronic Technology Co., Ltd.");
+ IDS.put(11127, "Epiphan Systems Inc.");
+ IDS.put(11130, "Spin Master Far East Ltd.");
+ IDS.put(11131, "Gigaset Digital Technology (Shenzhen) Co., Ltd.");
+ IDS.put(11132, "Noveltek Semiconductor Corp.");
+ IDS.put(11139, "Silicon Line GmbH");
+ IDS.put(11140, "Ever Win International Corp.");
+ IDS.put(11144, "Socionext Inc.");
+ IDS.put(11145, "Ugreen Group Limited");
+ IDS.put(11146, "Shanghai Pateo Electronic Equipment Mfg. Co., Ltd.");
+ IDS.put(1115, "Renesas Electronics Corp.");
+ IDS.put(11154, "i-BLADES, Inc.");
+ IDS.put(11155, "Altia Systems Inc.");
+ IDS.put(11156, "ShenZhen Baoyuanda Electronics Co., Ltd.");
+ IDS.put(11157, "iST - Integrated Service Technology Inc.");
+ IDS.put(11158, "HYUNDAI MOBIS Co., Ltd.");
+ IDS.put(11161, "360fly, Inc.");
+ IDS.put(11162, "HUIZHOU CHENG SHUO HARDWARE PLASTIC CO., LTD.");
+ IDS.put(11163, "Zhongshan Aute Electronics Technology Co., Ltd.");
+ IDS.put(11164, "Guangdong King Link Industrial Co., Ltd.");
+ IDS.put(11167, "Scietera Technologies, Inc.");
+ IDS.put(11168, "InVue Security Products");
+ IDS.put(11169, "I-Sheng Electric Wire & Cable Co., Ltd.");
+ IDS.put(11170, "China Daheng Group Inc Beijing Image Vision Tech Branch");
+ IDS.put(11171, "Shenzhen FeiTianXia Technology Ltd.");
+ IDS.put(11172, "Shenzhen HengJia New Energy Auto Part Co., Ltd.");
+ IDS.put(11175, "77 Elektronika Kft.");
+ IDS.put(11176, "YUDU EASON ELECTRONIC CO., LTD.");
+ IDS.put(1118, "Microsoft Corporation");
+ IDS.put(11181, "XIN JI (SHENZHEN) COMPUTER PARTS CO., LTD.");
+ IDS.put(11189, "Silk ID Systems");
+ IDS.put(11190, "3D Imaging & Simulations Corp. (3DISC)");
+ IDS.put(11191, "Dongguan ChengXiang Industrial Co., Ltd.");
+ IDS.put(11192, "OCC (Zhuhai) Electronic Co., Ltd.");
+ IDS.put(11194, "Sinseader Electronic Co., Ltd.");
+ IDS.put(11195, "DONGGUAN YELLOWKNIFE Industrial Co., Ltd.");
+ IDS.put(11197, "RF Creations Ltd.");
+ IDS.put(11198, "Chengyi Semiconductors (Shanghai) Co., Ltd.");
+ IDS.put(11199, "Shenzhen Shinning Electronic Co., Ltd.");
+ IDS.put(11200, "Shenzhen WFD Electronics Co., Ltd.");
+ IDS.put(11201, "Dongguan Sino Syncs Industrial Co., Ltd.");
+ IDS.put(11202, "JNTC Co., Ltd.");
+ IDS.put(11208, "DONGGUAN POLIXIN ELECTRIC CO., LTD.");
+ IDS.put(11209, "Tama Electric (Suzhou) Co., Ltd.");
+ IDS.put(1121, "Primax Electronics");
+ IDS.put(11210, "Exvision, Inc.");
+ IDS.put(11216, "mophie, LLC");
+ IDS.put(11219, "Dongguan ULT-unite electronic technology co., LTD");
+ IDS.put(11220, "JL Audio, Inc.");
+ IDS.put(11221, "Cable Matters Inc.");
+ IDS.put(11222, "CoroWare, Inc.");
+ IDS.put(11229, "Charm Sciences Inc.");
+ IDS.put(1123, "EATON");
+ IDS.put(11230, "Pickering Interfaces Limited");
+ IDS.put(11231, "Hangzhou Hikvision Digital Technology Co., Ltd.");
+ IDS.put(11232, "FULLINK ELECTRONICS TECHNOLOGY (SZ) LTD");
+ IDS.put(11233, "AutoChips Inc.");
+ IDS.put(11234, "Electric Connector Technology Co., Ltd.");
+ IDS.put(11237, "LELTEK");
+ IDS.put(11238, "Dongguan KaiWin Electronics Co., Ltd.");
+ IDS.put(11239, "BEFS Co., Ltd.");
+ IDS.put(11240, "Archisite, Inc.");
+ IDS.put(11241, "Magneti Marelli S.p.A Electr BL");
+ IDS.put(11246, "Ventev Mobile");
+ IDS.put(11247, "Quanta Storage Inc.");
+ IDS.put(11248, "Tech-Top Technology Limited");
+ IDS.put(11253, "Shenzhen YOOBAO Technology Co., Ltd.");
+ IDS.put(11254, "Shenzhen Sinotek Technology Co., Ltd.");
+ IDS.put(11255, "KEYW");
+ IDS.put(11256, "Visual Land Inc.");
+ IDS.put(11264, "MEEM SL Ltd");
+ IDS.put(11265, "Dongguan Arin Electronics Technology Co., Ltd.");
+ IDS.put(11266, "DongGuan City JianNuo Electronics Co., Ltd.");
+ IDS.put(11268, "Shenzhen XOX Electronics Co., Ltd.");
+ IDS.put(11269, "Protop International Inc.");
+ IDS.put(11270, "Microsemi Semiconductor (US) Inc.");
+ IDS.put(11271, "Webcloak LLC");
+ IDS.put(11272, "INVECAS INC.");
+ IDS.put(11274, "ATANS Technology Inc.");
+ IDS.put(11275, "Triple Win Precision Technology Co., Ltd.");
+ IDS.put(11276, "IC Realtech");
+ IDS.put(11277, "Embrava Pty Ltd");
+ IDS.put(1128, "Wieson Technologies Co., Ltd.");
+ IDS.put(11280, "Sinotronics Co., Ltd.");
+ IDS.put(11281, "ALLBEST ELECTRONICS TECHNOLOGY CO., LTD.");
+ IDS.put(11282, "Shenzhen Xin Kai Feng Electronics Factory");
+ IDS.put(11283, "MOST WELL Technology Corp.");
+ IDS.put(11284, "Buffalo Memory Co., Ltd.");
+ IDS.put(11285, "Xentris Wireless");
+ IDS.put(11286, "Priferential Accessories Ltd");
+ IDS.put(11289, "Sunlike Technology Co., Ltd.");
+ IDS.put(11290, "Young Fast Optoelectronics Co., Ltd.");
+ IDS.put(11291, "ISAW Camera Inc");
+ IDS.put(11298, "Qanba USA, LLC");
+ IDS.put(11299, "Super Micro Computer Inc.");
+ IDS.put(11302, "Micromax International Corporation");
+ IDS.put(11304, "Granite River Labs Japan Ltd.");
+ IDS.put(11305, "Coagent Enterprise Limited");
+ IDS.put(11306, "LEIA Inc.");
+ IDS.put(11309, "Shenzhen Ebull Technology Limited");
+ IDS.put(1131, "American Megatrends");
+ IDS.put(11310, "Hualun Technology Co., Ltd.");
+ IDS.put(11311, "Sensel, Inc.");
+ IDS.put(11319, "Shenzhen Adition Audio Science & Technology Co., Ltd.");
+ IDS.put(11320, "Goldenconn Electronics Technology (Suzhou) Co., Ltd.");
+ IDS.put(11321, "JIB Electronics Technology Co., Ltd.");
+ IDS.put(11322, "Changzhou Shinco Automotive Electronics Co., Ltd.");
+ IDS.put(11323, "Shenzhen Hangsheng Electronics Corp., Ltd.");
+ IDS.put(11324, "Beartooth Radio, Inc.");
+ IDS.put(11325, "Audience, A Knowles Company");
+ IDS.put(11327, "Nextbit Systems, Inc.");
+ IDS.put(11328, "Leadtrend");
+ IDS.put(11329, "Adaptertek Technology Co., Ltd.");
+ IDS.put(1133, "Logitech Inc.");
+ IDS.put(11330, "Feature Integration Technology Inc.");
+ IDS.put(11331, "Avegant Corporation");
+ IDS.put(11335, "Chunghsin International Electronics Co., Ltd.");
+ IDS.put(11336, "Delphi Electrical Centers (Shanghai) Co., Ltd.");
+ IDS.put(11341, "VVETEK DOO");
+ IDS.put(11347, "Huizhou Foryou General Electronics Co., Ltd.");
+ IDS.put(11348, "LifeWatch Technologies Ltd.");
+ IDS.put(11349, "Magicleap");
+ IDS.put(11355, "Dongguan City Shenglan Electronics Co., LTD.");
+ IDS.put(11356, "Neusoft Corporation");
+ IDS.put(11357, "SIP Simya Electronics Technology Co., Ltd.");
+ IDS.put(11358, "GNSD Automotive Co., Ltd.");
+ IDS.put(11359, "YOODS Co., Ltd.");
+ IDS.put(11360, "Sirin Mobile Technologies AG");
+ IDS.put(11361, "Jadmam Corporation dba: Boytone");
+ IDS.put(11373, "Gibson Innovations");
+ IDS.put(11374, "Shen Zhen Xian Shuo Technology Co. LTD");
+ IDS.put(11375, "PST Eletronica LTDA");
+ IDS.put(11376, "PERI, Inc.");
+ IDS.put(11377, "Bozhou BoTong Information Technology Co., Ltd.");
+ IDS.put(11383, "Profindustry GmbH");
+ IDS.put(11384, "BRAGI GmbH");
+ IDS.put(11385, "WAWGD, Inc. (DBA: Foresight Sports)");
+ IDS.put(11390, "Dongguan Allpass Electronic Co., Ltd.");
+ IDS.put(11391, "SHENZHEN D-VITEC INDUSTRIAL CO., LTD.");
+ IDS.put(11392, "motomobile AG");
+ IDS.put(11393, "Indie Semiconductor");
+ IDS.put(11397, "Audientes");
+ IDS.put(11403, "Huizhou Dehong Technology Co., Ltd.");
+ IDS.put(11404, "PowerCenter Technology Limited");
+ IDS.put(11405, "Mizco International, Inc.");
+ IDS.put(11408, "I. AM. PLUS, LLC");
+ IDS.put(11409, "Corigine, Inc.");
+ IDS.put(11410, "Ningbo Yinzhou Shengke Electronics Co., Ltd.");
+ IDS.put(11417, "Prusa Research s.r.o.");
+ IDS.put(11423, "e-Smart Systems Pvt. Ltd.");
+ IDS.put(11424, "Leagtech Jiangxi Electronic Co., Ltd.");
+ IDS.put(11425, "Dongguan Yujia Electronics Technology Co., Ltd.");
+ IDS.put(11426, "GuangZhou MingPing Electronics Technology");
+ IDS.put(11427, "DJI Technology Co., Ltd.");
+ IDS.put(11428, "Shenzhen Alex Technology Co., Ltd.");
+ IDS.put(11433, "JITS TECHNOLOGY CO., LIMITED");
+ IDS.put(11434, "LIVV Brand llc");
+ IDS.put(11444, "Ava Enterprises, Inc. dba: Boss Audio Systems");
+ IDS.put(11448, "Shenzhen Sydixon Electronic Technology Co., Ltd.");
+ IDS.put(11449, "On-Bright Electronics (Shanghai) Co., Ltd.");
+ IDS.put(11450, "Dongguan Puxu Industrial Co., Ltd.");
+ IDS.put(11451, "Shenzhen Soling Indusrtial Co., Ltd.");
+ IDS.put(11453, "EGGCYTE, INC.");
+ IDS.put(11455, "Donggguan Yuhua Electronic Co., Ltd.");
+ IDS.put(11456, "Hangzhou Zero Zero Technology Co., Ltd.");
+ IDS.put(11462, "Prodigy Technovations Pvt Ltd");
+ IDS.put(11463, "EmergiTech, Inc");
+ IDS.put(11464, "Hewlett Packard Enterprise");
+ IDS.put(11465, "Monolithic Power Systems Inc.");
+ IDS.put(11467, "USB Memory Direct");
+ IDS.put(11468, "Silicon Mitus Inc.");
+ IDS.put(11472, "Technics Global Electronics & JCE Co., Ltd.");
+ IDS.put(11478, "Immersive Media");
+ IDS.put(11479, "Cosemi Technologies Inc.");
+ IDS.put(11481, "Cambrionix Ltd");
+ IDS.put(11482, "CXUN Co. Ltd.");
+ IDS.put(11483, "China Tsp Inc");
+ IDS.put(11490, "Yanfeng Visteon (Chongqing) Automotive Electronics Co");
+ IDS.put(11491, "Alcorlink Corp.");
+ IDS.put(11492, "ISBC Ltd.");
+ IDS.put(11493, "InX8 Inc dba: AKiTiO");
+ IDS.put(11494, "SDAN Tecchnology Co., Ltd.");
+ IDS.put(11495, "Lemobile Information Technology (Beijing) Co., Ltd.");
+ IDS.put(11496, "GongGuan HWX Electronic Technology Co., Ltd.");
+ IDS.put(11497, "Suzhu Jingshi Electronic Technology Co., Ltd.");
+ IDS.put(11498, "Zhong Shan City Richsound Electronic Industrial Ltd.");
+ IDS.put(11499, "Dongguang Kangbang Electronics Co., Ltd.");
+ IDS.put(1151, "Plantronics, Inc.");
+ IDS.put(1154, "Kyocera Corporation");
+ IDS.put(1155, "STMicroelectronics");
+ IDS.put(1161, "Foxconn / Hon Hai");
+ IDS.put(1165, "ITE Tech Inc.");
+ IDS.put(1177, "Yamaha Corporation");
+ IDS.put(1188, "Hitachi, Ltd.");
+ IDS.put(1191, "Visioneer");
+ IDS.put(1193, "Canon Inc.");
+ IDS.put(1200, "Nikon Corporation");
+ IDS.put(1201, "Pan International");
+ IDS.put(1204, "Cypress Semiconductor");
+ IDS.put(1205, "ROHM Co., Ltd.");
+ IDS.put(1207, "Compal Electronics, Inc.");
+ IDS.put(1208, "Seiko Epson Corp.");
+ IDS.put(1211, "I-O Data Device, Inc.");
+ IDS.put(1221, "Fujitsu Ltd.");
+ IDS.put(1227, "FUJIFILM Corporation");
+ IDS.put(1238, "Mentor Graphics");
+ IDS.put(1240, "Microchip Technology Inc.");
+ IDS.put(1241, "Holtek Semiconductor, Inc.");
+ IDS.put(1242, "Panasonic Corporation");
+ IDS.put(1245, "Sharp Corporation");
+ IDS.put(1250, "Exar Corporation");
+ IDS.put(1254, "Identiv, Inc.");
+ IDS.put(1256, "Samsung Electronics Co., Ltd.");
+ IDS.put(1260, "Tokyo Electron Device Limited");
+ IDS.put(1266, "Chicony Electronics Co., Ltd.");
+ IDS.put(1271, "Newnex Technology Corp.");
+ IDS.put(1273, "Brother Industries, Ltd.");
+ IDS.put(1276, "SUNPLUS TECHNOLOGY CO., LTD.");
+ IDS.put(1278, "PFU Limited");
+ IDS.put(1281, "Fujikura/DDK");
+ IDS.put(1282, "Acer, Inc.");
+ IDS.put(1287, "Hosiden Corporation");
+ IDS.put(1293, "Belkin International, Inc.");
+ IDS.put(1300, "FCI Electronics");
+ IDS.put(1302, "Longwell Electronics/Longwell Company");
+ IDS.put(1305, "Star Micronics Co., LTD");
+ IDS.put(1309, "American Power Conversion");
+ IDS.put(1314, "ACON, Advanced-Connectek, Inc.");
+ IDS.put(1343, "Synopsys, Inc.");
+ IDS.put(1356, "Sony Corporation");
+ IDS.put(1360, "Fuji Xerox Co., Ltd.");
+ IDS.put(1367, "ATEN International Co. Ltd.");
+ IDS.put(1369, "Cadence Design Systems, Inc.");
+ IDS.put(1386, "WACOM Co., Ltd.");
+ IDS.put(1389, "EIZO Corporation");
+ IDS.put(1390, "Elecom Co., Ltd.");
+ IDS.put(1394, "Conexant Systems, Inc.");
+ IDS.put(1398, "BAFO/Quality Computer Accessories");
+ IDS.put(1403, "Y-E Data, Inc.");
+ IDS.put(1404, "AVM GmbH");
+ IDS.put(1410, "Roland Corporation");
+ IDS.put(1412, "RATOC Systems, Inc.");
+ IDS.put(1419, "Infineon Technologies");
+ IDS.put(1423, "Alcor Micro, Corp.");
+ IDS.put(1424, "OMRON Corporation");
+ IDS.put(1447, "Bose Corporation");
+ IDS.put(1449, "OmniVision Technologies, Inc.");
+ IDS.put(1452, "Apple");
+ IDS.put(1453, "Y.C. Cable U.S.A., Inc");
+ IDS.put(14627, "National Instruments");
+ IDS.put(1470, "Tyco Electronics Corp., a TE Connectivity Ltd. company");
+ IDS.put(1473, "MegaChips Corporation");
+ IDS.put(1478, "Qualcomm, Inc");
+ IDS.put(1480, "Foxlink/Cheng Uei Precision Industry Co., Ltd.");
+ IDS.put(1482, "Ricoh Company Ltd.");
+ IDS.put(1498, "Microtek International Inc.");
+ IDS.put(1504, "Symbol Technologies");
+ IDS.put(1507, "Genesys Logic, Inc.");
+ IDS.put(1509, "Fuji Electric Co., Ltd.");
+ IDS.put(1525, "Unixtar Technology Inc.");
+ IDS.put(1529, "Datalogic ADC");
+ IDS.put(1535, "LeCroy Corporation");
+ IDS.put(1539, "Novatek Microelectronics Corp.");
+ IDS.put(1545, "SMK Manufacturing Inc.");
+ IDS.put(1551, "Joinsoon Electronics Mfg. Co., Ltd.");
+ IDS.put(1555, "TransAct Technologies Incorporated");
+ IDS.put(1561, "Seiko Instruments Inc.");
+ IDS.put(1582, "JPC/MAIN SUPER Inc.");
+ IDS.put(1583, "Sin Sheng Terminal & Machine Inc.");
+ IDS.put(1593, "Chrontel, Inc.");
+ IDS.put(1611, "Analog Devices, Inc. Development Tools");
+ IDS.put(1612, "Ji-Haw Industrial Co., Ltd");
+ IDS.put(1614, "Suyin Corporation");
+ IDS.put(1621, "Space Shuttle Hi-Tech Co.,Ltd.");
+ IDS.put(1622, "Glory Mark Electronic Ltd.");
+ IDS.put(1623, "Tekcon Electronics Corp.");
+ IDS.put(1624, "Sigma Designs, Inc.");
+ IDS.put(1631, "Good Way Technology Co., Ltd. & GWC technology Inc");
+ IDS.put(1632, "TSAY-E (BVI) International Inc.");
+ IDS.put(1633, "Hamamatsu Photonics K.K.");
+ IDS.put(1642, "Total Technologies, Ltd.");
+ IDS.put(1659, "Prolific Technology, Inc.");
+ IDS.put(16700, "Dell Inc.");
+ IDS.put(1680, "Golden Bridge Electech Inc.");
+ IDS.put(1689, "Tektronix, Inc.");
+ IDS.put(1690, "Askey Computer Corporation");
+ IDS.put(1709, "Greatland Electronics Taiwan Ltd.");
+ IDS.put(1710, "Eurofins Digital Testing Belgium");
+ IDS.put(1720, "Pixela Corporation");
+ IDS.put(1724, "Oki Data Corporation");
+ IDS.put(1727, "Leoco Corporation");
+ IDS.put(1732, "Bizlink Technology, Inc.");
+ IDS.put(1736, "SIIG, Inc.");
+ IDS.put(1747, "Mitsubishi Electric Corporation");
+ IDS.put(1758, "Heisei Technology Co., Ltd.");
+ IDS.put(1802, "Oki Electric Industry Co., Ltd.");
+ IDS.put(1805, "Comoss Electronic Co., Ltd.");
+ IDS.put(1809, "Magic Control Technology Corp.");
+ IDS.put(1816, "Imation Corp.");
+ IDS.put(1838, "Sunix Co., Ltd.");
+ IDS.put(1846, "Lorom Industrial Co., Ltd.");
+ IDS.put(1848, "Mad Catz, Inc.");
+ IDS.put(1899, "HID Global GmbH");
+ IDS.put(1901, "Denso Corporation");
+ IDS.put(1913, "Fairchild Semiconductor");
+ IDS.put(1921, "SanDisk Corporation");
+ IDS.put(1937, "Copartner Technology Corporation");
+ IDS.put(1954, "National Technical Systems");
+ IDS.put(1971, "Plustek, Inc.");
+ IDS.put(1972, "OLYMPUS CORPORATION");
+ IDS.put(1975, "TIME Interconnect Ltd.");
+ IDS.put(1994, "AVerMedia Technologies, Inc.");
+ IDS.put(1999, "Casio Computer Co., Ltd.");
+ IDS.put(2015, "David Electronics Company, Ltd.");
+ IDS.put(2039, "Century Corporation");
+ IDS.put(2058, "Evermuch Technology Co., Ltd.");
+ IDS.put(2101, "Action Star Enterprise Co., Ltd.");
+ IDS.put(2112, "Argosy Research Inc.");
+ IDS.put(2122, "Wipro Limited");
+ IDS.put(2159, "MEC IMEX INC/HPT");
+ IDS.put(2205, "Icron Technologies Corporation");
+ IDS.put(2247, "TAI TWUN ENTERPRISE CO., LTD.");
+ IDS.put(2276, "Pioneer Corporation");
+ IDS.put(2278, "Gemalto SA");
+ IDS.put(2310, "FARADAY Technology Corp.");
+ IDS.put(2313, "Audio-Technica Corp.");
+ IDS.put(2316, "Silicon Motion, Inc. - Taiwan");
+ IDS.put(2334, "Garmin International");
+ IDS.put(2352, "Toshiba Corporation");
+ IDS.put(2362, "Pixart Imaging, Inc.");
+ IDS.put(2363, "Plextor LLC");
+ IDS.put(2366, "J.S.T. Mfg. Co., Ltd.");
+ IDS.put(2385, "Kingston Technology Company");
+ IDS.put(2389, "NVIDIA");
+ IDS.put(2395, "Medialogic Corporation");
+ IDS.put(2397, "Polycom, Inc.");
+ IDS.put(2468, "Contech Research, Inc.");
+ IDS.put(2472, "Lin Shiung Enterprise Co., Ltd.");
+ IDS.put(2475, "Japan Cash Machine Co., Ltd.");
+ IDS.put(2498, "NISCA Corporation");
+ IDS.put(2511, "Electronics Testing Center, Taiwan");
+ IDS.put(2522, "A-FOUR TECH CO., LTD.");
+ IDS.put(2555, "Altera");
+ IDS.put(2578, "Cambridge Silicon Radio Ltd.");
+ IDS.put(2583, "HOYA Corporation");
+ IDS.put(2631, "Hirose Electric Co., Ltd.");
+ IDS.put(2636, "COMPUTEX Co., Ltd.");
+ IDS.put(2640, "Mimaki Engineering Co., Ltd.");
+ IDS.put(2652, "Broadcom Corp.");
+ IDS.put(2667, "Green House Co., Ltd.");
+ IDS.put(2702, "Japan Aviation Electronics Industry Ltd. (JAE)");
+ IDS.put(2727, "Wincor Nixdorf GmbH & Co KG");
+ IDS.put(2733, "Rohde & Schwarz GmbH & Co. KG");
+ IDS.put(2787, "Allion Labs, Inc.");
+ IDS.put(2821, "ASUSTek Computer Inc.");
+ IDS.put(2849, "Yokogawa Electric Corporation");
+ IDS.put(2851, "Pan-Asia Electronics Co., Ltd.");
+ IDS.put(2894, "Musical Electronics Ltd.");
+ IDS.put(2907, "Anritsu Corporation");
+ IDS.put(2922, "Maxim Integrated Products");
+ IDS.put(2965, "ASIX Electronics Corporation");
+ IDS.put(2967, "O2Micro, Inc.");
+ IDS.put(3010, "Seagate Technology LLC");
+ IDS.put(3034, "Realtek Semiconductor Corp.");
+ IDS.put(3035, "Ericsson AB");
+ IDS.put(3044, "Elka International Ltd.");
+ IDS.put(3056, "Pace Micro Technology PLC");
+ IDS.put(3108, "Taiyo Yuden Co., Ltd.");
+ IDS.put(3129, "Aeroflex");
+ IDS.put(3132, "Radius Co., Ltd.");
+ IDS.put(3141, "Sonix Technology Co., Ltd.");
+ IDS.put(3158, "Billion Bright (HK) Corporation Limited");
+ IDS.put(3161, "Dong Guan Shinko Wire Co., Ltd.");
+ IDS.put(3170, "Chant Sincere Co., Ltd");
+ IDS.put(3190, "Solid State System Co., Ltd.");
+ IDS.put(3209, "Honda Tsushin Kogyo Co., Ltd");
+ IDS.put(3245, "Motorola Solutions");
+ IDS.put(3255, "Singatron Enterprise Co. Ltd.");
+ IDS.put(3268, "emsys Embedded Systems GmbH");
+ IDS.put(32902, "Intel Corporation");
+ IDS.put(3294, "Z-Com INC.");
+ IDS.put(3313, "e-CONN ELECTRONIC CO., LTD.");
+ IDS.put(3314, "ENE Technology Inc.");
+ IDS.put(3351, "NALTEC, Inc.");
+ IDS.put(3402, "NF Corporation");
+ IDS.put(3403, "Grape Systems Inc.");
+ IDS.put(3409, "Volex (Asia) Pte Ltd");
+ IDS.put(3425, "MEILU ELECTRONICS (SHENZHEN) CO., LTD.");
+ IDS.put(3441, "Hirakawa Hewtech Corp.");
+ IDS.put(3452, "Taiwan Line Tek Electronic Co., Ltd.");
+ IDS.put(3463, "Dolby Laboratories Inc.");
+ IDS.put(3468, "C-MEDIA ELECTRONICS INC.");
+ IDS.put(3472, "Sure-Fire Electrical Corporation");
+ IDS.put(3495, "IOGEAR, Inc.");
+ IDS.put(3504, "Micro-Star International Co., Ltd.");
+ IDS.put(3537, "Contek Electronics Co., Ltd.");
+ IDS.put(3540, "Custom Engineering SPA");
+ IDS.put(3641, "Smart Modular Technologies, Inc.");
+ IDS.put(3658, "Shenzhen Bao Hing Electric Wire & Cable Mfr. Co.");
+ IDS.put(3673, "Bourns, Inc.");
+ IDS.put(3690, "Megawin Technology Co., Ltd.");
+ IDS.put(3698, "Hsi-Chin Electronics Co., Ltd.");
+ IDS.put(3714, "Ching Tai Electric Wire & Cable Co., Ltd.");
+ IDS.put(3724, "Well Force Electronic Co., Ltd");
+ IDS.put(3725, "MediaTek Inc.");
+ IDS.put(3728, "CRU");
+ IDS.put(3744, "Ours Technology Inc.");
+ IDS.put(3762, "Y-S ELECTRONIC CO., LTD.");
+ IDS.put(3778, "Sweetray Industrial Ltd.");
+ IDS.put(3779, "Axell Corporation");
+ IDS.put(3782, "InnoVISION Multimedia Limited");
+ IDS.put(3790, "TaiSol Electronics Co., Ltd.");
+ IDS.put(3812, "Sunrich Technology (H.K.) Ltd.");
+ IDS.put(3868, "Funai Electric Co., Ltd.");
+ IDS.put(3873, "IOI Technology Corporation");
+ IDS.put(3890, "YFC-BonEagle Electric Co., Ltd.");
+ IDS.put(3896, "Nien-Yi Industrial Corp.");
+ IDS.put(3916, "WORLDWIDE CABLE OPTO CORP.");
+ IDS.put(3923, "Taiyo Cable (Dongguan) Co. Ltd.");
+ IDS.put(3924, "Kawai Musical Instruments Mfg. Co., Ltd.");
+ IDS.put(3936, "GuangZhou Chief Tech Electronic Technology Co. Ltd.");
+ IDS.put(3944, "UQUEST, LTD.");
+ IDS.put(3991, "CviLux Corporation");
+ IDS.put(4003, "Chief Land Electronic Co., Ltd.");
+ IDS.put(4046, "Sony Mobile Communications");
+ IDS.put(4087, "CHI SHING COMPUTER ACCESSORIES CO., LTD.");
+ IDS.put(4096, "Speed Tech Corp.");
+ IDS.put(4100, "LG Electronics Inc.");
+ IDS.put(4101, "Apacer Technology Inc.");
+ IDS.put(4134, "Newly Corporation");
+ IDS.put(4168, "Targus Group International");
+ IDS.put(4172, "AMCO TEC International Inc.");
+ IDS.put(4183, "ON Semiconductor");
+ IDS.put(4184, "Western Digital Technologies, Inc.");
+ IDS.put(4227, "CANON ELECTRONICS INC.");
+ IDS.put(4235, "Grand-tek Technology Co., Ltd.");
+ IDS.put(4236, "Robert Bosch GmbH");
+ IDS.put(4238, "Lotes Co., Ltd.");
+ IDS.put(4266, "Cables To Go");
+ IDS.put(4267, "Universal Global Scientific Industrial Co., Ltd.");
+ IDS.put(4292, "Silicon Laboratories, Inc.");
+ IDS.put(4301, "Kycon Inc.");
+ IDS.put(4362, "Moxa Inc.");
+ IDS.put(4370, "Golden Bright (Sichuan) Electronic Technology Co Ltd");
+ IDS.put(4382, "VSO ELECTRONICS CO., LTD.");
+ IDS.put(4398, "Master Hill Electric Wire and Cable Co., Ltd.");
+ IDS.put(4477, "Santa Electronic Inc.");
+ IDS.put(4505, "Sierra Wireless Inc.");
+ IDS.put(4522, "GlobalMedia Group, LLC");
+ IDS.put(4528, "ATECH FLASH TECHNOLOGY");
+ IDS.put(4643, "SKYCABLE ENTERPRISE CO., LTD.");
+ IDS.put(4703, "ADATA Technology Co., Ltd.");
+ IDS.put(4716, "Aristocrat Technologies");
+ IDS.put(4717, "Bel Stewart");
+ IDS.put(4742, "MARVELL SEMICONDUCTOR, INC.");
+ IDS.put(4756, "RISO KAGAKU CORP.");
+ IDS.put(4792, "Zhejiang Xinya Electronic Technology Co., Ltd.");
+ IDS.put(4817, "Huawei Technologies Co., Ltd.");
+ IDS.put(4823, "Better Holdings (HK) Limited");
+ IDS.put(4907, "Konica Minolta, Inc.");
+ IDS.put(4925, "Jasco Products Company");
+ IDS.put(4989, "Pericom Semiconductor Corp.");
+ IDS.put(5008, "TomTom International B.V.");
+ IDS.put(5075, "AzureWave Technologies, Inc.");
+ IDS.put(5117, "Initio Corporation");
+ IDS.put(5118, "Phison Electronics Corp.");
+ IDS.put(5134, "Telechips, Inc.");
+ IDS.put(5145, "ABILITY ENTERPRISE CO., LTD.");
+ IDS.put(5148, "Leviton Manufacturing");
+ IDS.put(5271, "Panstrong Company Ltd.");
+ IDS.put(5293, "CTK Corporation");
+ IDS.put(5296, "StarTech.com Ltd.");
+ IDS.put(5376, "Ellisys");
+ IDS.put(5404, "VeriSilicon Holdings Co., Ltd.");
+ IDS.put(5421, "JMicron Technology Corp.");
+ IDS.put(5422, "HLDS (Hitachi-LG Data Storage, Inc.)");
+ IDS.put(5440, "Phihong Technology Co., Ltd.");
+ IDS.put(5451, "PNY Technologies Inc.");
+ IDS.put(5453, "Rapid Conn, Connect County Holdings Bhd");
+ IDS.put(5454, "D & M Holdings, Inc.");
+ IDS.put(5480, "Sunf Pu Technology Co., Ltd");
+ IDS.put(5488, "ALLTOP TECHNOLOGY CO., LTD.");
+ IDS.put(5510, "Palconn Technology Co., Ltd.");
+ IDS.put(5528, "Kunshan Guoji Electronics Co., Ltd.");
+ IDS.put(5546, "DongGuan Ya Lian Electronics Co., Ltd.");
+ IDS.put(5645, "Samtec");
+ IDS.put(5694, "HongLin Electronics Co., Ltd.");
+ IDS.put(5753, "Total Phase");
+ IDS.put(5766, "ZOOM Corporation");
+ IDS.put(5836, "silex technology, Inc.");
+ IDS.put(5946, "F. Hoffmann-La Roche AG");
+ IDS.put(5960, "MQP Electronics Ltd.");
+ IDS.put(5964, "ASMedia Technology Inc.");
+ IDS.put(5998, "UD electronic corp.");
+ IDS.put(6001, "Shenzhen Alex Connector Co., Ltd.");
+ IDS.put(6002, "System Level Solutions, Inc.");
+ IDS.put(6018, "Spreadtrum Hong Kong Limited");
+ IDS.put(6024, "ShenZhen Litkconn Technology Co., Ltd.");
+ IDS.put(6053, "Advanced Connection Technology Inc.");
+ IDS.put(6095, "Hip Hing Cable & Plug Mfy. Ltd.");
+ IDS.put(6121, "DisplayLink (UK) Ltd.");
+ IDS.put(6127, "Lenovo");
+ IDS.put(6133, "K.K. Rocky");
+ IDS.put(6160, "Wanshih Electronic Co., Ltd.");
+ IDS.put(6185, "Dongguan YuQiu Electronics Co., Ltd.");
+ IDS.put(6193, "Gwo Jinn Industries Co., Ltd.");
+ IDS.put(6297, "Linkiss Co., Ltd.");
+ IDS.put(6353, "Google Inc.");
+ IDS.put(6394, "Kuang Ying Computer Equipment Co., Ltd.");
+ IDS.put(6421, "Nordic Semiconductor ASA");
+ IDS.put(6448, "Shenzhen Xianhe Technology Co., Ltd.");
+ IDS.put(6449, "Ningbo Broad Telecommunication Co., Ltd.");
+ IDS.put(6470, "Irisguard UK Ltd");
+ IDS.put(6473, "Lab126");
+ IDS.put(6481, "Hyperstone GmbH");
+ IDS.put(6487, "BIOS Corporation");
+ IDS.put(6626, "Solomon Systech Limited");
+ IDS.put(6639, "Pak Heng Technology (Shenzhen) Co., Ltd.");
+ IDS.put(6655, "Best Buy China Ltd.");
+ IDS.put(6666, "USB-IF non-workshop");
+ IDS.put(6709, "Artesyn Technologies Inc.");
+ IDS.put(6720, "TERMINUS TECHNOLOGY INC.");
+ IDS.put(6766, "Global Unichip Corp.");
+ IDS.put(6786, "Proconn Technology Co., Ltd.");
+ IDS.put(6794, "Simula Technology Inc.");
+ IDS.put(6795, "SGS Taiwan Ltd.");
+ IDS.put(6830, "Johnson Component & Equipments Co., Ltd.");
+ IDS.put(6834, "Allied Vision Technologies GmbH");
+ IDS.put(6859, "Salcomp Plc");
+ IDS.put(6865, "Desan Wire Co., Ltd.");
+ IDS.put(6944, "MStar Semiconductor, Inc.");
+ IDS.put(6984, "Plastron Precision Co., Ltd.");
+ IDS.put(7013, "The Hong Kong Standards and Testing Centre Ltd.");
+ IDS.put(7048, "ShenMing Electron (Dong Guan) Co., Ltd.");
+ IDS.put(7086, "Vuzix Corporation");
+ IDS.put(7108, "Ford Motor Co.");
+ IDS.put(7118, "Contac Cable Industrial Limited");
+ IDS.put(7119, "Sunplus Innovation Technology Inc.");
+ IDS.put(7120, "Hangzhou Riyue Electronics Co., Ltd.");
+ IDS.put(7158, "Orient Semiconductor Electronics, Ltd.");
+ IDS.put(7207, "SHENZHEN DNS INDUSTRIES CO., LTD.");
+ IDS.put(7217, "LS Mtron Ltd.");
+ IDS.put(7229, "NONIN MEDICAL INC.");
+ IDS.put(7275, "Philips & Lite-ON Digital Solutions Corporation");
+ IDS.put(7310, "ASTRON INTERNATIONAL CORP.");
+ IDS.put(7320, "ALPINE ELECTRONICS, INC.");
+ IDS.put(7347, "Aces Electronics Co., Ltd.");
+ IDS.put(7348, "OPEX CORPORATION");
+ IDS.put(7390, "Telecommunications Technology Association (TTA)");
+ IDS.put(7434, "Visteon Corporation");
+ IDS.put(7465, "Horng Tong Enterprise Co., Ltd.");
+ IDS.put(7501, "Pegatron Corporation");
+ IDS.put(7516, "Fresco Logic Inc.");
+ IDS.put(7529, "Walta Electronic Co., Ltd.");
+ IDS.put(7543, "Yueqing Changling Electronic Instrument Corp., Ltd.");
+ IDS.put(7584, "Parade Technologies, Inc.");
+ IDS.put(7647, "L&T Technology Services");
+ IDS.put(7649, "Actions Microelectronics Co., Ltd.");
+ IDS.put(7666, "China Telecommunication Technology Labs - Terminals");
+ IDS.put(7668, "SHEN ZHEN FORMAN PRECISION INDUSTRY CO., LTD.");
+ IDS.put(7682, "GLOBEMASTER TECHNOLOGIES CO., LTD.");
+ IDS.put(7696, "Point Grey Research Inc.");
+ IDS.put(7751, "HUNG TA H.T.ENTERPRISE CO., LTD.");
+ IDS.put(7758, "Etron Technology, Inc.");
+ IDS.put(7795, "COMLINK ELECTRONICS CO., LTD.");
+ IDS.put(7818, "HIBEST Electronic (DongGuan) Co., Ltd.");
+ IDS.put(7825, "Other World Computing");
+ IDS.put(7863, "WIN WIN PRECISION INDUSTRIAL CO., LTD.");
+ IDS.put(7879, "Gefen Inc.");
+ IDS.put(7881, "MOSER BAER INDIA LIMITED");
+ IDS.put(7898, "AIRTIES WIRELESS NETWORKS");
+ IDS.put(7956, "Astoria Networks GmbH");
+ IDS.put(7969, "Scosche Industries");
+ IDS.put(7976, "Cal-Comp Electronics & Communications");
+ IDS.put(7977, "Analogix Semiconductor, Inc.");
+ IDS.put(7989, "Amphenol ShouhMin Industry (ShenZhen) Co., Ltd");
+ IDS.put(7996, "Chang Yang Electronics Company Ltd.");
+ IDS.put(8073, "Dongguan Goldconn Electronics Co., Ltd.");
+ IDS.put(8074, "Morning Star Industrial Co., Ltd.");
+ IDS.put(8117, "Unify Software and Solutions GmbH & Co. KG");
+ IDS.put(8137, "NXP Semiconductors");
+ IDS.put(8181, "Changzhou Wujin BEST Electronic Cables Co., Ltd.");
+ IDS.put(8205, "Belkin Electronic (Changzhou) Co., Ltd.");
+ IDS.put(8220, "Freeport Resources Enterprises Corp.");
+ IDS.put(8222, "Qingdao Haier Telecom Co., Ltd.");
+ IDS.put(8284, "Shenzhen Tronixin Electronics Co., Ltd.");
+ IDS.put(8294, "Unicorn Electronics Components Co., Ltd.");
+ IDS.put(8334, "Luxshare-ICT");
+ IDS.put(8341, "CE LINK LIMITED");
+ IDS.put(8342, "Microconn Electronic Co., Ltd.");
+ IDS.put(8367, "Shenzhen CARVE Electronics Co., Ltd.");
+ IDS.put(8382, "BURY GmbH & Co. KG");
+ IDS.put(8384, "FENGHUA KINGSUN CO., LTD.");
+ IDS.put(8386, "Sumitomo Electric Ind., Ltd., Optical Comm. R&D Lab");
+ IDS.put(8439, "XIMEA s.r.o.");
+ IDS.put(8457, "VIA Labs, Inc.");
+ IDS.put(8492, "Shenzhen Linoya Electronic Co., Ltd.");
+ IDS.put(8494, "Amphenol AssembleTech (Xiamen) Co., Ltd.");
+ IDS.put(8524, "Y Soft Corporation");
+ IDS.put(8550, "JVC KENWOOD Corporation");
+ IDS.put(8564, "Transcend Information, Inc.");
+ IDS.put(8566, "TMC/Allion Test Labs");
+ IDS.put(8613, "Genesis Technology USA, Inc.");
+ IDS.put(8627, "Dongguan Teconn Electronics Technology Co., Ltd.");
+ IDS.put(8644, "Netcom Technology (HK) Limited");
+ IDS.put(8659, "Compupack Technology Co., Ltd.");
+ IDS.put(8667, "G-Max Technology Co., Ltd.");
+ IDS.put(8679, "Sagemcom Broadband SAS");
+ IDS.put(8695, "Wuerth-Elektronik eiSos GmbH & Co. KG");
+ IDS.put(8707, "Shin Shin Co., Ltd.");
+ IDS.put(8709, "3eYamaichi Electronics Co., Ltd.");
+ IDS.put(8710, "Wiretek International Investment Ltd.");
+ IDS.put(8711, "Fuzhou Rockchip Electronics Co., Ltd.");
+ IDS.put(8752, "Plugable Technologies");
+ IDS.put(8756, "T-CONN PRECISION CORPORATION");
+ IDS.put(8831, "Granite River Labs");
+ IDS.put(8842, "Hotron Precision Electronic Ind. Corp.");
+ IDS.put(8875, "Trigence Semiconductor, Inc.");
+ IDS.put(8888, "Motorola Mobility Inc.");
+ IDS.put(8904, "Karming Electronic (Shenzhen) Co., Ltd.");
+ IDS.put(8981, "Avery Design Systems, Inc.");
+ IDS.put(8993, "iKingdom Corp. (d.b.a. iConnectivity)");
+ IDS.put(9051, "KangXiang Electronic Co., Ltd.");
+ IDS.put(9068, "ZheJiang Chunsheng Electronics Co., Ltd.");
+ IDS.put(9130, "DOK (HK) Trading Limited");
+ IDS.put(9132, "Marunix Electron Limited");
+ IDS.put(9165, "Avconn Precise Connector Co., Ltd.");
+ IDS.put(9184, "BitifEye Digital Test Solutions GmbH");
+ IDS.put(9205, "Speed Conn Co., Ltd.");
+ IDS.put(9222, "INSIDE Secure");
+ IDS.put(9292, "Minebea Co., Ltd.");
+ IDS.put(9299, "BAANTO");
+ IDS.put(9338, "Suzhou Jutze Technologies Co., Ltd");
+ IDS.put(9355, "DONGGUAN SYNCONN PRECISION INDUSTRY CO. LTD.");
+ IDS.put(9382, "Shenzhen Pangngai Industrial Co., Ltd.");
+ IDS.put(9422, "Shenzhen Deren Electronic Co., Ltd.");
+ IDS.put(9424, "Smith Micro Software, Inc.");
+ IDS.put(9453, "ZEN FACTORY GROUP (ASIA) LTD.");
+ IDS.put(9481, "Chain-In Electronic Co., Ltd.");
+ IDS.put(9514, "SUZHOU KELI TECHNOLOGY DEVELOPMENT CO., LTD.");
+ IDS.put(9515, "TOP Exactitude Industry (ShenZhen) Co., Ltd.");
+ IDS.put(9525, "ShenZhen Hogend Precision Technology Co., Ltd.");
+ IDS.put(9527, "Norel Systems Ltd.");
+ IDS.put(9556, "ASSA ABLOY AB");
+ IDS.put(9575, "DongGuan LongTao Electronic Co., Ltd.");
+ IDS.put(9577, "DongGuan City MingJi Electronics Co., Ltd.");
+ IDS.put(9589, "Weida Hi-Tech Co., Ltd.");
+ IDS.put(9593, "Dongguan Wisechamp Electronic Co., Ltd.");
+ IDS.put(9613, "Sequans Communications");
+ IDS.put(9636, "ALGOLTEK, INC.");
+ IDS.put(9651, "DongGuan Elinke Industrial Co., Ltd.");
+ IDS.put(9679, "Corning Optical Communications LLC");
+ IDS.put(9714, "Dongguan Jinyue Electronics Co., Ltd.");
+ IDS.put(9723, "RICOH IMAGING COMPANY, LTD.");
+ IDS.put(9742, "DongGuan HYX Industrial Co., Ltd.");
+ IDS.put(9753, "Advanced Silicon SA");
+ IDS.put(9756, "EISST Limited");
+ IDS.put(9771, "YTOP Electronics Technical (Kunshan) Co., Ltd.");
+ IDS.put(9841, "Innovative Logic");
+ IDS.put(9842, "GoPro");
+ IDS.put(9846, "Basler AG");
+ IDS.put(9851, "Palpilot International Corp.");
+ IDS.put(9896, "UNIREX CORPORATION");
+ IDS.put(9917, "Integral Memory Plc.");
+ IDS.put(9973, "Morning Star Digital Connector Co., Ltd.");
+ IDS.put(9984, "MITACHI CO., LTD.");
+ IDS.put(9999, "HGST, a Western Digital Company");
+ }
+}
diff --git a/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java b/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java
new file mode 100644
index 0000000..6dd30e2
--- /dev/null
+++ b/libusbcamera/src/main/java/com/serenegiant/usb/UVCCamera.java
@@ -0,0 +1,1238 @@
+/*
+ * UVCCamera
+ * library and sample to access to UVC web camera on non-rooted Android device
+ *
+ * Copyright (c) 2014-2017 saki t_saki@serenegiant.com
+ *
+ * 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.
+ *
+ * All files in the folder are under this Apache License, Version 2.0.
+ * Files in the libjpeg-turbo, libusb, libuvc, rapidjson folder
+ * may have a different license, see the respective files.
+ */
+
+package com.serenegiant.usb;
+
+import android.graphics.SurfaceTexture;
+import android.hardware.usb.UsbDevice;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.Surface;
+import android.view.SurfaceHolder;
+
+import com.serenegiant.usb.USBMonitor.UsbControlBlock;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class UVCCamera {
+ private static final boolean DEBUG = false; // TODO set false when releasing
+ private static final String TAG = UVCCamera.class.getSimpleName();
+ private static final String DEFAULT_USBFS = "/dev/bus/usb";
+
+ public static final int DEFAULT_PREVIEW_WIDTH = 640;
+ public static final int DEFAULT_PREVIEW_HEIGHT = 480;
+ public static final int DEFAULT_PREVIEW_MODE = 0;
+ public static final int DEFAULT_PREVIEW_MIN_FPS = 1;
+ public static final int DEFAULT_PREVIEW_MAX_FPS = 30;
+ public static final float DEFAULT_BANDWIDTH = 1.0f;
+
+ public static final int FRAME_FORMAT_YUYV = 0;
+ public static final int FRAME_FORMAT_MJPEG = 1;
+
+ public static final int PIXEL_FORMAT_RAW = 0;
+ public static final int PIXEL_FORMAT_YUV = 1;
+ public static final int PIXEL_FORMAT_RGB565 = 2;
+ public static final int PIXEL_FORMAT_RGBX = 3;
+ public static final int PIXEL_FORMAT_YUV420SP = 4;
+ public static final int PIXEL_FORMAT_NV21 = 5; // = YVU420SemiPlanar
+
+ //--------------------------------------------------------------------------------
+ public static final int CTRL_SCANNING = 0x00000001; // D0: Scanning Mode
+ public static final int CTRL_AE = 0x00000002; // D1: Auto-Exposure Mode
+ public static final int CTRL_AE_PRIORITY = 0x00000004; // D2: Auto-Exposure Priority
+ public static final int CTRL_AE_ABS = 0x00000008; // D3: Exposure Time (Absolute)
+ public static final int CTRL_AR_REL = 0x00000010; // D4: Exposure Time (Relative)
+ public static final int CTRL_FOCUS_ABS = 0x00000020; // D5: Focus (Absolute)
+ public static final int CTRL_FOCUS_REL = 0x00000040; // D6: Focus (Relative)
+ public static final int CTRL_IRIS_ABS = 0x00000080; // D7: Iris (Absolute)
+ public static final int CTRL_IRIS_REL = 0x00000100; // D8: Iris (Relative)
+ public static final int CTRL_ZOOM_ABS = 0x00000200; // D9: Zoom (Absolute)
+ public static final int CTRL_ZOOM_REL = 0x00000400; // D10: Zoom (Relative)
+ public static final int CTRL_PANTILT_ABS = 0x00000800; // D11: PanTilt (Absolute)
+ public static final int CTRL_PANTILT_REL = 0x00001000; // D12: PanTilt (Relative)
+ public static final int CTRL_ROLL_ABS = 0x00002000; // D13: Roll (Absolute)
+ public static final int CTRL_ROLL_REL = 0x00004000; // D14: Roll (Relative)
+ public static final int CTRL_FOCUS_AUTO = 0x00020000; // D17: Focus, Auto
+ public static final int CTRL_PRIVACY = 0x00040000; // D18: Privacy
+ public static final int CTRL_FOCUS_SIMPLE = 0x00080000; // D19: Focus, Simple
+ public static final int CTRL_WINDOW = 0x00100000; // D20: Window
+
+ public static final int PU_BRIGHTNESS = 0x80000001; // D0: Brightness
+ public static final int PU_CONTRAST = 0x80000002; // D1: Contrast
+ public static final int PU_HUE = 0x80000004; // D2: Hue
+ public static final int PU_SATURATION = 0x80000008; // D3: Saturation
+ public static final int PU_SHARPNESS = 0x80000010; // D4: Sharpness
+ public static final int PU_GAMMA = 0x80000020; // D5: Gamma
+ public static final int PU_WB_TEMP = 0x80000040; // D6: White Balance Temperature
+ public static final int PU_WB_COMPO = 0x80000080; // D7: White Balance Component
+ public static final int PU_BACKLIGHT = 0x80000100; // D8: Backlight Compensation
+ public static final int PU_GAIN = 0x80000200; // D9: Gain
+ public static final int PU_POWER_LF = 0x80000400; // D10: Power Line Frequency
+ public static final int PU_HUE_AUTO = 0x80000800; // D11: Hue, Auto
+ public static final int PU_WB_TEMP_AUTO = 0x80001000; // D12: White Balance Temperature, Auto
+ public static final int PU_WB_COMPO_AUTO = 0x80002000; // D13: White Balance Component, Auto
+ public static final int PU_DIGITAL_MULT = 0x80004000; // D14: Digital Multiplier
+ public static final int PU_DIGITAL_LIMIT = 0x80008000; // D15: Digital Multiplier Limit
+ public static final int PU_AVIDEO_STD = 0x80010000; // D16: Analog Video Standard
+ public static final int PU_AVIDEO_LOCK = 0x80020000; // D17: Analog Video Lock Status
+ public static final int PU_CONTRAST_AUTO = 0x80040000; // D18: Contrast, Auto
+
+ // uvc_status_class from libuvc.h
+ public static final int STATUS_CLASS_CONTROL = 0x10;
+ public static final int STATUS_CLASS_CONTROL_CAMERA = 0x11;
+ public static final int STATUS_CLASS_CONTROL_PROCESSING = 0x12;
+
+ // uvc_status_attribute from libuvc.h
+ public static final int STATUS_ATTRIBUTE_VALUE_CHANGE = 0x00;
+ public static final int STATUS_ATTRIBUTE_INFO_CHANGE = 0x01;
+ public static final int STATUS_ATTRIBUTE_FAILURE_CHANGE = 0x02;
+ public static final int STATUS_ATTRIBUTE_UNKNOWN = 0xff;
+
+ private static boolean isLoaded;
+ static {
+ if (!isLoaded) {
+ System.loadLibrary("jpeg-turbo1500");
+ System.loadLibrary("usb100");
+ System.loadLibrary("uvc");
+ System.loadLibrary("UVCCamera");
+ isLoaded = true;
+ }
+ }
+
+ private UsbControlBlock mCtrlBlock;
+ protected long mControlSupports; // カメラコントロールでサポートしている機能フラグ
+ protected long mProcSupports; // プロセッシングユニットでサポートしている機能フラグ
+ protected int mCurrentFrameFormat = FRAME_FORMAT_MJPEG;
+ protected int mCurrentWidth = DEFAULT_PREVIEW_WIDTH, mCurrentHeight = DEFAULT_PREVIEW_HEIGHT;
+ protected float mCurrentBandwidthFactor = DEFAULT_BANDWIDTH;
+ protected String mSupportedSize;
+ protected List mCurrentSizeList;
+ // these fields from here are accessed from native code and do not change name and remove
+ protected long mNativePtr;
+ protected int mScanningModeMin, mScanningModeMax, mScanningModeDef;
+ protected int mExposureModeMin, mExposureModeMax, mExposureModeDef;
+ protected int mExposurePriorityMin, mExposurePriorityMax, mExposurePriorityDef;
+ protected int mExposureMin, mExposureMax, mExposureDef;
+ protected int mAutoFocusMin, mAutoFocusMax, mAutoFocusDef;
+ protected int mFocusMin, mFocusMax, mFocusDef;
+ protected int mFocusRelMin, mFocusRelMax, mFocusRelDef;
+ protected int mFocusSimpleMin, mFocusSimpleMax, mFocusSimpleDef;
+ protected int mIrisMin, mIrisMax, mIrisDef;
+ protected int mIrisRelMin, mIrisRelMax, mIrisRelDef;
+ protected int mPanMin, mPanMax, mPanDef;
+ protected int mTiltMin, mTiltMax, mTiltDef;
+ protected int mRollMin, mRollMax, mRollDef;
+ protected int mPanRelMin, mPanRelMax, mPanRelDef;
+ protected int mTiltRelMin, mTiltRelMax, mTiltRelDef;
+ protected int mRollRelMin, mRollRelMax, mRollRelDef;
+ protected int mPrivacyMin, mPrivacyMax, mPrivacyDef;
+ protected int mAutoWhiteBlanceMin, mAutoWhiteBlanceMax, mAutoWhiteBlanceDef;
+ protected int mAutoWhiteBlanceCompoMin, mAutoWhiteBlanceCompoMax, mAutoWhiteBlanceCompoDef;
+ protected int mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef;
+ protected int mWhiteBlanceCompoMin, mWhiteBlanceCompoMax, mWhiteBlanceCompoDef;
+ protected int mWhiteBlanceRelMin, mWhiteBlanceRelMax, mWhiteBlanceRelDef;
+ protected int mBacklightCompMin, mBacklightCompMax, mBacklightCompDef;
+ protected int mBrightnessMin, mBrightnessMax, mBrightnessDef;
+ protected int mContrastMin, mContrastMax, mContrastDef;
+ protected int mSharpnessMin, mSharpnessMax, mSharpnessDef;
+ protected int mGainMin, mGainMax, mGainDef;
+ protected int mGammaMin, mGammaMax, mGammaDef;
+ protected int mSaturationMin, mSaturationMax, mSaturationDef;
+ protected int mHueMin, mHueMax, mHueDef;
+ protected int mZoomMin, mZoomMax, mZoomDef;
+ protected int mZoomRelMin, mZoomRelMax, mZoomRelDef;
+ protected int mPowerlineFrequencyMin, mPowerlineFrequencyMax, mPowerlineFrequencyDef;
+ protected int mMultiplierMin, mMultiplierMax, mMultiplierDef;
+ protected int mMultiplierLimitMin, mMultiplierLimitMax, mMultiplierLimitDef;
+ protected int mAnalogVideoStandardMin, mAnalogVideoStandardMax, mAnalogVideoStandardDef;
+ protected int mAnalogVideoLockStateMin, mAnalogVideoLockStateMax, mAnalogVideoLockStateDef;
+ // until here
+ /**
+ * the sonctructor of this class should be call within the thread that has a looper
+ * (UI thread or a thread that called Looper.prepare)
+ */
+ public UVCCamera() {
+ mNativePtr = nativeCreate();
+ mSupportedSize = null;
+ }
+
+ /**
+ * connect to a UVC camera
+ * USB permission is necessary before this method is called
+ * @param ctrlBlock
+ */
+ public synchronized void open(final UsbControlBlock ctrlBlock) {
+ int result = -2;
+ StringBuilder sb = new StringBuilder();
+ try {
+ mCtrlBlock = ctrlBlock.clone();
+ result = nativeConnect(mNativePtr,
+ mCtrlBlock.getVenderId(), mCtrlBlock.getProductId(),
+ mCtrlBlock.getFileDescriptor(),
+ mCtrlBlock.getBusNum(),
+ mCtrlBlock.getDevNum(),
+ getUSBFSName(mCtrlBlock));
+ sb.append("调用nativeConnect返回值:"+result);
+// long id_camera, int venderId, int productId, int fileDescriptor, int busNum, int devAddr, String usbfs
+ } catch (final Exception e) {
+ Log.w(TAG, e);
+ for(int i = 0; i< e.getStackTrace().length; i++){
+ sb.append(e.getStackTrace()[i].toString());
+ sb.append("\n");
+ }
+ sb.append("core message ->"+e.getLocalizedMessage());
+ result = -1;
+ }
+
+ if (result != 0) {
+ throw new UnsupportedOperationException("open failed:result=" + result+"----->" +
+ "id_camera="+mNativePtr+";venderId="+mCtrlBlock.getVenderId()
+ +";productId="+mCtrlBlock.getProductId()+";fileDescriptor="+mCtrlBlock.getFileDescriptor()
+ +";busNum="+mCtrlBlock.getBusNum()+";devAddr="+mCtrlBlock.getDevNum()
+ +";usbfs="+getUSBFSName(mCtrlBlock)+"\n"+"Exception:"+sb.toString());
+ }
+
+ if (mNativePtr != 0 && TextUtils.isEmpty(mSupportedSize)) {
+ mSupportedSize = nativeGetSupportedSize(mNativePtr);
+ }
+ nativeSetPreviewSize(mNativePtr, DEFAULT_PREVIEW_WIDTH, DEFAULT_PREVIEW_HEIGHT,
+ DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, DEFAULT_PREVIEW_MODE, DEFAULT_BANDWIDTH);
+ }
+
+ /**
+ * set status callback
+ * @param callback
+ */
+ public void setStatusCallback(final IStatusCallback callback) {
+ if (mNativePtr != 0) {
+ nativeSetStatusCallback(mNativePtr, callback);
+ }
+ }
+
+ /**
+ * set button callback
+ * @param callback
+ */
+ public void setButtonCallback(final IButtonCallback callback) {
+ if (mNativePtr != 0) {
+ nativeSetButtonCallback(mNativePtr, callback);
+ }
+ }
+
+ /**
+ * close and release UVC camera
+ */
+ public synchronized void close() {
+ stopPreview();
+ if (mNativePtr != 0) {
+ nativeRelease(mNativePtr);
+// mNativePtr = 0; // nativeDestroyを呼ぶのでここでクリアしちゃダメ
+ }
+ if (mCtrlBlock != null) {
+ mCtrlBlock.close();
+ mCtrlBlock = null;
+ }
+ mControlSupports = mProcSupports = 0;
+ mCurrentFrameFormat = -1;
+ mCurrentBandwidthFactor = 0;
+ mSupportedSize = null;
+ mCurrentSizeList = null;
+ if (DEBUG) Log.v(TAG, "close:finished");
+ }
+
+ public UsbDevice getDevice() {
+ return mCtrlBlock != null ? mCtrlBlock.getDevice() : null;
+ }
+
+ public String getDeviceName(){
+ return mCtrlBlock != null ? mCtrlBlock.getDeviceName() : null;
+ }
+
+ public UsbControlBlock getUsbControlBlock() {
+ return mCtrlBlock;
+ }
+
+ public synchronized String getSupportedSize() {
+ return !TextUtils.isEmpty(mSupportedSize) ? mSupportedSize : (mSupportedSize = nativeGetSupportedSize(mNativePtr));
+ }
+
+ public Size getPreviewSize() {
+ Size result = null;
+ final List list = getSupportedSizeList();
+ for (final Size sz: list) {
+ if ((sz.width == mCurrentWidth)
+ || (sz.height == mCurrentHeight)) {
+ result =sz;
+ break;
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ @param height
+ */
+ public void setPreviewSize(final int width, final int height) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, mCurrentFrameFormat, mCurrentBandwidthFactor);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ * @param height
+ * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ */
+ public void setPreviewSize(final int width, final int height, final int frameFormat) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, mCurrentBandwidthFactor);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ @param height
+ @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ @param bandwidth [0.0f,1.0f]
+ */
+ public void setPreviewSize(final int width, final int height, final int frameFormat, final float bandwidth) {
+ setPreviewSize(width, height, DEFAULT_PREVIEW_MIN_FPS, DEFAULT_PREVIEW_MAX_FPS, frameFormat, bandwidth);
+ }
+
+ /**
+ * Set preview size and preview mode
+ * @param width
+ * @param height
+ * @param min_fps
+ * @param max_fps
+ * @param frameFormat either FRAME_FORMAT_YUYV(0) or FRAME_FORMAT_MJPEG(1)
+ * @param bandwidthFactor
+ */
+ public void setPreviewSize(final int width, final int height, final int min_fps, final int max_fps, final int frameFormat, final float bandwidthFactor) {
+ if ((width == 0) || (height == 0))
+ throw new IllegalArgumentException("invalid preview size");
+ if (mNativePtr != 0) {
+ final int result = nativeSetPreviewSize(mNativePtr, width, height, min_fps, max_fps, frameFormat, bandwidthFactor);
+ if (result != 0)
+ throw new IllegalArgumentException("Failed to set preview size");
+ mCurrentFrameFormat = frameFormat;
+ mCurrentWidth = width;
+ mCurrentHeight = height;
+ mCurrentBandwidthFactor = bandwidthFactor;
+ }
+ }
+
+ public List getSupportedSizeList() {
+ final int type = (mCurrentFrameFormat > 0) ? 6 : 4;
+ return getSupportedSize(type, mSupportedSize);
+ }
+
+ public static List getSupportedSize(final int type, final String supportedSize) {
+ final List result = new ArrayList();
+ if (!TextUtils.isEmpty(supportedSize))
+ try {
+ final JSONObject json = new JSONObject(supportedSize);
+ final JSONArray formats = json.getJSONArray("formats");
+ final int format_nums = formats.length();
+ for (int i = 0; i < format_nums; i++) {
+ final JSONObject format = formats.getJSONObject(i);
+ if(format.has("type") && format.has("size")) {
+ final int format_type = format.getInt("type");
+ if ((format_type == type) || (type == -1)) {
+ addSize(format, format_type, 0, result);
+ }
+ }
+ }
+ } catch (final JSONException e) {
+ e.printStackTrace();
+ }
+ return result;
+ }
+
+ private static final void addSize(final JSONObject format, final int formatType, final int frameType, final List size_list) throws JSONException {
+ final JSONArray size = format.getJSONArray("size");
+ final int size_nums = size.length();
+ for (int j = 0; j < size_nums; j++) {
+ final String[] sz = size.getString(j).split("x");
+ try {
+ size_list.add(new Size(formatType, frameType, j, Integer.parseInt(sz[0]), Integer.parseInt(sz[1])));
+ } catch (final Exception e) {
+ break;
+ }
+ }
+ }
+
+ /**
+ * set preview surface with SurfaceHolder
+ * you can use SurfaceHolder came from SurfaceView/GLSurfaceView
+ * @param holder
+ */
+ public synchronized void setPreviewDisplay(final SurfaceHolder holder) {
+ nativeSetPreviewDisplay(mNativePtr, holder.getSurface());
+ }
+
+ /**
+ * set preview surface with SurfaceTexture.
+ * this method require API >= 14
+ * @param texture
+ */
+ public synchronized void setPreviewTexture(final SurfaceTexture texture) { // API >= 11
+ final Surface surface = new Surface(texture); // XXX API >= 14
+ nativeSetPreviewDisplay(mNativePtr, surface);
+ }
+
+ /**
+ * set preview surface with Surface
+ * @param surface
+ */
+ public synchronized void setPreviewDisplay(final Surface surface) {
+ nativeSetPreviewDisplay(mNativePtr, surface);
+ }
+
+ /**
+ * set frame callback
+ * @param callback
+ * @param pixelFormat
+ */
+ public void setFrameCallback(final IFrameCallback callback, final int pixelFormat) {
+ if (mNativePtr != 0) {
+ nativeSetFrameCallback(mNativePtr, callback, pixelFormat);
+ }
+ }
+
+ /**
+ * start preview
+ */
+ public synchronized void startPreview() {
+ if (mCtrlBlock != null) {
+ nativeStartPreview(mNativePtr);
+ }
+ }
+
+ /**
+ * stop preview
+ */
+ public synchronized void stopPreview() {
+ setFrameCallback(null, 0);
+ if (mCtrlBlock != null) {
+ nativeStopPreview(mNativePtr);
+ }
+ }
+
+ /**
+ * destroy UVCCamera object
+ */
+ public synchronized void destroy() {
+ close();
+ if (mNativePtr != 0) {
+ nativeDestroy(mNativePtr);
+ mNativePtr = 0;
+ }
+ }
+
+ // wrong result may return when you call this just after camera open.
+ // it is better to wait several hundreads millseconds.
+ public boolean checkSupportFlag(final long flag) {
+ updateCameraParams();
+ if ((flag & 0x80000000) == 0x80000000)
+ return ((mProcSupports & flag) == (flag & 0x7ffffffF));
+ else
+ return (mControlSupports & flag) == flag;
+ }
+
+//================================================================================
+ public synchronized void setAutoFocus(final boolean autoFocus) {
+ if (mNativePtr != 0) {
+ nativeSetAutoFocus(mNativePtr, autoFocus);
+ }
+ }
+
+ public synchronized boolean getAutoFocus() {
+ boolean result = true;
+ if (mNativePtr != 0) {
+ result = nativeGetAutoFocus(mNativePtr) > 0;
+ }
+ return result;
+ }
+//================================================================================
+ /**
+ * @param focus [%]
+ */
+ public synchronized void setFocus(final int focus) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mFocusMax - mFocusMin);
+ if (range > 0)
+ nativeSetFocus(mNativePtr, (int)(focus / 100.f * range) + mFocusMin);
+ }
+ }
+
+ /**
+ * @param focus_abs
+ * @return focus[%]
+ */
+ public synchronized int getFocus(final int focus_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateFocusLimit(mNativePtr);
+ final float range = Math.abs(mFocusMax - mFocusMin);
+ if (range > 0) {
+ result = (int)((focus_abs - mFocusMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return focus[%]
+ */
+ public synchronized int getFocus() {
+ return getFocus(nativeGetFocus(mNativePtr));
+ }
+
+ public synchronized void resetFocus() {
+ if (mNativePtr != 0) {
+ nativeSetFocus(mNativePtr, mFocusDef);
+ }
+ }
+
+//================================================================================
+ public synchronized void setAutoWhiteBlance(final boolean autoWhiteBlance) {
+ if (mNativePtr != 0) {
+ nativeSetAutoWhiteBlance(mNativePtr, autoWhiteBlance);
+ }
+ }
+
+ public synchronized boolean getAutoWhiteBlance() {
+ boolean result = true;
+ if (mNativePtr != 0) {
+ result = nativeGetAutoWhiteBlance(mNativePtr) > 0;
+ }
+ return result;
+ }
+
+//================================================================================
+ /**
+ * @param whiteBlance [%]
+ */
+ public synchronized void setWhiteBlance(final int whiteBlance) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin);
+ if (range > 0)
+ nativeSetWhiteBlance(mNativePtr, (int)(whiteBlance / 100.f * range) + mWhiteBlanceMin);
+ }
+ }
+
+ /**
+ * @param whiteBlance_abs
+ * @return whiteBlance[%]
+ */
+ public synchronized int getWhiteBlance(final int whiteBlance_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateWhiteBlanceLimit(mNativePtr);
+ final float range = Math.abs(mWhiteBlanceMax - mWhiteBlanceMin);
+ if (range > 0) {
+ result = (int)((whiteBlance_abs - mWhiteBlanceMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return white blance[%]
+ */
+ public synchronized int getWhiteBlance() {
+ return getFocus(nativeGetWhiteBlance(mNativePtr));
+ }
+
+ public synchronized void resetWhiteBlance() {
+ if (mNativePtr != 0) {
+ nativeSetWhiteBlance(mNativePtr, mWhiteBlanceDef);
+ }
+ }
+//================================================================================
+ /**
+ * @param brightness [%]
+ */
+ public synchronized void setBrightness(final int brightness) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mBrightnessMax - mBrightnessMin);
+ if (range > 0)
+ nativeSetBrightness(mNativePtr, (int)(brightness / 100.f * range) + mBrightnessMin);
+ }
+ }
+
+ /**
+ * @param brightness_abs
+ * @return brightness[%]
+ */
+ public synchronized int getBrightness(final int brightness_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateBrightnessLimit(mNativePtr);
+ final float range = Math.abs(mBrightnessMax - mBrightnessMin);
+ if (range > 0) {
+ result = (int)((brightness_abs - mBrightnessMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return brightness[%]
+ */
+ public synchronized int getBrightness() {
+ return getBrightness(nativeGetBrightness(mNativePtr));
+ }
+
+ public synchronized void resetBrightness() {
+ if (mNativePtr != 0) {
+ nativeSetBrightness(mNativePtr, mBrightnessDef);
+ }
+ }
+
+//================================================================================
+ /**
+ * @param contrast [%]
+ */
+ public synchronized void setContrast(final int contrast) {
+ if (mNativePtr != 0) {
+ nativeUpdateContrastLimit(mNativePtr);
+ final float range = Math.abs(mContrastMax - mContrastMin);
+ if (range > 0)
+ nativeSetContrast(mNativePtr, (int)(contrast / 100.f * range) + mContrastMin);
+ }
+ }
+
+ /**
+ * @param contrast_abs
+ * @return contrast[%]
+ */
+ public synchronized int getContrast(final int contrast_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mContrastMax - mContrastMin);
+ if (range > 0) {
+ result = (int)((contrast_abs - mContrastMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return contrast[%]
+ */
+ public synchronized int getContrast() {
+ return getContrast(nativeGetContrast(mNativePtr));
+ }
+
+ public synchronized void resetContrast() {
+ if (mNativePtr != 0) {
+ nativeSetContrast(mNativePtr, mContrastDef);
+ }
+ }
+
+//================================================================================
+ /**
+ * @param sharpness [%]
+ */
+ public synchronized void setSharpness(final int sharpness) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mSharpnessMax - mSharpnessMin);
+ if (range > 0)
+ nativeSetSharpness(mNativePtr, (int)(sharpness / 100.f * range) + mSharpnessMin);
+ }
+ }
+
+ /**
+ * @param sharpness_abs
+ * @return sharpness[%]
+ */
+ public synchronized int getSharpness(final int sharpness_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateSharpnessLimit(mNativePtr);
+ final float range = Math.abs(mSharpnessMax - mSharpnessMin);
+ if (range > 0) {
+ result = (int)((sharpness_abs - mSharpnessMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return sharpness[%]
+ */
+ public synchronized int getSharpness() {
+ return getSharpness(nativeGetSharpness(mNativePtr));
+ }
+
+ public synchronized void resetSharpness() {
+ if (mNativePtr != 0) {
+ nativeSetSharpness(mNativePtr, mSharpnessDef);
+ }
+ }
+//================================================================================
+ /**
+ * @param gain [%]
+ */
+ public synchronized void setGain(final int gain) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mGainMax - mGainMin);
+ if (range > 0)
+ nativeSetGain(mNativePtr, (int)(gain / 100.f * range) + mGainMin);
+ }
+ }
+
+ /**
+ * @param gain_abs
+ * @return gain[%]
+ */
+ public synchronized int getGain(final int gain_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateGainLimit(mNativePtr);
+ final float range = Math.abs(mGainMax - mGainMin);
+ if (range > 0) {
+ result = (int)((gain_abs - mGainMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return gain[%]
+ */
+ public synchronized int getGain() {
+ return getGain(nativeGetGain(mNativePtr));
+ }
+
+ public synchronized void resetGain() {
+ if (mNativePtr != 0) {
+ nativeSetGain(mNativePtr, mGainDef);
+ }
+ }
+
+//================================================================================
+ /**
+ * @param gamma [%]
+ */
+ public synchronized void setGamma(final int gamma) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mGammaMax - mGammaMin);
+ if (range > 0)
+ nativeSetGamma(mNativePtr, (int)(gamma / 100.f * range) + mGammaMin);
+ }
+ }
+
+ /**
+ * @param gamma_abs
+ * @return gamma[%]
+ */
+ public synchronized int getGamma(final int gamma_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateGammaLimit(mNativePtr);
+ final float range = Math.abs(mGammaMax - mGammaMin);
+ if (range > 0) {
+ result = (int)((gamma_abs - mGammaMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return gamma[%]
+ */
+ public synchronized int getGamma() {
+ return getGamma(nativeGetGamma(mNativePtr));
+ }
+
+ public synchronized void resetGamma() {
+ if (mNativePtr != 0) {
+ nativeSetGamma(mNativePtr, mGammaDef);
+ }
+ }
+
+//================================================================================
+ /**
+ * @param saturation [%]
+ */
+ public synchronized void setSaturation(final int saturation) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mSaturationMax - mSaturationMin);
+ if (range > 0)
+ nativeSetSaturation(mNativePtr, (int)(saturation / 100.f * range) + mSaturationMin);
+ }
+ }
+
+ /**
+ * @param saturation_abs
+ * @return saturation[%]
+ */
+ public synchronized int getSaturation(final int saturation_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateSaturationLimit(mNativePtr);
+ final float range = Math.abs(mSaturationMax - mSaturationMin);
+ if (range > 0) {
+ result = (int)((saturation_abs - mSaturationMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return saturation[%]
+ */
+ public synchronized int getSaturation() {
+ return getSaturation(nativeGetSaturation(mNativePtr));
+ }
+
+ public synchronized void resetSaturation() {
+ if (mNativePtr != 0) {
+ nativeSetSaturation(mNativePtr, mSaturationDef);
+ }
+ }
+//================================================================================
+ /**
+ * @param hue [%]
+ */
+ public synchronized void setHue(final int hue) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mHueMax - mHueMin);
+ if (range > 0)
+ nativeSetHue(mNativePtr, (int)(hue / 100.f * range) + mHueMin);
+ }
+ }
+
+ /**
+ * @param hue_abs
+ * @return hue[%]
+ */
+ public synchronized int getHue(final int hue_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateHueLimit(mNativePtr);
+ final float range = Math.abs(mHueMax - mHueMin);
+ if (range > 0) {
+ result = (int)((hue_abs - mHueMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return hue[%]
+ */
+ public synchronized int getHue() {
+ return getHue(nativeGetHue(mNativePtr));
+ }
+
+ public synchronized void resetHue() {
+ if (mNativePtr != 0) {
+ nativeSetHue(mNativePtr, mSaturationDef);
+ }
+ }
+
+//================================================================================
+ public void setPowerlineFrequency(final int frequency) {
+ if (mNativePtr != 0)
+ nativeSetPowerlineFrequency(mNativePtr, frequency);
+ }
+
+ public int getPowerlineFrequency() {
+ return nativeGetPowerlineFrequency(mNativePtr);
+ }
+
+//================================================================================
+ /**
+ * this may not work well with some combination of camera and device
+ * @param zoom [%]
+ */
+ public synchronized void setZoom(final int zoom) {
+ if (mNativePtr != 0) {
+ final float range = Math.abs(mZoomMax - mZoomMin);
+ if (range > 0) {
+ final int z = (int)(zoom / 100.f * range) + mZoomMin;
+// Log.d(TAG, "setZoom:zoom=" + zoom + " ,value=" + z);
+ nativeSetZoom(mNativePtr, z);
+ }
+ }
+ }
+
+ /**
+ * @param zoom_abs
+ * @return zoom[%]
+ */
+ public synchronized int getZoom(final int zoom_abs) {
+ int result = 0;
+ if (mNativePtr != 0) {
+ nativeUpdateZoomLimit(mNativePtr);
+ final float range = Math.abs(mZoomMax - mZoomMin);
+ if (range > 0) {
+ result = (int)((zoom_abs - mZoomMin) * 100.f / range);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @return zoom[%]
+ */
+ public synchronized int getZoom() {
+ return getZoom(nativeGetZoom(mNativePtr));
+ }
+
+ public synchronized void resetZoom() {
+ if (mNativePtr != 0) {
+ nativeSetZoom(mNativePtr, mZoomDef);
+ }
+ }
+
+//================================================================================
+ public synchronized void updateCameraParams() {
+ if (mNativePtr != 0) {
+ if ((mControlSupports == 0) || (mProcSupports == 0)) {
+ // サポートしている機能フラグを取得
+ if (mControlSupports == 0)
+ mControlSupports = nativeGetCtrlSupports(mNativePtr);
+ if (mProcSupports == 0)
+ mProcSupports = nativeGetProcSupports(mNativePtr);
+ // 設定値を取得
+ if ((mControlSupports != 0) && (mProcSupports != 0)) {
+ nativeUpdateBrightnessLimit(mNativePtr);
+ nativeUpdateContrastLimit(mNativePtr);
+ nativeUpdateSharpnessLimit(mNativePtr);
+ nativeUpdateGainLimit(mNativePtr);
+ nativeUpdateGammaLimit(mNativePtr);
+ nativeUpdateSaturationLimit(mNativePtr);
+ nativeUpdateHueLimit(mNativePtr);
+ nativeUpdateZoomLimit(mNativePtr);
+ nativeUpdateWhiteBlanceLimit(mNativePtr);
+ nativeUpdateFocusLimit(mNativePtr);
+ }
+ if (DEBUG) {
+ dumpControls(mControlSupports);
+ dumpProc(mProcSupports);
+ Log.v(TAG, String.format("Brightness:min=%d,max=%d,def=%d", mBrightnessMin, mBrightnessMax, mBrightnessDef));
+ Log.v(TAG, String.format("Contrast:min=%d,max=%d,def=%d", mContrastMin, mContrastMax, mContrastDef));
+ Log.v(TAG, String.format("Sharpness:min=%d,max=%d,def=%d", mSharpnessMin, mSharpnessMax, mSharpnessDef));
+ Log.v(TAG, String.format("Gain:min=%d,max=%d,def=%d", mGainMin, mGainMax, mGainDef));
+ Log.v(TAG, String.format("Gamma:min=%d,max=%d,def=%d", mGammaMin, mGammaMax, mGammaDef));
+ Log.v(TAG, String.format("Saturation:min=%d,max=%d,def=%d", mSaturationMin, mSaturationMax, mSaturationDef));
+ Log.v(TAG, String.format("Hue:min=%d,max=%d,def=%d", mHueMin, mHueMax, mHueDef));
+ Log.v(TAG, String.format("Zoom:min=%d,max=%d,def=%d", mZoomMin, mZoomMax, mZoomDef));
+ Log.v(TAG, String.format("WhiteBlance:min=%d,max=%d,def=%d", mWhiteBlanceMin, mWhiteBlanceMax, mWhiteBlanceDef));
+ Log.v(TAG, String.format("Focus:min=%d,max=%d,def=%d", mFocusMin, mFocusMax, mFocusDef));
+ }
+ }
+ } else {
+ mControlSupports = mProcSupports = 0;
+ }
+ }
+
+ private static final String[] SUPPORTS_CTRL = {
+ "D0: Scanning Mode",
+ "D1: Auto-Exposure Mode",
+ "D2: Auto-Exposure Priority",
+ "D3: Exposure Time (Absolute)",
+ "D4: Exposure Time (Relative)",
+ "D5: Focus (Absolute)",
+ "D6: Focus (Relative)",
+ "D7: Iris (Absolute)",
+ "D8: Iris (Relative)",
+ "D9: Zoom (Absolute)",
+ "D10: Zoom (Relative)",
+ "D11: PanTilt (Absolute)",
+ "D12: PanTilt (Relative)",
+ "D13: Roll (Absolute)",
+ "D14: Roll (Relative)",
+ "D15: Reserved",
+ "D16: Reserved",
+ "D17: Focus, Auto",
+ "D18: Privacy",
+ "D19: Focus, Simple",
+ "D20: Window",
+ "D21: Region of Interest",
+ "D22: Reserved, set to zero",
+ "D23: Reserved, set to zero",
+ };
+
+ private static final String[] SUPPORTS_PROC = {
+ "D0: Brightness",
+ "D1: Contrast",
+ "D2: Hue",
+ "D3: Saturation",
+ "D4: Sharpness",
+ "D5: Gamma",
+ "D6: White Balance Temperature",
+ "D7: White Balance Component",
+ "D8: Backlight Compensation",
+ "D9: Gain",
+ "D10: Power Line Frequency",
+ "D11: Hue, Auto",
+ "D12: White Balance Temperature, Auto",
+ "D13: White Balance Component, Auto",
+ "D14: Digital Multiplier",
+ "D15: Digital Multiplier Limit",
+ "D16: Analog Video Standard",
+ "D17: Analog Video Lock Status",
+ "D18: Contrast, Auto",
+ "D19: Reserved. Set to zero",
+ "D20: Reserved. Set to zero",
+ "D21: Reserved. Set to zero",
+ "D22: Reserved. Set to zero",
+ "D23: Reserved. Set to zero",
+ };
+
+ private static final void dumpControls(final long controlSupports) {
+ Log.i(TAG, String.format("controlSupports=%x", controlSupports));
+ for (int i = 0; i < SUPPORTS_CTRL.length; i++) {
+ Log.i(TAG, SUPPORTS_CTRL[i] + ((controlSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled"));
+ }
+ }
+
+ private static final void dumpProc(final long procSupports) {
+ Log.i(TAG, String.format("procSupports=%x", procSupports));
+ for (int i = 0; i < SUPPORTS_PROC.length; i++) {
+ Log.i(TAG, SUPPORTS_PROC[i] + ((procSupports & (0x1 << i)) != 0 ? "=enabled" : "=disabled"));
+ }
+ }
+
+ private final String getUSBFSName(final UsbControlBlock ctrlBlock) {
+ String result = null;
+ final String name = ctrlBlock.getDeviceName();
+ final String[] v = !TextUtils.isEmpty(name) ? name.split("/") : null;
+ if ((v != null) && (v.length > 2)) {
+ final StringBuilder sb = new StringBuilder(v[0]);
+ for (int i = 1; i < v.length - 2; i++)
+ sb.append("/").append(v[i]);
+ result = sb.toString();
+ }
+ if (TextUtils.isEmpty(result)) {
+ Log.w(TAG, "failed to get USBFS path, try to use default path:" + name);
+ result = DEFAULT_USBFS;
+ }
+ return result;
+ }
+
+ // #nativeCreate and #nativeDestroy are not static methods.
+ private final native long nativeCreate();
+ private final native void nativeDestroy(final long id_camera);
+
+ private final native int nativeConnect(long id_camera, int venderId, int productId, int fileDescriptor, int busNum, int devAddr, String usbfs);
+ private static final native int nativeRelease(final long id_camera);
+
+ private static final native int nativeSetStatusCallback(final long mNativePtr, final IStatusCallback callback);
+ private static final native int nativeSetButtonCallback(final long mNativePtr, final IButtonCallback callback);
+
+ private static final native int nativeSetPreviewSize(final long id_camera, final int width, final int height, final int min_fps, final int max_fps, final int mode, final float bandwidth);
+ private static final native String nativeGetSupportedSize(final long id_camera);
+ private static final native int nativeStartPreview(final long id_camera);
+ private static final native int nativeStopPreview(final long id_camera);
+ private static final native int nativeSetPreviewDisplay(final long id_camera, final Surface surface);
+ private static final native int nativeSetFrameCallback(final long mNativePtr, final IFrameCallback callback, final int pixelFormat);
+
+//**********************************************************************
+ /**
+ * start movie capturing(this should call while previewing)
+ * @param surface
+ */
+ public void startCapture(final Surface surface) {
+ if (mCtrlBlock != null && surface != null) {
+ nativeSetCaptureDisplay(mNativePtr, surface);
+ } else
+ throw new NullPointerException("startCapture");
+ }
+
+ /**
+ * stop movie capturing
+ */
+ public void stopCapture() {
+ if (mCtrlBlock != null) {
+ nativeSetCaptureDisplay(mNativePtr, null);
+ }
+ }
+ private static final native int nativeSetCaptureDisplay(final long id_camera, final Surface surface);
+
+ private static final native long nativeGetCtrlSupports(final long id_camera);
+ private static final native long nativeGetProcSupports(final long id_camera);
+
+ private final native int nativeUpdateScanningModeLimit(final long id_camera);
+ private static final native int nativeSetScanningMode(final long id_camera, final int scanning_mode);
+ private static final native int nativeGetScanningMode(final long id_camera);
+
+ private final native int nativeUpdateExposureModeLimit(final long id_camera);
+ private static final native int nativeSetExposureMode(final long id_camera, final int exposureMode);
+ private static final native int nativeGetExposureMode(final long id_camera);
+
+ private final native int nativeUpdateExposurePriorityLimit(final long id_camera);
+ private static final native int nativeSetExposurePriority(final long id_camera, final int priority);
+ private static final native int nativeGetExposurePriority(final long id_camera);
+
+ private final native int nativeUpdateExposureLimit(final long id_camera);
+ private static final native int nativeSetExposure(final long id_camera, final int exposure);
+ private static final native int nativeGetExposure(final long id_camera);
+
+ private final native int nativeUpdateExposureRelLimit(final long id_camera);
+ private static final native int nativeSetExposureRel(final long id_camera, final int exposure_rel);
+ private static final native int nativeGetExposureRel(final long id_camera);
+
+ private final native int nativeUpdateAutoFocusLimit(final long id_camera);
+ private static final native int nativeSetAutoFocus(final long id_camera, final boolean autofocus);
+ private static final native int nativeGetAutoFocus(final long id_camera);
+
+ private final native int nativeUpdateFocusLimit(final long id_camera);
+ private static final native int nativeSetFocus(final long id_camera, final int focus);
+ private static final native int nativeGetFocus(final long id_camera);
+
+ private final native int nativeUpdateFocusRelLimit(final long id_camera);
+ private static final native int nativeSetFocusRel(final long id_camera, final int focus_rel);
+ private static final native int nativeGetFocusRel(final long id_camera);
+
+ private final native int nativeUpdateIrisLimit(final long id_camera);
+ private static final native int nativeSetIris(final long id_camera, final int iris);
+ private static final native int nativeGetIris(final long id_camera);
+
+ private final native int nativeUpdateIrisRelLimit(final long id_camera);
+ private static final native int nativeSetIrisRel(final long id_camera, final int iris_rel);
+ private static final native int nativeGetIrisRel(final long id_camera);
+
+ private final native int nativeUpdatePanLimit(final long id_camera);
+ private static final native int nativeSetPan(final long id_camera, final int pan);
+ private static final native int nativeGetPan(final long id_camera);
+
+ private final native int nativeUpdatePanRelLimit(final long id_camera);
+ private static final native int nativeSetPanRel(final long id_camera, final int pan_rel);
+ private static final native int nativeGetPanRel(final long id_camera);
+
+ private final native int nativeUpdateTiltLimit(final long id_camera);
+ private static final native int nativeSetTilt(final long id_camera, final int tilt);
+ private static final native int nativeGetTilt(final long id_camera);
+
+ private final native int nativeUpdateTiltRelLimit(final long id_camera);
+ private static final native int nativeSetTiltRel(final long id_camera, final int tilt_rel);
+ private static final native int nativeGetTiltRel(final long id_camera);
+
+ private final native int nativeUpdateRollLimit(final long id_camera);
+ private static final native int nativeSetRoll(final long id_camera, final int roll);
+ private static final native int nativeGetRoll(final long id_camera);
+
+ private final native int nativeUpdateRollRelLimit(final long id_camera);
+ private static final native int nativeSetRollRel(final long id_camera, final int roll_rel);
+ private static final native int nativeGetRollRel(final long id_camera);
+
+ private final native int nativeUpdateAutoWhiteBlanceLimit(final long id_camera);
+ private static final native int nativeSetAutoWhiteBlance(final long id_camera, final boolean autoWhiteBlance);
+ private static final native int nativeGetAutoWhiteBlance(final long id_camera);
+
+ private final native int nativeUpdateAutoWhiteBlanceCompoLimit(final long id_camera);
+ private static final native int nativeSetAutoWhiteBlanceCompo(final long id_camera, final boolean autoWhiteBlanceCompo);
+ private static final native int nativeGetAutoWhiteBlanceCompo(final long id_camera);
+
+ private final native int nativeUpdateWhiteBlanceLimit(final long id_camera);
+ private static final native int nativeSetWhiteBlance(final long id_camera, final int whiteBlance);
+ private static final native int nativeGetWhiteBlance(final long id_camera);
+
+ private final native int nativeUpdateWhiteBlanceCompoLimit(final long id_camera);
+ private static final native int nativeSetWhiteBlanceCompo(final long id_camera, final int whiteBlance_compo);
+ private static final native int nativeGetWhiteBlanceCompo(final long id_camera);
+
+ private final native int nativeUpdateBacklightCompLimit(final long id_camera);
+ private static final native int nativeSetBacklightComp(final long id_camera, final int backlight_comp);
+ private static final native int nativeGetBacklightComp(final long id_camera);
+
+ private final native int nativeUpdateBrightnessLimit(final long id_camera);
+ private static final native int nativeSetBrightness(final long id_camera, final int brightness);
+ private static final native int nativeGetBrightness(final long id_camera);
+
+ private final native int nativeUpdateContrastLimit(final long id_camera);
+ private static final native int nativeSetContrast(final long id_camera, final int contrast);
+ private static final native int nativeGetContrast(final long id_camera);
+
+ private final native int nativeUpdateAutoContrastLimit(final long id_camera);
+ private static final native int nativeSetAutoContrast(final long id_camera, final boolean autocontrast);
+ private static final native int nativeGetAutoContrast(final long id_camera);
+
+ private final native int nativeUpdateSharpnessLimit(final long id_camera);
+ private static final native int nativeSetSharpness(final long id_camera, final int sharpness);
+ private static final native int nativeGetSharpness(final long id_camera);
+
+ private final native int nativeUpdateGainLimit(final long id_camera);
+ private static final native int nativeSetGain(final long id_camera, final int gain);
+ private static final native int nativeGetGain(final long id_camera);
+
+ private final native int nativeUpdateGammaLimit(final long id_camera);
+ private static final native int nativeSetGamma(final long id_camera, final int gamma);
+ private static final native int nativeGetGamma(final long id_camera);
+
+ private final native int nativeUpdateSaturationLimit(final long id_camera);
+ private static final native int nativeSetSaturation(final long id_camera, final int saturation);
+ private static final native int nativeGetSaturation(final long id_camera);
+
+ private final native int nativeUpdateHueLimit(final long id_camera);
+ private static final native int nativeSetHue(final long id_camera, final int hue);
+ private static final native int nativeGetHue(final long id_camera);
+
+ private final native int nativeUpdateAutoHueLimit(final long id_camera);
+ private static final native int nativeSetAutoHue(final long id_camera, final boolean autohue);
+ private static final native int nativeGetAutoHue(final long id_camera);
+
+ private final native int nativeUpdatePowerlineFrequencyLimit(final long id_camera);
+ private static final native int nativeSetPowerlineFrequency(final long id_camera, final int frequency);
+ private static final native int nativeGetPowerlineFrequency(final long id_camera);
+
+ private final native int nativeUpdateZoomLimit(final long id_camera);
+ private static final native int nativeSetZoom(final long id_camera, final int zoom);
+ private static final native int nativeGetZoom(final long id_camera);
+
+ private final native int nativeUpdateZoomRelLimit(final long id_camera);
+ private static final native int nativeSetZoomRel(final long id_camera, final int zoom_rel);
+ private static final native int nativeGetZoomRel(final long id_camera);
+
+ private final native int nativeUpdateDigitalMultiplierLimit(final long id_camera);
+ private static final native int nativeSetDigitalMultiplier(final long id_camera, final int multiplier);
+ private static final native int nativeGetDigitalMultiplier(final long id_camera);
+
+ private final native int nativeUpdateDigitalMultiplierLimitLimit(final long id_camera);
+ private static final native int nativeSetDigitalMultiplierLimit(final long id_camera, final int multiplier_limit);
+ private static final native int nativeGetDigitalMultiplierLimit(final long id_camera);
+
+ private final native int nativeUpdateAnalogVideoStandardLimit(final long id_camera);
+ private static final native int nativeSetAnalogVideoStandard(final long id_camera, final int standard);
+ private static final native int nativeGetAnalogVideoStandard(final long id_camera);
+
+ private final native int nativeUpdateAnalogVideoLockStateLimit(final long id_camera);
+ private static final native int nativeSetAnalogVideoLoackState(final long id_camera, final int state);
+ private static final native int nativeGetAnalogVideoLoackState(final long id_camera);
+
+ private final native int nativeUpdatePrivacyLimit(final long id_camera);
+ private static final native int nativeSetPrivacy(final long id_camera, final boolean privacy);
+ private static final native int nativeGetPrivacy(final long id_camera);
+}
diff --git a/libusbcamera/src/main/jniLibs/armeabi-v7a/libUVCCamera.so b/libusbcamera/src/main/jniLibs/armeabi-v7a/libUVCCamera.so
new file mode 100644
index 0000000..a88d6f8
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi-v7a/libUVCCamera.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi-v7a/libjpeg-turbo1500.so b/libusbcamera/src/main/jniLibs/armeabi-v7a/libjpeg-turbo1500.so
new file mode 100644
index 0000000..65b46b0
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi-v7a/libjpeg-turbo1500.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi-v7a/libusb100.so b/libusbcamera/src/main/jniLibs/armeabi-v7a/libusb100.so
new file mode 100644
index 0000000..b4ba0a0
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi-v7a/libusb100.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi-v7a/libuvc.so b/libusbcamera/src/main/jniLibs/armeabi-v7a/libuvc.so
new file mode 100644
index 0000000..37455fe
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi-v7a/libuvc.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi/libUVCCamera.so b/libusbcamera/src/main/jniLibs/armeabi/libUVCCamera.so
new file mode 100644
index 0000000..ba285a7
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi/libUVCCamera.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi/libjpeg-turbo1500.so b/libusbcamera/src/main/jniLibs/armeabi/libjpeg-turbo1500.so
new file mode 100644
index 0000000..348ab5f
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi/libjpeg-turbo1500.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi/libusb100.so b/libusbcamera/src/main/jniLibs/armeabi/libusb100.so
new file mode 100644
index 0000000..63ce300
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi/libusb100.so differ
diff --git a/libusbcamera/src/main/jniLibs/armeabi/libuvc.so b/libusbcamera/src/main/jniLibs/armeabi/libuvc.so
new file mode 100644
index 0000000..f92428a
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/armeabi/libuvc.so differ
diff --git a/libusbcamera/src/main/jniLibs/mips/libUVCCamera.so b/libusbcamera/src/main/jniLibs/mips/libUVCCamera.so
new file mode 100644
index 0000000..cb25100
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/mips/libUVCCamera.so differ
diff --git a/libusbcamera/src/main/jniLibs/mips/libjpeg-turbo1500.so b/libusbcamera/src/main/jniLibs/mips/libjpeg-turbo1500.so
new file mode 100644
index 0000000..af82255
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/mips/libjpeg-turbo1500.so differ
diff --git a/libusbcamera/src/main/jniLibs/mips/libusb100.so b/libusbcamera/src/main/jniLibs/mips/libusb100.so
new file mode 100644
index 0000000..730dd5f
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/mips/libusb100.so differ
diff --git a/libusbcamera/src/main/jniLibs/mips/libuvc.so b/libusbcamera/src/main/jniLibs/mips/libuvc.so
new file mode 100644
index 0000000..9958d95
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/mips/libuvc.so differ
diff --git a/libusbcamera/src/main/jniLibs/x86/libUVCCamera.so b/libusbcamera/src/main/jniLibs/x86/libUVCCamera.so
new file mode 100644
index 0000000..d5f9034
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/x86/libUVCCamera.so differ
diff --git a/libusbcamera/src/main/jniLibs/x86/libjpeg-turbo1500.so b/libusbcamera/src/main/jniLibs/x86/libjpeg-turbo1500.so
new file mode 100644
index 0000000..46c87b5
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/x86/libjpeg-turbo1500.so differ
diff --git a/libusbcamera/src/main/jniLibs/x86/libusb100.so b/libusbcamera/src/main/jniLibs/x86/libusb100.so
new file mode 100644
index 0000000..12f636e
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/x86/libusb100.so differ
diff --git a/libusbcamera/src/main/jniLibs/x86/libuvc.so b/libusbcamera/src/main/jniLibs/x86/libuvc.so
new file mode 100644
index 0000000..6889634
Binary files /dev/null and b/libusbcamera/src/main/jniLibs/x86/libuvc.so differ
diff --git a/libusbcamera/src/main/res/layout/dialog_camera.xml b/libusbcamera/src/main/res/layout/dialog_camera.xml
new file mode 100644
index 0000000..3d25643
--- /dev/null
+++ b/libusbcamera/src/main/res/layout/dialog_camera.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libusbcamera/src/main/res/layout/listitem_device.xml b/libusbcamera/src/main/res/layout/listitem_device.xml
new file mode 100644
index 0000000..903baff
--- /dev/null
+++ b/libusbcamera/src/main/res/layout/listitem_device.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/libusbcamera/src/main/res/values/dimens.xml b/libusbcamera/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..997fea8
--- /dev/null
+++ b/libusbcamera/src/main/res/values/dimens.xml
@@ -0,0 +1,30 @@
+
+
+
+ 16dp
+ 16dp
+ 48dp
+ 18sp
+ 32dp
+
diff --git a/libusbcamera/src/main/res/values/strings.xml b/libusbcamera/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c7a70e8
--- /dev/null
+++ b/libusbcamera/src/main/res/values/strings.xml
@@ -0,0 +1,7 @@
+
+ libusbcamera
+ 请选择USB摄像头
+ 刷新
+ Camera
+ 未搜索到可用USB Camera设备
+
diff --git a/libusbcamera/src/main/res/xml/device_filter.xml b/libusbcamera/src/main/res/xml/device_filter.xml
new file mode 100644
index 0000000..da573ff
--- /dev/null
+++ b/libusbcamera/src/main/res/xml/device_filter.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/libusbcamera/src/test/java/com/jiangdg/libusbcamera/ExampleUnitTest.java b/libusbcamera/src/test/java/com/jiangdg/libusbcamera/ExampleUnitTest.java
new file mode 100644
index 0000000..cea7b86
--- /dev/null
+++ b/libusbcamera/src/test/java/com/jiangdg/libusbcamera/ExampleUnitTest.java
@@ -0,0 +1,17 @@
+package com.jiangdg.libusbcamera;
+
+import org.junit.Test;
+
+import static org.junit.Assert.*;
+
+/**
+ * Example local unit test, which will execute on the development machine (host).
+ *
+ * @see Testing documentation
+ */
+public class ExampleUnitTest {
+ @Test
+ public void addition_isCorrect() throws Exception {
+ assertEquals(4, 2 + 2);
+ }
+}
\ No newline at end of file
diff --git a/local.properties b/local.properties
new file mode 100644
index 0000000..3ec53d5
--- /dev/null
+++ b/local.properties
@@ -0,0 +1,10 @@
+## This file is automatically generated by Android Studio.
+# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
+#
+# This file should *NOT* be checked into Version Control Systems,
+# as it contains information specific to your local configuration.
+#
+# Location of the SDK. This is only used by Gradle.
+# For customization when using a Version Control System, please read the
+# header note.
+sdk.dir=E\:\\Environment\\android-sdk-windows
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..24630ca
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1 @@
+include ':app', ':libusbcamera'