作者 张卫卫

第一次提交

正在显示 55 个修改的文件 包含 1816 行增加0 行删除
  1 +<component name="ProjectCodeStyleConfiguration">
  2 + <code_scheme name="Project" version="173">
  3 + <JetCodeStyleSettings>
  4 + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
  5 + </JetCodeStyleSettings>
  6 + <codeStyleSettings language="XML">
  7 + <indentOptions>
  8 + <option name="CONTINUATION_INDENT_SIZE" value="4" />
  9 + </indentOptions>
  10 + <arrangement>
  11 + <rules>
  12 + <section>
  13 + <rule>
  14 + <match>
  15 + <AND>
  16 + <NAME>xmlns:android</NAME>
  17 + <XML_ATTRIBUTE />
  18 + <XML_NAMESPACE>^$</XML_NAMESPACE>
  19 + </AND>
  20 + </match>
  21 + </rule>
  22 + </section>
  23 + <section>
  24 + <rule>
  25 + <match>
  26 + <AND>
  27 + <NAME>xmlns:.*</NAME>
  28 + <XML_ATTRIBUTE />
  29 + <XML_NAMESPACE>^$</XML_NAMESPACE>
  30 + </AND>
  31 + </match>
  32 + <order>BY_NAME</order>
  33 + </rule>
  34 + </section>
  35 + <section>
  36 + <rule>
  37 + <match>
  38 + <AND>
  39 + <NAME>.*:id</NAME>
  40 + <XML_ATTRIBUTE />
  41 + <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
  42 + </AND>
  43 + </match>
  44 + </rule>
  45 + </section>
  46 + <section>
  47 + <rule>
  48 + <match>
  49 + <AND>
  50 + <NAME>.*:name</NAME>
  51 + <XML_ATTRIBUTE />
  52 + <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
  53 + </AND>
  54 + </match>
  55 + </rule>
  56 + </section>
  57 + <section>
  58 + <rule>
  59 + <match>
  60 + <AND>
  61 + <NAME>name</NAME>
  62 + <XML_ATTRIBUTE />
  63 + <XML_NAMESPACE>^$</XML_NAMESPACE>
  64 + </AND>
  65 + </match>
  66 + </rule>
  67 + </section>
  68 + <section>
  69 + <rule>
  70 + <match>
  71 + <AND>
  72 + <NAME>style</NAME>
  73 + <XML_ATTRIBUTE />
  74 + <XML_NAMESPACE>^$</XML_NAMESPACE>
  75 + </AND>
  76 + </match>
  77 + </rule>
  78 + </section>
  79 + <section>
  80 + <rule>
  81 + <match>
  82 + <AND>
  83 + <NAME>.*</NAME>
  84 + <XML_ATTRIBUTE />
  85 + <XML_NAMESPACE>^$</XML_NAMESPACE>
  86 + </AND>
  87 + </match>
  88 + <order>BY_NAME</order>
  89 + </rule>
  90 + </section>
  91 + <section>
  92 + <rule>
  93 + <match>
  94 + <AND>
  95 + <NAME>.*</NAME>
  96 + <XML_ATTRIBUTE />
  97 + <XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
  98 + </AND>
  99 + </match>
  100 + <order>ANDROID_ATTRIBUTE_ORDER</order>
  101 + </rule>
  102 + </section>
  103 + <section>
  104 + <rule>
  105 + <match>
  106 + <AND>
  107 + <NAME>.*</NAME>
  108 + <XML_ATTRIBUTE />
  109 + <XML_NAMESPACE>.*</XML_NAMESPACE>
  110 + </AND>
  111 + </match>
  112 + <order>BY_NAME</order>
  113 + </rule>
  114 + </section>
  115 + </rules>
  116 + </arrangement>
  117 + </codeStyleSettings>
  118 + <codeStyleSettings language="kotlin">
  119 + <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
  120 + </codeStyleSettings>
  121 + </code_scheme>
  122 +</component>
  1 +<component name="ProjectCodeStyleConfiguration">
  2 + <state>
  3 + <option name="USE_PER_PROJECT_SETTINGS" value="true" />
  4 + </state>
  5 +</component>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="Encoding">
  4 + <file url="PROJECT" charset="UTF-8" />
  5 + </component>
  6 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="GradleSettings">
  4 + <option name="linkedExternalProjectsSettings">
  5 + <GradleProjectSettings>
  6 + <option name="testRunner" value="PLATFORM" />
  7 + <option name="distributionType" value="DEFAULT_WRAPPED" />
  8 + <option name="externalProjectPath" value="$PROJECT_DIR$" />
  9 + <option name="modules">
  10 + <set>
  11 + <option value="$PROJECT_DIR$" />
  12 + <option value="$PROJECT_DIR$/sdk" />
  13 + </set>
  14 + </option>
  15 + <option name="resolveModulePerSourceSet" value="false" />
  16 + </GradleProjectSettings>
  17 + </option>
  18 + </component>
  19 +</project>
  1 +<component name="InspectionProjectProfileManager">
  2 + <profile version="1.0">
  3 + <option name="myName" value="Project Default" />
  4 + <inspection_tool class="JavaDoc" enabled="true" level="WARNING" enabled_by_default="true">
  5 + <option name="TOP_LEVEL_CLASS_OPTIONS">
  6 + <value>
  7 + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
  8 + <option name="REQUIRED_TAGS" value="" />
  9 + </value>
  10 + </option>
  11 + <option name="INNER_CLASS_OPTIONS">
  12 + <value>
  13 + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
  14 + <option name="REQUIRED_TAGS" value="" />
  15 + </value>
  16 + </option>
  17 + <option name="METHOD_OPTIONS">
  18 + <value>
  19 + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
  20 + <option name="REQUIRED_TAGS" value="@return@param@throws or @exception" />
  21 + </value>
  22 + </option>
  23 + <option name="FIELD_OPTIONS">
  24 + <value>
  25 + <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" />
  26 + <option name="REQUIRED_TAGS" value="" />
  27 + </value>
  28 + </option>
  29 + <option name="IGNORE_DEPRECATED" value="false" />
  30 + <option name="IGNORE_JAVADOC_PERIOD" value="true" />
  31 + <option name="IGNORE_DUPLICATED_THROWS" value="false" />
  32 + <option name="IGNORE_POINT_TO_ITSELF" value="false" />
  33 + <option name="myAdditionalJavadocTags" value="date" />
  34 + </inspection_tool>
  35 + </profile>
  36 +</component>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" default="true" project-jdk-name="1.8" project-jdk-type="JavaSDK">
  4 + <output url="file://$PROJECT_DIR$/build/classes" />
  5 + </component>
  6 + <component name="ProjectType">
  7 + <option name="id" value="Android" />
  8 + </component>
  9 + <component name="masterDetails">
  10 + <states>
  11 + <state key="ProjectJDKs.UI">
  12 + <settings>
  13 + <last-edited>1.8</last-edited>
  14 + <splitter-proportions>
  15 + <option name="proportions">
  16 + <list>
  17 + <option value="0.2" />
  18 + </list>
  19 + </option>
  20 + </splitter-proportions>
  21 + </settings>
  22 + </state>
  23 + </states>
  24 + </component>
  25 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="RunConfigurationProducerService">
  4 + <option name="ignoredProducers">
  5 + <set>
  6 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
  7 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
  8 + <option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
  9 + </set>
  10 + </option>
  11 + </component>
  12 +</project>
  1 +<?xml version="1.0" encoding="UTF-8"?>
  2 +<project version="4">
  3 + <component name="VcsDirectoryMappings">
  4 + <mapping directory="$PROJECT_DIR$" vcs="Git" />
  5 + </component>
  6 +</project>
  1 +// Top-level build file where you can add configuration options common to all sub-projects/modules.
  2 +
  3 +buildscript {
  4 + ext.kotlin_version = '1.3.70'
  5 + repositories {
  6 + google()
  7 + jcenter()
  8 +
  9 + }
  10 + dependencies {
  11 + classpath 'com.android.tools.build:gradle:3.6.1'
  12 + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
  13 +
  14 + // NOTE: Do not place your application dependencies here; they belong
  15 + // in the individual module build.gradle files
  16 + }
  17 +}
  18 +
  19 +allprojects {
  20 + repositories {
  21 + google()
  22 + jcenter()
  23 +
  24 + }
  25 +}
  26 +
  27 +task clean(type: Delete) {
  28 + delete rootProject.buildDir
  29 +}
  1 +# Project-wide Gradle settings.
  2 +# IDE (e.g. Android Studio) users:
  3 +# Gradle settings configured through the IDE *will override*
  4 +# any settings specified in this file.
  5 +# For more details on how to configure your build environment visit
  6 +# http://www.gradle.org/docs/current/userguide/build_environment.html
  7 +# Specifies the JVM arguments used for the daemon process.
  8 +# The setting is particularly useful for tweaking memory settings.
  9 +org.gradle.jvmargs=-Xmx1536m
  10 +# When configured, Gradle will run in incubating parallel mode.
  11 +# This option should only be used with decoupled projects. More details, visit
  12 +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
  13 +# org.gradle.parallel=true
  14 +# Kotlin code style for this project: "official" or "obsolete":
  15 +kotlin.code.style=official
