[
  {
    "path": ".gitignore",
    "content": ".gradle\n/local.properties\n/.idea/workspace.xml\n/.idea/libraries\n.DS_Store\n/build\n/captures\n"
  },
  {
    "path": ".idea/.name",
    "content": "RxFace"
  },
  {
    "path": ".idea/compiler.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"CompilerConfiguration\">\n    <option name=\"DEFAULT_COMPILER\" value=\"Javac\" />\n    <resourceExtensions />\n    <wildcardResourcePatterns>\n      <entry name=\"!?*.java\" />\n      <entry name=\"!?*.form\" />\n      <entry name=\"!?*.class\" />\n      <entry name=\"!?*.groovy\" />\n      <entry name=\"!?*.scala\" />\n      <entry name=\"!?*.flex\" />\n      <entry name=\"!?*.kt\" />\n      <entry name=\"!?*.clj\" />\n    </wildcardResourcePatterns>\n    <annotationProcessing>\n      <profile default=\"true\" name=\"Default\" enabled=\"false\">\n        <processorPath useClasspath=\"true\" />\n      </profile>\n    </annotationProcessing>\n  </component>\n</project>"
  },
  {
    "path": ".idea/copyright/profiles_settings.xml",
    "content": "<component name=\"CopyrightManager\">\n  <settings default=\"\" />\n</component>"
  },
  {
    "path": ".idea/gradle.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"GradleSettings\">\n    <option name=\"linkedExternalProjectsSettings\">\n      <GradleProjectSettings>\n        <option name=\"distributionType\" value=\"DEFAULT_WRAPPED\" />\n        <option name=\"externalProjectPath\" value=\"$PROJECT_DIR$\" />\n        <option name=\"gradleJvm\" value=\"1.8\" />\n        <option name=\"modules\">\n          <set>\n            <option value=\"$PROJECT_DIR$\" />\n            <option value=\"$PROJECT_DIR$/app\" />\n          </set>\n        </option>\n      </GradleProjectSettings>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/misc.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"EntryPointsManager\">\n    <entry_points version=\"2.0\" />\n  </component>\n  <component name=\"NullableNotNullManager\">\n    <option name=\"myDefaultNullable\" value=\"android.support.annotation.Nullable\" />\n    <option name=\"myDefaultNotNull\" value=\"android.support.annotation.NonNull\" />\n    <option name=\"myNullables\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.Nullable\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nullable\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.Nullable\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.Nullable\" />\n        </list>\n      </value>\n    </option>\n    <option name=\"myNotNulls\">\n      <value>\n        <list size=\"4\">\n          <item index=\"0\" class=\"java.lang.String\" itemvalue=\"org.jetbrains.annotations.NotNull\" />\n          <item index=\"1\" class=\"java.lang.String\" itemvalue=\"javax.annotation.Nonnull\" />\n          <item index=\"2\" class=\"java.lang.String\" itemvalue=\"edu.umd.cs.findbugs.annotations.NonNull\" />\n          <item index=\"3\" class=\"java.lang.String\" itemvalue=\"android.support.annotation.NonNull\" />\n        </list>\n      </value>\n    </option>\n  </component>\n  <component name=\"ProjectLevelVcsManager\" settingsEditedManually=\"false\">\n    <OptionsSetting value=\"true\" id=\"Add\" />\n    <OptionsSetting value=\"true\" id=\"Remove\" />\n    <OptionsSetting value=\"true\" id=\"Checkout\" />\n    <OptionsSetting value=\"true\" id=\"Update\" />\n    <OptionsSetting value=\"true\" id=\"Status\" />\n    <OptionsSetting value=\"true\" id=\"Edit\" />\n    <ConfirmationsSetting value=\"0\" id=\"Add\" />\n    <ConfirmationsSetting value=\"0\" id=\"Remove\" />\n  </component>\n  <component name=\"ProjectRootManager\" version=\"2\" languageLevel=\"JDK_1_7\" assert-keyword=\"true\" jdk-15=\"true\" project-jdk-name=\"1.8\" project-jdk-type=\"JavaSDK\">\n    <output url=\"file://$PROJECT_DIR$/build/classes\" />\n  </component>\n  <component name=\"ProjectType\">\n    <option name=\"id\" value=\"Android\" />\n  </component>\n  <component name=\"masterDetails\">\n    <states>\n      <state key=\"ProjectJDKs.UI\">\n        <settings>\n          <last-edited>Android API 21 Platform</last-edited>\n          <splitter-proportions>\n            <option name=\"proportions\">\n              <list>\n                <option value=\"0.2\" />\n              </list>\n            </option>\n          </splitter-proportions>\n        </settings>\n      </state>\n    </states>\n  </component>\n</project>"
  },
  {
    "path": ".idea/modules.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"ProjectModuleManager\">\n    <modules>\n      <module fileurl=\"file://$PROJECT_DIR$/RxFace.iml\" filepath=\"$PROJECT_DIR$/RxFace.iml\" />\n      <module fileurl=\"file://$PROJECT_DIR$/app/app.iml\" filepath=\"$PROJECT_DIR$/app/app.iml\" />\n    </modules>\n  </component>\n</project>"
  },
  {
    "path": ".idea/runConfigurations.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"RunConfigurationProducerService\">\n    <option name=\"ignoredProducers\">\n      <set>\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer\" />\n        <option value=\"org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer\" />\n      </set>\n    </option>\n  </component>\n</project>"
  },
  {
    "path": ".idea/vcs.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project version=\"4\">\n  <component name=\"VcsDirectoryMappings\">\n    <mapping directory=\"\" vcs=\"\" />\n  </component>\n</project>"
  },
  {
    "path": "Face.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"Face\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$\" external.system.id=\"GRADLE\" external.system.module.group=\"\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"java-gradle\" name=\"Java-Gradle\">\n      <configuration>\n        <option name=\"BUILD_FOLDER_PATH\" value=\"$MODULE_DIR$/build\" />\n        <option name=\"BUILDABLE\" value=\"false\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <excludeFolder url=\"file://$MODULE_DIR$/.gradle\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>"
  },
  {
    "path": "README.md",
    "content": "RxFace\n=====================\n\n用 RxJava, Retrofit, Okhttp 处理人脸识别的简单用例\n\n## Overview\n\n这是一个人脸识别的简单 Demo, 使用了 [FacePlusPlus](http://www.faceplusplus.com.cn/) 的接口。他们的`/detection/detect` 人脸识别接口可以使用普通的 `get` 也可以用 `post` 传递图片二进制流的形式。其中 `post` 的时候遇到了相当多的坑，下面会提。\n\n该 demo 的网络请求库使用了 [Retrofit](https://github.com/square/retrofit) 并集成了 [OkHttp](https://github.com/square/okhttp)，使用 [RxJava](https://github.com/ReactiveX/RxJava) 进行封装，方便以流的形式处理网络回调以及图片处理，View 的注入框架用了 [ButterKnife](https://github.com/JakeWharton/butterknife)，图片加载使用 [Glide](https://github.com/bumptech/glide)。\n\n## Versions\n\n### v1.1\n\n1. 增加了 compose 复用 work thread 处理数据，然后在 main thread 处理结果的逻辑:你可以在这片文章看到更多：[RxWeekend——RxJava周末狂欢](http://www.jianshu.com/p/ce228f517586)\n2. mSubscription.unsubscribe();\n3. 增加了Service 返回错误情况的处理\n\n### v1.0\n\n\n\n## Main difficulties\n\n当直接使用 `get` 通过传图片 Url 拿到人脸识别数据的话是相当简单的，如下请求链接只要使用 `Retrofit` 的 `get` 请求的 `@QueryMap` 传递参数即可: [Get数据Demo](http://apicn.faceplusplus.com/v2/detection/detect?api_key=7cd1e10dc037bbe9e6db2813d6127475&api_secret=gruCjvStG159LCJutENBt6yzeLK_5ggX&url=http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg)。\n\n\n主要存在的困难点是，当获取本地图片，再使用 `post` 传二进制图片数据时，`post` 要使用 `MultipartTypedOutput`，可参考 [stackoverflow](http://stackoverflow.com/questions/25249042/retrofit-multiple-images-attached-in-one-multipart-request/25260556#25260556) 的回答。然而，这样并没有结束，根据 FacePlusPlus 提供的 SDK Sample 里的 Httpurlconnection 的得到的 `post` 请求头是这样的：\n\n `[Content-Disposition: form-data; name=\"api_key\", Content-Type: text/plain; charset=US-ASCII, Content-Transfer-Encoding: 8bit]`\n \n `[Content-Disposition: form-data; name=\"img\"; filename=\"NoName\", Content-Type: application/octet-stream, Content-Transfer-Encoding: binary]`\n \n 而使用 `Retrofit` 默认实现的话，我们这样来实现：\n \n ```java\n public static MultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){\n    byte[] data = getBitmapByte(bitmap);\n    MultipartTypedOutput multipartTypedOutput = new MultipartTypedOutput();\n    multipartTypedOutput.addPart(\"api_key\", new TypedString(Constants.API_KEY));\n    multipartTypedOutput.addPart(\"api_secret\", new TypedString(Constants.API_SECRET));\n    multipartTypedOutput.addPart(\"img\", new TypedByteArray(\"application/octet-stream\", data));\n    return multipartTypedOutput;\n}\n ```\n \n 根据 Sample 的请求头，`RestAdapter` 的请求头参数我们这样设置来：\n \n ```java\n private RequestInterceptor mRequestInterceptor = new RequestInterceptor() {\n    @Override\n    public void intercept(RequestFacade request) {\n        request.addHeader(\"connection\", \"keep-alive\");\n        request.addHeader(\"Content-Type\", \"multipart/form-data; boundary=\"+ getBoundary() + \"; charset=UTF-8\");\n    }\n};\n ```\n \n \n但是！！！它得到的String参数的头是这样的，这里没有贴出其他的差异，\n\n ```java\nContent-Disposition: form-data; name=\"api_key\"\nContent-Type: text/plain; charset=UTF-8\nContent-Length: 32\nContent-Transfer-Encoding: 8bit\n ``` \n \n所以需要重写三个类:\n\n`MultipartTypedOutput` 为 final 类，所以重写为 `CustomMultipartTypedOutput`，并使其构造函数，增加 boundary 的设置；\n\n`TypedString `默认的编码格式是`UTF-8`，所以重写为 `AsciiTypeString`类，使其编码格式改为 `US-ASCII`；\n\n`TypedByteArray` 默认的的 `fileName()` 方法返回的是 null，而当传图片数据时需要 fileName 为 \"NoName\"，所以重写为 `CustomTypedByteArray` 类，设置其 fileName 为 \"NoName\"。\n \n 同时需要注意的是在设置 `RestAdapter` 的 header 时，其 boundary 一定要和 `CustomMultipartTypedOutput` 的 boundary 相同，否则服务端无法匹配的！（这个地方，一时没注意，被整了一个多小时才发现！！） \n \n 最后 body 的传参，这样来得到：\n \n ```java\n public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){\n    byte[] data = getBitmapByte(bitmap);\n    CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary);\n    multipartTypedOutput.addPart(\"api_key\", \"8bit\", new AsciiTypeString(Constants.API_KEY));\n    multipartTypedOutput.addPart(\"api_secret\", \"8bit\", new AsciiTypeString(Constants.API_SECRET));\n    multipartTypedOutput.addPart(\"img\", new CustomTypedByteArray(\"application/octet-stream\", data));\n    return multipartTypedOutput;\n}\n ```\n\n\n## Preview\n\n![image_screen](https://raw.githubusercontent.com/MrFuFuFu/RxFace/master/images/image_screen.png)\n\n![movie_screen](https://raw.githubusercontent.com/MrFuFuFu/RxFace/master/images/movie_screen.gif)\n\n\n## More about me\n\n* [MrFu-傅圆的个人博客](http://mrfu.me/)\n\n\n\n## Acknowledgments\n\n* [Glide](https://github.com/bumptech/glide) -Glide\n* [Retrofit](https://github.com/square/retrofit) - Retrofit\n* [OkHttp](https://github.com/square/okhttp) - OkHttp\n* [RxJava](https://github.com/ReactiveX/RxJava) - RxJava\n* [ButterKnife](https://github.com/JakeWharton/butterknife) - ButterKnife\n\n\n\nLicense\n============\n\n    Copyright 2015 MrFu\n\n\tLicensed under the Apache License, Version 2.0 (the \"License\");\n\tyou may not use this file except in compliance with the License.\n\tYou may obtain a copy of the License at\n\n     http://www.apache.org/licenses/LICENSE-2.0\n\n\tUnless required by applicable law or agreed to in writing, software\n\tdistributed under the License is distributed on an \"AS IS\" BASIS,\n\tWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n\tSee the License for the specific language governing permissions and\n\tlimitations under the License.\n\n[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/MrFuFuFu/rxface/trend.png)](https://bitdeli.com/free \"Bitdeli Badge\")\n\n"
  },
  {
    "path": "RxFace.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\"RxFace\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$\" external.system.id=\"GRADLE\" external.system.module.group=\"\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"java-gradle\" name=\"Java-Gradle\">\n      <configuration>\n        <option name=\"BUILD_FOLDER_PATH\" value=\"$MODULE_DIR$/build\" />\n        <option name=\"BUILDABLE\" value=\"false\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"true\">\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <excludeFolder url=\"file://$MODULE_DIR$/.gradle\" />\n    </content>\n    <orderEntry type=\"inheritedJdk\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n  </component>\n</module>"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/app.iml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<module external.linked.project.id=\":app\" external.linked.project.path=\"$MODULE_DIR$\" external.root.project.path=\"$MODULE_DIR$/..\" external.system.id=\"GRADLE\" external.system.module.group=\"RxFace\" external.system.module.version=\"unspecified\" type=\"JAVA_MODULE\" version=\"4\">\n  <component name=\"FacetManager\">\n    <facet type=\"android-gradle\" name=\"Android-Gradle\">\n      <configuration>\n        <option name=\"GRADLE_PROJECT_PATH\" value=\":app\" />\n      </configuration>\n    </facet>\n    <facet type=\"android\" name=\"Android\">\n      <configuration>\n        <option name=\"SELECTED_BUILD_VARIANT\" value=\"debug\" />\n        <option name=\"SELECTED_TEST_ARTIFACT\" value=\"_android_test_\" />\n        <option name=\"ASSEMBLE_TASK_NAME\" value=\"assembleDebug\" />\n        <option name=\"COMPILE_JAVA_TASK_NAME\" value=\"compileDebugSources\" />\n        <option name=\"ASSEMBLE_TEST_TASK_NAME\" value=\"assembleDebugAndroidTest\" />\n        <option name=\"COMPILE_JAVA_TEST_TASK_NAME\" value=\"compileDebugAndroidTestSources\" />\n        <afterSyncTasks>\n          <task>generateDebugAndroidTestSources</task>\n          <task>generateDebugSources</task>\n        </afterSyncTasks>\n        <option name=\"ALLOW_USER_CONFIGURATION\" value=\"false\" />\n        <option name=\"MANIFEST_FILE_RELATIVE_PATH\" value=\"/src/main/AndroidManifest.xml\" />\n        <option name=\"RES_FOLDER_RELATIVE_PATH\" value=\"/src/main/res\" />\n        <option name=\"RES_FOLDERS_RELATIVE_PATH\" value=\"file://$MODULE_DIR$/src/main/res\" />\n        <option name=\"ASSETS_FOLDER_RELATIVE_PATH\" value=\"/src/main/assets\" />\n      </configuration>\n    </facet>\n  </component>\n  <component name=\"NewModuleRootManager\" LANGUAGE_LEVEL=\"JDK_1_7\" inherit-compiler-output=\"false\">\n    <output url=\"file://$MODULE_DIR$/build/intermediates/classes/debug\" />\n    <output-test url=\"file://$MODULE_DIR$/build/intermediates/classes/androidTest/debug\" />\n    <exclude-output />\n    <content url=\"file://$MODULE_DIR$\">\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/debug\" isTestSource=\"false\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/resValues/debug\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/r/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/aidl/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/buildConfig/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/source/rs/androidTest/debug\" isTestSource=\"true\" generated=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/rs/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/build/generated/res/resValues/androidTest/debug\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/debug/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/res\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/resources\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/assets\" type=\"java-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/aidl\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/java\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/jni\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/main/rs\" isTestSource=\"false\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/res\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/resources\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/assets\" type=\"java-test-resource\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/aidl\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/java\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/jni\" isTestSource=\"true\" />\n      <sourceFolder url=\"file://$MODULE_DIR$/src/androidTest/rs\" isTestSource=\"true\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/assets\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/bundles\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/classes\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/coverage-instrumented-classes\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dependency-cache\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dex\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/dex-cache\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/appcompat-v7/23.1.1/jars\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/design/23.1.1/jars\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/recyclerview-v7/23.1.1/jars\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/exploded-aar/com.android.support/support-v4/23.1.1/jars\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/exploded-aar/io.reactivex/rxandroid/1.0.1/jars\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/incremental\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/jacoco\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/javaResources\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/libs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/lint\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/manifests\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/ndk\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/pre-dexed\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/proguard\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/res\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/rs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/intermediates/symbols\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/outputs\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/reports\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/test-results\" />\n      <excludeFolder url=\"file://$MODULE_DIR$/build/tmp\" />\n    </content>\n    <orderEntry type=\"jdk\" jdkName=\"Android API 23 Platform\" jdkType=\"Android SDK\" />\n    <orderEntry type=\"sourceFolder\" forTests=\"false\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"butterknife-7.0.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"gson-2.3.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"rxandroid-1.0.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"retrofit-1.9.0\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"design-23.1.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"recyclerview-v7-23.1.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"support-v4-23.1.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"rxjava-1.0.14\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"okhttp-2.0.0\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"okio-1.0.0\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"support-annotations-23.1.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"appcompat-v7-23.1.1\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"glide-3.6.0\" level=\"project\" />\n    <orderEntry type=\"library\" exported=\"\" name=\"okhttp-urlconnection-2.0.0\" level=\"project\" />\n  </component>\n</module>"
  },
  {
    "path": "app/build.gradle",
    "content": "apply plugin: 'com.android.application'\n\nandroid {\n    compileSdkVersion 23\n    buildToolsVersion \"23.0.2\"\n\n    defaultConfig {\n        applicationId \"mrfu.face\"\n        minSdkVersion 14\n        targetSdkVersion 23\n        versionCode 3\n        versionName \"1.1\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    lintOptions {\n        abortOnError false\n    }\n\n//    compileOptions {\n//        sourceCompatibility JavaVersion.VERSION_1_8\n//        targetCompatibility JavaVersion.VERSION_1_8\n//    }\n}\n\ndependencies {\n    compile fileTree(dir: 'libs', include: ['*.jar'])\n    testCompile 'junit:junit:4.12'\n    compile 'com.android.support:appcompat-v7:23.1.1'\n    compile 'com.android.support:design:23.1.1'\n    compile 'com.squareup.retrofit:retrofit:1.9.0'\n    compile 'io.reactivex:rxjava:1.0.14'\n    compile 'io.reactivex:rxandroid:1.0.1'\n    compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'\n    compile 'com.squareup.okhttp:okhttp:2.0.0'\n    compile 'com.jakewharton:butterknife:7.0.1'\n    compile 'com.squareup.okhttp:okhttp-urlconnection:2.0.0'\n    compile 'com.github.bumptech.glide:glide:3.6.0'\n}"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# By default, the flags in this file are appended to flags specified\n# in /Users/MrFu/Desktop/Android/adt-bundle-mac-x86_64-20131030/sdk/tools/proguard/proguard-android.txt\n# You can edit the include path and order by changing the proguardFiles\n# directive in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# Add any project specific keep options here:\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n"
  },
  {
    "path": "app/src/androidTest/java/mrfu/face/ApplicationTest.java",
    "content": "package mrfu.face;\n\nimport android.app.Application;\nimport android.test.ApplicationTestCase;\n\n/**\n * <a href=\"http://d.android.com/tools/testing/testing_android.html\">Testing Fundamentals</a>\n */\npublic class ApplicationTest extends ApplicationTestCase<Application> {\n    public ApplicationTest() {\n        super(Application.class);\n    }\n}"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"mrfu.rxface\" >\n\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n\n    <application\n        android:name=\".AppApplication\"\n        android:allowBackup=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\" >\n        <activity\n            android:name=\".MainActivity\"\n            android:label=\"@string/app_name\"\n            android:theme=\"@style/AppTheme.NoActionBar\" >\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/AppApplication.java",
    "content": "package mrfu.rxface;\n\nimport android.app.Application;\nimport android.content.Context;\n\n/**\n * Created by MrFu on 15/12/15.\n */\npublic class AppApplication extends Application {\n\n    private static Context context;\n\n    public static Context getContext() {\n        return context;\n    }\n\n    @Override\n    public void onCreate() {\n        super.onCreate();\n        context = getApplicationContext();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/BaseActivity.java",
    "content": "package mrfu.rxface;\n\nimport android.os.Bundle;\nimport android.support.v7.app.AppCompatActivity;\n\nimport butterknife.ButterKnife;\nimport rx.Subscription;\nimport rx.subscriptions.Subscriptions;\n\n/**\n * Created by MrFu on 16/1/10.\n */\npublic class BaseActivity extends AppCompatActivity {\n    protected Subscription mSubscription = Subscriptions.empty();\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n    }\n\n    @Override\n    public void setContentView(int layoutResID) {\n        super.setContentView(layoutResID);\n        ButterKnife.bind(BaseActivity.this);\n    }\n\n    @Override\n    protected void onDestroy() {\n        super.onDestroy();\n        ButterKnife.unbind(BaseActivity.this);\n        if (mSubscription != null && !mSubscription.isUnsubscribed()) mSubscription.unsubscribe();\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/MainActivity.java",
    "content": "package mrfu.rxface;\n\nimport android.content.Intent;\nimport android.graphics.Bitmap;\nimport android.graphics.drawable.BitmapDrawable;\nimport android.net.Uri;\nimport android.os.Bundle;\nimport android.support.v7.widget.Toolbar;\nimport android.text.TextUtils;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.MenuItem;\nimport android.view.View;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.bumptech.glide.Glide;\n\nimport java.util.HashMap;\nimport java.util.Map;\nimport java.util.concurrent.ExecutionException;\n\nimport butterknife.Bind;\nimport butterknife.OnClick;\nimport mrfu.rxface.business.Constants;\nimport mrfu.rxface.business.DealData;\nimport mrfu.rxface.loader.FaceApi;\nimport mrfu.rxface.models.FaceResponse;\nimport mrfu.rxface.models.NeedDataEntity;\nimport rx.Observable;\nimport rx.Observer;\nimport rx.android.schedulers.AndroidSchedulers;\nimport rx.functions.Func1;\nimport rx.schedulers.Schedulers;\n\npublic class MainActivity extends BaseActivity {\n\n    @Bind(R.id.iv_face_get)\n    ImageView iv_face_get;\n\n    @Bind(R.id.iv_face_post)\n    ImageView iv_face_post;\n\n    @Bind(R.id.btn_recongize_get)\n    Button btn_recongize_get;\n\n    @Bind(R.id.btn_recongize_post)\n    Button btn_recongize_post;\n\n    @Bind(R.id.tv_age_get)\n    TextView tv_age_get;\n\n    @Bind(R.id.tv_age_post)\n    TextView tv_age_post;\n\n    /**\n     * 0:GET button\n     * 1:POST button\n     */\n    private int type = 0;\n\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n\n        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);\n        setSupportActionBar(toolbar);\n\n        Glide.with(this).load(Constants.IMAGE_URL).fitCenter().into(iv_face_get);\n        iv_face_post.setImageResource(R.drawable.jobs);\n    }\n\n    @OnClick(R.id.btn_recongize_get)\n    public void btn_recongize_get(View view){\n        type = 0;\n        Map<String, String> options = new HashMap<>();\n        options.put(\"api_key\", Constants.API_KEY);\n        options.put(\"api_secret\", Constants.API_SECRET);\n        options.put(\"url\", Constants.IMAGE_URL);\n        mSubscription = FaceApi.getIns()//api_key={apiKey}&api_secret={apiSecret}&url={imgUrl}\n                .getDataUrlGet(options)\n                .observeOn(Schedulers.newThread())\n                .flatMap(new Func1<FaceResponse, Observable<NeedDataEntity>>() {\n                    @Override\n                    public Observable<NeedDataEntity> call(FaceResponse faceResponse) {\n                        Bitmap bitmap = null;\n                        try {\n                            //java.lang.IllegalArgumentException: YOu must call this method on a background thread\n                            bitmap = Glide.with(MainActivity.this).load(Constants.IMAGE_URL).asBitmap().into(-1, -1).get();\n                        } catch (InterruptedException | ExecutionException e) {\n                            e.printStackTrace();\n                        }\n                        NeedDataEntity entity = new NeedDataEntity();\n                        entity.bitmap = DealData.drawLineGetBitmap(faceResponse, bitmap);\n                        entity.displayStr = DealData.getDisplayInfo(faceResponse);\n                        return Observable.just(entity);\n                    }\n                })\n                .observeOn(AndroidSchedulers.mainThread())\n                .subscribe(setBitmapDataObserver);\n    }\n\n    @OnClick(R.id.btn_recongize_post)\n    public void btn_recongize_post(View view){\n        type = 1;\n        BitmapDrawable mDrawable =  (BitmapDrawable) iv_face_post.getDrawable();\n        final Bitmap bitmap = mDrawable.getBitmap();\n\n        mSubscription = FaceApi.getIns()\n                .getDataPost(DealData.mulipartData(bitmap, FaceApi.getIns().getBoundary()))\n                .flatMap(new Func1<FaceResponse, Observable<NeedDataEntity>>() {\n                    @Override\n                    public Observable<NeedDataEntity> call(FaceResponse faceResponse) {\n                        NeedDataEntity entity = new NeedDataEntity();\n                        entity.bitmap = DealData.drawLineGetBitmap(faceResponse, bitmap);\n                        entity.displayStr = DealData.getDisplayInfo(faceResponse);\n                        return Observable.just(entity);\n                    }\n                })\n                .subscribe(setBitmapDataObserver);\n    }\n\n    private Observer<NeedDataEntity> setBitmapDataObserver = new Observer<NeedDataEntity>() {\n\n        @Override\n        public void onNext(final NeedDataEntity needDataEntity) {\n            if (needDataEntity == null){\n                return;\n            }\n            switch (type){\n                case 0:\n                    if (!TextUtils.isEmpty(needDataEntity.displayStr)){\n                        tv_age_get.setText(needDataEntity.displayStr);\n                    }\n                    if (needDataEntity.bitmap != null){\n                        iv_face_get.setImageBitmap(needDataEntity.bitmap);\n                    }\n                    break;\n                case 1:\n                    if (!TextUtils.isEmpty(needDataEntity.displayStr)){\n                        tv_age_post.setText(needDataEntity.displayStr);\n                    }\n                    if (needDataEntity.bitmap != null){\n                        iv_face_post.setImageBitmap(needDataEntity.bitmap);\n                    }\n                    break;\n                default:\n                    break;\n            }\n        }\n\n        @Override\n        public void onCompleted() {\n            Log.i(\"MrFu\", \"onCompleted\");\n        }\n\n        @Override\n        public void onError(final Throwable error) {\n            error.printStackTrace();\n        }\n    };\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n        // Inflate the menu; this adds items to the action bar if it is present.\n        getMenuInflater().inflate(R.menu.menu_main, menu);\n        return true;\n    }\n\n    @Override\n    public boolean onOptionsItemSelected(MenuItem item) {\n        // Handle action bar item clicks here. The action bar will\n        // automatically handle clicks on the Home/Up button, so long\n        // as you specify a parent activity in AndroidManifest.xml.\n        int id = item.getItemId();\n\n        //noinspection SimplifiableIfStatement\n        if (id == R.id.action_settings) {\n            Uri uri = Uri.parse(\"https://github.com/MrFuFuFu/RxFace\");\n            Intent i = new Intent(Intent.ACTION_VIEW, uri);\n            i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);\n            startActivity(i);\n            return true;\n        }\n\n        return super.onOptionsItemSelected(item);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/business/Constants.java",
    "content": "package mrfu.rxface.business;\n\n/**\n * Created by MrFu on 15/12/15.\n */\npublic class Constants {\n//    public static final String FACE_SERVER_IP = \"https://apicn.faceplusplus.com/v2\";\n    public static final String FACE_SERVER_IP = \"http://apicn.faceplusplus.com/v2\";\n\n    public static final String API_KEY = \"7cd1e10dc037bbe9e6db2813d6127475\";\n    public static final String API_SECRET = \"gruCjvStG159LCJutENBt6yzeLK_5ggX\";\n\n    public static final String IMAGE_URL = \"http://www.ipmm.cn/UploadImage/20130502/2013050209272791.jpg\";\n\n    public static final int TIME_OUT = 30 * 1000;\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/business/DealData.java",
    "content": "package mrfu.rxface.business;\n\nimport android.graphics.Bitmap;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\n\nimport java.io.ByteArrayOutputStream;\n\nimport mrfu.rxface.loader.custom.AsciiTypeString;\nimport mrfu.rxface.loader.custom.CustomMultipartTypedOutput;\nimport mrfu.rxface.loader.custom.CustomTypedByteArray;\nimport mrfu.rxface.models.FaceResponse;\n\n/**\n * Created by MrFu on 15/12/15.\n */\npublic class DealData {\n\n    /**\n     * 设置参数\n     * 使用 MultipartTypedOutput, 而不使用 Retrofit @Multipart 的 @Part 和 @PartMap 的形式\n     * http://stackoverflow.com/questions/25249042/retrofit-multiple-images-attached-in-one-multipart-request/25260556#25260556\n     *\n     * @see CustomMultipartTypedOutput 重写 MultipartTypedOutput 使之接受 boundary 参数\n     * @see AsciiTypeString , 重写 TypedByteArray, 使其编码格式为 US-ASCII\n     * @see CustomTypedByteArray , 重写 TypedByteArray 设置其 fileName 为 \"NoName\",\n     * 以上参数格式和参数类型都必须指定,否则会返回对应的错误\n     * http://www.faceplusplus.com.cn/detection_detect/\n     * @param bitmap need upload image\n     * @param boundary must same with http header boundary\n     * @return http post body param\n     */\n    public static CustomMultipartTypedOutput mulipartData(Bitmap bitmap, String boundary){\n        byte[] data = getBitmapByte(bitmap);\n        CustomMultipartTypedOutput multipartTypedOutput = new CustomMultipartTypedOutput(boundary);\n        multipartTypedOutput.addPart(\"api_key\", \"8bit\", new AsciiTypeString(Constants.API_KEY));\n        multipartTypedOutput.addPart(\"api_secret\", \"8bit\", new AsciiTypeString(Constants.API_SECRET));\n        multipartTypedOutput.addPart(\"img\", new CustomTypedByteArray(\"application/octet-stream\", data));\n        return multipartTypedOutput;\n    }\n\n    /**\n     * convert bitmap to byte[]\n     * @param bitmap need convert bitmap\n     * @return byte[]\n     */\n    private static byte[] getBitmapByte(Bitmap bitmap){\n        ByteArrayOutputStream stream = new ByteArrayOutputStream();\n        float scale = Math.min(1, Math.min(600f / bitmap.getWidth(), 600f / bitmap.getHeight()));\n        Matrix matrix = new Matrix();\n        matrix.postScale(scale, scale);\n        Bitmap imgSmall = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);\n        imgSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);\n        return stream.toByteArray();\n    }\n\n    /**\n     * draw to include face bitmap\n     * @param entity data\n     * @param viewBitmap display bitmap\n     * @return face bitmap\n     */\n    public static Bitmap drawLineGetBitmap(FaceResponse entity, Bitmap viewBitmap){\n        //use the red paint\n        Paint paint = new Paint();\n        paint.setColor(Color.RED);\n        paint.setStrokeWidth(Math.max(viewBitmap.getWidth(), viewBitmap.getHeight()) / 100f);\n\n        //create a new canvas\n        Bitmap bitmap = Bitmap.createBitmap(viewBitmap.getWidth(), viewBitmap.getHeight(), viewBitmap.getConfig());\n        Canvas canvas = new Canvas(bitmap);\n        canvas.drawBitmap(viewBitmap, new Matrix(), null);\n\n        try {\n            //find out all faces\n            final int count = entity.face.size();\n            for (int i = 0; i < count; ++i) {\n                float x, y, w, h;\n                //get the center point\n                x = (float) entity.face.get(i).position.center.x;\n                y = (float) entity.face.get(i).position.center.y;\n                //get face size\n                w = (float) entity.face.get(i).position.width;\n                h = (float) entity.face.get(i).position.height;\n                //change percent value to the real size\n                x = x / 100 * viewBitmap.getWidth();\n                w = w / 100 * viewBitmap.getWidth() * 0.7f;\n                y = y / 100 * viewBitmap.getHeight();\n                h = h / 100 * viewBitmap.getHeight() * 0.7f;\n                //draw the box to mark it out\n                canvas.drawLine(x - w, y - h, x - w, y + h, paint);\n                canvas.drawLine(x - w, y - h, x + w, y - h, paint);\n                canvas.drawLine(x + w, y + h, x - w, y + h, paint);\n                canvas.drawLine(x + w, y + h, x + w, y - h, paint);\n            }\n            //save new image\n            viewBitmap = bitmap;\n            return viewBitmap;\n        }catch (Exception e){\n            e.printStackTrace();\n        }\n        return null;\n    }\n\n    public static String getDisplayInfo(FaceResponse entity){\n        if (entity == null || entity.face == null || entity.face.size() == 0\n                || entity.face.get(0).attribute == null){\n            return \"\";\n        }\n        int age = 0;\n        if (entity.face.get(0).attribute.age != null){\n            age = entity.face.get(0).attribute.age.value;\n        }\n        String gender = \"\";\n        if (entity.face.get(0).attribute.gender != null){\n            gender = \"male\".equalsIgnoreCase(entity.face.get(0).attribute.gender.value) ? \"男\" : \"女\";\n        }\n        return \"年龄约 \" + age + \" 性别为 \" + gender;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/ExecutorManager.java",
    "content": "package mrfu.rxface.loader;\n\nimport android.os.Build;\nimport android.support.annotation.NonNull;\n\nimport java.io.File;\nimport java.io.FileFilter;\nimport java.util.concurrent.BlockingQueue;\nimport java.util.concurrent.ExecutorService;\nimport java.util.concurrent.LinkedBlockingQueue;\nimport java.util.concurrent.RejectedExecutionHandler;\nimport java.util.concurrent.ThreadFactory;\nimport java.util.concurrent.ThreadPoolExecutor;\nimport java.util.concurrent.TimeUnit;\nimport java.util.concurrent.atomic.AtomicInteger;\n\n/**\n * Created by Joker on 2015/8/24.\n */\npublic class ExecutorManager {\n\n    public static final int DEVICE_INFO_UNKNOWN = 0;\n    public static ExecutorService eventExecutor;\n    //private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();\n    private static final int CPU_COUNT = ExecutorManager.getCountOfCPU();\n    private static final int CORE_POOL_SIZE = CPU_COUNT + 1;\n    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;\n    private static final int KEEP_ALIVE = 1;\n    private static final BlockingQueue<Runnable> eventPoolWaitQueue = new LinkedBlockingQueue<>(128);\n    private static final ThreadFactory eventThreadFactory = new ThreadFactory() {\n        private final AtomicInteger mCount = new AtomicInteger(1);\n\n        public Thread newThread(@NonNull Runnable r) {\n            return new Thread(r, \"eventAsyncAndBackground #\" + mCount.getAndIncrement());\n        }\n    };\n\n    private static final RejectedExecutionHandler eventHandler =\n            new ThreadPoolExecutor.CallerRunsPolicy();\n\n    static {\n        eventExecutor =\n                new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS,\n                        eventPoolWaitQueue, eventThreadFactory, eventHandler);\n    }\n\n    /**\n     * Linux中的设备都是以文件的形式存在，CPU也不例外，因此CPU的文件个数就等价与核数。\n     * Android的CPU 设备文件位于/sys/devices/system/cpu/目录，文件名的的格式为cpu\\d+。\n     *\n     * 引用：http://www.jianshu.com/p/f7add443cd32#，感谢 liangfeizc :)\n     * https://github.com/facebook/device-year-class\n     */\n    public static int getCountOfCPU() {\n\n        if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {\n            return 1;\n        }\n        int count;\n        try {\n            count = new File(\"/sys/devices/system/cpu/\").listFiles(CPU_FILTER).length;\n        } catch (SecurityException | NullPointerException e) {\n            count = DEVICE_INFO_UNKNOWN;\n        }\n        return count;\n    }\n\n    private static final FileFilter CPU_FILTER = new FileFilter() {\n        @Override public boolean accept(File pathname) {\n\n            String path = pathname.getName();\n            if (path.startsWith(\"cpu\")) {\n                for (int i = 3; i < path.length(); i++) {\n                    if (path.charAt(i) < '0' || path.charAt(i) > '9') {\n                        return false;\n                    }\n                }\n                return true;\n            }\n            return false;\n        }\n    };\n}"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/FaceApi.java",
    "content": "package mrfu.rxface.loader;\n\nimport com.squareup.okhttp.OkHttpClient;\n\nimport java.util.Map;\nimport java.util.Random;\nimport java.util.concurrent.TimeUnit;\n\nimport mrfu.rxface.BuildConfig;\nimport mrfu.rxface.business.Constants;\nimport mrfu.rxface.loader.custom.CustomMultipartTypedOutput;\nimport mrfu.rxface.models.FaceResponse;\nimport retrofit.RequestInterceptor;\nimport retrofit.RestAdapter;\nimport retrofit.client.OkClient;\nimport retrofit.http.Body;\nimport retrofit.http.GET;\nimport retrofit.http.POST;\nimport retrofit.http.QueryMap;\nimport rx.Observable;\nimport rx.functions.Func1;\n\n/**\n * face api\n * Created by MrFu on 15/12/15.\n */\npublic class FaceApi {\n    private static String mBoundry;\n    private final static int boundaryLength = 32;\n    private final static String boundaryAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_\";\n\n    public static FaceApi instance;\n\n    public static FaceApi getIns() {\n        if (null == instance) {\n            synchronized (FaceApi.class) {\n                if (null == instance) {\n                    instance = new FaceApi();\n                }\n            }\n        }\n        return instance;\n    }\n\n    private final MrFuService mWebService;\n\n    public FaceApi() {\n        RestAdapter restAdapter = new RestAdapter.Builder()\n                .setEndpoint(Constants.FACE_SERVER_IP)\n                .setClient(new OkClient(new OkHttpClient()))\n                .setLogLevel(BuildConfig.DEBUG ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)\n                .setRequestInterceptor(mRequestInterceptor)\n                .build();\n        mWebService = restAdapter.create(MrFuService.class);\n        mBoundry = setBoundary();\n    }\n\n    private RequestInterceptor mRequestInterceptor = new RequestInterceptor() {\n        @Override\n        public void intercept(RequestFacade request) {\n            request.addHeader(\"connection\", \"keep-alive\");\n            request.addHeader(\"Content-Type\", \"multipart/form-data; boundary=\"+ getBoundary() + \"; charset=UTF-8\");\n        }\n    };\n\n    public String getBoundary(){\n        return mBoundry;\n    }\n\n    /**\n     * 设置 Content-Type 的 boundary,这里有个强坑:\n     * header 的 boundary 和 CustomMultipartTypedOutput 的 boundary 必须相同!!\n     * @return mBoundry\n     */\n    private static String setBoundary() {\n        StringBuilder sb = new StringBuilder();\n        Random random = new Random();\n        for (int i = 0; i < boundaryLength; ++i)\n            sb.append(boundaryAlphabet.charAt(random.nextInt(boundaryAlphabet.length())));\n        return sb.toString();\n    }\n\n    public interface MrFuService {\n        @POST(\"/detection/detect\")\n        Observable<FaceResponse> uploadImagePost(\n                @Body CustomMultipartTypedOutput listMultipartOutput\n        );\n\n        //http://apicn.faceplusplus.com/v2/detection/detect?api_key=7cd1e10dc037bbe9e6db2813d6127475&api_secret=gruCjvStG159LCJutENBt6yzeLK_5ggX&url=http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg\n        @GET(\"/detection/detect\")\n        Observable<FaceResponse> uploadImageUrlGet(\n                @QueryMap Map<String, String> options\n        );\n    }\n\n    public Observable<FaceResponse> getDataPost(CustomMultipartTypedOutput listMultipartOutput) {\n        return mWebService.uploadImagePost(listMultipartOutput)\n                .timeout(Constants.TIME_OUT, TimeUnit.MILLISECONDS)\n                .concatMap(new Func1<FaceResponse, Observable<FaceResponse>>() {\n                    @Override\n                    public Observable<FaceResponse> call(FaceResponse faceResponse) {\n                        return faceResponse.filterWebServiceErrors();\n                    }\n                }).compose(SchedulersCompat.<FaceResponse>applyExecutorSchedulers());\n    }\n\n    public Observable<FaceResponse> getDataUrlGet(Map<String, String> options) {\n        return mWebService.uploadImageUrlGet(options)\n                .timeout(Constants.TIME_OUT, TimeUnit.MILLISECONDS)\n                .concatMap(new Func1<FaceResponse, Observable<FaceResponse>>() {\n                    @Override\n                    public Observable<FaceResponse> call(FaceResponse faceResponse) {\n                        return faceResponse.filterWebServiceErrors();\n                    }\n                }).compose(SchedulersCompat.<FaceResponse>applyExecutorSchedulers());//http://www.jianshu.com/p/e9e03194199e\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/SchedulersCompat.java",
    "content": "package mrfu.rxface.loader;\n\nimport rx.Observable;\nimport rx.android.schedulers.AndroidSchedulers;\nimport rx.schedulers.Schedulers;\n\n/**\n * 小鄧子:【译】避免打断链式结构：使用.compose( )操作符 http://www.jianshu.com/p/e9e03194199e\n * Created by Joker on 2015/8/10.\n */\npublic class SchedulersCompat {\n\n    private static final Observable.Transformer computationTransformer =\n            new Observable.Transformer() {\n                @Override public Object call(Object observable) {\n                    return ((Observable) observable).subscribeOn(Schedulers.computation())\n                            .observeOn(AndroidSchedulers.mainThread());\n                }\n            };\n\n    private static final Observable.Transformer ioTransformer = new Observable.Transformer() {\n        @Override public Object call(Object observable) {\n            return ((Observable) observable).subscribeOn(Schedulers.io())\n                    .observeOn(AndroidSchedulers.mainThread());\n        }\n    };\n    private static final Observable.Transformer newTransformer = new Observable.Transformer() {\n        @Override public Object call(Object observable) {\n            return ((Observable) observable).subscribeOn(Schedulers.newThread())\n                    .observeOn(AndroidSchedulers.mainThread());\n        }\n    };\n    private static final Observable.Transformer trampolineTransformer = new Observable.Transformer() {\n        @Override public Object call(Object observable) {\n            return ((Observable) observable).subscribeOn(Schedulers.trampoline())\n                    .observeOn(AndroidSchedulers.mainThread());\n        }\n    };\n\n    private static final Observable.Transformer executorTransformer = new Observable.Transformer() {\n        @Override public Object call(Object observable) {\n            return ((Observable) observable).subscribeOn(Schedulers.from(ExecutorManager.eventExecutor))\n                    .observeOn(AndroidSchedulers.mainThread());\n        }\n    };\n\n    /**\n     * Don't break the chain: use RxJava's compose() operator\n     */\n    public static <T> Observable.Transformer<T, T> applyComputationSchedulers() {\n\n        return (Observable.Transformer<T, T>) computationTransformer;\n    }\n\n    public static <T> Observable.Transformer<T, T> applyIoSchedulers() {\n\n        return (Observable.Transformer<T, T>) ioTransformer;\n    }\n\n    public static <T> Observable.Transformer<T, T> applyNewSchedulers() {\n\n        return (Observable.Transformer<T, T>) newTransformer;\n    }\n\n    public static <T> Observable.Transformer<T, T> applyTrampolineSchedulers() {\n\n        return (Observable.Transformer<T, T>) trampolineTransformer;\n    }\n\n    public static <T> Observable.Transformer<T, T> applyExecutorSchedulers() {\n\n        return (Observable.Transformer<T, T>) executorTransformer;\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/WebServiceException.java",
    "content": "package mrfu.rxface.loader;\n\n/**\n * Created by MrFu on 16/1/10.\n */\npublic class WebServiceException extends Exception {\n    public WebServiceException(String detailMessage) {\n        super(detailMessage);\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/custom/AsciiTypeString.java",
    "content": "package mrfu.rxface.loader.custom;\n\nimport java.io.UnsupportedEncodingException;\n\nimport retrofit.mime.TypedByteArray;\n\n/**\n * 重写 TypedByteArray, 使其编码格式为 US-ASCII\n * Created by MrFu on 15/12/16.\n */\npublic class AsciiTypeString extends TypedByteArray {\n\n    public AsciiTypeString(String string) {\n        super(\"text/plain; charset=US-ASCII\", convertToBytes(string));\n    }\n\n    private static byte[] convertToBytes(String string) {\n        try {\n            return string.getBytes(\"US-ASCII\");\n        } catch (UnsupportedEncodingException e) {\n            throw new RuntimeException(e);\n        }\n    }\n\n    @Override public String toString() {\n        try {\n            return \"TypedString[\" + new String(getBytes(), \"US-ASCII\") + \"]\";\n        } catch (UnsupportedEncodingException e) {\n            throw new AssertionError(\"Must be able to decode US-ASCII\");\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/custom/CustomMultipartTypedOutput.java",
    "content": "package mrfu.rxface.loader.custom;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\nimport java.util.ArrayList;\nimport java.util.LinkedList;\nimport java.util.List;\nimport java.util.UUID;\n\nimport retrofit.mime.TypedOutput;\n\n/**\n * 重写 MultipartTypedOutput 使之接受 boundary 参数\n * Created by MrFu on 15/12/16.\n */\npublic class CustomMultipartTypedOutput implements TypedOutput {\n    public static final String DEFAULT_TRANSFER_ENCODING = \"binary\";\n\n    private static final class MimePart {\n        private final TypedOutput body;\n        private final String name;\n        private final String transferEncoding;\n        private final boolean isFirst;\n        private final String boundary;\n\n        private byte[] partBoundary;\n        private byte[] partHeader;\n        private boolean isBuilt;\n\n        public MimePart(String name, String transferEncoding, TypedOutput body, String boundary,\n                        boolean isFirst) {\n            this.name = name;\n            this.transferEncoding = transferEncoding;\n            this.body = body;\n            this.isFirst = isFirst;\n            this.boundary = boundary;\n        }\n\n        public void writeTo(OutputStream out) throws IOException {\n            build();\n            out.write(partBoundary);\n            out.write(partHeader);\n            body.writeTo(out);\n        }\n\n        public long size() {\n            build();\n            if (body.length() > -1) {\n                return body.length() + partBoundary.length + partHeader.length;\n            } else {\n                return -1;\n            }\n        }\n\n        private void build() {\n            if (isBuilt) return;\n            partBoundary = buildBoundary(boundary, isFirst, false);\n            partHeader = buildHeader(name, transferEncoding, body);\n            isBuilt = true;\n        }\n    }\n\n    private final List<MimePart> mimeParts = new LinkedList<MimePart>();\n\n    private final byte[] footer;\n    private final String boundary;\n    private long length;\n\n    public CustomMultipartTypedOutput() {\n        this(UUID.randomUUID().toString());\n    }\n\n    public CustomMultipartTypedOutput(String boundary) {\n        this.boundary = boundary;\n        footer = buildBoundary(boundary, false, true);\n        length = footer.length;\n    }\n\n    List<byte[]> getParts() throws IOException {\n        List<byte[]> parts = new ArrayList<byte[]>(mimeParts.size());\n        for (MimePart part : mimeParts) {\n            ByteArrayOutputStream bos = new ByteArrayOutputStream();\n            part.writeTo(bos);\n            parts.add(bos.toByteArray());\n        }\n        return parts;\n    }\n\n    public void addPart(String name, TypedOutput body) {\n        addPart(name, DEFAULT_TRANSFER_ENCODING, body);\n    }\n\n    public void addPart(String name, String transferEncoding, TypedOutput body) {\n        if (name == null) {\n            throw new NullPointerException(\"Part name must not be null.\");\n        }\n        if (transferEncoding == null) {\n            throw new NullPointerException(\"Transfer encoding must not be null.\");\n        }\n        if (body == null) {\n            throw new NullPointerException(\"Part body must not be null.\");\n        }\n\n        MimePart part = new MimePart(name, transferEncoding, body, boundary, mimeParts.isEmpty());\n        mimeParts.add(part);\n\n        long size = part.size();\n        if (size == -1) {\n            length = -1;\n        } else if (length != -1) {\n            length += size;\n        }\n    }\n\n    public int getPartCount() {\n        return mimeParts.size();\n    }\n\n    @Override public String fileName() {\n        return null;\n    }\n\n    @Override public String mimeType() {\n        return \"multipart/form-data; boundary=\" + boundary;\n    }\n\n    @Override public long length() {\n        return length;\n    }\n\n    @Override public void writeTo(OutputStream out) throws IOException {\n        for (MimePart part : mimeParts) {\n            part.writeTo(out);\n        }\n        out.write(footer);\n    }\n\n    private static byte[] buildBoundary(String boundary, boolean first, boolean last) {\n        try {\n            // Pre-size for the last boundary, the worst case scenario.\n            StringBuilder sb = new StringBuilder(boundary.length() + 8);\n\n            if (!first) {\n                sb.append(\"\\r\\n\");\n            }\n            sb.append(\"--\");\n            sb.append(boundary);\n            if (last) {\n                sb.append(\"--\");\n            }\n            sb.append(\"\\r\\n\");\n            return sb.toString().getBytes(\"UTF-8\");\n        } catch (IOException ex) {\n            throw new RuntimeException(\"Unable to write multipart boundary\", ex);\n        }\n    }\n\n    private static byte[] buildHeader(String name, String transferEncoding, TypedOutput value) {\n        try {\n            // Initial size estimate based on always-present strings and conservative value lengths.\n            StringBuilder headers = new StringBuilder(128);\n\n            headers.append(\"Content-Disposition: form-data; name=\\\"\");\n            headers.append(name);\n\n            String fileName = value.fileName();\n            if (fileName != null) {\n                headers.append(\"\\\"; filename=\\\"\");\n                headers.append(fileName);\n            }\n\n            headers.append(\"\\\"\\r\\nContent-Type: \");\n            headers.append(value.mimeType());\n\n            long length = value.length();\n            if (length != -1) {\n                headers.append(\"\\r\\nContent-Length: \").append(length);\n            }\n\n            headers.append(\"\\r\\nContent-Transfer-Encoding: \");\n            headers.append(transferEncoding);\n            headers.append(\"\\r\\n\\r\\n\");\n\n            return headers.toString().getBytes(\"UTF-8\");\n        } catch (IOException ex) {\n            throw new RuntimeException(\"Unable to write multipart header\", ex);\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/loader/custom/CustomTypedByteArray.java",
    "content": "package mrfu.rxface.loader.custom;\n\nimport retrofit.mime.TypedByteArray;\n\n/**\n * 重写 TypedByteArray 设置其 fileName 为 \"NoName\",\n * Created by MrFu on 15/12/16.\n */\npublic class CustomTypedByteArray extends TypedByteArray {\n    /**\n     * Constructs a new typed byte array.  Sets mimeType to {@code application/unknown} if absent.\n     *\n     * @param mimeType\n     * @param bytes\n     * @throws NullPointerException if bytes are null\n     */\n    public CustomTypedByteArray(String mimeType, byte[] bytes) {\n        super(mimeType, bytes);\n    }\n\n    @Override\n    public String fileName() {\n        return \"NoName\";\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/models/BaseResponse.java",
    "content": "package mrfu.rxface.models;\n\nimport mrfu.rxface.loader.WebServiceException;\nimport rx.Observable;\n\n/**\n * Created by MrFu on 16/1/10.\n */\npublic class BaseResponse {\n    public Observable filterWebServiceErrors() {\n        if (true){//judge result status is ok~~\n            return Observable.just(this);\n        }else {\n            return Observable.error(new WebServiceException(\"Service return Error message\"));\n        }\n        //demo code just like blow, the common is a class object, you can deal the error code in here.\n//        public class BaseResponse {\n//            public Common common;\n//\n//            public Observable filterWebServiceErrors() {\n//                if (common == null){\n//                    return Observable.error(new WebServiceException(\"啊喔,服务器除了点小问题\"));\n//                }else {\n//                    int code = Integer.parseInt(common.status);\n//                    switch (code){\n//                        case Constants.RESULT_OK://正常\n//                            return Observable.just(this);\n//                        case Constants.RESULT_NORMAL_UPDATE://普通升级\n//                        case Constants.RESULT_FORCE_UPDATE://墙纸升级\n//                            if (AppApplication.getInstance().updateModel == null) {\n//                                AppApplication.getInstance().updateModel = common.update;\n//                                AppApplication.getInstance().updateModel.code = code;\n//                            }\n//                            return Observable.just(this);\n//                        default://出错\n//                            return Observable.error(new WebServiceException(BaseResponse.this.common.memo));\n//                    }\n//                }\n//            }\n//        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/models/FaceResponse.java",
    "content": "package mrfu.rxface.models;\n\nimport java.util.List;\n\n/**\n * Created by MrFu on 15/12/16.\n */\npublic class FaceResponse extends BaseResponse{\n\n    /**\n     * face : [{\"attribute\":{\"age\":{\"range\":6,\"value\":18},\"gender\":{\"confidence\":99.9996,\"value\":\"Male\"},\"race\":{\"confidence\":99.8977,\"value\":\"White\"},\"smiling\":{\"value\":81.2229}},\"face_id\":\"5bf244c54d5731974e25ee024b950cd3\",\"position\":{\"center\":{\"x\":47.833333,\"y\":49.036403},\"eye_left\":{\"x\":42.140333,\"y\":42.28394},\"eye_right\":{\"x\":53.658,\"y\":42.389936},\"height\":31.263383,\"mouth_left\":{\"x\":42.352167,\"y\":56.311991},\"mouth_right\":{\"x\":53.747833,\"y\":56.89379},\"nose\":{\"x\":47.392667,\"y\":51.794004},\"width\":24.333333},\"tag\":\"\"},{\"attribute\":{\"age\":{\"range\":5,\"value\":24},\"gender\":{\"confidence\":99.5758,\"value\":\"Male\"},\"race\":{\"confidence\":99.94800000000001,\"value\":\"White\"},\"smiling\":{\"value\":93.0865}},\"face_id\":\"092cb7660d3f8d115b8af331465624a1\",\"position\":{\"center\":{\"x\":83.833333,\"y\":52.462527},\"eye_left\":{\"x\":77.052667,\"y\":47.005353},\"eye_right\":{\"x\":87.198,\"y\":44.253961},\"height\":28.265525,\"mouth_left\":{\"x\":77.304167,\"y\":60.063169},\"mouth_right\":{\"x\":86.635167,\"y\":60.254176},\"nose\":{\"x\":86.9965,\"y\":54.840685},\"width\":22},\"tag\":\"\"},{\"attribute\":{\"age\":{\"range\":11,\"value\":38},\"gender\":{\"confidence\":74.2956,\"value\":\"Female\"},\"race\":{\"confidence\":96.8155,\"value\":\"White\"},\"smiling\":{\"value\":86.556}},\"face_id\":\"3c9d898f732c84d07d760f184bfec814\",\"position\":{\"center\":{\"x\":13,\"y\":52.248394},\"eye_left\":{\"x\":8.483217,\"y\":44.584797},\"eye_right\":{\"x\":18.669667,\"y\":46.517987},\"height\":27.837259,\"mouth_left\":{\"x\":8.44005,\"y\":60.162955},\"mouth_right\":{\"x\":17.914333,\"y\":60.197645},\"nose\":{\"x\":8.59085,\"y\":53.623769},\"width\":21.666667},\"tag\":\"\"}]\n     * img_height : 490\n     * img_id : f3f8c2826537ce51ca1995143e8b9289\n     * img_width : 629\n     * session_id : a7f871065a064bdfabe06de48189dcac\n     * url : http://imglife.gmw.cn/attachement/jpg/site2/20111014/002564a5d7d21002188831.jpg\n     */\n\n    public int img_height;\n    public String img_id;\n    public int img_width;\n    public String session_id;\n    public String url;\n    /**\n     * attribute : {\"age\":{\"range\":6,\"value\":18},\"gender\":{\"confidence\":99.9996,\"value\":\"Male\"},\"race\":{\"confidence\":99.8977,\"value\":\"White\"},\"smiling\":{\"value\":81.2229}}\n     * face_id : 5bf244c54d5731974e25ee024b950cd3\n     * position : {\"center\":{\"x\":47.833333,\"y\":49.036403},\"eye_left\":{\"x\":42.140333,\"y\":42.28394},\"eye_right\":{\"x\":53.658,\"y\":42.389936},\"height\":31.263383,\"mouth_left\":{\"x\":42.352167,\"y\":56.311991},\"mouth_right\":{\"x\":53.747833,\"y\":56.89379},\"nose\":{\"x\":47.392667,\"y\":51.794004},\"width\":24.333333}\n     * tag :\n     */\n\n    public List<FaceEntity> face;\n\n    public static class FaceEntity {\n        /**\n         * age : {\"range\":6,\"value\":18}\n         * gender : {\"confidence\":99.9996,\"value\":\"Male\"}\n         * race : {\"confidence\":99.8977,\"value\":\"White\"}\n         * smiling : {\"value\":81.2229}\n         */\n\n        public AttributeEntity attribute;\n        public String face_id;\n        /**\n         * center : {\"x\":47.833333,\"y\":49.036403}\n         * eye_left : {\"x\":42.140333,\"y\":42.28394}\n         * eye_right : {\"x\":53.658,\"y\":42.389936}\n         * height : 31.263383\n         * mouth_left : {\"x\":42.352167,\"y\":56.311991}\n         * mouth_right : {\"x\":53.747833,\"y\":56.89379}\n         * nose : {\"x\":47.392667,\"y\":51.794004}\n         * width : 24.333333\n         */\n\n        public PositionEntity position;\n        public String tag;\n\n        public static class AttributeEntity {\n            /**\n             * range : 6\n             * value : 18\n             */\n\n            public AgeEntity age;\n            /**\n             * confidence : 99.9996\n             * value : Male\n             */\n\n            public GenderEntity gender;\n            /**\n             * confidence : 99.8977\n             * value : White\n             */\n\n            public RaceEntity race;\n            /**\n             * value : 81.2229\n             */\n\n            public SmilingEntity smiling;\n\n            public static class AgeEntity {\n                public int range;\n                public int value;\n            }\n\n            public static class GenderEntity {\n                public double confidence;\n                public String value;\n            }\n\n            public static class RaceEntity {\n                public double confidence;\n                public String value;\n            }\n\n            public static class SmilingEntity {\n                public double value;\n            }\n        }\n\n        public static class PositionEntity {\n            /**\n             * x : 47.833333\n             * y : 49.036403\n             */\n\n            public CenterEntity center;\n            /**\n             * x : 42.140333\n             * y : 42.28394\n             */\n\n            public EyeLeftEntity eye_left;\n            /**\n             * x : 53.658\n             * y : 42.389936\n             */\n\n            public EyeRightEntity eye_right;\n            public double height;\n            /**\n             * x : 42.352167\n             * y : 56.311991\n             */\n\n            public MouthLeftEntity mouth_left;\n            /**\n             * x : 53.747833\n             * y : 56.89379\n             */\n\n            public MouthRightEntity mouth_right;\n            /**\n             * x : 47.392667\n             * y : 51.794004\n             */\n\n            public NoseEntity nose;\n            public double width;\n\n            public static class CenterEntity {\n                public double x;\n                public double y;\n            }\n\n            public static class EyeLeftEntity {\n                public double x;\n                public double y;\n            }\n\n            public static class EyeRightEntity {\n                public double x;\n                public double y;\n            }\n\n            public static class MouthLeftEntity {\n                public double x;\n                public double y;\n            }\n\n            public static class MouthRightEntity {\n                public double x;\n                public double y;\n            }\n\n            public static class NoseEntity {\n                public double x;\n                public double y;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/mrfu/rxface/models/NeedDataEntity.java",
    "content": "package mrfu.rxface.models;\n\nimport android.graphics.Bitmap;\n\n/**\n * Created by MrFu on 15/12/16.\n */\npublic class NeedDataEntity {\n    public Bitmap bitmap;\n    public String displayStr;\n}\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<android.support.design.widget.CoordinatorLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:fitsSystemWindows=\"true\"\n    tools:context=\".MainActivity\">\n\n    <android.support.design.widget.AppBarLayout\n        android:layout_height=\"wrap_content\"\n        android:layout_width=\"match_parent\"\n        android:theme=\"@style/AppTheme.AppBarOverlay\">\n\n        <android.support.v7.widget.Toolbar\n            android:id=\"@+id/toolbar\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"?attr/actionBarSize\"\n            android:background=\"?attr/colorPrimary\"\n            app:popupTheme=\"@style/AppTheme.PopupOverlay\"/>\n\n    </android.support.design.widget.AppBarLayout>\n\n    <include layout=\"@layout/content_main\"/>\n\n    <!--<android.support.design.widget.FloatingActionButton-->\n        <!--android:id=\"@+id/fab\"-->\n        <!--android:layout_width=\"wrap_content\"-->\n        <!--android:layout_height=\"wrap_content\"-->\n        <!--android:layout_gravity=\"bottom|end\"-->\n        <!--android:layout_margin=\"@dimen/fab_margin\"-->\n        <!--android:src=\"@android:drawable/ic_dialog_email\"/>-->\n\n</android.support.design.widget.CoordinatorLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/content_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<LinearLayout\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:orientation=\"vertical\"\n    android:paddingBottom=\"@dimen/activity_vertical_margin\"\n    android:paddingLeft=\"@dimen/activity_horizontal_margin\"\n    android:paddingRight=\"@dimen/activity_horizontal_margin\"\n    android:paddingTop=\"@dimen/activity_vertical_margin\"\n    app:layout_behavior=\"@string/appbar_scrolling_view_behavior\"\n    tools:context=\".MainActivity\"\n    tools:showIn=\"@layout/activity_main\">\n\n\n    <ImageView\n        android:id=\"@+id/iv_face_get\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"2\"/>\n\n\n    <ImageView\n        android:id=\"@+id/iv_face_post\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_weight=\"2\"/>\n\n    <RelativeLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"0dp\"\n        android:layout_marginTop=\"10dp\"\n        android:layout_weight=\"1\">\n\n        <Button\n            android:id=\"@+id/btn_recongize_get\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_below=\"@+id/tv_age_get\"\n            android:layout_marginLeft=\"20dp\"\n            android:layout_marginTop=\"10dp\"\n            android:padding=\"20dp\"\n            android:text=\"@string/reconginze_get\"/>\n\n        <TextView\n            android:id=\"@+id/tv_age_get\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentLeft=\"true\"\n            android:layout_marginLeft=\"20dp\"/>\n\n        <Button\n            android:id=\"@+id/btn_recongize_post\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_below=\"@+id/tv_age_post\"\n            android:layout_marginRight=\"20dp\"\n            android:layout_marginTop=\"10dp\"\n            android:padding=\"20dp\"\n            android:text=\"@string/reconginze_post\"/>\n\n        <TextView\n            android:id=\"@+id/tv_age_post\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_alignParentRight=\"true\"\n            android:layout_marginRight=\"20dp\"/>\n\n    </RelativeLayout>\n\n\n</LinearLayout>\n"
  },
  {
    "path": "app/src/main/res/menu/menu_main.xml",
    "content": "<menu xmlns:android=\"http://schemas.android.com/apk/res/android\"\n      xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n      xmlns:tools=\"http://schemas.android.com/tools\"\n      tools:context=\".MainActivity\">\n    <item android:id=\"@+id/action_settings\"\n          android:title=\"@string/github\"\n          android:orderInCategory=\"100\"\n          app:showAsAction=\"never\"/>\n</menu>\n"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/dimens.xml",
    "content": "<resources>\n    <!-- Default screen margins, per the Android Design guidelines. -->\n    <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n    <dimen name=\"activity_vertical_margin\">16dp</dimen>\n    <dimen name=\"fab_margin\">16dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">RxFace</string>\n    <string name=\"action_settings\">Settings</string>\n    <string name=\"github\">Github</string>\n    <string name=\"reconginze_get\">Get识别</string>\n    <string name=\"reconginze_post\">Post识别</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.AppCompat.Light.DarkActionBar\">\n        <!-- Customize your theme here. -->\n        <item name=\"colorPrimary\">@color/colorPrimary</item>\n        <item name=\"colorPrimaryDark\">@color/colorPrimaryDark</item>\n        <item name=\"colorAccent\">@color/colorAccent</item>\n    </style>\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n    </style>\n    <style name=\"AppTheme.AppBarOverlay\" parent=\"ThemeOverlay.AppCompat.Dark.ActionBar\"/>\n    <style name=\"AppTheme.PopupOverlay\" parent=\"ThemeOverlay.AppCompat.Light\"/>\n\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-v21/styles.xml",
    "content": "<resources>>\n    <style name=\"AppTheme.NoActionBar\">\n        <item name=\"windowActionBar\">false</item>\n        <item name=\"windowNoTitle\">true</item>\n        <item name=\"android:windowDrawsSystemBarBackgrounds\">true</item>\n        <item name=\"android:statusBarColor\">@android:color/transparent</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values-w820dp/dimens.xml",
    "content": "<resources>\n    <!-- Example customization of dimensions originally defined in res/values/dimens.xml\n         (such as screen margins) for screens with more than 820dp of available width. This\n         would include 7\" and 10\" devices in landscape (~960dp and ~1280dp respectively). -->\n    <dimen name=\"activity_horizontal_margin\">64dp</dimen>\n</resources>\n"
  },
  {
    "path": "app/src/test/java/mrfu/face/ExampleUnitTest.java",
    "content": "package mrfu.face;\n\nimport org.junit.Test;\n\nimport static org.junit.Assert.*;\n\n/**\n * To work on unit tests, switch the Test Artifact in the Build Variants view.\n */\npublic class ExampleUnitTest {\n    @Test\n    public void addition_isCorrect() throws Exception {\n        assertEquals(4, 2 + 2);\n    }\n}"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    repositories {\n        jcenter()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:1.3.0'\n//        classpath 'me.tatarka:gradle-retrolambda:3.2.3'\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        jcenter()\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Dec 15 21:05:14 CST 2015\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-2.4-all.zip\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\n# Default value: -Xmx10248m -XX:MaxPermSize=256m\n# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8\n\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env bash\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS=\"\"\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn ( ) {\n    echo \"$*\"\n}\n\ndie ( ) {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\nesac\n\n# For Cygwin, ensure paths are in UNIX format before anything is touched.\nif $cygwin ; then\n    [ -n \"$JAVA_HOME\" ] && JAVA_HOME=`cygpath --unix \"$JAVA_HOME\"`\nfi\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >&-\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >&-\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin, switch paths to Windows format before running java\nif $cygwin ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules\nfunction splitJvmOpts() {\n    JVM_OPTS=(\"$@\")\n}\neval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS\nJVM_OPTS[${#JVM_OPTS[*]}]=\"-Dorg.gradle.appname=$APP_BASE_NAME\"\n\nexec \"$JAVACMD\" \"${JVM_OPTS[@]}\" -classpath \"$CLASSPATH\" org.gradle.wrapper.GradleWrapperMain \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@if \"%DEBUG%\" == \"\" @echo off\r\n@rem ##########################################################################\r\n@rem\r\n@rem  Gradle startup script for Windows\r\n@rem\r\n@rem ##########################################################################\r\n\r\n@rem Set local scope for the variables with windows NT shell\r\nif \"%OS%\"==\"Windows_NT\" setlocal\r\n\r\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r\nset DEFAULT_JVM_OPTS=\r\n\r\nset DIRNAME=%~dp0\r\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\r\nset APP_BASE_NAME=%~n0\r\nset APP_HOME=%DIRNAME%\r\n\r\n@rem Find java.exe\r\nif defined JAVA_HOME goto findJavaFromJavaHome\r\n\r\nset JAVA_EXE=java.exe\r\n%JAVA_EXE% -version >NUL 2>&1\r\nif \"%ERRORLEVEL%\" == \"0\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:findJavaFromJavaHome\r\nset JAVA_HOME=%JAVA_HOME:\"=%\r\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\r\n\r\nif exist \"%JAVA_EXE%\" goto init\r\n\r\necho.\r\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r\necho.\r\necho Please set the JAVA_HOME variable in your environment to match the\r\necho location of your Java installation.\r\n\r\ngoto fail\r\n\r\n:init\r\n@rem Get command-line arguments, handling Windowz variants\r\n\r\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\r\nif \"%@eval[2+2]\" == \"4\" goto 4NT_args\r\n\r\n:win9xME_args\r\n@rem Slurp the command line arguments.\r\nset CMD_LINE_ARGS=\r\nset _SKIP=2\r\n\r\n:win9xME_args_slurp\r\nif \"x%~1\" == \"x\" goto execute\r\n\r\nset CMD_LINE_ARGS=%*\r\ngoto execute\r\n\r\n:4NT_args\r\n@rem Get arguments from the 4NT Shell from JP Software\r\nset CMD_LINE_ARGS=%$\r\n\r\n:execute\r\n@rem Setup the command line\r\n\r\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\r\n\r\n@rem Execute Gradle\r\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r\n\r\n:end\r\n@rem End local scope for the variables with windows NT shell\r\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\r\n\r\n:fail\r\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r\nrem the _cmd.exe /c_ return code!\r\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\r\nexit /b 1\r\n\r\n:mainEnd\r\nif \"%OS%\"==\"Windows_NT\" endlocal\r\n\r\n:omega\r\n"
  },
  {
    "path": "sdk_unuse/HttpRequests.java",
    "content": "package mrfu.face.sdk;\r\n\r\nimport java.io.IOException;\r\nimport java.io.InputStream;\r\nimport java.net.HttpURLConnection;\r\nimport java.net.URL;\r\nimport java.util.Date;\r\n\r\nimport org.apache.http.entity.mime.MultipartEntity;\r\nimport org.apache.http.entity.mime.content.StringBody;\r\nimport org.json.JSONException;\r\nimport org.json.JSONObject;\r\n\r\nimport com.facepp.error.FaceppParseException;\r\n\r\n/**\r\n * request to faceplusplus.com<br />\r\n * {@code new HttpRequests(apiKey, apiSecret).request(\"detection\", \"detect\", postParameters)}<br />\r\n * \r\n * {@code new HttpRequests(apiKey, apiSecret).train()}\r\n * @author moon5ckq\r\n * @since 1.0.0\r\n * @version 1.3.0\r\n */\r\npublic class HttpRequests {\r\n\t\r\n\tstatic final private String WEBSITE_CN = \"https://apicn.faceplusplus.com/v2/\";\r\n\tstatic final private String DWEBSITE_CN = \"http://apicn.faceplusplus.com/v2/\";\r\n\tstatic final private String WEBSITE_US = \"https://apius.faceplusplus.com/v2/\";\r\n\tstatic final private String DWEBSITE_US = \"http://apius.faceplusplus.com/v2/\";\r\n\t \r\n\tstatic final private int BUFFERSIZE = 1048576;\r\n\tstatic final private int TIMEOUT = 30000;\r\n\tstatic final private int SYNC_TIMEOUT = 60000;\r\n\t\r\n\tprivate String webSite;\r\n\tprivate String apiKey, apiSecret;\r\n\tprivate PostParameters params;\r\n\tprivate int httpTimeOut = TIMEOUT;\r\n\t\r\n\t\r\n\t/**\r\n\t * default is 30 sec\r\n\t * set http timeout limit (million second)\r\n\t * @param timeOut\r\n\t */\r\n\tpublic void setHttpTimeOut(int timeOut) {\r\n\t\tthis.httpTimeOut = timeOut;\r\n\t}\r\n\t\r\n\t/**\r\n\t * (million second)\r\n\t * @return http timeout limit\r\n\t */\r\n\tpublic int getHttpTimeOut() {\r\n\t\treturn this.httpTimeOut;\r\n\t}\r\n\t\r\n\t/**\r\n\t * @return api_key\r\n\t */\r\n\tpublic String getApiKey() {\r\n\t\treturn apiKey;\r\n\t}\r\n\r\n\t/**\r\n\t * @param apiKey\r\n\t */\r\n\tpublic void setApiKey(String apiKey) {\r\n\t\tthis.apiKey = apiKey;\r\n\t}\r\n\r\n\t/**\r\n\t * @return api_secret\r\n\t */\r\n\tpublic String getApiSecret() {\r\n\t\treturn apiSecret;\r\n\t}\r\n\r\n\t/**\r\n\t * @param apiSecret\r\n\t */\r\n\tpublic void setApiSecret(String apiSecret) {\r\n\t\tthis.apiSecret = apiSecret;\r\n\t}\r\n\t\r\n\t/**\r\n\t * if isCN is true, then use AliCloud, false to Amazon<br />\r\n\t * if isDebug is true, then use http, otherwise https\r\n\t * @param isCN\r\n\t * @param isDebug\r\n\t */\r\n\tpublic void setWebSite(boolean isCN, boolean isDebug) {\r\n\t\tif (isCN && isDebug) webSite = DWEBSITE_CN;\r\n\t\telse if (isCN && !isDebug) webSite = WEBSITE_CN;\r\n\t\telse if (!isCN && isDebug) webSite = DWEBSITE_US;\r\n\t\telse if (!isCN && !isDebug) webSite = WEBSITE_US;\r\n\t}\r\n\t\r\n\t/**\r\n\t * @return a webSite clone\r\n\t */\r\n\tpublic String getWebSite() {\r\n\t\treturn new String(webSite);\r\n\t}\r\n\r\n\t/**\r\n\t * {@link #request(String, String, PostParameters)}<br />\r\n\t * faceplusplus.com/[control]/[action]<br />\r\n\t * default use parameters which {@link #getParams}\r\n\t * @param control\r\n\t * @param action\r\n\t * @return a result object\r\n\t */\r\n\tpublic JSONObject request(String control, String action) throws FaceppParseException {\r\n\t\treturn request(control, action, getParams());\r\n\t}\r\n\t\r\n\r\n\t/**\r\n\t * default timeout time is 1 minute\r\n\t * @param sessionId\r\n\t * @return the getSession Result\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject getSessionSync(String sessionId) throws FaceppParseException {\r\n\t\treturn getSessionSync(sessionId, SYNC_TIMEOUT);\r\n\t}\r\n\t\r\n\t/**\r\n\t * timeout time is [timeOut]ms, the method is synchronized.\r\n\t * @param sessionId\r\n\t * @param timeOut\r\n\t * @return the getSession Result\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject getSessionSync(String sessionId, long timeOut) throws FaceppParseException {\r\n\t\tfinal StringBuilder sb = new StringBuilder();\r\n\t\tlong t = new Date().getTime() + timeOut;\r\n\t\twhile (true) {\r\n\t\t\tJSONObject rst =  HttpRequests.this.request(\"info\", \"get_session\", new PostParameters().setSessionId(sessionId));\r\n\t\t\ttry {\r\n\t\t\t\tif (rst.getString(\"status\").equals(\"SUCC\")) {\r\n\t\t\t\t\tsb.append(rst.toString());\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t} else if (rst.getString(\"status\").equals(\"INVALID_SESSION\")) {\r\n\t\t\t\t\tsb.append(\"INVALID_SESSION\");\r\n\t\t\t\t\tbreak;\r\n\t\t\t\t}\r\n\t\t\t} catch (JSONException e) {\r\n\t\t\t\tsb.append(\"Unknow error.\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\ttry {\r\n\t\t\t\tThread.sleep(1000);\r\n\t\t\t} catch (InterruptedException e) {\r\n\t\t\t\tsb.append(\"Thread.sleep error.\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t\tif (new Date().getTime() >= t) {\r\n\t\t\t\tsb.append(\"Time Out\");\r\n\t\t\t\tbreak;\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\tString rst = sb.toString();\r\n\t\tif (rst.equals(\"INVALID_SESSION\")) {\r\n\t\t\tthrow new FaceppParseException(\"Invaild session, unknow error.\");\r\n\t\t} else if (rst.equals(\"Unknow error.\")) {\r\n\t\t\tthrow new FaceppParseException(\"Unknow error.\");\r\n\t\t} else if (rst.equals(\"Thread.sleep error.\")) {\r\n\t\t\tthrow new FaceppParseException(\"Thread.sleep error.\");\r\n\t\t} else if (rst.equals(\"Time Out\")) {\r\n\t\t\tthrow new FaceppParseException(\"Get session time out.\");\r\n\t\t} else {\r\n\t\t\ttry {\r\n\t\t\t\tJSONObject result = new JSONObject(rst);\r\n\t\t\t\tresult.put(\"response_code\", 200);\r\n\t\t\t\treturn result;\r\n\t\t\t} catch (JSONException e) {\r\n\t\t\t}\r\n\t\t}\r\n\t\t\r\n\t\treturn null;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceplusplus.com/[control]/[action]?[params]<br />\r\n\t * http request timeout time is 5000ms\r\n\t * @param control\r\n\t * @param action\r\n\t * @param params\r\n\t * @return a result object\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject request(String control, String action, PostParameters params) throws FaceppParseException {\r\n\t\tURL url;\r\n\t\tHttpURLConnection urlConn = null;\r\n\t\ttry {\r\n\t\t\turl = new URL(webSite+control+\"/\"+action);\r\n\t\t\turlConn = (HttpURLConnection) url.openConnection();\r\n\t        urlConn.setRequestMethod(\"POST\");\r\n\t        urlConn.setConnectTimeout(httpTimeOut);\r\n\t        urlConn.setReadTimeout(httpTimeOut);\r\n\t        urlConn.setDoOutput(true);\r\n\r\n\t        urlConn.setRequestProperty(\"connection\", \"keep-alive\");\r\n\t        urlConn.setRequestProperty(\"Content-Type\", \"multipart/form-data; boundary=\" + params.boundaryString());\r\n\t\t\t\r\n\t        MultipartEntity reqEntity =params.getMultiPart();\r\n\r\n            reqEntity.addPart(\"api_key\", new StringBody(apiKey));\r\n            reqEntity.addPart(\"api_secret\", new StringBody(apiSecret));\r\n\r\n            reqEntity.writeTo(urlConn.getOutputStream());\r\n\r\n            String resultString = null;\r\n            if (urlConn.getResponseCode() == 200)\r\n            \tresultString = readString(urlConn.getInputStream());\r\n            else\r\n            \tresultString = readString(urlConn.getErrorStream());\r\n            //FaceppResult result = new FaceppResult( new JSONObject(resultString), urlConn.getResponseCode());\r\n\r\n            JSONObject result = new JSONObject(resultString);\r\n            if (result.has(\"error\")) {\r\n\r\n            \tif (result.getString(\"error\").equals(\"API not found\"))\r\n            \t\tthrow new FaceppParseException(\"API not found\");\r\n\r\n            \tthrow new FaceppParseException(\"API error.\", result.getInt(\"error_code\"),\r\n            \t\t\tresult.getString(\"error\"), urlConn.getResponseCode());\r\n            }\r\n            result.put(\"response_code\", urlConn.getResponseCode());\r\n            urlConn.getInputStream().close();\r\n\r\n            return result;\r\n\t\t} catch (Exception e) {\t\t\tthrow new FaceppParseException(\"error :\" + e.toString());\r\n\r\n\t\t} finally {\t\t\tif (urlConn != null)\r\n\t\t\t\turlConn.disconnect();\r\n\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate static String readString(InputStream is) {\r\n\t\tStringBuffer rst = new StringBuffer();\r\n\t\t\r\n\t\tbyte[] buffer = new byte[BUFFERSIZE];\r\n\t\tint len = 0;\r\n\t\t\r\n\t\ttry {\r\n\t\t\twhile ((len = is.read(buffer)) > 0)\r\n\t\t\t\tfor (int i = 0; i < len; ++i)\r\n\t\t\t\t\trst.append((char)buffer[i]);\r\n\t\t} catch (IOException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t\t\r\n\t\treturn rst.toString();\r\n\t}\r\n\t\r\n\t/**\r\n\t * create {@link HttpRequests} <br />\r\n\t * api_key=...,api_secret=... <br />\r\n\t * use https and AliCloud default\r\n\t * @param apiKey\r\n\t * @param apiSecret\r\n\t */\r\n\tpublic HttpRequests(String apiKey, String apiSecret) {\r\n\t\tsuper();\r\n\t\tthis.apiKey = apiKey;\r\n\t\tthis.apiSecret = apiSecret;\r\n\t\tthis.webSite = WEBSITE_CN;\r\n\t}\r\n\r\n\t/**\r\n\t * use https default\r\n\t * create a empty {@link HttpRequests} object\r\n\t */\r\n\tpublic HttpRequests() {\r\n\t\tsuper();\r\n\t}\r\n\t\r\n\t/**\r\n\t * create {@link HttpRequests} <br />\r\n\t * api_key=...,api_secret=...<br />\r\n\t * the isCN and isDebug use like {@link setWebSite}}\r\n\t * @param apiKey\r\n\t * @param apiSecret\r\n\t * @param isCN\r\n\t * @param isDebug\r\n\t */\r\n\tpublic HttpRequests(String apiKey, String apiSecret, boolean isCN, boolean isDebug) {\r\n\t\tsuper();\r\n\t\tthis.apiKey = apiKey;\r\n\t\tthis.apiSecret = apiSecret;\r\n\t\tsetWebSite(isCN, isDebug);\r\n\t}\r\n\t\r\n\t/**\r\n\t * @return {@link PostParameters} object\r\n\t */\r\n\tpublic PostParameters getParams() {\r\n\t\tif (params == null) params = new PostParameters();\r\n\t\treturn params;\r\n\t}\r\n\t\r\n\t/**\r\n\t * set default PostParameters\r\n\t * @param params\r\n\t */\r\n\tpublic void setParams(PostParameters params) {\r\n\t\tthis.params = params;\r\n\t}\r\n\t\r\n\t/**\r\n\t * used by offline detect\r\n\t * example: request.offlineDetect(detecter.getImageByteArray(), detecter.getResultJsonString());\r\n\t * @param image\r\n\t * @param jsonResult\r\n\t * @return\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject offlineDetect(byte[] image, String jsonResult) throws FaceppParseException {\r\n\t\treturn offlineDetect(image, jsonResult, this.params);\r\n\t}\r\n\t/**\r\n\t * used by offline detect\r\n\t * example: request.offlineDetect(detecter.getImageByteArray(), detecter.getResultJsonString(), params);\r\n\t * @param image\r\n\t * @param jsonResult\r\n\t * @param params\r\n\t * @return\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject offlineDetect(byte[] image, String jsonResult, PostParameters params) throws FaceppParseException{\r\n\t\tif (params == null) params = new PostParameters();\r\n\t\tparams.setImg(image);\r\n\t\tparams.setMode(\"offline\");\r\n\t\tparams.addAttribute(\"offline_result\", jsonResult);\r\n\t\treturn request(\"detection\", \"detect\", params);\r\n\t}\r\n\t\r\n\t//all api here\r\n\tpublic JSONObject detectionDetect() throws FaceppParseException {\r\n\t\treturn request(\"detection\", \"detect\");\r\n\t}\r\n\tpublic JSONObject detectionDetect(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"detection\", \"detect\", params);\r\n\t}\r\n\t\r\n\tpublic JSONObject detectionLandmark() throws FaceppParseException {\r\n\t\treturn request(\"detection\", \"landmark\");\r\n\t}\r\n\tpublic JSONObject detectionLandmark(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"detection\", \"landmark\", params);\r\n\t}\r\n\r\n\tpublic JSONObject trainVerify() throws FaceppParseException {\r\n\t\treturn request(\"train\", \"verify\");\r\n\t}\r\n\tpublic JSONObject trainVerify(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"train\", \"verify\", params);\r\n\t}\r\n\r\n\tpublic JSONObject trainSearch() throws FaceppParseException {\r\n\t\treturn request(\"train\", \"search\");\r\n\t}\r\n\tpublic JSONObject trainSearch(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"train\", \"search\", params);\r\n\t}\r\n\r\n\tpublic JSONObject trainIdentify() throws FaceppParseException {\r\n\t\treturn request(\"train\", \"identify\");\r\n\t}\r\n\tpublic JSONObject trainIdentify(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"train\", \"identify\", params);\r\n\t}\r\n\r\n\tpublic JSONObject recognitionCompare() throws FaceppParseException {\r\n\t\treturn request(\"recognition\", \"compare\");\r\n\t}\r\n\tpublic JSONObject recognitionCompare(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"recognition\", \"compare\", params);\r\n\t}\r\n\r\n\tpublic JSONObject recognitionVerify() throws FaceppParseException {\r\n\t\treturn request(\"recognition\", \"verify\");\r\n\t}\r\n\tpublic JSONObject recognitionVerify(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"recognition\", \"verify\", params);\r\n\t}\r\n\r\n\tpublic JSONObject recognitionSearch() throws FaceppParseException {\r\n\t\treturn request(\"recognition\", \"search\");\r\n\t}\r\n\tpublic JSONObject recognitionSearch(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"recognition\", \"search\", params);\r\n\t}\r\n\r\n\tpublic JSONObject recognitionIdentify() throws FaceppParseException {\r\n\t\treturn request(\"recognition\", \"identify\");\r\n\t}\r\n\tpublic JSONObject recognitionIdentify(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"recognition\", \"identify\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupingGrouping() throws FaceppParseException {\r\n\t\treturn request(\"grouping\", \"grouping\");\r\n\t}\r\n\tpublic JSONObject groupingGrouping(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"grouping\", \"grouping\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personCreate() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"create\");\r\n\t}\r\n\tpublic JSONObject personCreate(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"create\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personDelete() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"delete\");\r\n\t}\r\n\tpublic JSONObject personDelete(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"delete\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personAddFace() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"add_face\");\r\n\t}\r\n\tpublic JSONObject personAddFace(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"add_face\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personRemoveFace() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"remove_face\");\r\n\t}\r\n\tpublic JSONObject personRemoveFace(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"remove_face\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personSetInfo() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"set_info\");\r\n\t}\r\n\tpublic JSONObject personSetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"set_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject personGetInfo() throws FaceppParseException {\r\n\t\treturn request(\"person\", \"get_info\");\r\n\t}\r\n\tpublic JSONObject personGetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"person\", \"get_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetCreate() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"create\");\r\n\t}\r\n\tpublic JSONObject facesetCreate(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"create\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetDelete() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"delete\");\r\n\t}\r\n\tpublic JSONObject facesetDelete(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"delete\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetAddFace() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"add_face\");\r\n\t}\r\n\tpublic JSONObject facesetAddFace(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"add_face\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetRemoveFace() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"remove_face\");\r\n\t}\r\n\tpublic JSONObject facesetRemoveFace(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"remove_face\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetSetInfo() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"set_info\");\r\n\t}\r\n\tpublic JSONObject facesetSetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"set_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject facesetGetInfo() throws FaceppParseException {\r\n\t\treturn request(\"faceset\", \"get_info\");\r\n\t}\r\n\tpublic JSONObject facesetGetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"faceset\", \"get_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupCreate() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"create\");\r\n\t}\r\n\tpublic JSONObject groupCreate(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"create\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupDelete() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"delete\");\r\n\t}\r\n\tpublic JSONObject groupDelete(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"delete\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupAddPerson() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"add_person\");\r\n\t}\r\n\tpublic JSONObject groupAddPerson(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"add_person\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupRemovePerson() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"remove_person\");\r\n\t}\r\n\tpublic JSONObject groupRemovePerson(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"remove_person\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupSetInfo() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"set_info\");\r\n\t}\r\n\tpublic JSONObject groupSetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"set_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject groupGetInfo() throws FaceppParseException {\r\n\t\treturn request(\"group\", \"get_info\");\r\n\t}\r\n\tpublic JSONObject groupGetInfo(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"group\", \"get_info\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetImage() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_image\");\r\n\t}\r\n\tpublic JSONObject infoGetImage(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_image\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetFace() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_face\");\r\n\t}\r\n\tpublic JSONObject infoGetFace(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_face\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetPersonList() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_person_list\");\r\n\t}\r\n\tpublic JSONObject infoGetPersonList(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_person_list\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetFacesetList() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_faceset_list\");\r\n\t}\r\n\tpublic JSONObject infoGetFacesetList(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_faceset_list\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetGroupList() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_group_list\");\r\n\t}\r\n\tpublic JSONObject infoGetGroupList(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_group_list\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetSession() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_session\");\r\n\t}\r\n\tpublic JSONObject infoGetSession(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_session\", params);\r\n\t}\r\n\r\n\t/**\r\n\t * @deprecated this api is deprecated\r\n\t * @return\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject infoGetQuota() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_quota\");\r\n\t}\r\n\t/**\r\n\t * @deprecated this api is deprecated\r\n\t * @return\r\n\t * @throws FaceppParseException\r\n\t */\r\n\tpublic JSONObject infoGetQuota(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_quota\", params);\r\n\t}\r\n\r\n\tpublic JSONObject infoGetApp() throws FaceppParseException {\r\n\t\treturn request(\"info\", \"get_app\");\r\n\t}\r\n\tpublic JSONObject infoGetApp(PostParameters params) throws FaceppParseException{\r\n\t\treturn request(\"info\", \"get_app\", params);\r\n\t}\r\n\r\n}\r\n"
  },
  {
    "path": "sdk_unuse/MainActivity.java",
    "content": "package mrfu.face.sdk;\n\nimport android.app.Activity;\nimport android.content.Intent;\nimport android.database.Cursor;\nimport android.graphics.Bitmap;\nimport android.graphics.BitmapFactory;\nimport android.graphics.BitmapFactory.Options;\nimport android.graphics.Canvas;\nimport android.graphics.Color;\nimport android.graphics.Matrix;\nimport android.graphics.Paint;\nimport android.os.Bundle;\nimport android.provider.MediaStore.Images.ImageColumns;\nimport android.util.Log;\nimport android.view.Menu;\nimport android.view.View;\nimport android.view.View.OnClickListener;\nimport android.widget.Button;\nimport android.widget.ImageView;\nimport android.widget.TextView;\n\nimport com.facepp.error.FaceppParseException;\n\nimport org.json.JSONException;\nimport org.json.JSONObject;\n\nimport java.io.ByteArrayOutputStream;\n\nimport mrfu.face.R;\n\n/**\n * A simple demo, get a picture form your phone<br />\n * Use the facepp api to detect<br />\n * Find all face on the picture, and mark them out.\n * @author moon5ckq\n */\npublic class MainActivity extends Activity {\n\n\tfinal private static String TAG = \"MainActivity\";\n\tfinal private int PICTURE_CHOOSE = 1;\n\t\n\tprivate ImageView imageView = null;\n\tprivate Bitmap img = null;\n\tprivate Button buttonDetect = null;\n\tprivate TextView textView = null;\n\t\n\t\n    @Override\n    public void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setContentView(R.layout.activity_main);\n        \n//        Button button = (Button)this.findViewById(R.id.button1);\n//        button.setOnClickListener(new OnClickListener() {\n//\t\t\t\n//\t\t\tpublic void onClick(View arg0) {\n//\t\t\t\t//get a picture form your phone\n//\t\t\t\tIntent photoPickerIntent = new Intent(Intent.ACTION_PICK);\n//\t\t        photoPickerIntent.setType(\"image/*\");\n//\t\t        startActivityForResult(photoPickerIntent, PICTURE_CHOOSE);\n//\t\t\t}\n//\t\t});\n        \n//        textView = (TextView)this.findViewById(R.id.textView1);\n        \n//        buttonDetect = (Button)this.findViewById(R.id.button2);\n        buttonDetect.setVisibility(View.INVISIBLE);\n        buttonDetect.setOnClickListener(new OnClickListener() {\n\t\t\tpublic void onClick(View arg0) {\n\t\t\t\t\n\t\t\t\ttextView.setText(\"Waiting ...\");\n\t\t\t\t\n\t\t\t\tFaceppDetect faceppDetect = new FaceppDetect();\n\t\t\t\tfaceppDetect.setDetectCallback(new DetectCallback() {\n\t\t\t\t\t\n\t\t\t\t\tpublic void detectResult(JSONObject rst) {\n\t\t\t\t\t\t//Log.v(TAG, rst.toString());\n\t\t\t\t\t\t\n\t\t\t\t\t\t//use the red paint\n\t\t\t\t\t\tPaint paint = new Paint();\n\t\t\t\t\t\tpaint.setColor(Color.RED);\n\t\t\t\t\t\tpaint.setStrokeWidth(Math.max(img.getWidth(), img.getHeight()) / 100f);\n\n\t\t\t\t\t\t//create a new canvas\n\t\t\t\t\t\tBitmap bitmap = Bitmap.createBitmap(img.getWidth(), img.getHeight(), img.getConfig());\n\t\t\t\t\t\tCanvas canvas = new Canvas(bitmap);\n\t\t\t\t\t\tcanvas.drawBitmap(img, new Matrix(), null);\n\t\t\t\t\t\t\n\t\t\t\t\t\t\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\t//find out all faces\n\t\t\t\t\t\t\tfinal int count = rst.getJSONArray(\"face\").length();\n\t\t\t\t\t\t\tfor (int i = 0; i < count; ++i) {\n\t\t\t\t\t\t\t\tfloat x, y, w, h;\n\t\t\t\t\t\t\t\t//get the center point\n\t\t\t\t\t\t\t\tx = (float)rst.getJSONArray(\"face\").getJSONObject(i)\n\t\t\t\t\t\t\t\t\t\t.getJSONObject(\"position\").getJSONObject(\"center\").getDouble(\"x\");\n\t\t\t\t\t\t\t\ty = (float)rst.getJSONArray(\"face\").getJSONObject(i)\n\t\t\t\t\t\t\t\t\t\t.getJSONObject(\"position\").getJSONObject(\"center\").getDouble(\"y\");\n\n\t\t\t\t\t\t\t\t//get face size\n\t\t\t\t\t\t\t\tw = (float)rst.getJSONArray(\"face\").getJSONObject(i)\n\t\t\t\t\t\t\t\t\t\t.getJSONObject(\"position\").getDouble(\"width\");\n\t\t\t\t\t\t\t\th = (float)rst.getJSONArray(\"face\").getJSONObject(i)\n\t\t\t\t\t\t\t\t\t\t.getJSONObject(\"position\").getDouble(\"height\");\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t//change percent value to the real size\n\t\t\t\t\t\t\t\tx = x / 100 * img.getWidth();\n\t\t\t\t\t\t\t\tw = w / 100 * img.getWidth() * 0.7f;\n\t\t\t\t\t\t\t\ty = y / 100 * img.getHeight();\n\t\t\t\t\t\t\t\th = h / 100 * img.getHeight() * 0.7f;\n\n\t\t\t\t\t\t\t\t//draw the box to mark it out\n\t\t\t\t\t\t\t\tcanvas.drawLine(x - w, y - h, x - w, y + h, paint);\n\t\t\t\t\t\t\t\tcanvas.drawLine(x - w, y - h, x + w, y - h, paint);\n\t\t\t\t\t\t\t\tcanvas.drawLine(x + w, y + h, x - w, y + h, paint);\n\t\t\t\t\t\t\t\tcanvas.drawLine(x + w, y + h, x + w, y - h, paint);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t//save new image\n\t\t\t\t\t\t\timg = bitmap;\n\n\t\t\t\t\t\t\tMainActivity.this.runOnUiThread(new Runnable() {\n\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\t//show the image\n\t\t\t\t\t\t\t\t\timageView.setImageBitmap(img);\n\t\t\t\t\t\t\t\t\ttextView.setText(\"Finished, \"+ count + \" faces.\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t} catch (JSONException e) {\n\t\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\t\tMainActivity.this.runOnUiThread(new Runnable() {\n\t\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\t\ttextView.setText(\"Error.\");\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\t\n\t\t\t\t\t}\n\t\t\t\t});\n\t\t\t\tfaceppDetect.detect(img);\n\t\t\t}\n\t\t});\n        \n//        imageView = (ImageView)this.findViewById(R.id.imageView1);\n        imageView.setImageBitmap(img);\n    }\n\n    @Override\n    public boolean onCreateOptionsMenu(Menu menu) {\n//        getMenuInflater().inflate(R.menu.activity_main, menu);\n        return true;\n    }\n\n    @Override\n    protected void onActivityResult(int requestCode, int resultCode, Intent intent) {\n    \tsuper.onActivityResult(requestCode, resultCode, intent);\n    \t\n    \t//the image picker callback\n    \tif (requestCode == PICTURE_CHOOSE) {\n    \t\tif (intent != null) {\n    \t\t\t//The Android api ~~~ \n    \t\t\t//Log.d(TAG, \"idButSelPic Photopicker: \" + intent.getDataString());\n    \t\t\tCursor cursor = getContentResolver().query(intent.getData(), null, null, null, null);\n    \t\t\tcursor.moveToFirst();\n    \t\t\tint idx = cursor.getColumnIndex(ImageColumns.DATA);\n    \t\t\tString fileSrc = cursor.getString(idx); \n    \t\t\t//Log.d(TAG, \"Picture:\" + fileSrc);\n    \t\t\t\n    \t\t\t//just read size\n    \t\t\tOptions options = new Options();\n    \t\t\toptions.inJustDecodeBounds = true;\n    \t\t\timg = BitmapFactory.decodeFile(fileSrc, options);\n\n    \t\t\t//scale size to read\n    \t\t\toptions.inSampleSize = Math.max(1, (int)Math.ceil(Math.max((double)options.outWidth / 1024f, (double)options.outHeight / 1024f)));\n    \t\t\toptions.inJustDecodeBounds = false;\n    \t\t\timg = BitmapFactory.decodeFile(fileSrc, options);\n    \t\t\ttextView.setText(\"Clik Detect. ==>\");\n    \t\t\t\n    \t\t\t\n    \t\t\timageView.setImageBitmap(img);\n    \t\t\tbuttonDetect.setVisibility(View.VISIBLE);\n    \t\t}\n    \t\telse {\n    \t\t\tLog.d(TAG, \"idButSelPic Photopicker canceled\");\n    \t\t}\n    \t}\n    }\n\n    private class FaceppDetect {\n    \tDetectCallback callback = null;\n    \t\n    \tpublic void setDetectCallback(DetectCallback detectCallback) { \n    \t\tcallback = detectCallback;\n    \t}\n\n    \tpublic void detect(final Bitmap image) {\n    \t\t\n    \t\tnew Thread(new Runnable() {\n\t\t\t\t\n\t\t\t\tpublic void run() {\n\t\t\t\t\tHttpRequests httpRequests = new HttpRequests(\"4480afa9b8b364e30ba03819f3e9eff5\", \"Pz9VFT8AP3g_Pz8_dz84cRY_bz8_Pz8M\", true, false);\n\t\t    \t\t//Log.v(TAG, \"image size : \" + img.getWidth() + \" \" + img.getHeight());\n\t\t    \t\t\n\t\t    \t\tByteArrayOutputStream stream = new ByteArrayOutputStream();\n\t\t    \t\tfloat scale = Math.min(1, Math.min(600f / img.getWidth(), 600f / img.getHeight()));\n\t\t    \t\tMatrix matrix = new Matrix();\n\t\t    \t\tmatrix.postScale(scale, scale);\n\n\t\t    \t\tBitmap imgSmall = Bitmap.createBitmap(img, 0, 0, img.getWidth(), img.getHeight(), matrix, false);\n\t\t    \t\t//Log.v(TAG, \"imgSmall size : \" + imgSmall.getWidth() + \" \" + imgSmall.getHeight());\n\t\t    \t\t\n\t\t    \t\timgSmall.compress(Bitmap.CompressFormat.JPEG, 100, stream);\n\t\t    \t\tbyte[] array = stream.toByteArray();\n\t\t    \t\t\n\t\t    \t\ttry {\n\t\t    \t\t\t//detect\n\t\t\t\t\t\tJSONObject result = httpRequests.detectionDetect(new PostParameters().setImg(array));\n\t\t\t\t\t\t//finished , then call the callback function\n\t\t\t\t\t\tif (callback != null) {\n\t\t\t\t\t\t\tcallback.detectResult(result);\n\t\t\t\t\t\t}\n\t\t\t\t\t} catch (FaceppParseException e) {\n\t\t\t\t\t\te.printStackTrace();\n\t\t\t\t\t\tMainActivity.this.runOnUiThread(new Runnable() {\n\t\t\t\t\t\t\tpublic void run() {\n\t\t\t\t\t\t\t\ttextView.setText(\"Network error.\");\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\t\n\t\t\t\t}\n\t\t\t}).start();\n    \t}\n    }\n\n    interface DetectCallback {\n    \tvoid detectResult(JSONObject rst);\n\t}\n}\n"
  },
  {
    "path": "sdk_unuse/PostParameters.java",
    "content": "package mrfu.face.sdk;\r\n\r\nimport java.io.File;\r\nimport java.io.UnsupportedEncodingException;\r\nimport java.nio.charset.Charset;\r\nimport java.util.ArrayList;\r\nimport java.util.Random;\r\n\r\nimport org.apache.http.entity.mime.HttpMultipartMode;\r\nimport org.apache.http.entity.mime.MultipartEntity;\r\nimport org.apache.http.entity.mime.content.ByteArrayBody;\r\nimport org.apache.http.entity.mime.content.FileBody;\r\nimport org.apache.http.entity.mime.content.StringBody;\r\n\r\n/**\r\n * Http Multipart<br />\r\n * {@code new PostParameters().setMode(\"oneface\").setImg(new File(\"...\")).setTag(\"some message\")}\r\n * @author moon5kcq\r\n * @since 1.0.0\r\n * @version 1.3.0\r\n */\r\npublic class PostParameters {\r\n\tprivate MultipartEntity multiPart = null;\r\n\tprivate final static int boundaryLength = 32;\r\n\tprivate final static String boundaryAlphabet = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_\";\r\n\tprivate String boundary;\r\n\t\r\n\t/**\r\n\t * auto generate boundary string\r\n\t * @return a boundary string\r\n\t */\r\n\tprivate String getBoundary() {\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tRandom random = new Random();\r\n\t\tfor (int i = 0; i < boundaryLength; ++i)\r\n\t\t\tsb.append(boundaryAlphabet.charAt(random.nextInt(boundaryAlphabet.length())));\r\n\t\treturn sb.toString();\r\n\t}\r\n\t\r\n\t/**\r\n\t * @return get MultipartEntity (apache)\r\n\t */\r\n\tpublic MultipartEntity getMultiPart() {\r\n\t\treturn multiPart;\r\n\t}\r\n\t\r\n\t/**\r\n\t * default boundary is auto generate {@link #getBoundary()}\r\n\t */\r\n\tpublic PostParameters() {\r\n\t\tsuper();\r\n\t\tboundary = getBoundary();\r\n\t\tmultiPart = new MultipartEntity(HttpMultipartMode.STRICT , boundary,  Charset.forName(\"UTF-8\"));\r\n\t}\r\n\t\r\n\t/**\r\n\t * @return multipart boundary string\r\n\t */\r\n\tpublic String boundaryString() {\r\n\t\treturn boundary;\r\n\t}\r\n\t\r\n\t/**\r\n\t * async=true|false\r\n\t * @param flag\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setAsync(boolean flag) {\r\n\t\taddString(\"async\", \"\"+flag);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * url=...\r\n\t * @param url\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setUrl(String url){\r\n\t\taddString(\"url\", url);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * attribute = gender | age | race | all | none\r\n\t * @param type\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setAttribute(String type){\r\n\t\taddString(\"attribute\", type);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * tag=...\r\n\t * @param tag\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setTag(String tag){\r\n\t\taddString(\"tag\", tag);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * img=...\r\n\t * @param file\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setImg(File file) {\r\n\t\tmultiPart.addPart(\"img\", new FileBody(file));\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * img=...\r\n\t * @param data\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setImg(byte[] data) {\r\n\t\tsetImg(data, \"NoName\");\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * img=...(name in multipart is ...)\r\n\t * @param data\r\n\t * @param fileName\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setImg(byte[] data, String fileName) {\r\n\t\tmultiPart.addPart(\"img\", new ByteArrayBody(data, fileName));\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * face_id1=...\r\n\t * @param id\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFaceId1(String id){\r\n\t\taddString(\"face_id1\", id);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * face_id2=...\r\n\t * @param id\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFaceId2(String id){\r\n\t\taddString(\"face_id2\", id);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * group_name=...\r\n\t * @param groupName\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupName(String groupName){\r\n\t\taddString(\"group_name\", groupName);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * group_id=...\r\n\t * @param groupId\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupId(String groupId){\r\n\t\taddString(\"group_id\", groupId);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * key_face_id=...\r\n\t * @param id\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setKeyFaceId(String id){\r\n\t\taddString(\"key_face_id\", id);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * count=...\r\n\t * @param count\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setCount(int count) {\r\n\t\taddString(\"count\", new Integer(count).toString());\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * type= all | search | recognize\r\n\t * @param type\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setType(String type) {\r\n\t\taddString(\"type\", type);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * face_id=...\r\n\t * @param faceId\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFaceId(String faceId) {\r\n\t\taddString(\"face_id\", faceId);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceset_id=...\r\n\t * @param facesetId\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetId(String facesetId) {\r\n\t\taddString(\"faceset_id\", facesetId);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceset_id=..., ..., ...\r\n\t * @param facesetIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetId(String[] facesetId) {\r\n\t\tsetFacesetId(toStringList(facesetId));\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceset_id=...\r\n\t * @param facesetIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetId(ArrayList<String> facesetId) {\r\n\t\tsetFacesetId(toStringList(facesetId));\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * person_id=...\r\n\t * @param personId\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonId(String personId) {\r\n\t\taddString(\"person_id\", personId);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * person_name=...\r\n\t * @param personName\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonName(String personName) {\r\n\t\taddString(\"person_name\", personName);\r\n\t\treturn this;\r\n\t}\r\n\t\t\r\n\t/**\r\n\t * name=...\r\n\t * @param name\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setName(String name) {\r\n\t\taddString(\"name\", name);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * session_id=...\r\n\t * @param id\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setSessionId(String id) {\r\n\t\taddString(\"session_id\", id);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * mode= oneface | normal\r\n\t * @param type\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setMode(String type) {\r\n\t\taddString(\"mode\", type);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t\r\n\t/**\r\n\t * face_id=... , ... , ...\r\n\t * @param faceIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFaceId(String[] faceIds) {\r\n\t\treturn setFaceId(toStringList(faceIds));\r\n\t}\r\n\t/**\r\n\t * person_id=... , ... , ...\r\n\t * @param personIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonId(String[] personIds) {\r\n\t\treturn setPersonId(toStringList(personIds));\r\n\t}\r\n\t/**\r\n\t * person_name=... , ... , ...\r\n\t * @param personNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonName(String[] personNames) {\r\n\t\treturn setPersonName(toStringList(personNames));\r\n\t}\r\n\t/**\r\n\t * group_id=... , ... , ...\r\n\t * @param groupIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupId(String[] groupIds) {\r\n\t\treturn setGroupId(toStringList(groupIds));\r\n\t}\r\n\t/**\r\n\t * group_name=... , ... , ...\r\n\t * @param groupNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupName(String[] groupNames) {\r\n\t\treturn setGroupName(toStringList(groupNames));\r\n\t}\r\n\t\r\n\t/**\r\n\t * face=... , ... , ...\r\n\t * @param faceIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFaceId(ArrayList<String> faceIds) {\r\n\t\treturn setFaceId(toStringList(faceIds));\r\n\t}\r\n\t/**\r\n\t * person_id=... , ... , ...\r\n\t * @param personIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonId(ArrayList<String> personIds) {\r\n\t\treturn setPersonId(toStringList(personIds));\r\n\t}\r\n\t/**\r\n\t * person_name=... , ... , ...\r\n\t * @param personNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setPersonName(ArrayList<String> personNames) {\r\n\t\treturn setPersonName(toStringList(personNames));\r\n\t}\r\n\t/**\r\n\t * group_id=... , ... , ...\r\n\t * @param groupIds\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupId(ArrayList<String> groupIds) {\r\n\t\treturn  setGroupId(toStringList(groupIds));\r\n\t}\r\n\t/**\r\n\t * group_name=... , ... , ...\r\n\t * @param groupNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setGroupName(ArrayList<String> groupNames) {\r\n\t\treturn setGroupName(toStringList(groupNames));\r\n\t}\r\n\t\r\n\t/**\r\n\t * img_id=...\r\n\t * @param imgId\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setImgId(String imgId) {\r\n\t\taddString(\"img_id\", imgId);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceset_name=...\r\n\t * @param facesetName\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetName(String facesetName) {\r\n\t\taddString(\"faceset_name\", facesetName);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\t/**\r\n\t * faceset_name=... , ... , ...\r\n\t * @param facesetNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetName(ArrayList<String> facesetNames) {\r\n\t\treturn setFacesetName(toStringList(facesetNames));\r\n\t}\r\n\t/**\r\n\t * faceset_name=... , ... , ...\r\n\t * @param facesetNames\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters setFacesetName(String[] facesetNames) {\r\n\t\treturn setFacesetName(toStringList(facesetNames));\r\n\t}\r\n\t\r\n\t/**\r\n\t * `attr`=`value`\r\n\t * @param attr value\r\n\t * @return this\r\n\t */\r\n\tpublic PostParameters addAttribute(String attr, String value) {\r\n\t\taddString(attr, value);\r\n\t\treturn this;\r\n\t}\r\n\t\r\n\tprivate void addString(String id, String str) {\r\n\t\ttry {\r\n\t\t\tmultiPart.addPart(id, new StringBody(str, Charset.forName(\"UTF-8\")));\r\n\t\t} catch (UnsupportedEncodingException e) {\r\n\t\t\te.printStackTrace();\r\n\t\t}\r\n\t}\r\n\t\r\n\tprivate String toStringList(String[] sa) {\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor (int i = 0; i < sa.length; ++i) {\r\n\t\t\tif (i != 0) sb.append(',');\r\n\t\t\tsb.append(sa[i]);\r\n\t\t}\r\n\t\t\r\n\t\treturn sb.toString();\r\n\t}\r\n\t\r\n\tprivate String toStringList(ArrayList<String> sa) {\r\n\t\tStringBuilder sb = new StringBuilder();\r\n\t\tfor (int i = 0; i < sa.size(); ++i) {\r\n\t\t\tif (i != 0) sb.append(',');\r\n\t\t\tsb.append(sa.get(i));\r\n\t\t}\r\n\t\t\r\n\t\treturn sb.toString();\r\n\t}\r\n}\r\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app'\n"
  }
]