不能预览此文件类型
  1 +#Tue Mar 17 15:17:02 CST 2020
  2 +distributionBase=GRADLE_USER_HOME
  3 +distributionPath=wrapper/dists
  4 +zipStoreBase=GRADLE_USER_HOME
  5 +zipStorePath=wrapper/dists
  6 +distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip
  1 +#!/usr/bin/env sh
  2 +
  3 +##############################################################################
  4 +##
  5 +## Gradle start up script for UN*X
  6 +##
  7 +##############################################################################
  8 +
  9 +# Attempt to set APP_HOME
  10 +# Resolve links: $0 may be a link
  11 +PRG="$0"
  12 +# Need this for relative symlinks.
  13 +while [ -h "$PRG" ] ; do
  14 + ls=`ls -ld "$PRG"`
  15 + link=`expr "$ls" : '.*-> \(.*\)$'`
  16 + if expr "$link" : '/.*' > /dev/null; then
  17 + PRG="$link"
  18 + else
  19 + PRG=`dirname "$PRG"`"/$link"
  20 + fi
  21 +done
  22 +SAVED="`pwd`"
  23 +cd "`dirname \"$PRG\"`/" >/dev/null
  24 +APP_HOME="`pwd -P`"
  25 +cd "$SAVED" >/dev/null
  26 +
  27 +APP_NAME="Gradle"
  28 +APP_BASE_NAME=`basename "$0"`
  29 +
  30 +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  31 +DEFAULT_JVM_OPTS=""
  32 +
  33 +# Use the maximum available, or set MAX_FD != -1 to use that value.
  34 +MAX_FD="maximum"
  35 +
  36 +warn () {
  37 + echo "$*"
  38 +}
  39 +
  40 +die () {
  41 + echo
  42 + echo "$*"
  43 + echo
  44 + exit 1
  45 +}
  46 +
  47 +# OS specific support (must be 'true' or 'false').
  48 +cygwin=false
  49 +msys=false
  50 +darwin=false
  51 +nonstop=false
  52 +case "`uname`" in
  53 + CYGWIN* )
  54 + cygwin=true
  55 + ;;
  56 + Darwin* )
  57 + darwin=true
  58 + ;;
  59 + MINGW* )
  60 + msys=true
  61 + ;;
  62 + NONSTOP* )
  63 + nonstop=true
  64 + ;;
  65 +esac
  66 +
  67 +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
  68 +
  69 +# Determine the Java command to use to start the JVM.
  70 +if [ -n "$JAVA_HOME" ] ; then
  71 + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
  72 + # IBM's JDK on AIX uses strange locations for the executables
  73 + JAVACMD="$JAVA_HOME/jre/sh/java"
  74 + else
  75 + JAVACMD="$JAVA_HOME/bin/java"
  76 + fi
  77 + if [ ! -x "$JAVACMD" ] ; then
  78 + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
  79 +
  80 +Please set the JAVA_HOME variable in your environment to match the
  81 +location of your Java installation."
  82 + fi
  83 +else
  84 + JAVACMD="java"
  85 + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  86 +
  87 +Please set the JAVA_HOME variable in your environment to match the
  88 +location of your Java installation."
  89 +fi
  90 +
  91 +# Increase the maximum file descriptors if we can.
  92 +if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
  93 + MAX_FD_LIMIT=`ulimit -H -n`
  94 + if [ $? -eq 0 ] ; then
  95 + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
  96 + MAX_FD="$MAX_FD_LIMIT"
  97 + fi
  98 + ulimit -n $MAX_FD
  99 + if [ $? -ne 0 ] ; then
  100 + warn "Could not set maximum file descriptor limit: $MAX_FD"
  101 + fi
  102 + else
  103 + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
  104 + fi
  105 +fi
  106 +
  107 +# For Darwin, add options to specify how the application appears in the dock
  108 +if $darwin; then
  109 + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
  110 +fi
  111 +
  112 +# For Cygwin, switch paths to Windows format before running java
  113 +if $cygwin ; then
  114 + APP_HOME=`cygpath --path --mixed "$APP_HOME"`
  115 + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
  116 + JAVACMD=`cygpath --unix "$JAVACMD"`
  117 +
  118 + # We build the pattern for arguments to be converted via cygpath
  119 + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
  120 + SEP=""
  121 + for dir in $ROOTDIRSRAW ; do
  122 + ROOTDIRS="$ROOTDIRS$SEP$dir"
  123 + SEP="|"
  124 + done
  125 + OURCYGPATTERN="(^($ROOTDIRS))"
  126 + # Add a user-defined pattern to the cygpath arguments
  127 + if [ "$GRADLE_CYGPATTERN" != "" ] ; then
  128 + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
  129 + fi
  130 + # Now convert the arguments - kludge to limit ourselves to /bin/sh
  131 + i=0
  132 + for arg in "$@" ; do
  133 + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
  134 + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
  135 +
  136 + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
  137 + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
  138 + else
  139 + eval `echo args$i`="\"$arg\""
  140 + fi
  141 + i=$((i+1))
  142 + done
  143 + case $i in
  144 + (0) set -- ;;
  145 + (1) set -- "$args0" ;;
  146 + (2) set -- "$args0" "$args1" ;;
  147 + (3) set -- "$args0" "$args1" "$args2" ;;
  148 + (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
  149 + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
  150 + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
  151 + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
  152 + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
  153 + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
  154 + esac
  155 +fi
  156 +
  157 +# Escape application args
  158 +save () {
  159 + for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
  160 + echo " "
  161 +}
  162 +APP_ARGS=$(save "$@")
  163 +
  164 +# Collect all arguments for the java command, following the shell quoting and substitution rules
  165 +eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
  166 +
  167 +# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
  168 +if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
  169 + cd "$(dirname "$0")"
  170 +fi
  171 +
  172 +exec "$JAVACMD" "$@"
  1 +@if "%DEBUG%" == "" @echo off
  2 +@rem ##########################################################################
  3 +@rem
  4 +@rem Gradle startup script for Windows
  5 +@rem
  6 +@rem ##########################################################################
  7 +
  8 +@rem Set local scope for the variables with windows NT shell
  9 +if "%OS%"=="Windows_NT" setlocal
  10 +
  11 +set DIRNAME=%~dp0
  12 +if "%DIRNAME%" == "" set DIRNAME=.
  13 +set APP_BASE_NAME=%~n0
  14 +set APP_HOME=%DIRNAME%
  15 +
  16 +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
  17 +set DEFAULT_JVM_OPTS=
  18 +
  19 +@rem Find java.exe
  20 +if defined JAVA_HOME goto findJavaFromJavaHome
  21 +
  22 +set JAVA_EXE=java.exe
  23 +%JAVA_EXE% -version >NUL 2>&1
  24 +if "%ERRORLEVEL%" == "0" goto init
  25 +
  26 +echo.
  27 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
  28 +echo.
  29 +echo Please set the JAVA_HOME variable in your environment to match the
  30 +echo location of your Java installation.
  31 +
  32 +goto fail
  33 +
  34 +:findJavaFromJavaHome
  35 +set JAVA_HOME=%JAVA_HOME:"=%
  36 +set JAVA_EXE=%JAVA_HOME%/bin/java.exe
  37 +
  38 +if exist "%JAVA_EXE%" goto init
  39 +
  40 +echo.
  41 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
  42 +echo.
  43 +echo Please set the JAVA_HOME variable in your environment to match the
  44 +echo location of your Java installation.
  45 +
  46 +goto fail
  47 +
  48 +:init
  49 +@rem Get command-line arguments, handling Windows variants
  50 +
  51 +if not "%OS%" == "Windows_NT" goto win9xME_args
  52 +
  53 +:win9xME_args
  54 +@rem Slurp the command line arguments.
  55 +set CMD_LINE_ARGS=
  56 +set _SKIP=2
  57 +
  58 +:win9xME_args_slurp
  59 +if "x%~1" == "x" goto execute
  60 +
  61 +set CMD_LINE_ARGS=%*
  62 +
  63 +:execute
  64 +@rem Setup the command line
  65 +
  66 +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
  67 +
  68 +@rem Execute Gradle
  69 +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
  70 +
  71 +:end
  72 +@rem End local scope for the variables with windows NT shell
  73 +if "%ERRORLEVEL%"=="0" goto mainEnd
  74 +
  75 +:fail
  76 +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
  77 +rem the _cmd.exe /c_ return code!
  78 +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
  79 +exit /b 1
  80 +
  81 +:mainEnd
  82 +if "%OS%"=="Windows_NT" endlocal
  83 +
  84 +:omega
  1 +apply plugin: 'com.android.library'
  2 +apply plugin: 'kotlin-android'
  3 +apply plugin: 'kotlin-android-extensions'
  4 +
  5 +android {
  6 + compileSdkVersion 28
  7 + buildToolsVersion "29.0.3"
  8 +
  9 + defaultConfig {
  10 +// applicationId "com.yhkj.rebotsdk"
  11 + minSdkVersion 23
  12 + targetSdkVersion 28
  13 + versionCode 1
  14 + versionName "1.0"
  15 +
  16 + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
  17 + }
  18 +
  19 + buildTypes {
  20 + release {
  21 + minifyEnabled false
  22 + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
  23 + }
  24 + }
  25 +
  26 +}
  27 +
  28 +dependencies {
  29 + implementation fileTree(dir: 'libs', include: ['*.aar', '*.jar'], exclude: [])
  30 + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
  31 + implementation 'com.android.support:appcompat-v7:28.0.0'
  32 + testImplementation 'junit:junit:4.12'
  33 + androidTestImplementation 'com.android.support.test:runner:1.0.2'
  34 + androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
  35 + implementation 'com.apkfuns.logutils:library:1.4.0'
  36 +}
不能预览此文件类型
不能预览此文件类型
  1 +# Add project specific ProGuard rules here.
  2 +# You can control the set of applied configuration files using the
  3 +# proguardFiles setting in build.gradle.
  4 +#
  5 +# For more details, see
  6 +# http://developer.android.com/guide/developing/tools/proguard.html
  7 +
  8 +# If your project uses WebView with JS, uncomment the following
  9 +# and specify the fully qualified class name to the JavaScript interface
  10 +# class:
  11 +#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
  12 +# public *;
  13 +#}
  14 +
  15 +# Uncomment this to preserve the line number information for
  16 +# debugging stack traces.
  17 +#-keepattributes SourceFile,LineNumberTable
  18 +
  19 +# If you keep the line number information, uncomment this to
  20 +# hide the original source file name.
  21 +#-renamesourcefileattribute SourceFile
  1 +package com.yhkj.rebotsdk
  2 +
  3 +import android.support.test.InstrumentationRegistry
  4 +import android.support.test.runner.AndroidJUnit4
  5 +
  6 +import org.junit.Test
  7 +import org.junit.runner.RunWith
  8 +
  9 +import org.junit.Assert.*
  10 +
  11 +/**
  12 + * Instrumented test, which will execute on an Android device.
  13 + *
  14 + * See [testing documentation](http://d.android.com/tools/testing).
  15 + */
  16 +@RunWith(AndroidJUnit4::class)
  17 +class ExampleInstrumentedTest {
  18 + @Test
  19 + fun useAppContext() {
  20 + // Context of the app under test.
  21 + val appContext = InstrumentationRegistry.getInstrumentation().targetContext
  22 + assertEquals("com.yhkj.rebotsdk", appContext.packageName)
  23 + }
  24 +}
  1 +<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  2 + package="com.yhkj.rebotsdk">
  3 +
  4 + <application
  5 + android:allowBackup="true"
  6 + android:icon="@mipmap/ic_launcher"
  7 + android:label="@string/app_name"
  8 + android:roundIcon="@mipmap/ic_launcher_round"
  9 + android:supportsRtl="true"
  10 + android:theme="@style/AppTheme" />
  11 +</manifest>
  1 +package com.yhkj.rebotsdk.engine
  2 +
  3 +
  4 +/**
  5 + * 状态码
  6 + */
  7 +internal class EngineStatusCode {
  8 +
  9 + companion object {
  10 + const val SUCCEED = 1000 //操作成功
  11 + const val FAILED = -1000 //操作失败
  12 + }
  13 +
  14 +}
  15 +
  16 +/**
  17 + * 状态
  18 + * @param code 状态码
  19 + * @param msg 状态消息
  20 + */
  21 +internal data class EngineStatusData(val code: Int, val msg: String)
  22 +
  23 +
  24 +/**
  25 + * 人脸识别结果
  26 + * @param faceId 人脸记录Id
  27 + * @param score 比对分数
  28 + */
  29 +internal data class IdentifyData(val faceId: String, val score: Float)
  1 +package com.yhkj.rebotsdk.engine
  2 +
  3 +import android.content.Context
  4 +
  5 +
  6 +internal interface EngineInterface {
  7 +
  8 +
  9 + /**
  10 + * 激活人脸license
  11 + */
  12 + fun activeFace(
  13 + context: Context, active_key: String, appId: String,
  14 + sdkKey: String
  15 + ): Int
  16 +
  17 + /**
  18 + * 初始化
  19 + * @param context 上下文对象
  20 + * @return 是否初始化成功
  21 + */
  22 + fun init(context: Context?): EngineStatusData
  23 +
  24 + /**
  25 + * 销毁
  26 + */
  27 + fun unInit()
  28 +
  29 + /**
  30 + * 获取人脸数量
  31 + */
  32 + fun getFaceNumber(context: Context?): Int
  33 +
  34 + /**
  35 + * 清除人脸
  36 + */
  37 + fun clearAllFaces(context: Context?): Int
  38 +
  39 +
  40 + /**
  41 + * 用于预览时注册人脸
  42 + *
  43 + * @param context 上下文对象
  44 + * @param nv21 NV21数据
  45 + * @param width NV21宽度
  46 + * @param height NV21高度
  47 + * * @param faceInfo {@link FaceEngine#detectFaces(byte[], int, int, int, List)}获取的人脸信息
  48 + * @param faceId 保存的名字,若为空则使用时间戳
  49 + * @return 是否注册成功
  50 + */
  51 + fun registerNv21(
  52 + context: Context?, nv21: ByteArray?, width: Int, height: Int,
  53 + faceId: String?
  54 + ): Boolean
  55 +
  56 + /**
  57 + * 用于注册照片人脸
  58 + *
  59 + * @param context 上下文对象
  60 + * @param bgr24 bgr24数据
  61 + * @param width bgr24宽度
  62 + * @param height bgr24高度
  63 + * @param faceId 保存的名字,若为空则使用时间戳
  64 + * @return 是否注册成功
  65 + */
  66 + fun registerBgr24(
  67 + context: Context?, bgr24: ByteArray?, width: Int, height: Int, faceId: String?
  68 + ): Boolean
  69 +
  70 +
  71 + /**
  72 + * 人脸检索
  73 + * @return 符合条件的人脸信息
  74 + */
  75 + fun identify(sourceData: ByteArray): IdentifyData
  76 +}
  1 +package com.yhkj.rebotsdk.engine
  2 +
  3 +import com.yhkj.rebotsdk.engine.arcface.ArcFaceEngine
  4 +
  5 +
  6 +internal class FaceEngineFactory {
  7 +
  8 + fun createEngine() : EngineInterface = ArcFaceEngine()
  9 +
  10 +}
  1 +package com.yhkj.rebotsdk.engine.arcface
  2 +
  3 +import android.content.Context
  4 +import android.graphics.Bitmap
  5 +import android.graphics.Rect
  6 +import com.apkfuns.logutils.LogUtils
  7 +import com.arcsoft.face.ErrorInfo
  8 +import com.arcsoft.face.FaceEngine
  9 +import com.arcsoft.face.FaceFeature
  10 +import com.arcsoft.face.FaceInfo
  11 +import com.arcsoft.face.enums.DetectFaceOrientPriority
  12 +import com.arcsoft.face.enums.DetectMode
  13 +import com.arcsoft.imageutil.ArcSoftImageFormat
  14 +import com.arcsoft.imageutil.ArcSoftImageUtil
  15 +import com.arcsoft.imageutil.ArcSoftImageUtilError
  16 +import com.arcsoft.imageutil.ArcSoftRotateDegree
  17 +import com.yhkj.rebotsdk.engine.EngineInterface
  18 +import com.yhkj.rebotsdk.engine.EngineStatusCode
  19 +import com.yhkj.rebotsdk.engine.EngineStatusData
  20 +import com.yhkj.rebotsdk.engine.IdentifyData
  21 +import java.io.File
  22 +import java.io.FileInputStream
  23 +import java.io.FileOutputStream
  24 +import java.io.IOException
  25 +import kotlin.math.max
  26 +import kotlin.math.min
  27 +
  28 +/**
  29 + * Created by zhangweiwei on 2020/3/16.
  30 + */
  31 +internal class ArcFaceEngine : EngineInterface {
  32 +
  33 +
  34 + private var faceEngine: FaceEngine? = null
  35 + private var faceRegisterInfoList: ArrayList<FaceRegisterInfo>? = null
  36 + private var ROOT_PATH: String? = null
  37 + private val IMG_SUFFIX = ".jpg"
  38 +
  39 + /**
  40 + * 存放注册图的目录
  41 + */
  42 + private val SAVE_IMG_DIR = "register" + File.separator + "imgs"
  43 +
  44 + /**
  45 + * 存放特征的目录
  46 + */
  47 + private val SAVE_FEATURE_DIR = "register" + File.separator + "features"
  48 +
  49 +
  50 + override fun activeFace(
  51 + context: Context,
  52 + active_key: String,
  53 + appId: String,
  54 + sdkKey: String
  55 + ): Int {
  56 + return FaceEngine.activeOnline(
  57 + context, active_key, appId, sdkKey
  58 + )
  59 + }
  60 +
  61 + override fun init(context: Context?): EngineStatusData {
  62 + synchronized(this) {
  63 + if (faceEngine == null && context != null) {
  64 + faceEngine = FaceEngine()
  65 + val engineCode: Int = faceEngine!!.init(
  66 + context,
  67 + DetectMode.ASF_DETECT_MODE_IMAGE,
  68 + DetectFaceOrientPriority.ASF_OP_0_ONLY,
  69 + 16,
  70 + 1,
  71 + FaceEngine.ASF_FACE_RECOGNITION or FaceEngine.ASF_FACE_DETECT
  72 + )
  73 + return if (engineCode == ErrorInfo.MOK) {
  74 + initFaceList(context)
  75 + EngineStatusData(
  76 + EngineStatusCode.SUCCEED,
  77 + "init succeed"
  78 + )
  79 + } else {
  80 + faceEngine = null
  81 + EngineStatusData(
  82 + EngineStatusCode.FAILED,
  83 + "init failed"
  84 + )
  85 + }
  86 + }
  87 + return EngineStatusData(
  88 + EngineStatusCode.FAILED,
  89 + "init failed"
  90 + )
  91 + }
  92 + }
  93 +
  94 + override fun unInit() {
  95 + synchronized(this) {
  96 + faceRegisterInfoList?.clear()
  97 + faceRegisterInfoList = null
  98 + faceEngine?.unInit()
  99 + faceEngine = null
  100 + }
  101 + }
  102 +
  103 + /**
  104 + * 初始化人脸特征数据以及人脸特征数据对应的注册图
  105 + *
  106 + * @param context 上下文对象
  107 + */
  108 + private fun initFaceList(context: Context) {
  109 + synchronized(this) {
  110 + if (ROOT_PATH == null) {
  111 + ROOT_PATH = context.filesDir.absolutePath
  112 + }
  113 + val featureDir =
  114 + File(ROOT_PATH + File.separator + SAVE_FEATURE_DIR)
  115 + if (!featureDir.exists() || !featureDir.isDirectory) {
  116 + return
  117 + }
  118 + val featureFiles: Array<File> = featureDir.listFiles()
  119 + if (featureFiles.isEmpty()) {
  120 + return
  121 + }
  122 + faceRegisterInfoList = java.util.ArrayList<FaceRegisterInfo>()
  123 + for (featureFile in featureFiles) {
  124 + try {
  125 + val fis = FileInputStream(featureFile)
  126 + val feature = ByteArray(FaceFeature.FEATURE_SIZE)
  127 + fis.read(feature)
  128 + fis.close()
  129 + faceRegisterInfoList!!.add(
  130 + FaceRegisterInfo(feature, featureFile.name)
  131 + )
  132 + } catch (e: IOException) {
  133 + e.printStackTrace()
  134 + }
  135 + }
  136 + }
  137 + }
  138 +
  139 + override fun getFaceNumber(context: Context?): Int {
  140 + synchronized(this) {
  141 + if (context == null) {
  142 + return 0
  143 + }
  144 + if (ROOT_PATH == null) {
  145 + ROOT_PATH = context.filesDir.absolutePath
  146 + }
  147 + val featureFileDir =
  148 + File(ROOT_PATH + File.separator + SAVE_FEATURE_DIR)
  149 + var featureCount = 0
  150 + if (featureFileDir.exists() && featureFileDir.isDirectory) {
  151 + val featureFiles: Array<String> = featureFileDir.list()
  152 + featureCount = featureFiles.size
  153 + }
  154 + var imageCount = 0
  155 + val imgFileDir =
  156 + File(ROOT_PATH + File.separator + SAVE_IMG_DIR)
  157 + if (imgFileDir.exists() && imgFileDir.isDirectory) {
  158 + val imageFiles: Array<String> = imgFileDir.list()
  159 + imageCount = imageFiles.size
  160 + }
  161 + return if (featureCount > imageCount) imageCount else featureCount
  162 + }
  163 + }
  164 +
  165 + override fun clearAllFaces(context: Context?): Int {
  166 + synchronized(this) {
  167 + if (context == null) {
  168 + return 0
  169 + }
  170 + if (ROOT_PATH == null) {
  171 + ROOT_PATH = context.filesDir.absolutePath
  172 + }
  173 + faceRegisterInfoList?.clear()
  174 + val featureFileDir =
  175 + File(ROOT_PATH + File.separator + SAVE_FEATURE_DIR)
  176 + var deletedFeatureCount = 0
  177 + if (featureFileDir.exists() && featureFileDir.isDirectory) {
  178 + val featureFiles: Array<File> = featureFileDir.listFiles()
  179 + if (featureFiles.isNotEmpty()) {
  180 + for (featureFile in featureFiles) {
  181 + if (featureFile.delete()) {
  182 + deletedFeatureCount++
  183 + }
  184 + }
  185 + }
  186 + }
  187 + var deletedImageCount = 0
  188 + val imgFileDir =
  189 + File(ROOT_PATH + File.separator + SAVE_IMG_DIR)
  190 + if (imgFileDir.exists() && imgFileDir.isDirectory) {
  191 + val imgFiles: Array<File> = imgFileDir.listFiles()
  192 + if (imgFiles.isNotEmpty()) {
  193 + for (imgFile in imgFiles) {
  194 + if (imgFile.delete()) {
  195 + deletedImageCount++
  196 + }
  197 + }
  198 + }
  199 + }
  200 + return if (deletedFeatureCount > deletedImageCount) deletedImageCount else deletedFeatureCount
  201 + }
  202 + }
  203 +
  204 +
  205 + override fun registerNv21(
  206 + context: Context?,
  207 + nv21: ByteArray?,
  208 + width: Int,
  209 + height: Int,
  210 + faceId: String?
  211 + ): Boolean {
  212 + synchronized(this) {
  213 + if (faceEngine == null || context == null || nv21 == null || width % 4 != 0 || nv21.size != width * height * 3 / 2) {
  214 + LogUtils.e("registerNv21: invalid params")
  215 + return false
  216 + }
  217 + val faceInfoList: List<FaceInfo>? = null
  218 + val faceInfoCode: Int = faceEngine!!.detectFaces(
  219 + nv21,
  220 + width,
  221 + height,
  222 + FaceEngine.CP_PAF_NV21,
  223 + faceInfoList
  224 + )
  225 + if(faceInfoCode != ErrorInfo.MOK){
  226 + return false
  227 + }
  228 + TrackUtil.keepMaxFace(faceInfoList)
  229 + val faceInfo=faceInfoList?.get(0)
  230 + if (ROOT_PATH == null) {
  231 + ROOT_PATH = context.filesDir.absolutePath
  232 + }
  233 + //特征存储的文件夹
  234 + val featureDir =
  235 + File(ROOT_PATH + File.separator + SAVE_FEATURE_DIR)
  236 + if (!featureDir.exists() && !featureDir.mkdirs()) {
  237 + LogUtils.e("registerNv21: can not create feature directory")
  238 + return false
  239 + }
  240 + //图片存储的文件夹
  241 + val imgDir =
  242 + File(ROOT_PATH + File.separator + SAVE_IMG_DIR)
  243 + if (!imgDir.exists() && !imgDir.mkdirs()) {
  244 + LogUtils.e("registerNv21: can not create image directory")
  245 + return false
  246 + }
  247 + val faceFeature = FaceFeature()
  248 + //特征提取
  249 + val code: Int =
  250 + faceEngine!!.extractFaceFeature(
  251 + nv21,
  252 + width,
  253 + height,
  254 + FaceEngine.CP_PAF_NV21,
  255 + faceInfo,
  256 + faceFeature
  257 + )
  258 + return if (code != ErrorInfo.MOK) {
  259 + LogUtils.e("registerNv21: extractFaceFeature failed , code is $code")
  260 + false
  261 + } else {
  262 + val registerFaceId =
  263 + faceId ?: System.currentTimeMillis().toString()
  264 + try {
  265 + // 保存注册结果(注册图、特征数据)
  266 + // 为了美观,扩大rect截取注册图
  267 + val cropRect: Rect? =
  268 + getBestRect(width, height, faceInfo!!.rect)
  269 + if (cropRect == null) {
  270 + LogUtils.e("registerNv21: cropRect is null!")
  271 + return false
  272 + }
  273 + cropRect.left = cropRect.left and 3.inv()
  274 + cropRect.top = cropRect.top and 3.inv()
  275 + cropRect.right = cropRect.right and 3.inv()
  276 + cropRect.bottom = cropRect.bottom and 3.inv()
  277 + val file =
  278 + File(imgDir.toString() + File.separator + registerFaceId + IMG_SUFFIX)
  279 +
  280 +
  281 + // 创建一个头像的Bitmap,存放旋转结果图
  282 + val headBmp: Bitmap? = getHeadImage(
  283 + nv21,
  284 + width,
  285 + height,
  286 + faceInfo.orient,
  287 + cropRect,
  288 + ArcSoftImageFormat.NV21
  289 + )
  290 + val fosImage = FileOutputStream(file)
  291 + headBmp?.compress(Bitmap.CompressFormat.JPEG, 100, fosImage)
  292 + fosImage.close()
  293 + val fosFeature =
  294 + FileOutputStream(featureDir.toString() + File.separator + registerFaceId)
  295 + fosFeature.write(faceFeature.featureData)
  296 + fosFeature.close()
  297 +
  298 + //内存中的数据同步
  299 + if (faceRegisterInfoList == null) {
  300 + faceRegisterInfoList =
  301 + java.util.ArrayList<FaceRegisterInfo>()
  302 + }
  303 + faceRegisterInfoList!!.add(
  304 + FaceRegisterInfo(faceFeature.featureData, registerFaceId)
  305 + )
  306 + true
  307 + } catch (e: IOException) {
  308 + e.printStackTrace()
  309 + false
  310 + }
  311 + }
  312 + }
  313 + }
  314 +
  315 + override fun registerBgr24(
  316 + context: Context?,
  317 + bgr24: ByteArray?,
  318 + width: Int,
  319 + height: Int,
  320 + faceId: String?
  321 + ): Boolean {
  322 + synchronized(this) {
  323 + if (faceEngine == null || context == null || bgr24 == null || width % 4 != 0 || bgr24.size != width * height * 3) {
  324 + LogUtils.e("registerBgr24: invalid params")
  325 + return false
  326 + }
  327 + if (ROOT_PATH == null) {
  328 + ROOT_PATH = context.filesDir.absolutePath
  329 + }
  330 + //特征存储的文件夹
  331 + val featureDir =
  332 + File(ROOT_PATH + File.separator + SAVE_FEATURE_DIR)
  333 + if (!featureDir.exists() && !featureDir.mkdirs()) {
  334 + LogUtils.e("registerBgr24: can not create feature directory")
  335 + return false
  336 + }
  337 + //图片存储的文件夹
  338 + val imgDir =
  339 + File(ROOT_PATH + File.separator + SAVE_IMG_DIR)
  340 + if (!imgDir.exists() && !imgDir.mkdirs()) {
  341 + LogUtils.e("registerBgr24: can not create image directory")
  342 + return false
  343 + }
  344 + //人脸检测
  345 + val faceInfoList: List<FaceInfo> = java.util.ArrayList()
  346 + var code: Int = faceEngine!!.detectFaces(
  347 + bgr24,
  348 + width,
  349 + height,
  350 + FaceEngine.CP_PAF_BGR24,
  351 + faceInfoList
  352 + )
  353 + return if (code == ErrorInfo.MOK && faceInfoList.size > 0) {
  354 + val faceFeature = FaceFeature()
  355 +
  356 + //特征提取
  357 + code = faceEngine!!.extractFaceFeature(
  358 + bgr24,
  359 + width,
  360 + height,
  361 + FaceEngine.CP_PAF_BGR24,
  362 + faceInfoList[0],
  363 + faceFeature
  364 + )
  365 + val registerFaceId =
  366 + faceId ?: System.currentTimeMillis().toString()
  367 + try {
  368 + //保存注册结果(注册图、特征数据)
  369 + if (code == ErrorInfo.MOK) {
  370 + //为了美观,扩大rect截取注册图
  371 + val cropRect: Rect? =
  372 + getBestRect(
  373 + width,
  374 + height,
  375 + faceInfoList[0].rect
  376 + )
  377 + if (cropRect == null) {
  378 + LogUtils.e("registerBgr24: cropRect is null")
  379 + return false
  380 + }
  381 + cropRect.left = cropRect.left and 3.inv()
  382 + cropRect.top = cropRect.top and 3.inv()
  383 + cropRect.right = cropRect.right and 3.inv()
  384 + cropRect.bottom = cropRect.bottom and 3.inv()
  385 + val file =
  386 + File(imgDir.toString() + File.separator + registerFaceId + IMG_SUFFIX)
  387 + val fosImage = FileOutputStream(file)
  388 +
  389 +
  390 + // 创建一个头像的Bitmap,存放旋转结果图
  391 + val headBmp = getHeadImage(
  392 + bgr24,
  393 + width,
  394 + height,
  395 + faceInfoList[0].orient,
  396 + cropRect,
  397 + ArcSoftImageFormat.BGR24
  398 + )
  399 + // 保存到本地
  400 + headBmp!!.compress(Bitmap.CompressFormat.JPEG, 100, fosImage)
  401 + fosImage.close()
  402 +
  403 + // 保存特征数据
  404 + val fosFeature =
  405 + FileOutputStream(featureDir.toString() + File.separator + registerFaceId)
  406 + fosFeature.write(faceFeature.featureData)
  407 + fosFeature.close()
  408 +
  409 + // 内存中的数据同步
  410 + if (faceRegisterInfoList == null) {
  411 + faceRegisterInfoList =
  412 + java.util.ArrayList<FaceRegisterInfo>()
  413 + }
  414 + faceRegisterInfoList?.apply {
  415 + this.add(
  416 + FaceRegisterInfo(faceFeature.featureData, registerFaceId)
  417 + )
  418 + }
  419 +
  420 + true
  421 + } else {
  422 + LogUtils.e("registerBgr24: extract face feature failed, code is $code")
  423 + false
  424 + }
  425 + } catch (e: IOException) {
  426 + e.printStackTrace()
  427 + false
  428 + }
  429 + } else {
  430 + LogUtils.e("registerBgr24: no face detected, code is $code")
  431 + false
  432 + }
  433 + }
  434 + }
  435 +
  436 + override fun identify(sourceData: ByteArray): IdentifyData {
  437 + TODO("Not yet implemented")
  438 + }
  439 +
  440 + /**
  441 + * 截取合适的头像并旋转,保存为注册头像
  442 + *
  443 + * @param originImageData 原始的BGR24数据
  444 + * @param width BGR24图像宽度
  445 + * @param height BGR24图像高度
  446 + * @param orient 人脸角度
  447 + * @param cropRect 裁剪的位置
  448 + * @param imageFormat 图像格式
  449 + * @return 头像的图像数据
  450 + */
  451 + private fun getHeadImage(
  452 + originImageData: ByteArray,
  453 + width: Int,
  454 + height: Int,
  455 + orient: Int,
  456 + cropRect: Rect,
  457 + imageFormat: ArcSoftImageFormat
  458 + ): Bitmap? {
  459 + val headImageData =
  460 + ArcSoftImageUtil.createImageData(cropRect.width(), cropRect.height(), imageFormat)
  461 + val cropCode = ArcSoftImageUtil.cropImage(
  462 + originImageData,
  463 + headImageData,
  464 + width,
  465 + height,
  466 + cropRect,
  467 + imageFormat
  468 + )
  469 + if (cropCode != ArcSoftImageUtilError.CODE_SUCCESS) {
  470 + throw RuntimeException("crop image failed, code is $cropCode")
  471 + }
  472 +
  473 + //判断人脸旋转角度,若不为0度则旋转注册图
  474 + var rotateHeadImageData: ByteArray? = null
  475 + val rotateCode: Int
  476 + val cropImageWidth: Int
  477 + val cropImageHeight: Int
  478 + // 90度或270度的情况,需要宽高互换
  479 + if (orient == FaceEngine.ASF_OC_90 || orient == FaceEngine.ASF_OC_270) {
  480 + cropImageWidth = cropRect.height()
  481 + cropImageHeight = cropRect.width()
  482 + } else {
  483 + cropImageWidth = cropRect.width()
  484 + cropImageHeight = cropRect.height()
  485 + }
  486 + var rotateDegree: ArcSoftRotateDegree? = null
  487 + when (orient) {
  488 + FaceEngine.ASF_OC_90 -> rotateDegree = ArcSoftRotateDegree.DEGREE_270
  489 + FaceEngine.ASF_OC_180 -> rotateDegree = ArcSoftRotateDegree.DEGREE_180
  490 + FaceEngine.ASF_OC_270 -> rotateDegree = ArcSoftRotateDegree.DEGREE_90
  491 + FaceEngine.ASF_OC_0 -> rotateHeadImageData = headImageData
  492 + else -> rotateHeadImageData = headImageData
  493 + }
  494 + // 非0度的情况,旋转图像
  495 + if (rotateDegree != null) {
  496 + rotateHeadImageData = ByteArray(headImageData.size)
  497 + rotateCode = ArcSoftImageUtil.rotateImage(
  498 + headImageData,
  499 + rotateHeadImageData,
  500 + cropRect.width(),
  501 + cropRect.height(),
  502 + rotateDegree,
  503 + imageFormat
  504 + )
  505 + if (rotateCode != ArcSoftImageUtilError.CODE_SUCCESS) {
  506 + throw RuntimeException("rotate image failed, code is $rotateCode")
  507 + }
  508 + }
  509 + // 将创建一个Bitmap,并将图像数据存放到Bitmap中
  510 + val headBmp =
  511 + Bitmap.createBitmap(cropImageWidth, cropImageHeight, Bitmap.Config.RGB_565)
  512 + if (ArcSoftImageUtil.imageDataToBitmap(
  513 + rotateHeadImageData,
  514 + headBmp,
  515 + imageFormat
  516 + ) != ArcSoftImageUtilError.CODE_SUCCESS
  517 + ) {
  518 + throw RuntimeException("failed to transform image data to bitmap")
  519 + }
  520 + return headBmp
  521 + }
  522 +
  523 + /**
  524 + * 将图像中需要截取的Rect向外扩张一倍,若扩张一倍会溢出,则扩张到边界,若Rect已溢出,则收缩到边界
  525 + *
  526 + * @param width 图像宽度
  527 + * @param height 图像高度
  528 + * @param srcRect 原Rect
  529 + * @return 调整后的Rect
  530 + */
  531 + private fun getBestRect(width: Int, height: Int, srcRect: Rect?): Rect? {
  532 + if (srcRect == null) {
  533 + return null
  534 + }
  535 + val rect = Rect(srcRect)
  536 +
  537 + // 原rect边界已溢出宽高的情况
  538 + val maxOverFlow = max(
  539 + -rect.left,
  540 + max(
  541 + -rect.top,
  542 + max(rect.right - width, rect.bottom - height)
  543 + )
  544 + )
  545 + if (maxOverFlow >= 0) {
  546 + rect.inset(maxOverFlow, maxOverFlow)
  547 + return rect
  548 + }
  549 +
  550 + // 原rect边界未溢出宽高的情况
  551 + var padding = rect.height() / 2
  552 +
  553 + // 若以此padding扩张rect会溢出,取最大padding为四个边距的最小值
  554 + if (!(rect.left - padding > 0 && rect.right + padding < width && rect.top - padding > 0 && rect.bottom + padding < height)) {
  555 + padding = min(
  556 + min(
  557 + min(
  558 + rect.left,
  559 + width - rect.right
  560 + ), height - rect.bottom
  561 + ), rect.top
  562 + )
  563 + }
  564 + rect.inset(-padding, -padding)
  565 + return rect
  566 + }
  567 +
  568 +
  569 +}
  1 +package com.yhkj.rebotsdk.engine.arcface;
  2 +
  3 +public class FaceRegisterInfo {
  4 + private byte[] featureData;
  5 + private String name;
  6 +
  7 + public FaceRegisterInfo(byte[] faceFeature, String name) {
  8 + this.featureData = faceFeature;
  9 + this.name = name;
  10 + }
  11 +
  12 + public String getName() {
  13 + return name;
  14 + }
  15 +
  16 + public void setName(String name) {
  17 + this.name = name;
  18 + }
  19 +
  20 + public byte[] getFeatureData() {
  21 + return featureData;
  22 + }
  23 +
  24 + public void setFeatureData(byte[] featureData) {
  25 + this.featureData = featureData;
  26 + }
  27 +}
  1 +package com.yhkj.rebotsdk.engine.arcface;
  2 +
  3 +import com.arcsoft.face.FaceInfo;
  4 +
  5 +import java.util.List;
  6 +
  7 +public class TrackUtil {
  8 +
  9 + public static boolean isSameFace(FaceInfo faceInfo1, FaceInfo faceInfo2) {
  10 + return faceInfo1.getFaceId() == faceInfo2.getFaceId();
  11 + }
  12 +
  13 + public static void keepMaxFace(List<FaceInfo> ftFaceList) {
  14 + if (ftFaceList == null || ftFaceList.size() <= 1) {
  15 + return;
  16 + }
  17 + FaceInfo maxFaceInfo = ftFaceList.get(0);
  18 + for (FaceInfo faceInfo : ftFaceList) {
  19 + if (faceInfo.getRect().width() > maxFaceInfo.getRect().width()) {
  20 + maxFaceInfo = faceInfo;
  21 + }
  22 + }
  23 + ftFaceList.clear();
  24 + ftFaceList.add(maxFaceInfo);
  25 + }
  26 +
  27 +}
  1 +package com.yhkj.rebotsdk.face
  2 +
  3 +
  4 +
  5 +/**
  6 + * 状态码
  7 + */
  8 +class StatusCode {
  9 +
  10 + companion object {
  11 + const val SUCCEED = 1000 //操作成功
  12 + const val FAILED = -1000 //操作失败
  13 + const val CAPTURE_FAILED = -1001 //操作失败
  14 + const val INVALID_DATA = -1002 //数据异常
  15 + const val REGISTER_FAILED = -1003 //注册失败
  16 +
  17 + }
  18 +
  19 +}
  20 +
  21 +/**
  22 + * 状态
  23 + * @param code 状态码
  24 + * @param msg 状态消息
  25 + */
  26 +data class StatusData(val code: Int, val msg: String)
  1 +package com.yhkj.rebotsdk.face
  2 +
  3 +import android.content.Context
  4 +
  5 +
  6 +interface FaceInterface {
  7 +
  8 + /**
  9 + * 录入模式监听
  10 + */
  11 + interface OnEnrollListener {
  12 +
  13 +
  14 + /**
  15 + * 录入结束(并未注册到人脸引擎中,仅获取到符合录入的人脸数据)
  16 + */
  17 + fun onEnrollFinished(fingerData : ByteArray)
  18 +
  19 + /**
  20 + * 录入异常
  21 + * @param statusData 提示信息
  22 + */
  23 + fun onEnrollException(statusData: StatusData)
  24 +
  25 + }
  26 +
  27 + /**
  28 + * 识别模式监听
  29 + */
  30 + interface OnVerifyListener {
  31 +
  32 + /**
  33 + * 人脸识别成功
  34 + * @param faceId 人脸Id
  35 + */
  36 + fun onVerifySucceed(faceId: String)
  37 +
  38 + /**
  39 + * 人脸识别失败
  40 + * @param msg 提示消息
  41 + */
  42 + fun onVerifyFailed(msg: String)
  43 +
  44 + }
  45 +
  46 +
  47 + /**
  48 + * 激活人脸license
  49 + */
  50 + fun activeFace(context:Context,active_key:String,appId:String,
  51 + sdkKey:String): Int
  52 +
  53 + /**
  54 + * 初始化人脸模块
  55 + * @param appId
  56 + * @param appKey
  57 + */
  58 + fun initFace(context:Context): StatusData
  59 +
  60 +
  61 +
  62 + /**
  63 + * 设置录入模式监听
  64 + * @param listener 录入模式监听
  65 + */
  66 + fun setEnrollListener(listener: OnEnrollListener)
  67 +
  68 + /**
  69 + * 设置识别模式监听
  70 + * @param listener 识别模式监听
  71 + */
  72 + fun setVerifyListener(listener: OnVerifyListener)
  73 +
  74 +
  75 + /**
  76 + * 释放(仅需调用一次)
  77 + */
  78 + fun release()
  79 +
  80 + /**
  81 + * 注册人脸
  82 + * @param faceId 人脸Id
  83 + * @param faceData 人脸数据
  84 + * @return 注册结果
  85 + */
  86 + fun registerFace(context: Context?, nv21: ByteArray?, width: Int, height: Int,
  87 + faceData: ByteArray,faceId: String) : Boolean
  88 +
  89 + /**
  90 + * 删除指定人脸
  91 + * @param faceId 人脸Id
  92 + * @return 删除结果
  93 + */
  94 + fun deleteFace(faceId: String): Boolean
  95 +
  96 + /**
  97 + * 清除人脸
  98 + */
  99 + fun clearFace(): Boolean
  100 +
  101 +}
  1 +package com.yhkj.rebotsdk.face
  2 +
  3 +import android.content.Context
  4 +import com.yhkj.rebotsdk.engine.EngineInterface
  5 +import com.yhkj.rebotsdk.engine.EngineStatusCode
  6 +import com.yhkj.rebotsdk.engine.FaceEngineFactory
  7 +
  8 +
  9 +internal class FaceManager private constructor() : FaceInterface {
  10 +
  11 + private lateinit var mFaceEngine: EngineInterface
  12 + private var mOnEnrollListener: FaceInterface.OnEnrollListener? = null
  13 + private var mOnVerifyListener: FaceInterface.OnVerifyListener? = null
  14 +
  15 + companion object {
  16 +
  17 + val instance: FaceManager by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
  18 + FaceManager()
  19 + }
  20 +
  21 + }
  22 +
  23 + override fun activeFace(
  24 + context: Context,
  25 + active_key: String,
  26 + appId: String,
  27 + sdkKey: String
  28 + ): Int {
  29 + mFaceEngine = FaceEngineFactory().createEngine()
  30 + return mFaceEngine.activeFace(context, active_key, appId, sdkKey)
  31 + }
  32 +
  33 + override fun initFace(context: Context): StatusData {
  34 + return mFaceEngine.init(context).let {
  35 + when (it.code) {
  36 + EngineStatusCode.SUCCEED -> {
  37 + StatusData(StatusCode.SUCCEED, it.msg)
  38 + }
  39 + else -> {
  40 + StatusData(StatusCode.FAILED, it.msg)
  41 + }
  42 + }
  43 + }
  44 + }
  45 +
  46 + override fun setEnrollListener(listener: FaceInterface.OnEnrollListener) {
  47 + mOnEnrollListener=listener
  48 + }
  49 +
  50 + override fun setVerifyListener(listener: FaceInterface.OnVerifyListener) {
  51 + mOnVerifyListener=listener
  52 + }
  53 +
  54 + override fun release() {
  55 + mFaceEngine.unInit()
  56 + }
  57 +
  58 + override fun registerFace(context: Context?, nv21: ByteArray?, width: Int, height: Int,
  59 + faceData: ByteArray,faceId: String): Boolean {
  60 + TODO("Not yet implemented")
  61 + }
  62 +
  63 + override fun deleteFace(faceId: String): Boolean {
  64 + TODO("Not yet implemented")
  65 + }
  66 +
  67 + override fun clearFace(): Boolean {
  68 + TODO("Not yet implemented")
  69 + }
  70 +
  71 +
  72 +}
  1 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  2 + xmlns:aapt="http://schemas.android.com/aapt"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportHeight="108"
  6 + android:viewportWidth="108">
  7 + <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
  8 + <aapt:attr name="android:fillColor">
  9 + <gradient
  10 + android:endX="85.84757"
  11 + android:endY="92.4963"
  12 + android:startX="42.9492"
  13 + android:startY="49.59793"
  14 + android:type="linear">
  15 + <item
  16 + android:color="#44000000"
  17 + android:offset="0.0" />
  18 + <item
  19 + android:color="#00000000"
  20 + android:offset="1.0" />
  21 + </gradient>
  22 + </aapt:attr>
  23 + </path>
  24 + <path
  25 + android:fillColor="#FFFFFF"
  26 + android:fillType="nonZero"
  27 + android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
  28 + android:strokeColor="#00000000"
  29 + android:strokeWidth="1" />
  30 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:width="108dp"
  4 + android:height="108dp"
  5 + android:viewportHeight="108"
  6 + android:viewportWidth="108">
  7 + <path
  8 + android:fillColor="#3DDC84"
  9 + android:pathData="M0,0h108v108h-108z" />
  10 + <path
  11 + android:fillColor="#00000000"
  12 + android:pathData="M9,0L9,108"
  13 + android:strokeColor="#33FFFFFF"
  14 + android:strokeWidth="0.8" />
  15 + <path
  16 + android:fillColor="#00000000"
  17 + android:pathData="M19,0L19,108"
  18 + android:strokeColor="#33FFFFFF"
  19 + android:strokeWidth="0.8" />
  20 + <path
  21 + android:fillColor="#00000000"
  22 + android:pathData="M29,0L29,108"
  23 + android:strokeColor="#33FFFFFF"
  24 + android:strokeWidth="0.8" />
  25 + <path
  26 + android:fillColor="#00000000"
  27 + android:pathData="M39,0L39,108"
  28 + android:strokeColor="#33FFFFFF"
  29 + android:strokeWidth="0.8" />
  30 + <path
  31 + android:fillColor="#00000000"
  32 + android:pathData="M49,0L49,108"
  33 + android:strokeColor="#33FFFFFF"
  34 + android:strokeWidth="0.8" />
  35 + <path
  36 + android:fillColor="#00000000"
  37 + android:pathData="M59,0L59,108"
  38 + android:strokeColor="#33FFFFFF"
  39 + android:strokeWidth="0.8" />
  40 + <path
  41 + android:fillColor="#00000000"
  42 + android:pathData="M69,0L69,108"
  43 + android:strokeColor="#33FFFFFF"
  44 + android:strokeWidth="0.8" />
  45 + <path
  46 + android:fillColor="#00000000"
  47 + android:pathData="M79,0L79,108"
  48 + android:strokeColor="#33FFFFFF"
  49 + android:strokeWidth="0.8" />
  50 + <path
  51 + android:fillColor="#00000000"
  52 + android:pathData="M89,0L89,108"
  53 + android:strokeColor="#33FFFFFF"
  54 + android:strokeWidth="0.8" />
  55 + <path
  56 + android:fillColor="#00000000"
  57 + android:pathData="M99,0L99,108"
  58 + android:strokeColor="#33FFFFFF"
  59 + android:strokeWidth="0.8" />
  60 + <path
  61 + android:fillColor="#00000000"
  62 + android:pathData="M0,9L108,9"
  63 + android:strokeColor="#33FFFFFF"
  64 + android:strokeWidth="0.8" />
  65 + <path
  66 + android:fillColor="#00000000"
  67 + android:pathData="M0,19L108,19"
  68 + android:strokeColor="#33FFFFFF"
  69 + android:strokeWidth="0.8" />
  70 + <path
  71 + android:fillColor="#00000000"
  72 + android:pathData="M0,29L108,29"
  73 + android:strokeColor="#33FFFFFF"
  74 + android:strokeWidth="0.8" />
  75 + <path
  76 + android:fillColor="#00000000"
  77 + android:pathData="M0,39L108,39"
  78 + android:strokeColor="#33FFFFFF"
  79 + android:strokeWidth="0.8" />
  80 + <path
  81 + android:fillColor="#00000000"
  82 + android:pathData="M0,49L108,49"
  83 + android:strokeColor="#33FFFFFF"
  84 + android:strokeWidth="0.8" />
  85 + <path
  86 + android:fillColor="#00000000"
  87 + android:pathData="M0,59L108,59"
  88 + android:strokeColor="#33FFFFFF"
  89 + android:strokeWidth="0.8" />
  90 + <path
  91 + android:fillColor="#00000000"
  92 + android:pathData="M0,69L108,69"
  93 + android:strokeColor="#33FFFFFF"
  94 + android:strokeWidth="0.8" />
  95 + <path
  96 + android:fillColor="#00000000"
  97 + android:pathData="M0,79L108,79"
  98 + android:strokeColor="#33FFFFFF"
  99 + android:strokeWidth="0.8" />
  100 + <path
  101 + android:fillColor="#00000000"
  102 + android:pathData="M0,89L108,89"
  103 + android:strokeColor="#33FFFFFF"
  104 + android:strokeWidth="0.8" />
  105 + <path
  106 + android:fillColor="#00000000"
  107 + android:pathData="M0,99L108,99"
  108 + android:strokeColor="#33FFFFFF"
  109 + android:strokeWidth="0.8" />
  110 + <path
  111 + android:fillColor="#00000000"
  112 + android:pathData="M19,29L89,29"
  113 + android:strokeColor="#33FFFFFF"
  114 + android:strokeWidth="0.8" />
  115 + <path
  116 + android:fillColor="#00000000"
  117 + android:pathData="M19,39L89,39"
  118 + android:strokeColor="#33FFFFFF"
  119 + android:strokeWidth="0.8" />
  120 + <path
  121 + android:fillColor="#00000000"
  122 + android:pathData="M19,49L89,49"
  123 + android:strokeColor="#33FFFFFF"
  124 + android:strokeWidth="0.8" />
  125 + <path
  126 + android:fillColor="#00000000"
  127 + android:pathData="M19,59L89,59"
  128 + android:strokeColor="#33FFFFFF"
  129 + android:strokeWidth="0.8" />
  130 + <path
  131 + android:fillColor="#00000000"
  132 + android:pathData="M19,69L89,69"
  133 + android:strokeColor="#33FFFFFF"
  134 + android:strokeWidth="0.8" />
  135 + <path
  136 + android:fillColor="#00000000"
  137 + android:pathData="M19,79L89,79"
  138 + android:strokeColor="#33FFFFFF"
  139 + android:strokeWidth="0.8" />
  140 + <path
  141 + android:fillColor="#00000000"
  142 + android:pathData="M29,19L29,89"
  143 + android:strokeColor="#33FFFFFF"
  144 + android:strokeWidth="0.8" />
  145 + <path
  146 + android:fillColor="#00000000"
  147 + android:pathData="M39,19L39,89"
  148 + android:strokeColor="#33FFFFFF"
  149 + android:strokeWidth="0.8" />
  150 + <path
  151 + android:fillColor="#00000000"
  152 + android:pathData="M49,19L49,89"
  153 + android:strokeColor="#33FFFFFF"
  154 + android:strokeWidth="0.8" />
  155 + <path
  156 + android:fillColor="#00000000"
  157 + android:pathData="M59,19L59,89"
  158 + android:strokeColor="#33FFFFFF"
  159 + android:strokeWidth="0.8" />
  160 + <path
  161 + android:fillColor="#00000000"
  162 + android:pathData="M69,19L69,89"
  163 + android:strokeColor="#33FFFFFF"
  164 + android:strokeWidth="0.8" />
  165 + <path
  166 + android:fillColor="#00000000"
  167 + android:pathData="M79,19L79,89"
  168 + android:strokeColor="#33FFFFFF"
  169 + android:strokeWidth="0.8" />
  170 +</vector>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
  3 + <background android:drawable="@drawable/ic_launcher_background" />
  4 + <foreground android:drawable="@drawable/ic_launcher_foreground" />
  5 +</adaptive-icon>
  1 +<?xml version="1.0" encoding="utf-8"?>
  2 +<resources>
  3 + <color name="colorPrimary">#6200EE</color>
  4 + <color name="colorPrimaryDark">#3700B3</color>
  5 + <color name="colorAccent">#03DAC5</color>
  6 +</resources>
  1 +<resources>
  2 + <string name="app_name">rebot_sdk</string>
  3 +</resources>
  1 +<resources>
  2 +
  3 + <!-- Base application theme. -->
  4 + <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  5 + <!-- Customize your theme here. -->
  6 + <item name="colorPrimary">@color/colorPrimary</item>
  7 + <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
  8 + <item name="colorAccent">@color/colorAccent</item>
  9 + </style>
  10 +
  11 +</resources>
  1 +package com.yhkj.rebotsdk
  2 +
  3 +import org.junit.Test
  4 +
  5 +import org.junit.Assert.*
  6 +
  7 +/**
  8 + * Example local unit test, which will execute on the development machine (host).
  9 + *
  10 + * See [testing documentation](http://d.android.com/tools/testing).
  11 + */
  12 +class ExampleUnitTest {
  13 + @Test
  14 + fun addition_isCorrect() {
  15 + assertEquals(4, 2 + 2)
  16 + }
  17 +}
  1 +rootProject.name='rebot_sdk'
  2 +include ':sdk'