├── .gitignore ├── .idea ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── encodings.xml ├── gradle.xml ├── libraries │ ├── BmobSDK_3_5_3_20161116.xml │ ├── FaceppSDK.xml │ ├── Msc.xml │ ├── android_android_24.xml │ ├── animated_vector_drawable_24_2_1.xml │ ├── appcompat_v7_24_2_1.xml │ ├── constraint_layout_1_0_1.xml │ ├── constraint_layout_solver_1_0_1.xml │ ├── converter_gson_2_1_0.xml │ ├── espresso_core_2_2_2.xml │ ├── espresso_idling_resource_2_2_2.xml │ ├── exposed_instrumentation_api_publish_0_5.xml │ ├── eyekey_sdk_2_2.xml │ ├── gson_2_7.xml │ ├── hamcrest_core_1_3.xml │ ├── hamcrest_integration_1_3.xml │ ├── hamcrest_library_1_3.xml │ ├── javawriter_2_1_1.xml │ ├── javax_annotation_api_1_2.xml │ ├── javax_inject_1.xml │ ├── jsr305_2_0_1.xml │ ├── junit_4_12.xml │ ├── okhttp_3_4_1.xml │ ├── okio_1_9_0.xml │ ├── org_apache_http_legacy.xml │ ├── org_apache_http_legacy_android_24.xml │ ├── picasso_2_4_0.xml │ ├── recyclerview_v7_24_2_1.xml │ ├── retrofit_2_1_0.xml │ ├── rules_0_5.xml │ ├── runner_0_5.xml │ ├── rxandroid_1_2_0.xml │ ├── rxjava_1_1_6.xml │ ├── support_annotations_24_2_1.xml │ ├── support_compat_24_2_1.xml │ ├── support_core_ui_24_2_1.xml │ ├── support_core_utils_24_2_1.xml │ ├── support_fragment_24_2_1.xml │ ├── support_media_compat_24_2_1.xml │ ├── support_v4_24_2_1.xml │ └── support_vector_drawable_24_2_1.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── BmobSDK_3.5.3_20161116.jar │ ├── FaceppSDK.jar │ ├── Msc.jar │ ├── arm64-v8a │ │ ├── libBmobStat.so │ │ ├── libbmob.so │ │ └── libmsc.so │ ├── armeabi-v7a │ │ ├── libBmobStat.so │ │ ├── libbmob.so │ │ └── libmsc.so │ ├── armeabi │ │ ├── libBmobStat.so │ │ ├── libbmob.so │ │ └── libmsc.so │ ├── converter-gson-2.1.0.jar │ ├── eyekey-sdk_2.2.jar │ ├── gson-2.7.jar │ ├── mips │ │ ├── libBmobStat.so │ │ └── libbmob.so │ ├── mips64 │ │ ├── libBmobStat.so │ │ └── libbmob.so │ ├── okhttp-3.4.1.jar │ ├── okio-1.9.0.jar │ ├── org.apache.http.legacy.jar │ ├── picasso-2.4.0.jar │ ├── retrofit-2.1.0.jar │ ├── rxandroid-1.2.0.jar │ ├── rxjava-1.1.6.jar │ ├── x86 │ │ ├── libBmobStat.so │ │ └── libbmob.so │ └── x86_64 │ │ ├── libBmobStat.so │ │ └── libbmob.so ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── henu │ │ └── swface │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ ├── com │ │ │ └── henu │ │ │ │ └── swface │ │ │ │ ├── Adapter │ │ │ │ ├── FaceDetailAdapter.java │ │ │ │ ├── ManageAdapter.java │ │ │ │ └── StickyListAdapter.java │ │ │ │ ├── Database │ │ │ │ ├── BmobDataHelper.java │ │ │ │ ├── DatabaseAdapter.java │ │ │ │ ├── DatabaseHelper.java │ │ │ │ ├── FaceMetaData.java │ │ │ │ ├── SignLogData.java │ │ │ │ └── UserMetaData.java │ │ │ │ ├── MyApplication.java │ │ │ │ ├── Utils │ │ │ │ ├── DateUtil.java │ │ │ │ ├── FaceRect.java │ │ │ │ ├── FaceSetUtil.java │ │ │ │ ├── FaceUtil.java │ │ │ │ ├── FinalUtil.java │ │ │ │ ├── JSONUtil.java │ │ │ │ ├── ParseResult.java │ │ │ │ ├── PictureUtil.java │ │ │ │ └── RoundTransform.java │ │ │ │ ├── VO │ │ │ │ ├── Face.java │ │ │ │ ├── FaceSignIn.java │ │ │ │ ├── SignLog.java │ │ │ │ ├── UserHasSigned.java │ │ │ │ └── UserLogin.java │ │ │ │ └── activity │ │ │ │ ├── AddFaceActivity.java │ │ │ │ ├── BaseVideoActivity.java │ │ │ │ ├── DialogInputNameActivity.java │ │ │ │ ├── FaceDetailActivity.java │ │ │ │ ├── LoginActivity.java │ │ │ │ ├── MainActivity.java │ │ │ │ ├── ManageActivity.java │ │ │ │ ├── RegisterFaceActivity.java │ │ │ │ ├── SignInActivity.java │ │ │ │ ├── SignLogActivity.java │ │ │ │ └── ViewPictureActivity.java │ │ ├── in │ │ │ └── srain │ │ │ │ └── cube │ │ │ │ └── views │ │ │ │ └── ptr │ │ │ │ ├── PtrClassicDefaultHeader.java │ │ │ │ ├── PtrClassicFrameLayout.java │ │ │ │ ├── PtrDefaultHandler.java │ │ │ │ ├── PtrFrameLayout.java │ │ │ │ ├── PtrHandler.java │ │ │ │ ├── PtrUIHandler.java │ │ │ │ ├── PtrUIHandlerHolder.java │ │ │ │ ├── PtrUIHandlerHook.java │ │ │ │ ├── header │ │ │ │ ├── MaterialHeader.java │ │ │ │ ├── MaterialProgressDrawable.java │ │ │ │ ├── StoreHouseBarItem.java │ │ │ │ ├── StoreHouseHeader.java │ │ │ │ └── StoreHousePath.java │ │ │ │ ├── indicator │ │ │ │ ├── PtrIndicator.java │ │ │ │ └── PtrTensionIndicator.java │ │ │ │ └── util │ │ │ │ ├── PtrCLog.java │ │ │ │ └── PtrLocalDisplay.java │ │ └── se │ │ │ └── emilsjolander │ │ │ └── stickylistheaders │ │ │ ├── AdapterWrapper.java │ │ │ ├── CheckableWrapperView.java │ │ │ ├── DistinctMultiHashMap.java │ │ │ ├── DualHashMap.java │ │ │ ├── ExpandableStickyListHeadersAdapter.java │ │ │ ├── ExpandableStickyListHeadersListView.java │ │ │ ├── SectionIndexerAdapterWrapper.java │ │ │ ├── StickyListHeadersAdapter.java │ │ │ ├── StickyListHeadersListView.java │ │ │ ├── WrapperView.java │ │ │ └── WrapperViewList.java │ ├── jniLibs │ │ └── armeabi │ │ │ └── libmsc.so │ └── res │ │ ├── drawable │ │ └── ptr_rotate_arrow.png │ │ ├── layout │ │ ├── activity_dialog_input_name.xml │ │ ├── activity_face_detail.xml │ │ ├── activity_login.xml │ │ ├── activity_main.xml │ │ ├── activity_manage.xml │ │ ├── activity_sign_log.xml │ │ ├── activity_video.xml │ │ ├── activity_view_picture.xml │ │ ├── cube_ptr_classic_default_header.xml │ │ ├── cube_ptr_simple_loading.xml │ │ ├── dialog_sign_in_success.xml │ │ ├── header.xml │ │ ├── list_footer.xml │ │ ├── list_header.xml │ │ ├── recyclerview_face_detail_item.xml │ │ ├── recycleview_manage_item.xml │ │ └── sticky_list_item_layout.xml │ │ ├── menu │ │ ├── base_toolbar_menu.xml │ │ ├── face_detail_toobar_menu.xml │ │ └── sign_log_toobar_menu.xml │ │ ├── mipmap-hdpi │ │ ├── button_login_press.png │ │ ├── button_tijiao.png │ │ ├── button_tijiao_press.png │ │ ├── ic_launcher.png │ │ └── login_icon.png │ │ ├── mipmap-mdpi │ │ ├── button_login.png │ │ ├── button_login_press.png │ │ ├── button_tijiao.png │ │ ├── button_tijiao_press.png │ │ ├── ic_launcher.png │ │ └── login_icon.png │ │ ├── mipmap-xhdpi │ │ ├── add_face_picture.png │ │ ├── app_icon.png │ │ ├── bacground_2.png │ │ ├── button_back.png │ │ ├── button_login.png │ │ ├── button_login_press.png │ │ ├── button_tijiao.png │ │ ├── button_tijiao_press.png │ │ ├── delete_image.png │ │ ├── delete_picture.png │ │ ├── detail_picture.png │ │ ├── edit_button.png │ │ ├── ic_launcher.png │ │ ├── loading.jpg │ │ ├── login_icon.png │ │ └── no_face.jpg │ │ ├── mipmap-xxhdpi │ │ ├── bacground_1.png │ │ ├── button_login.png │ │ ├── button_login_press.png │ │ ├── button_tijiao.png │ │ ├── button_tijiao_press.png │ │ ├── ic_launcher.png │ │ ├── sign_failed.png │ │ ├── sign_log_title.png │ │ └── sign_success.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── arrays.xml │ │ ├── attrs.xml │ │ ├── colors.xml │ │ ├── cube_ptr_attrs.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── henu │ └── swface │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # Intellij 36 | *.iml 37 | .idea/workspace.xml 38 | 39 | # Keystore files 40 | *.jks 41 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /.idea/libraries/BmobSDK_3_5_3_20161116.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/FaceppSDK.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/Msc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/android_android_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/animated_vector_drawable_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/appcompat_v7_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/constraint_layout_1_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/constraint_layout_solver_1_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/converter_gson_2_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/espresso_core_2_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/espresso_idling_resource_2_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/exposed_instrumentation_api_publish_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/eyekey_sdk_2_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/gson_2_7.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_core_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_integration_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/hamcrest_library_1_3.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javawriter_2_1_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javax_annotation_api_1_2.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/javax_inject_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/jsr305_2_0_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/junit_4_12.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/okhttp_3_4_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/okio_1_9_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/org_apache_http_legacy.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/org_apache_http_legacy_android_24.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/picasso_2_4_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/recyclerview_v7_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/libraries/retrofit_2_1_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/rules_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/runner_0_5.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/libraries/rxandroid_1_2_0.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/rxjava_1_1_6.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/libraries/support_annotations_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.idea/libraries/support_compat_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_core_ui_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_core_utils_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_fragment_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_media_compat_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /.idea/libraries/support_v4_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/libraries/support_vector_drawable_24_2_1.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 46 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swface 2 | 人脸识别签到系统,基于科大讯飞人脸识别和Face++人脸识别api 3 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 24 5 | buildToolsVersion "25.0.0" 6 | useLibrary 'org.apache.http.legacy' 7 | defaultConfig { 8 | applicationId "com.henu.swface" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | sourceSets { 16 | main.jniLibs.srcDirs = ['libs'] 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | 24 | // packagingOptions { 25 | // exclude 'META-INF/DEPENDENCIES' 26 | //// exclude 'META-INF/NOTICE' 27 | // exclude 'META-INF/LICENSE' 28 | //// exclude 'META-INF/LICENSE.txt' 29 | //// exclude 'META-INF/NOTICE.txt' 30 | //// exclude 'META-INF/ASL2.0' 31 | //// exclude 'META-INF/notice.txt' 32 | // } 33 | } 34 | } 35 | 36 | dependencies { 37 | compile fileTree(include: ['*.jar'], dir: 'libs') 38 | androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', { 39 | exclude group: 'com.android.support', module: 'support-annotations' 40 | }) 41 | compile files('libs/converter-gson-2.1.0.jar') 42 | compile files('libs/eyekey-sdk_2.2.jar') 43 | compile files('libs/gson-2.7.jar') 44 | compile files('libs/okhttp-3.4.1.jar') 45 | compile files('libs/okio-1.9.0.jar') 46 | compile files('libs/retrofit-2.1.0.jar') 47 | compile files('libs/FaceppSDK.jar') 48 | compile files('libs/org.apache.http.legacy.jar') 49 | compile files('libs/BmobSDK_3.5.3_20161116.jar') 50 | compile files('libs/rxandroid-1.2.0.jar') 51 | compile files('libs/rxjava-1.1.6.jar') 52 | compile 'com.android.support:appcompat-v7:24.2.1' 53 | compile 'com.android.support:recyclerview-v7:24.2.1' 54 | compile 'com.android.support.constraint:constraint-layout:1.0.1' 55 | testCompile 'junit:junit:4.12' 56 | } 57 | -------------------------------------------------------------------------------- /app/libs/BmobSDK_3.5.3_20161116.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/BmobSDK_3.5.3_20161116.jar -------------------------------------------------------------------------------- /app/libs/FaceppSDK.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/FaceppSDK.jar -------------------------------------------------------------------------------- /app/libs/Msc.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/Msc.jar -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/arm64-v8a/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/arm64-v8a/libbmob.so -------------------------------------------------------------------------------- /app/libs/arm64-v8a/libmsc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/arm64-v8a/libmsc.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi-v7a/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi-v7a/libbmob.so -------------------------------------------------------------------------------- /app/libs/armeabi-v7a/libmsc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi-v7a/libmsc.so -------------------------------------------------------------------------------- /app/libs/armeabi/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/armeabi/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi/libbmob.so -------------------------------------------------------------------------------- /app/libs/armeabi/libmsc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/armeabi/libmsc.so -------------------------------------------------------------------------------- /app/libs/converter-gson-2.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/converter-gson-2.1.0.jar -------------------------------------------------------------------------------- /app/libs/eyekey-sdk_2.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/eyekey-sdk_2.2.jar -------------------------------------------------------------------------------- /app/libs/gson-2.7.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/gson-2.7.jar -------------------------------------------------------------------------------- /app/libs/mips/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/mips/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/mips/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/mips/libbmob.so -------------------------------------------------------------------------------- /app/libs/mips64/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/mips64/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/mips64/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/mips64/libbmob.so -------------------------------------------------------------------------------- /app/libs/okhttp-3.4.1.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/okhttp-3.4.1.jar -------------------------------------------------------------------------------- /app/libs/okio-1.9.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/okio-1.9.0.jar -------------------------------------------------------------------------------- /app/libs/org.apache.http.legacy.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/org.apache.http.legacy.jar -------------------------------------------------------------------------------- /app/libs/picasso-2.4.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/picasso-2.4.0.jar -------------------------------------------------------------------------------- /app/libs/retrofit-2.1.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/retrofit-2.1.0.jar -------------------------------------------------------------------------------- /app/libs/rxandroid-1.2.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/rxandroid-1.2.0.jar -------------------------------------------------------------------------------- /app/libs/rxjava-1.1.6.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/rxjava-1.1.6.jar -------------------------------------------------------------------------------- /app/libs/x86/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/x86/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/x86/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/x86/libbmob.so -------------------------------------------------------------------------------- /app/libs/x86_64/libBmobStat.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/x86_64/libBmobStat.so -------------------------------------------------------------------------------- /app/libs/x86_64/libbmob.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/libs/x86_64/libbmob.so -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Administrator\AppData\Local\Android\Sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/henu/swface/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumentation test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.henu.swface", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Adapter/FaceDetailAdapter.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Adapter; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.net.Uri; 6 | import android.os.Vibrator; 7 | import android.support.v7.widget.RecyclerView; 8 | import android.util.Log; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.widget.ImageView; 13 | 14 | import com.henu.swface.R; 15 | import com.henu.swface.VO.UserHasSigned; 16 | import com.henu.swface.activity.AddFaceActivity; 17 | import com.henu.swface.activity.RegisterFaceActivity; 18 | import com.squareup.picasso.Picasso; 19 | 20 | import java.util.List; 21 | 22 | /** 23 | * Created by 宋浩祥 on 2017/4/15. 24 | */ 25 | 26 | public class FaceDetailAdapter extends RecyclerView.Adapter { 27 | 28 | private List imageList; 29 | private OnItemLongClickListener onItemLongClickListener; 30 | private static final String TAG = FaceDetailAdapter.class.getSimpleName(); 31 | private OnDeleteClickListener onDeleteClickListener; 32 | private OnFaceClickListener onFaceClickListener; 33 | private int imageView_width = -1; 34 | private boolean onLongClick = false; 35 | 36 | public void setOnLongClick(boolean onLongClick) { 37 | this.onLongClick = onLongClick; 38 | } 39 | 40 | public FaceDetailAdapter(List imageList) { 41 | this.imageList = imageList; 42 | } 43 | 44 | @Override 45 | public ViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) { 46 | final Context context = parent.getContext(); 47 | //获取屏幕宽度便于动态改变imageview长宽 48 | if (imageView_width < 0) { 49 | imageView_width = (parent.getWidth() - 30) / 3; 50 | } 51 | View view = LayoutInflater.from(context).inflate(R.layout.recyclerview_face_detail_item, parent, false); 52 | final ViewHolder viewHolder = new ViewHolder(view); 53 | 54 | if (onFaceClickListener != null) { 55 | viewHolder.imageView_face_detail.setOnClickListener(new View.OnClickListener() { 56 | @Override 57 | public void onClick(View view) { 58 | int position = viewHolder.getAdapterPosition(); 59 | onFaceClickListener.onFaceClick(view,position); 60 | } 61 | }); 62 | } 63 | if (onItemLongClickListener != null) { 64 | viewHolder.imageView_face_detail.setOnLongClickListener(new View.OnLongClickListener() { 65 | @Override 66 | public boolean onLongClick(View view) { 67 | int position = viewHolder.getAdapterPosition(); 68 | Log.i(TAG, "onLongClick: " + position); 69 | if (position < imageList.size()) { 70 | onLongClick = true; 71 | onItemLongClickListener.onItemLongClick(view); 72 | } 73 | return false; 74 | } 75 | }); 76 | } 77 | 78 | if (onDeleteClickListener != null) { 79 | viewHolder.imageView_delete_face_detail.setOnClickListener(new View.OnClickListener() { 80 | @Override 81 | public void onClick(View view) { 82 | if (onLongClick) { 83 | int position = viewHolder.getAdapterPosition(); 84 | onDeleteClickListener.onDeleteClick(view, position); 85 | } 86 | } 87 | }); 88 | } 89 | return viewHolder; 90 | } 91 | 92 | 93 | @Override 94 | public void onBindViewHolder(ViewHolder holder, int position) { 95 | Context context = holder.itemView.getContext(); 96 | if (position < imageList.size()) { 97 | if (onLongClick) { 98 | holder.imageView_delete_face_detail.setVisibility(View.VISIBLE); 99 | } else { 100 | holder.imageView_delete_face_detail.setVisibility(View.GONE); 101 | } 102 | Uri imagePath = imageList.get(position); 103 | Picasso.with(context).load(imagePath).placeholder(R.mipmap.loading).into(holder.imageView_face_detail); 104 | } else { 105 | Picasso.with(context).load(R.mipmap.add_face_picture).placeholder(R.mipmap.loading).into(holder.imageView_face_detail); 106 | } 107 | if (imageView_width > 0) { 108 | //动态改变imageview长宽使其保持正方形 109 | ViewGroup.LayoutParams ps = holder.imageView_face_detail.getLayoutParams(); 110 | ps.height = imageView_width; 111 | holder.imageView_face_detail.setLayoutParams(ps); 112 | } 113 | } 114 | 115 | @Override 116 | public int getItemCount() { 117 | if (imageList.size() < 5) { 118 | return imageList.size() + 1; 119 | } else { 120 | return imageList.size(); 121 | } 122 | } 123 | 124 | static class ViewHolder extends RecyclerView.ViewHolder { 125 | ImageView imageView_face_detail; 126 | ImageView imageView_delete_face_detail; 127 | 128 | public ViewHolder(View itemView) { 129 | super(itemView); 130 | imageView_face_detail = (ImageView) itemView.findViewById(R.id.imageView_face_detail); 131 | imageView_delete_face_detail = (ImageView) itemView.findViewById(R.id.imageView_delete_face_detail); 132 | 133 | } 134 | } 135 | 136 | public void setOnItemLongClickListener(OnItemLongClickListener onItemLongClickListener) { 137 | this.onItemLongClickListener = onItemLongClickListener; 138 | } 139 | 140 | public void setOnDeleteClickListener(OnDeleteClickListener onDeleteClickListener) { 141 | this.onDeleteClickListener = onDeleteClickListener; 142 | } 143 | 144 | public void setOnFaceClickListener(OnFaceClickListener onFaceClickListener) { 145 | this.onFaceClickListener = onFaceClickListener; 146 | } 147 | 148 | 149 | public interface OnItemLongClickListener { 150 | void onItemLongClick(View view); 151 | } 152 | 153 | public interface OnDeleteClickListener { 154 | void onDeleteClick(View view, int index); 155 | } 156 | 157 | public interface OnFaceClickListener { 158 | void onFaceClick(View view,int position); 159 | } 160 | 161 | } 162 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Database/DatabaseHelper.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Database; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | 7 | /** 8 | * Created by 宋浩祥 on 2017/3/7. 9 | */ 10 | 11 | public class DatabaseHelper extends SQLiteOpenHelper { 12 | 13 | private static final String DB_NAME = "Swface.db"; 14 | private static final int version = 1; 15 | private static final String CREATE_TABLE_FACES = "CREATE TABLE IF NOT EXISTS Faces(id INTEGER PRIMARY KEY AUTOINCREMENT," + 16 | "request_id TEXT,gender TEXT,glass TEXT,ethnicity TEXT," + 17 | "time_used INTEGER,age INTEGER,face_rectangle_width INTEGER,face_rectangle_top INTEGER," + 18 | "face_rectangle_left INTEGER,face_rectangle_height INTEGER," + 19 | "left_normal_glass_eye_open REAL,left_no_glass_eye_close REAL,left_occlusion REAL," + 20 | "left_no_glass_eye_open REAL,left_normal_glass_eye_close REAL,left_dark_glasses REAL," + 21 | "right_normal_glass_eye_open REAL,right_no_glass_eye_close REAL,right_occlusion REAL," + 22 | "right_no_glass_eye_open REAL,right_normal_glass_eye_close REAL,right_dark_glasses REAL," + 23 | "headpose_yaw_angle REAL,headpose_pitch_angle REAL,headpose_roll_angle REAL,blurness REAL," + 24 | "smile REAL,facequality REAL,face_token TEXT,image_path TEXT)"; 25 | private static final String DROP_TABLE_FACES = "DROP TABLE IF EXISTS Faces"; 26 | private static final String CREATE_TABLE_USER = "CREATE TABLE IF NOT EXISTS UserHasSigned(object_id TEXT PRIMARY KEY," + 27 | "user_name TEXT,face_token1 TEXT,face_token2 TEXT,face_token3 TEXT,face_token4 TEXT,face_token5 TEXT,face_url1 TEXT," + 28 | "face_url2 TEXT,face_url3 TEXT,face_url4 TEXT,face_url5 TEXT,created_at TEXT,updated_at TEXT)"; 29 | private static final String DROP_TABLE_USER = "DROP TABLE IF EXISTS UserHasSigned"; 30 | private static final String CREATE_TABLE_SIGNLOG = "CREATE TABLE IF NOT EXISTS SignLog(id INTEGER PRIMARY KEY AUTOINCREMENT," + 31 | "object_id TEXT,user_name TEXT,confidence REAL,time NUMERIC)"; 32 | private static final String DROP_TABLE_SIGNLOG = "DROP TABLE IF EXISTS SignLog"; 33 | 34 | public DatabaseHelper(Context context) { 35 | super(context, DB_NAME, null, version); 36 | } 37 | 38 | @Override 39 | public void onCreate(SQLiteDatabase db) { 40 | db.execSQL(CREATE_TABLE_FACES); 41 | db.execSQL(CREATE_TABLE_USER); 42 | db.execSQL(CREATE_TABLE_SIGNLOG); 43 | } 44 | 45 | @Override 46 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 47 | db.execSQL(DROP_TABLE_FACES); 48 | db.execSQL(DROP_TABLE_USER); 49 | db.execSQL(DROP_TABLE_SIGNLOG); 50 | db.execSQL(CREATE_TABLE_FACES); 51 | db.execSQL(CREATE_TABLE_USER); 52 | db.execSQL(CREATE_TABLE_SIGNLOG); 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Database/FaceMetaData.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Database; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | /** 6 | * Created by 宋浩祥 on 2017/3/7. 7 | */ 8 | 9 | public final class FaceMetaData { 10 | private FaceMetaData() { 11 | } 12 | 13 | 14 | 15 | 16 | public static abstract class FaceTable implements BaseColumns{ 17 | public static final String TABLE_NAME = "Faces"; 18 | public static final String REQUEST_ID = "request_id"; 19 | public static final String GENDER = "gender"; 20 | public static final String GLASS = "glass"; 21 | public static final String ETHNICITY = "ethnicity"; 22 | public static final String TIME_USED = "time_used"; 23 | public static final String AGE = "age"; 24 | public static final String FACE_RECTANGLE_WIDTH = "face_rectangle_width"; 25 | public static final String FACE_RECTANGLE_TOP = "face_rectangle_top"; 26 | public static final String FACE_RECTANGLE_LEFT = "face_rectangle_left"; 27 | public static final String FACE_RECTANGLE_HEIGHT = "face_rectangle_height"; 28 | public static final String LEFT_NORMAL_GLASS_EYE_OPEN = "left_normal_glass_eye_open"; 29 | public static final String LEFT_NO_GLASS_EYE_CLOSE = "left_no_glass_eye_close"; 30 | public static final String LEFT_OCCLUSION = "left_occlusion"; 31 | public static final String LEFT_NO_GLASS_EYE_OPEN = "left_no_glass_eye_open"; 32 | public static final String LEFT_NORMAL_GLASS_EYE_CLOSE ="left_normal_glass_eye_close"; 33 | public static final String LEFT_DARK_GLASSES = "left_dark_glasses"; 34 | public static final String RIGHT_NORMAL_GLASS_EYE_OPEN = "right_normal_glass_eye_open"; 35 | public static final String RIGHT_NO_GLASS_EYE_CLOSE = "right_no_glass_eye_close"; 36 | public static final String RIGHT_OCCLUSION = "right_occlusion"; 37 | public static final String RIGHT_NO_GLASS_EYE_OPEN = "right_no_glass_eye_open"; 38 | public static final String RIGHT_NORMAL_GLASS_EYE_CLOSE = "right_normal_glass_eye_close"; 39 | public static final String RIGHT_DARK_GLASSES = "right_dark_glasses"; 40 | public static final String HEADPOSE_YAW_ANGLE = "headpose_yaw_angle"; 41 | public static final String HEADPOSE_PITCH_ANGLE = "headpose_pitch_angle"; 42 | public static final String HEADPOSE_ROLL_ANGLE = "headpose_roll_angle"; 43 | public static final String BLURNESS = "blurness"; 44 | public static final String SMILE = "smile"; 45 | public static final String FACEQUALITY = "facequality"; 46 | public static final String FACE_TOKEN = "face_token"; 47 | public static final String IMAGE_PATH = "image_path"; 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Database/SignLogData.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Database; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | /** 6 | * Created by 宋浩祥 on 2017/4/4. 7 | */ 8 | 9 | public final class SignLogData{ 10 | public SignLogData() { 11 | } 12 | 13 | public static abstract class SignLogTable implements BaseColumns{ 14 | public static final String TABLE_NAME = "SignLog"; 15 | public static final String OBJECT_ID = "object_id"; 16 | public static final String USER_NAME = "user_name"; 17 | public static final String CONFIDENCE = "confidence"; 18 | public static final String TIME = "time"; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Database/UserMetaData.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Database; 2 | 3 | import android.provider.BaseColumns; 4 | 5 | /** 6 | * Created by Administrator on 2017/3/29. 7 | */ 8 | 9 | public final class UserMetaData { 10 | public UserMetaData() { 11 | } 12 | public static abstract class UserTable implements BaseColumns{ 13 | public static final String TABLE_NAME = "UserHasSigned"; 14 | public static final String OBJECT_ID = "object_id"; 15 | public static final String USER_NAME = "user_name"; 16 | public static final String FACE_TOKEN1 = "face_token1"; 17 | public static final String FACE_TOKEN2 = "face_token2"; 18 | public static final String FACE_TOKEN3 = "face_token3"; 19 | public static final String FACE_TOKEN4 = "face_token4"; 20 | public static final String FACE_TOKEN5 = "face_token5"; 21 | public static final String FACE_URL1 = "face_url1"; 22 | public static final String FACE_URL2 = "face_url2"; 23 | public static final String FACE_URL3 = "face_url3"; 24 | public static final String FACE_URL4 = "face_url4"; 25 | public static final String FACE_URL5 = "face_url5"; 26 | public static final String CREATED_AT = "created_at"; 27 | public static final String UPDATED_AT = "updated_at"; 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/MyApplication.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import cn.bmob.v3.Bmob; 7 | 8 | /** 9 | * Created by Administrator on 2017/4/20. 10 | */ 11 | 12 | public class MyApplication extends Application { 13 | 14 | @Override 15 | public void onCreate() { 16 | super.onCreate(); 17 | //第一:默认初始化 18 | Bmob.initialize(this, "6e64317b6509bb503f7d9ab8404bf54c"); 19 | Log.i("onCreate: ","bmob!!!!"); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/DateUtil.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.provider.ContactsContract; 4 | 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | /** 9 | * Created by Administrator on 2017/5/30. 10 | */ 11 | 12 | public class DateUtil { 13 | 14 | public static Date getDateFromLong(long time){ 15 | Calendar calendar = Calendar.getInstance(); 16 | calendar.setTimeInMillis(time); 17 | return calendar.getTime(); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/FaceRect.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.graphics.Point; 4 | import android.graphics.Rect; 5 | 6 | /** 7 | * @author 宋浩祥 8 | * FaceRect是用于表示人脸检测的结果,其中包括了 人脸的角度、得分、检测框位置、关键点 9 | */ 10 | public class FaceRect { 11 | public float score; 12 | 13 | public Rect bound = new Rect(); 14 | public Point point[]; 15 | 16 | public Rect raw_bound = new Rect(); 17 | public Point raw_point[]; 18 | 19 | @Override 20 | public String toString() { 21 | return bound.toString(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/FaceSetUtil.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.content.Context; 4 | import android.content.SharedPreferences; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | 9 | import okhttp3.MediaType; 10 | import okhttp3.MultipartBody; 11 | import okhttp3.OkHttpClient; 12 | import okhttp3.Request; 13 | import okhttp3.RequestBody; 14 | import okhttp3.Response; 15 | 16 | /** 17 | * Created by Administrator on 2017/4/22. 18 | */ 19 | 20 | public class FaceSetUtil { 21 | public static Response searchFaceset(Context context, String API_KEY, String API_Secret, File imageFile) { 22 | OkHttpClient client = new OkHttpClient(); 23 | RequestBody requestBody = postBodySearchFaceSet(context, API_KEY, API_Secret, imageFile); 24 | Request request = new Request.Builder().url("https://api-cn.faceplusplus.com/facepp/v3/search") 25 | .post(requestBody).build(); 26 | Response response = null; 27 | try { 28 | int attemp = 0; 29 | do { 30 | try { 31 | Thread.sleep(50); 32 | } catch (InterruptedException e) { 33 | e.printStackTrace(); 34 | } 35 | response = client.newCall(request).execute(); 36 | attemp++; 37 | } while (response.code() != 200 && attemp < 10); 38 | } catch (IOException e) { 39 | e.printStackTrace(); 40 | } 41 | return response; 42 | } 43 | 44 | protected static RequestBody postBodySearchFaceSet(Context context, String API_KEY, String API_Secret, File file) { 45 | // 设置请求体 46 | MediaType MEDIA_TYPE_JPG = MediaType.parse("image/jpg"); 47 | RequestBody body = MultipartBody.create(MEDIA_TYPE_JPG, file); 48 | MultipartBody.Builder builder = new MultipartBody.Builder(); 49 | builder.setType(MultipartBody.FORM); 50 | //这里是 封装上传图片参数 51 | builder.addFormDataPart("api_key", API_KEY); 52 | builder.addFormDataPart("api_secret", API_Secret); 53 | builder.addFormDataPart("image_file", file.getName(), body); 54 | SharedPreferences sp = context.getSharedPreferences("login", Context.MODE_PRIVATE); 55 | String outerId = sp.getString("username", "default"); 56 | builder.addFormDataPart("outer_id", outerId); 57 | return builder.build(); 58 | } 59 | 60 | public static Response createFaceSet(String API_KEY, String API_Secret, String display_name, String outer_id, String face_token) { 61 | OkHttpClient client = new OkHttpClient(); 62 | //封装请求体 63 | RequestBody requestBody = postBodyCreateFaceSet(API_KEY, API_Secret, display_name, outer_id, face_token); 64 | Request request = new Request.Builder().url("https://api-cn.faceplusplus.com/facepp/v3/faceset/create") 65 | .post(requestBody).build(); 66 | Response response = null; 67 | try { 68 | response = client.newCall(request).execute(); 69 | } catch (IOException e) { 70 | e.printStackTrace(); 71 | } 72 | return response; 73 | } 74 | 75 | private static RequestBody postBodyCreateFaceSet(String API_KEY, String API_Secret, String display_name, String outer_id, String face_token) { 76 | // 设置请求体 77 | MultipartBody.Builder builder = new MultipartBody.Builder(); 78 | builder.setType(MultipartBody.FORM); 79 | builder.addFormDataPart("api_key", API_KEY); 80 | builder.addFormDataPart("api_secret", API_Secret); 81 | builder.addFormDataPart("display_name", display_name); 82 | builder.addFormDataPart("outer_id", outer_id); 83 | builder.addFormDataPart("face_tokens", face_token); 84 | builder.addFormDataPart("force_merge", "1"); 85 | return builder.build(); 86 | } 87 | 88 | public static Response removeFaceFormFaceSet(String API_KEY, String API_Secret, String outer_id, String face_token){ 89 | OkHttpClient client = new OkHttpClient(); 90 | RequestBody requestBody = postBodyRemoveFaceFormFaceSet(API_KEY, API_Secret, outer_id,face_token); 91 | Request request = new Request.Builder().url("https://api-cn.faceplusplus.com/facepp/v3/faceset/removeface") 92 | .post(requestBody).build(); 93 | Response response = null; 94 | try { 95 | int attemp = 0; 96 | do { 97 | try { 98 | Thread.sleep(50); 99 | } catch (InterruptedException e) { 100 | e.printStackTrace(); 101 | } 102 | response = client.newCall(request).execute(); 103 | attemp++; 104 | } while (response.code() != 200 && attemp < 10); 105 | } catch (IOException e) { 106 | e.printStackTrace(); 107 | } 108 | return response; 109 | } 110 | 111 | private static RequestBody postBodyRemoveFaceFormFaceSet(String API_KEY, String API_Secret, String outer_id, String face_token){ 112 | // 设置请求体 113 | MultipartBody.Builder builder = new MultipartBody.Builder(); 114 | builder.setType(MultipartBody.FORM); 115 | builder.addFormDataPart("api_key", API_KEY); 116 | builder.addFormDataPart("api_secret", API_Secret); 117 | builder.addFormDataPart("outer_id", outer_id); 118 | builder.addFormDataPart("face_tokens", face_token); 119 | return builder.build(); 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/FaceUtil.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.SharedPreferences; 7 | import android.graphics.Bitmap; 8 | import android.graphics.BitmapFactory; 9 | import android.graphics.Canvas; 10 | import android.graphics.Color; 11 | import android.graphics.Matrix; 12 | import android.graphics.Paint; 13 | import android.graphics.Paint.Style; 14 | import android.graphics.Point; 15 | import android.graphics.Rect; 16 | import android.media.ExifInterface; 17 | import android.net.Uri; 18 | import android.os.Environment; 19 | import android.provider.MediaStore; 20 | 21 | import java.io.BufferedOutputStream; 22 | import java.io.File; 23 | import java.io.FileFilter; 24 | import java.io.FileNotFoundException; 25 | import java.io.FileOutputStream; 26 | import java.io.IOException; 27 | import java.util.regex.Pattern; 28 | 29 | import okhttp3.MediaType; 30 | import okhttp3.MultipartBody; 31 | import okhttp3.OkHttpClient; 32 | import okhttp3.Request; 33 | import okhttp3.RequestBody; 34 | import okhttp3.Response; 35 | 36 | public class FaceUtil { 37 | 38 | 39 | public static Response detectFace(String faceToken, String API_KEY, String API_Secret) { 40 | OkHttpClient client = new OkHttpClient(); 41 | //封装请求体 42 | RequestBody requestBody = postBodyDetectFace(faceToken, API_KEY, API_Secret); 43 | Request request = new Request.Builder().url("https://api-cn.faceplusplus.com/facepp/v3/face/analyze") 44 | .post(requestBody).build(); 45 | Response response = null; 46 | try { 47 | response = client.newCall(request).execute(); 48 | int attemp = 0; 49 | while (response.code() != 200 && attemp < 10){ 50 | try { 51 | Thread.sleep(50); 52 | } catch (InterruptedException e) { 53 | e.printStackTrace(); 54 | } 55 | response = client.newCall(request).execute(); 56 | attemp++; 57 | } 58 | } catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | return response; 62 | } 63 | public static Response detectFace(File imageFile, String API_KEY, String API_Secret) { 64 | OkHttpClient client = new OkHttpClient(); 65 | //封装请求体 66 | RequestBody requestBody = postBodyDetectFace(imageFile, API_KEY, API_Secret); 67 | Request request = new Request.Builder().url("https://api-cn.faceplusplus.com/facepp/v3/detect") 68 | .post(requestBody).build(); 69 | Response response = null; 70 | try { 71 | response = client.newCall(request).execute(); 72 | } catch (IOException e) { 73 | e.printStackTrace(); 74 | } 75 | return response; 76 | } 77 | private static RequestBody postBodyDetectFace(String faceToken, String API_KEY, String API_Secret) { 78 | 79 | MultipartBody.Builder builder = new MultipartBody.Builder(); 80 | builder.setType(MultipartBody.FORM); 81 | builder.addFormDataPart("api_key", API_KEY); 82 | builder.addFormDataPart("api_secret", API_Secret); 83 | builder.addFormDataPart("face_tokens", faceToken); 84 | //builder.addFormDataPart("return_landmark", "1"); 85 | builder.addFormDataPart("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,ethnicity"); 86 | return builder.build(); 87 | } 88 | 89 | 90 | 91 | 92 | private static RequestBody postBodyDetectFace(File file, String API_KEY, String API_Secret) { 93 | // 设置请求体 94 | MediaType MEDIA_TYPE_JPG = MediaType.parse("image/jpg"); 95 | RequestBody body = MultipartBody.create(MEDIA_TYPE_JPG, file); 96 | MultipartBody.Builder builder = new MultipartBody.Builder(); 97 | builder.setType(MultipartBody.FORM); 98 | builder.addFormDataPart("api_key", API_KEY); 99 | builder.addFormDataPart("api_secret", API_Secret); 100 | builder.addFormDataPart("image_file", file.getName(), body); 101 | //builder.addFormDataPart("return_landmark", "1"); 102 | builder.addFormDataPart("return_attributes", "gender,age,smiling,headpose,facequality,blur,eyestatus,ethnicity"); 103 | return builder.build(); 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/FinalUtil.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | /** 4 | * Created by Administrator on 2017/4/16. 5 | */ 6 | 7 | public final class FinalUtil { 8 | //face++的api_key 9 | public final static String API_KEY = "lJsij4n8pYEj3bW-tSJqEhRgkdfHobC8"; 10 | public final static String API_Secret = "i1H3kRBBzJ2Wo_1T-6RsbRmWgcHAREww"; 11 | 12 | public final static int DETECT_SUCCESS = 0X100; 13 | public final static int DETECT_FAILED_IO_EXCEPTION = 0X101; 14 | public final static int DETECT_FAILED_NO_FACE = 0X102; 15 | public final static int DETECT_FAILED_LIMIT_EXCEEDED = 0X103; 16 | 17 | public final static int UPDATE_PICTURE_SUCCESS = 0X120; 18 | public final static int UPDATE_PICTURE_EXCEPTION = 0X121; 19 | 20 | public static final int SIGN_IN_SUCCESS = 0x200; 21 | public final static int SIGN_IN_FAILED_IOEXCEPTION = 0x201; 22 | public final static int SIGN_IN_FAILED_FILENOTFIND = 0x202; 23 | public final static int SIGN_IN_FAILED_NOUSER = 0x203; 24 | public final static int ADDUSERSUCCESS = 0x210; 25 | 26 | public final static int UPDATE_DETAIL_SUCCESS = 0X300; 27 | public final static int UPDATE_DETAIL_IO_EXCEPTION = 0X301; 28 | 29 | public final static int ADD_FACE_SUCCESS = 0X400; 30 | public final static int ADD_FACE_IO_EXCEPTION = 0X401; 31 | public final static int ADD_FACE_TO_FACESET_SUCCESS = 0X410; 32 | public final static int ADD_FACE_TO_FACESET_EXCEPTION = 0X411; 33 | 34 | public final static int SYN_DATA_SUCCESS = 0x500; 35 | public final static int SYN_DATA_FAILED = 0x501; 36 | 37 | public final static int REMOVE_FACE_SUCCESS = 0X600; 38 | public final static int REMOVE_FACE_IO_EXCEPTION = 0X601; 39 | public final static int REMOVE_FACE_BMOB_EXCEPTION = 0X602; 40 | 41 | public final static int REMOVE_USER_SUCCESS = 0X700; 42 | public final static int REMOVE_USER_IO_EXCEPTION = 0X701; 43 | public final static int REMOVE_USER_BMOB_EXCEPTION = 0X702; 44 | 45 | public final static int LOAD_SIGN_LOG_SUCCESS = 0X800; 46 | public final static int LOAD_SIGN_LOG_IO_EXCEPTION = 0X801; 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/JSONUtil.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.util.Log; 4 | 5 | import com.henu.swface.VO.Face; 6 | import com.henu.swface.VO.FaceSignIn; 7 | 8 | import org.json.JSONArray; 9 | import org.json.JSONException; 10 | import org.json.JSONObject; 11 | 12 | /** 13 | * Created by Administrator on 2017/3/24. 14 | */ 15 | 16 | public class JSONUtil { 17 | private static final String TAG = "JSONUtil"; 18 | 19 | public JSONUtil() { 20 | } 21 | 22 | public Face parseDetectFaceJSON(String JSON){ 23 | System.out.println("!!!!!!!!!"+JSON); 24 | Face face = new Face(); 25 | try { 26 | JSONObject jsonObject = new JSONObject(JSON); 27 | face.setRequest_id(jsonObject.get("request_id").toString()); 28 | face.setTime_used(jsonObject.getInt("time_used")); 29 | JSONArray faceArray = jsonObject.getJSONArray("faces"); 30 | if(faceArray==null){ 31 | return null; 32 | } 33 | JSONObject faceObject = faceArray.getJSONObject(0); 34 | face.setFace_rectangle_width(faceObject.getJSONObject("face_rectangle").getInt("width")); 35 | face.setFace_rectangle_top(faceObject.getJSONObject("face_rectangle").getInt("top")); 36 | face.setFace_rectangle_left(faceObject.getJSONObject("face_rectangle").getInt("left")); 37 | face.setFace_rectangle_high(faceObject.getJSONObject("face_rectangle").getInt("height")); 38 | JSONObject attributesObject = faceObject.getJSONObject("attributes"); 39 | face.setGender(attributesObject.getJSONObject("gender").get("value").toString()); 40 | face.setAge(attributesObject.getJSONObject("age").getInt("value")); 41 | face.setGlass(attributesObject.getJSONObject("glass").get("value").toString()); 42 | face.setHeadpose_yaw_angle(attributesObject.getJSONObject("headpose").getDouble("yaw_angle")); 43 | face.setHeadpose_pitch_angle(attributesObject.getJSONObject("headpose").getDouble("pitch_angle")); 44 | face.setHeadpose_roll_angle(attributesObject.getJSONObject("headpose").getDouble("roll_angle")); 45 | face.setSmile(attributesObject.getJSONObject("smile").getDouble("value")); 46 | face.setFacequality(attributesObject.getJSONObject("facequality").getDouble("value")); 47 | face.setEthnicity(attributesObject.getJSONObject("ethnicity").get("value").toString()); 48 | face.setFace_token(faceObject.get("face_token").toString()); 49 | JSONObject eyestatusObject = attributesObject.getJSONObject("eyestatus"); 50 | face.setlNormalGlassEyeOpen(eyestatusObject.getJSONObject("left_eye_status").getDouble("normal_glass_eye_open")); 51 | face.setlNoGlassEyeClose(eyestatusObject.getJSONObject("left_eye_status").getDouble("no_glass_eye_close")); 52 | face.setLeft_occlusion(eyestatusObject.getJSONObject("left_eye_status").getDouble("occlusion")); 53 | face.setLeftNoGlassEyeOpen(eyestatusObject.getJSONObject("left_eye_status").getDouble("no_glass_eye_open")); 54 | face.setlNormalGlassEyeClose(eyestatusObject.getJSONObject("left_eye_status").getDouble("normal_glass_eye_close")); 55 | face.setLeft_dark_glasses(eyestatusObject.getJSONObject("left_eye_status").getDouble("dark_glasses")); 56 | face.setrNormalGlassEyeOpen(eyestatusObject.getJSONObject("right_eye_status").getDouble("normal_glass_eye_open")); 57 | face.setrNoGlassEyeClose(eyestatusObject.getJSONObject("right_eye_status").getDouble("no_glass_eye_close")); 58 | face.setRight_occlusion(eyestatusObject.getJSONObject("right_eye_status").getDouble("occlusion")); 59 | face.setrNoGlassEyeOpen(eyestatusObject.getJSONObject("right_eye_status").getDouble("no_glass_eye_open")); 60 | face.setrNormalGlassEyeClose(eyestatusObject.getJSONObject("right_eye_status").getDouble("normal_glass_eye_close")); 61 | face.setRight_dark_glasses(eyestatusObject.getJSONObject("right_eye_status").getDouble("dark_glasses")); 62 | face.setBlurness(attributesObject.getJSONObject("blur").getJSONObject("blurness").getDouble("value")); 63 | } catch (JSONException e) { 64 | e.printStackTrace(); 65 | return null; 66 | } 67 | return face; 68 | } 69 | 70 | public FaceSignIn parseSearchFaceJSON(String JSON){ 71 | Log.i(TAG, "parseSearchFaceJSON: " + JSON); 72 | FaceSignIn faceSignIn = new FaceSignIn(); 73 | try { 74 | JSONObject jsonObject = new JSONObject(JSON); 75 | faceSignIn.setTime_used(jsonObject.getInt("time_used")); 76 | JSONObject thresholds = jsonObject.getJSONObject("thresholds"); 77 | faceSignIn.setThresholds3(thresholds.getDouble("1e-3")); 78 | faceSignIn.setThresholds4(thresholds.getDouble("1e-4")); 79 | faceSignIn.setThresholds5(thresholds.getDouble("1e-5")); 80 | JSONArray results = jsonObject.getJSONArray("results"); 81 | faceSignIn.setConfidence(results.getJSONObject(0).getDouble("confidence")); 82 | faceSignIn.setFace_token(results.getJSONObject(0).getString("face_token")); 83 | } catch (JSONException e) { 84 | e.printStackTrace(); 85 | } 86 | Log.i(TAG, "parseSearchFaceJSON_faceSignIn: "+faceSignIn.toString()); 87 | return faceSignIn; 88 | 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /app/src/main/java/com/henu/swface/Utils/ParseResult.java: -------------------------------------------------------------------------------- 1 | package com.henu.swface.Utils; 2 | 3 | import android.graphics.Point; 4 | import android.text.TextUtils; 5 | 6 | import org.json.JSONArray; 7 | import org.json.JSONException; 8 | import org.json.JSONObject; 9 | import org.json.JSONTokener; 10 | 11 | import java.util.Iterator; 12 | 13 | public class ParseResult { 14 | /** 15 | * 离线人脸框结果解析方法 16 | * @param json 17 | * @return 18 | */ 19 | static public FaceRect[] parseResult(String json){ 20 | FaceRect[] rect = null; 21 | if(TextUtils.isEmpty(json)) { 22 | return null; 23 | } 24 | try { 25 | JSONTokener tokener = new JSONTokener(json); 26 | JSONObject joResult = new JSONObject(tokener); 27 | int ret = joResult.optInt("ret"); 28 | if(ret != 0) { 29 | return null; 30 | } 31 | // 获取每个人脸的结果 32 | JSONArray items = joResult.getJSONArray("face"); 33 | // 获取人脸数目 34 | rect = new FaceRect[items.length()]; 35 | for(int i=0; i 0 13 | && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) 14 | .getTop() < absListView.getPaddingTop()); 15 | } else { 16 | return view.getScrollY() > 0; 17 | } 18 | } else { 19 | return view.canScrollVertically(-1); 20 | } 21 | } 22 | 23 | /** 24 | * Default implement for check can perform pull to refresh 25 | * 26 | * @param frame 27 | * @param content 28 | * @param header 29 | * @return 30 | */ 31 | public static boolean checkContentCanBePulledDown(PtrFrameLayout frame, View content, View header) { 32 | return !canChildScrollUp(content); 33 | } 34 | 35 | @Override 36 | public boolean checkCanDoRefresh(PtrFrameLayout frame, View content, View header) { 37 | return checkContentCanBePulledDown(frame, content, header); 38 | } 39 | } -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/PtrHandler.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr; 2 | 3 | import android.view.View; 4 | 5 | public interface PtrHandler { 6 | 7 | /** 8 | * Check can do refresh or not. For example the content is empty or the first child is in view. 9 | *

10 | * {@link in.srain.cube.views.ptr.PtrDefaultHandler#checkContentCanBePulledDown} 11 | */ 12 | public boolean checkCanDoRefresh(final PtrFrameLayout frame, final View content, final View header); 13 | 14 | /** 15 | * When refresh begin 16 | * 17 | * @param frame 18 | */ 19 | public void onRefreshBegin(final PtrFrameLayout frame); 20 | } -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/PtrUIHandler.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr; 2 | 3 | import in.srain.cube.views.ptr.indicator.PtrIndicator; 4 | 5 | /** 6 | * 7 | */ 8 | public interface PtrUIHandler { 9 | 10 | /** 11 | * When the content view has reached top and refresh has been completed, view will be reset. 12 | * 13 | * @param frame 14 | */ 15 | public void onUIReset(PtrFrameLayout frame); 16 | 17 | /** 18 | * prepare for loading 19 | * 20 | * @param frame 21 | */ 22 | public void onUIRefreshPrepare(PtrFrameLayout frame); 23 | 24 | /** 25 | * perform refreshing UI 26 | */ 27 | public void onUIRefreshBegin(PtrFrameLayout frame); 28 | 29 | /** 30 | * perform UI after refresh 31 | */ 32 | public void onUIRefreshComplete(PtrFrameLayout frame); 33 | 34 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator); 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/PtrUIHandlerHolder.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr; 2 | 3 | import in.srain.cube.views.ptr.indicator.PtrIndicator; 4 | 5 | /** 6 | * A single linked list to wrap PtrUIHandler 7 | */ 8 | class PtrUIHandlerHolder implements PtrUIHandler { 9 | 10 | private PtrUIHandler mHandler; 11 | private PtrUIHandlerHolder mNext; 12 | 13 | private boolean contains(PtrUIHandler handler) { 14 | return mHandler != null && mHandler == handler; 15 | } 16 | 17 | private PtrUIHandlerHolder() { 18 | 19 | } 20 | 21 | public boolean hasHandler() { 22 | return mHandler != null; 23 | } 24 | 25 | private PtrUIHandler getHandler() { 26 | return mHandler; 27 | } 28 | 29 | public static void addHandler(PtrUIHandlerHolder head, PtrUIHandler handler) { 30 | 31 | if (null == handler) { 32 | return; 33 | } 34 | if (head == null) { 35 | return; 36 | } 37 | if (null == head.mHandler) { 38 | head.mHandler = handler; 39 | return; 40 | } 41 | 42 | PtrUIHandlerHolder current = head; 43 | for (; ; current = current.mNext) { 44 | 45 | // duplicated 46 | if (current.contains(handler)) { 47 | return; 48 | } 49 | if (current.mNext == null) { 50 | break; 51 | } 52 | } 53 | 54 | PtrUIHandlerHolder newHolder = new PtrUIHandlerHolder(); 55 | newHolder.mHandler = handler; 56 | current.mNext = newHolder; 57 | } 58 | 59 | public static PtrUIHandlerHolder create() { 60 | return new PtrUIHandlerHolder(); 61 | } 62 | 63 | public static PtrUIHandlerHolder removeHandler(PtrUIHandlerHolder head, PtrUIHandler handler) { 64 | if (head == null || handler == null || null == head.mHandler) { 65 | return head; 66 | } 67 | 68 | PtrUIHandlerHolder current = head; 69 | PtrUIHandlerHolder pre = null; 70 | do { 71 | 72 | // delete current: link pre to next, unlink next from current; 73 | // pre will no change, current move to next element; 74 | if (current.contains(handler)) { 75 | 76 | // current is head 77 | if (pre == null) { 78 | 79 | head = current.mNext; 80 | current.mNext = null; 81 | 82 | current = head; 83 | } else { 84 | 85 | pre.mNext = current.mNext; 86 | current.mNext = null; 87 | current = pre.mNext; 88 | } 89 | } else { 90 | pre = current; 91 | current = current.mNext; 92 | } 93 | 94 | } while (current != null); 95 | 96 | if (head == null) { 97 | head = new PtrUIHandlerHolder(); 98 | } 99 | return head; 100 | } 101 | 102 | @Override 103 | public void onUIReset(PtrFrameLayout frame) { 104 | PtrUIHandlerHolder current = this; 105 | do { 106 | final PtrUIHandler handler = current.getHandler(); 107 | if (null != handler) { 108 | handler.onUIReset(frame); 109 | } 110 | } while ((current = current.mNext) != null); 111 | } 112 | 113 | @Override 114 | public void onUIRefreshPrepare(PtrFrameLayout frame) { 115 | if (!hasHandler()) { 116 | return; 117 | } 118 | PtrUIHandlerHolder current = this; 119 | do { 120 | final PtrUIHandler handler = current.getHandler(); 121 | if (null != handler) { 122 | handler.onUIRefreshPrepare(frame); 123 | } 124 | } while ((current = current.mNext) != null); 125 | } 126 | 127 | @Override 128 | public void onUIRefreshBegin(PtrFrameLayout frame) { 129 | PtrUIHandlerHolder current = this; 130 | do { 131 | final PtrUIHandler handler = current.getHandler(); 132 | if (null != handler) { 133 | handler.onUIRefreshBegin(frame); 134 | } 135 | } while ((current = current.mNext) != null); 136 | } 137 | 138 | @Override 139 | public void onUIRefreshComplete(PtrFrameLayout frame) { 140 | PtrUIHandlerHolder current = this; 141 | do { 142 | final PtrUIHandler handler = current.getHandler(); 143 | if (null != handler) { 144 | handler.onUIRefreshComplete(frame); 145 | } 146 | } while ((current = current.mNext) != null); 147 | } 148 | 149 | @Override 150 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { 151 | PtrUIHandlerHolder current = this; 152 | do { 153 | final PtrUIHandler handler = current.getHandler(); 154 | if (null != handler) { 155 | handler.onUIPositionChange(frame, isUnderTouch, status, ptrIndicator); 156 | } 157 | } while ((current = current.mNext) != null); 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/PtrUIHandlerHook.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr; 2 | 3 | /** 4 | * Run a hook runnable, the runnable will run only once. 5 | * After the runnable is done, call resume to resume. 6 | * Once run, call takeover will directory call the resume action 7 | */ 8 | public abstract class PtrUIHandlerHook implements Runnable { 9 | 10 | private Runnable mResumeAction; 11 | private static final byte STATUS_PREPARE = 0; 12 | private static final byte STATUS_IN_HOOK = 1; 13 | private static final byte STATUS_RESUMED = 2; 14 | private byte mStatus = STATUS_PREPARE; 15 | 16 | public void takeOver() { 17 | takeOver(null); 18 | } 19 | 20 | public void takeOver(Runnable resumeAction) { 21 | if (resumeAction != null) { 22 | mResumeAction = resumeAction; 23 | } 24 | switch (mStatus) { 25 | case STATUS_PREPARE: 26 | mStatus = STATUS_IN_HOOK; 27 | run(); 28 | break; 29 | case STATUS_IN_HOOK: 30 | break; 31 | case STATUS_RESUMED: 32 | resume(); 33 | break; 34 | } 35 | } 36 | 37 | public void reset() { 38 | mStatus = STATUS_PREPARE; 39 | } 40 | 41 | public void resume() { 42 | if (mResumeAction != null) { 43 | mResumeAction.run(); 44 | } 45 | mStatus = STATUS_RESUMED; 46 | } 47 | 48 | /** 49 | * Hook should always have a resume action, which is hooked by this hook. 50 | * 51 | * @param runnable 52 | */ 53 | public void setResumeAction(Runnable runnable) { 54 | mResumeAction = runnable; 55 | } 56 | } -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/header/MaterialHeader.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr.header; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Color; 6 | import android.graphics.Rect; 7 | import android.graphics.drawable.Drawable; 8 | import android.util.AttributeSet; 9 | import android.view.View; 10 | import android.view.animation.Animation; 11 | import android.view.animation.Transformation; 12 | import in.srain.cube.views.ptr.PtrFrameLayout; 13 | import in.srain.cube.views.ptr.PtrUIHandler; 14 | import in.srain.cube.views.ptr.PtrUIHandlerHook; 15 | import in.srain.cube.views.ptr.indicator.PtrIndicator; 16 | 17 | public class MaterialHeader extends View implements PtrUIHandler { 18 | 19 | private MaterialProgressDrawable mDrawable; 20 | private float mScale = 1f; 21 | private PtrFrameLayout mPtrFrameLayout; 22 | 23 | private Animation mScaleAnimation = new Animation() { 24 | @Override 25 | public void applyTransformation(float interpolatedTime, Transformation t) { 26 | mScale = 1f - interpolatedTime; 27 | mDrawable.setAlpha((int) (255 * mScale)); 28 | invalidate(); 29 | } 30 | }; 31 | 32 | public MaterialHeader(Context context) { 33 | super(context); 34 | initView(); 35 | } 36 | 37 | public MaterialHeader(Context context, AttributeSet attrs) { 38 | super(context, attrs); 39 | initView(); 40 | } 41 | 42 | public MaterialHeader(Context context, AttributeSet attrs, int defStyleAttr) { 43 | super(context, attrs, defStyleAttr); 44 | initView(); 45 | } 46 | 47 | public void setPtrFrameLayout(PtrFrameLayout layout) { 48 | 49 | final PtrUIHandlerHook mPtrUIHandlerHook = new PtrUIHandlerHook() { 50 | @Override 51 | public void run() { 52 | startAnimation(mScaleAnimation); 53 | } 54 | }; 55 | 56 | mScaleAnimation.setDuration(200); 57 | mScaleAnimation.setAnimationListener(new Animation.AnimationListener() { 58 | @Override 59 | public void onAnimationStart(Animation animation) { 60 | 61 | } 62 | 63 | @Override 64 | public void onAnimationEnd(Animation animation) { 65 | mPtrUIHandlerHook.resume(); 66 | } 67 | 68 | @Override 69 | public void onAnimationRepeat(Animation animation) { 70 | 71 | } 72 | }); 73 | 74 | mPtrFrameLayout = layout; 75 | mPtrFrameLayout.setRefreshCompleteHook(mPtrUIHandlerHook); 76 | } 77 | 78 | private void initView() { 79 | mDrawable = new MaterialProgressDrawable(getContext(), this); 80 | mDrawable.setBackgroundColor(Color.WHITE); 81 | mDrawable.setCallback(this); 82 | } 83 | 84 | @Override 85 | public void invalidateDrawable(Drawable dr) { 86 | if (dr == mDrawable) { 87 | invalidate(); 88 | } else { 89 | super.invalidateDrawable(dr); 90 | } 91 | } 92 | 93 | public void setColorSchemeColors(int[] colors) { 94 | mDrawable.setColorSchemeColors(colors); 95 | invalidate(); 96 | } 97 | 98 | @Override 99 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 100 | int height = mDrawable.getIntrinsicHeight() + getPaddingTop() + getPaddingBottom(); 101 | heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY); 102 | super.onMeasure(widthMeasureSpec, heightMeasureSpec); 103 | } 104 | 105 | @Override 106 | protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 107 | final int size = mDrawable.getIntrinsicHeight(); 108 | mDrawable.setBounds(0, 0, size, size); 109 | } 110 | 111 | @Override 112 | protected void onDraw(Canvas canvas) { 113 | final int saveCount = canvas.save(); 114 | Rect rect = mDrawable.getBounds(); 115 | int l = getPaddingLeft() + (getMeasuredWidth() - mDrawable.getIntrinsicWidth()) / 2; 116 | canvas.translate(l, getPaddingTop()); 117 | canvas.scale(mScale, mScale, rect.exactCenterX(), rect.exactCenterY()); 118 | mDrawable.draw(canvas); 119 | canvas.restoreToCount(saveCount); 120 | } 121 | 122 | /** 123 | * When the content view has reached top and refresh has been completed, view will be reset. 124 | * 125 | * @param frame 126 | */ 127 | @Override 128 | public void onUIReset(PtrFrameLayout frame) { 129 | mScale = 1f; 130 | mDrawable.stop(); 131 | } 132 | 133 | /** 134 | * prepare for loading 135 | * 136 | * @param frame 137 | */ 138 | @Override 139 | public void onUIRefreshPrepare(PtrFrameLayout frame) { 140 | } 141 | 142 | /** 143 | * perform refreshing UI 144 | * 145 | * @param frame 146 | */ 147 | @Override 148 | public void onUIRefreshBegin(PtrFrameLayout frame) { 149 | mDrawable.setAlpha(255); 150 | mDrawable.start(); 151 | } 152 | 153 | /** 154 | * perform UI after refresh 155 | * 156 | * @param frame 157 | */ 158 | @Override 159 | public void onUIRefreshComplete(PtrFrameLayout frame) { 160 | mDrawable.stop(); 161 | } 162 | 163 | @Override 164 | public void onUIPositionChange(PtrFrameLayout frame, boolean isUnderTouch, byte status, PtrIndicator ptrIndicator) { 165 | 166 | float percent = Math.min(1f, ptrIndicator.getCurrentPercent()); 167 | 168 | if (status == PtrFrameLayout.PTR_STATUS_PREPARE) { 169 | mDrawable.setAlpha((int) (255 * percent)); 170 | mDrawable.showArrow(true); 171 | 172 | float strokeStart = ((percent) * .8f); 173 | mDrawable.setStartEndTrim(0f, Math.min(0.8f, strokeStart)); 174 | mDrawable.setArrowScale(Math.min(1f, percent)); 175 | 176 | // magic 177 | float rotation = (-0.25f + .4f * percent + percent * 2) * .5f; 178 | mDrawable.setProgressRotation(rotation); 179 | invalidate(); 180 | } 181 | } 182 | } 183 | -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/header/StoreHouseBarItem.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr.header; 2 | 3 | import android.graphics.Canvas; 4 | import android.graphics.Paint; 5 | import android.graphics.PointF; 6 | import android.view.animation.Animation; 7 | import android.view.animation.Transformation; 8 | 9 | import java.util.Random; 10 | 11 | /** 12 | * Created by srain on 11/6/14. 13 | */ 14 | public class StoreHouseBarItem extends Animation { 15 | 16 | public PointF midPoint; 17 | public float translationX; 18 | public int index; 19 | 20 | private final Paint mPaint = new Paint(); 21 | private float mFromAlpha = 1.0f; 22 | private float mToAlpha = 0.4f; 23 | private PointF mCStartPoint; 24 | private PointF mCEndPoint; 25 | 26 | public StoreHouseBarItem(int index, PointF start, PointF end, int color, int lineWidth) { 27 | this.index = index; 28 | 29 | midPoint = new PointF((start.x + end.x) / 2, (start.y + end.y) / 2); 30 | 31 | mCStartPoint = new PointF(start.x - midPoint.x, start.y - midPoint.y); 32 | mCEndPoint = new PointF(end.x - midPoint.x, end.y - midPoint.y); 33 | 34 | setColor(color); 35 | setLineWidth(lineWidth); 36 | mPaint.setAntiAlias(true); 37 | mPaint.setStyle(Paint.Style.STROKE); 38 | } 39 | 40 | public void setLineWidth(int width) { 41 | mPaint.setStrokeWidth(width); 42 | } 43 | 44 | public void setColor(int color) { 45 | mPaint.setColor(color); 46 | } 47 | 48 | public void resetPosition(int horizontalRandomness) { 49 | Random random = new Random(); 50 | int randomNumber = -random.nextInt(horizontalRandomness) + horizontalRandomness; 51 | translationX = randomNumber; 52 | } 53 | 54 | @Override 55 | protected void applyTransformation(float interpolatedTime, Transformation t) { 56 | float alpha = mFromAlpha; 57 | alpha = alpha + ((mToAlpha - alpha) * interpolatedTime); 58 | setAlpha(alpha); 59 | } 60 | 61 | public void start(float fromAlpha, float toAlpha) { 62 | mFromAlpha = fromAlpha; 63 | mToAlpha = toAlpha; 64 | super.start(); 65 | } 66 | 67 | public void setAlpha(float alpha) { 68 | mPaint.setAlpha((int) (alpha * 255)); 69 | } 70 | 71 | public void draw(Canvas canvas) { 72 | canvas.drawLine(mCStartPoint.x, mCStartPoint.y, mCEndPoint.x, mCEndPoint.y, mPaint); 73 | } 74 | } -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/indicator/PtrIndicator.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr.indicator; 2 | 3 | import android.graphics.PointF; 4 | 5 | public class PtrIndicator { 6 | 7 | public final static int POS_START = 0; 8 | protected int mOffsetToRefresh = 0; 9 | private PointF mPtLastMove = new PointF(); 10 | private float mOffsetX; 11 | private float mOffsetY; 12 | private int mCurrentPos = 0; 13 | private int mLastPos = 0; 14 | private int mHeaderHeight; 15 | private int mPressedPos = 0; 16 | 17 | private float mRatioOfHeaderHeightToRefresh = 1.2f; 18 | private float mResistance = 1.7f; 19 | private boolean mIsUnderTouch = false; 20 | private int mOffsetToKeepHeaderWhileLoading = -1; 21 | // record the refresh complete position 22 | private int mRefreshCompleteY = 0; 23 | 24 | public boolean isUnderTouch() { 25 | return mIsUnderTouch; 26 | } 27 | 28 | public float getResistance() { 29 | return mResistance; 30 | } 31 | 32 | public void setResistance(float resistance) { 33 | mResistance = resistance; 34 | } 35 | 36 | public void onRelease() { 37 | mIsUnderTouch = false; 38 | } 39 | 40 | public void onUIRefreshComplete() { 41 | mRefreshCompleteY = mCurrentPos; 42 | } 43 | 44 | public boolean goDownCrossFinishPosition() { 45 | return mCurrentPos >= mRefreshCompleteY; 46 | } 47 | 48 | protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) { 49 | setOffset(offsetX, offsetY / mResistance); 50 | } 51 | 52 | public void setRatioOfHeaderHeightToRefresh(float ratio) { 53 | mRatioOfHeaderHeightToRefresh = ratio; 54 | mOffsetToRefresh = (int) (mHeaderHeight * ratio); 55 | } 56 | 57 | public float getRatioOfHeaderToHeightRefresh() { 58 | return mRatioOfHeaderHeightToRefresh; 59 | } 60 | 61 | public int getOffsetToRefresh() { 62 | return mOffsetToRefresh; 63 | } 64 | 65 | public void setOffsetToRefresh(int offset) { 66 | mRatioOfHeaderHeightToRefresh = mHeaderHeight * 1f / offset; 67 | mOffsetToRefresh = offset; 68 | } 69 | 70 | public void onPressDown(float x, float y) { 71 | mIsUnderTouch = true; 72 | mPressedPos = mCurrentPos; 73 | mPtLastMove.set(x, y); 74 | } 75 | 76 | public final void onMove(float x, float y) { 77 | float offsetX = x - mPtLastMove.x; 78 | float offsetY = (y - mPtLastMove.y); 79 | processOnMove(x, y, offsetX, offsetY); 80 | mPtLastMove.set(x, y); 81 | } 82 | 83 | protected void setOffset(float x, float y) { 84 | mOffsetX = x; 85 | mOffsetY = y; 86 | } 87 | 88 | public float getOffsetX() { 89 | return mOffsetX; 90 | } 91 | 92 | public float getOffsetY() { 93 | return mOffsetY; 94 | } 95 | 96 | public int getLastPosY() { 97 | return mLastPos; 98 | } 99 | 100 | public int getCurrentPosY() { 101 | return mCurrentPos; 102 | } 103 | 104 | /** 105 | * Update current position before update the UI 106 | */ 107 | public final void setCurrentPos(int current) { 108 | mLastPos = mCurrentPos; 109 | mCurrentPos = current; 110 | onUpdatePos(current, mLastPos); 111 | } 112 | 113 | protected void onUpdatePos(int current, int last) { 114 | 115 | } 116 | 117 | public int getHeaderHeight() { 118 | return mHeaderHeight; 119 | } 120 | 121 | public void setHeaderHeight(int height) { 122 | mHeaderHeight = height; 123 | updateHeight(); 124 | } 125 | 126 | protected void updateHeight() { 127 | mOffsetToRefresh = (int) (mRatioOfHeaderHeightToRefresh * mHeaderHeight); 128 | } 129 | 130 | public void convertFrom(PtrIndicator ptrSlider) { 131 | mCurrentPos = ptrSlider.mCurrentPos; 132 | mLastPos = ptrSlider.mLastPos; 133 | mHeaderHeight = ptrSlider.mHeaderHeight; 134 | } 135 | 136 | public boolean hasLeftStartPosition() { 137 | return mCurrentPos > POS_START; 138 | } 139 | 140 | public boolean hasJustLeftStartPosition() { 141 | return mLastPos == POS_START && hasLeftStartPosition(); 142 | } 143 | 144 | public boolean hasJustBackToStartPosition() { 145 | return mLastPos != POS_START && isInStartPosition(); 146 | } 147 | 148 | public boolean isOverOffsetToRefresh() { 149 | return mCurrentPos >= getOffsetToRefresh(); 150 | } 151 | 152 | public boolean hasMovedAfterPressedDown() { 153 | return mCurrentPos != mPressedPos; 154 | } 155 | 156 | public boolean isInStartPosition() { 157 | return mCurrentPos == POS_START; 158 | } 159 | 160 | public boolean crossRefreshLineFromTopToBottom() { 161 | return mLastPos < getOffsetToRefresh() && mCurrentPos >= getOffsetToRefresh(); 162 | } 163 | 164 | public boolean hasJustReachedHeaderHeightFromTopToBottom() { 165 | return mLastPos < mHeaderHeight && mCurrentPos >= mHeaderHeight; 166 | } 167 | 168 | public boolean isOverOffsetToKeepHeaderWhileLoading() { 169 | return mCurrentPos > getOffsetToKeepHeaderWhileLoading(); 170 | } 171 | 172 | public void setOffsetToKeepHeaderWhileLoading(int offset) { 173 | mOffsetToKeepHeaderWhileLoading = offset; 174 | } 175 | 176 | public int getOffsetToKeepHeaderWhileLoading() { 177 | return mOffsetToKeepHeaderWhileLoading >= 0 ? mOffsetToKeepHeaderWhileLoading : mHeaderHeight; 178 | } 179 | 180 | public boolean isAlreadyHere(int to) { 181 | return mCurrentPos == to; 182 | } 183 | 184 | public float getLastPercent() { 185 | final float oldPercent = mHeaderHeight == 0 ? 0 : mLastPos * 1f / mHeaderHeight; 186 | return oldPercent; 187 | } 188 | 189 | public float getCurrentPercent() { 190 | final float currentPercent = mHeaderHeight == 0 ? 0 : mCurrentPos * 1f / mHeaderHeight; 191 | return currentPercent; 192 | } 193 | 194 | public boolean willOverTop(int to) { 195 | return to < POS_START; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/indicator/PtrTensionIndicator.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr.indicator; 2 | 3 | public class PtrTensionIndicator extends PtrIndicator { 4 | 5 | private float DRAG_RATE = 0.5f; 6 | private float mDownY; 7 | private float mDownPos; 8 | private float mOneHeight = 0; 9 | 10 | private float mCurrentDragPercent; 11 | 12 | private int mReleasePos; 13 | private float mReleasePercent = -1; 14 | 15 | @Override 16 | public void onPressDown(float x, float y) { 17 | super.onPressDown(x, y); 18 | mDownY = y; 19 | mDownPos = getCurrentPosY(); 20 | } 21 | 22 | @Override 23 | public void onRelease() { 24 | super.onRelease(); 25 | mReleasePos = getCurrentPosY(); 26 | mReleasePercent = mCurrentDragPercent; 27 | } 28 | 29 | @Override 30 | public void onUIRefreshComplete() { 31 | mReleasePos = getCurrentPosY(); 32 | mReleasePercent = getOverDragPercent(); 33 | } 34 | 35 | @Override 36 | public void setHeaderHeight(int height) { 37 | super.setHeaderHeight(height); 38 | mOneHeight = height * 4f / 5; 39 | } 40 | 41 | @Override 42 | protected void processOnMove(float currentX, float currentY, float offsetX, float offsetY) { 43 | 44 | if (currentY < mDownY) { 45 | super.processOnMove(currentX, currentY, offsetX, offsetY); 46 | return; 47 | } 48 | 49 | // distance from top 50 | final float scrollTop = (currentY - mDownY) * DRAG_RATE + mDownPos; 51 | final float currentDragPercent = scrollTop / mOneHeight; 52 | 53 | if (currentDragPercent < 0) { 54 | setOffset(offsetX, 0); 55 | return; 56 | } 57 | 58 | mCurrentDragPercent = currentDragPercent; 59 | 60 | // 0 ~ 1 61 | float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent)); 62 | float extraOS = scrollTop - mOneHeight; 63 | 64 | // 0 ~ 2 65 | // if extraOS lower than 0, which means scrollTop lower than onHeight, tensionSlingshotPercent will be 0. 66 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight); 67 | 68 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f; 69 | float extraMove = (mOneHeight) * tensionPercent / 2; 70 | int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove); 71 | int change = targetY - getCurrentPosY(); 72 | 73 | setOffset(currentX, change); 74 | } 75 | 76 | private float offsetToTarget(float scrollTop) { 77 | 78 | // distance from top 79 | final float currentDragPercent = scrollTop / mOneHeight; 80 | 81 | mCurrentDragPercent = currentDragPercent; 82 | 83 | // 0 ~ 1 84 | float boundedDragPercent = Math.min(1f, Math.abs(currentDragPercent)); 85 | float extraOS = scrollTop - mOneHeight; 86 | 87 | // 0 ~ 2 88 | // if extraOS lower than 0, which means scrollTop lower than mOneHeight, tensionSlingshotPercent will be 0. 89 | float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, mOneHeight * 2) / mOneHeight); 90 | 91 | float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow((tensionSlingshotPercent / 4), 2)) * 2f; 92 | float extraMove = (mOneHeight) * tensionPercent / 2; 93 | int targetY = (int) ((mOneHeight * boundedDragPercent) + extraMove); 94 | 95 | return 0; 96 | } 97 | 98 | @Override 99 | public int getOffsetToKeepHeaderWhileLoading() { 100 | return getOffsetToRefresh(); 101 | } 102 | 103 | @Override 104 | public int getOffsetToRefresh() { 105 | return (int) mOneHeight; 106 | } 107 | 108 | public float getOverDragPercent() { 109 | if (isUnderTouch()) { 110 | return mCurrentDragPercent; 111 | } else { 112 | if (mReleasePercent <= 0) { 113 | return 1.0f * getCurrentPosY() / getOffsetToKeepHeaderWhileLoading(); 114 | } 115 | // after release 116 | return mReleasePercent * getCurrentPosY() / mReleasePos; 117 | } 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /app/src/main/java/in/srain/cube/views/ptr/util/PtrLocalDisplay.java: -------------------------------------------------------------------------------- 1 | package in.srain.cube.views.ptr.util; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | import android.view.View; 6 | import android.view.WindowManager; 7 | 8 | public class PtrLocalDisplay { 9 | 10 | public static int SCREEN_WIDTH_PIXELS; 11 | public static int SCREEN_HEIGHT_PIXELS; 12 | public static float SCREEN_DENSITY; 13 | public static int SCREEN_WIDTH_DP; 14 | public static int SCREEN_HEIGHT_DP; 15 | 16 | public static void init(Context context) { 17 | if (context == null) { 18 | return; 19 | } 20 | DisplayMetrics dm = new DisplayMetrics(); 21 | WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); 22 | wm.getDefaultDisplay().getMetrics(dm); 23 | SCREEN_WIDTH_PIXELS = dm.widthPixels; 24 | SCREEN_HEIGHT_PIXELS = dm.heightPixels; 25 | SCREEN_DENSITY = dm.density; 26 | SCREEN_WIDTH_DP = (int) (SCREEN_WIDTH_PIXELS / dm.density); 27 | SCREEN_HEIGHT_DP = (int) (SCREEN_HEIGHT_PIXELS / dm.density); 28 | } 29 | 30 | public static int dp2px(float dp) { 31 | final float scale = SCREEN_DENSITY; 32 | return (int) (dp * scale + 0.5f); 33 | } 34 | 35 | public static int designedDP2px(float designedDp) { 36 | if (SCREEN_WIDTH_DP != 320) { 37 | designedDp = designedDp * SCREEN_WIDTH_DP / 320f; 38 | } 39 | return dp2px(designedDp); 40 | } 41 | 42 | public static void setPadding(final View view, float left, float top, float right, float bottom) { 43 | view.setPadding(designedDP2px(left), dp2px(top), designedDP2px(right), dp2px(bottom)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/CheckableWrapperView.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.content.Context; 4 | import android.widget.Checkable; 5 | 6 | /** 7 | * A WrapperView that implements the checkable interface 8 | * 9 | * @author Emil Sjölander 10 | */ 11 | class CheckableWrapperView extends WrapperView implements Checkable { 12 | 13 | public CheckableWrapperView(final Context context) { 14 | super(context); 15 | } 16 | 17 | @Override 18 | public boolean isChecked() { 19 | return ((Checkable) mItem).isChecked(); 20 | } 21 | 22 | @Override 23 | public void setChecked(final boolean checked) { 24 | ((Checkable) mItem).setChecked(checked); 25 | } 26 | 27 | @Override 28 | public void toggle() { 29 | setChecked(!isChecked()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/DistinctMultiHashMap.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import java.util.ArrayList; 4 | import java.util.LinkedHashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.Set; 8 | 9 | /** 10 | * a hash map can maintain an one-to-many relationship which the value only belongs to one “one” part 11 | * and the map also support getKey by value quickly 12 | * 13 | * @author lsjwzh 14 | */ 15 | class DistinctMultiHashMap { 16 | private IDMapper mIDMapper; 17 | 18 | interface IDMapper{ 19 | public Object keyToKeyId(TKey key); 20 | public TKey keyIdToKey(Object keyId); 21 | public Object valueToValueId(TItemValue value); 22 | public TItemValue valueIdToValue(Object valueId); 23 | } 24 | 25 | LinkedHashMap> mKeyToValuesMap = new LinkedHashMap>(); 26 | LinkedHashMap mValueToKeyIndexer = new LinkedHashMap(); 27 | 28 | DistinctMultiHashMap(){ 29 | this(new IDMapper() { 30 | @Override 31 | public Object keyToKeyId(TKey key) { 32 | return key; 33 | } 34 | 35 | @Override 36 | public TKey keyIdToKey(Object keyId) { 37 | return (TKey) keyId; 38 | } 39 | 40 | @Override 41 | public Object valueToValueId(TItemValue value) { 42 | return value; 43 | } 44 | 45 | @Override 46 | public TItemValue valueIdToValue(Object valueId) { 47 | return (TItemValue) valueId; 48 | } 49 | }); 50 | } 51 | DistinctMultiHashMap(IDMapper idMapper){ 52 | mIDMapper = idMapper; 53 | } 54 | 55 | public List get(TKey key){ 56 | //todo immutable 57 | return mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)); 58 | } 59 | public TKey getKey(TItemValue value){ 60 | return mValueToKeyIndexer.get(mIDMapper.valueToValueId(value)); 61 | } 62 | 63 | public void add(TKey key,TItemValue value){ 64 | Object keyId = mIDMapper.keyToKeyId(key); 65 | if(mKeyToValuesMap.get(keyId)==null){ 66 | mKeyToValuesMap.put(keyId,new ArrayList()); 67 | } 68 | //remove old relationship 69 | TKey keyForValue = getKey(value); 70 | if(keyForValue !=null){ 71 | mKeyToValuesMap.get(mIDMapper.keyToKeyId(keyForValue)).remove(value); 72 | } 73 | mValueToKeyIndexer.put(mIDMapper.valueToValueId(value), key); 74 | if(!containsValue(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)),value)) { 75 | mKeyToValuesMap.get(mIDMapper.keyToKeyId(key)).add(value); 76 | } 77 | } 78 | 79 | public void removeKey(TKey key){ 80 | if(mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))!=null){ 81 | for (TItemValue value : mKeyToValuesMap.get(mIDMapper.keyToKeyId(key))){ 82 | mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); 83 | } 84 | mKeyToValuesMap.remove(mIDMapper.keyToKeyId(key)); 85 | } 86 | } 87 | public void removeValue(TItemValue value){ 88 | if(getKey(value)!=null){ 89 | List itemValues = mKeyToValuesMap.get(mIDMapper.keyToKeyId(getKey(value))); 90 | if(itemValues!=null){ 91 | itemValues.remove(value); 92 | } 93 | } 94 | mValueToKeyIndexer.remove(mIDMapper.valueToValueId(value)); 95 | } 96 | 97 | public void clear(){ 98 | mValueToKeyIndexer.clear(); 99 | mKeyToValuesMap.clear(); 100 | } 101 | 102 | public void clearValues(){ 103 | for (Map.Entry> entry:entrySet()){ 104 | if(entry.getValue()!=null){ 105 | entry.getValue().clear(); 106 | } 107 | } 108 | mValueToKeyIndexer.clear(); 109 | } 110 | 111 | public Set>> entrySet(){ 112 | return mKeyToValuesMap.entrySet(); 113 | } 114 | 115 | public Set> reverseEntrySet(){ 116 | return mValueToKeyIndexer.entrySet(); 117 | } 118 | 119 | public int size(){ 120 | return mKeyToValuesMap.size(); 121 | } 122 | public int valuesSize(){ 123 | return mValueToKeyIndexer.size(); 124 | } 125 | 126 | protected boolean containsValue(List list,TItemValue value){ 127 | for (TItemValue itemValue :list){ 128 | if(mIDMapper.valueToValueId(itemValue).equals(mIDMapper.valueToValueId(value))){ 129 | return true; 130 | } 131 | } 132 | return false; 133 | } 134 | 135 | /** 136 | * @param position 137 | * @return 138 | */ 139 | public TItemValue getValueByPosition(int position){ 140 | Object[] vauleIdArray = mValueToKeyIndexer.keySet().toArray(); 141 | if(position>vauleIdArray.length){ 142 | throw new IndexOutOfBoundsException(); 143 | } 144 | Object valueId = vauleIdArray[position]; 145 | return mIDMapper.valueIdToValue(valueId); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/DualHashMap.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import java.util.HashMap; 4 | 5 | /** 6 | * simple two way hashmap 7 | * @author lsjwzh 8 | */ 9 | class DualHashMap { 10 | HashMap mKeyToValue = new HashMap(); 11 | HashMap mValueToKey = new HashMap(); 12 | 13 | public void put(TKey t1, TValue t2){ 14 | remove(t1); 15 | removeByValue(t2); 16 | mKeyToValue.put(t1, t2); 17 | mValueToKey.put(t2, t1); 18 | } 19 | 20 | public TKey getKey(TValue value){ 21 | return mValueToKey.get(value); 22 | } 23 | public TValue get(TKey key){ 24 | return mKeyToValue.get(key); 25 | } 26 | 27 | public void remove(TKey key){ 28 | if(get(key)!=null){ 29 | mValueToKey.remove(get(key)); 30 | } 31 | mKeyToValue.remove(key); 32 | } 33 | public void removeByValue(TValue value){ 34 | if(getKey(value)!=null){ 35 | mKeyToValue.remove(getKey(value)); 36 | } 37 | mValueToKey.remove(value); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/ExpandableStickyListHeadersAdapter.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.database.DataSetObserver; 4 | import android.view.View; 5 | import android.view.ViewGroup; 6 | import android.widget.BaseAdapter; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * @author lsjwzh 14 | */ 15 | class ExpandableStickyListHeadersAdapter extends BaseAdapter implements StickyListHeadersAdapter { 16 | 17 | private final StickyListHeadersAdapter mInnerAdapter; 18 | DualHashMap mViewToItemIdMap = new DualHashMap(); 19 | DistinctMultiHashMap mHeaderIdToViewMap = new DistinctMultiHashMap(); 20 | List mCollapseHeaderIds = new ArrayList(); 21 | 22 | ExpandableStickyListHeadersAdapter(StickyListHeadersAdapter innerAdapter){ 23 | this.mInnerAdapter = innerAdapter; 24 | } 25 | 26 | @Override 27 | public View getHeaderView(int position, View convertView, ViewGroup parent) { 28 | return mInnerAdapter.getHeaderView(position,convertView,parent); 29 | } 30 | 31 | @Override 32 | public long getHeaderId(int position) { 33 | return mInnerAdapter.getHeaderId(position); 34 | } 35 | 36 | @Override 37 | public boolean areAllItemsEnabled() { 38 | return mInnerAdapter.areAllItemsEnabled(); 39 | } 40 | 41 | @Override 42 | public boolean isEnabled(int i) { 43 | return mInnerAdapter.isEnabled(i); 44 | } 45 | 46 | @Override 47 | public void registerDataSetObserver(DataSetObserver dataSetObserver) { 48 | mInnerAdapter.registerDataSetObserver(dataSetObserver); 49 | } 50 | 51 | @Override 52 | public void unregisterDataSetObserver(DataSetObserver dataSetObserver) { 53 | mInnerAdapter.unregisterDataSetObserver(dataSetObserver); 54 | } 55 | 56 | @Override 57 | public int getCount() { 58 | return mInnerAdapter.getCount(); 59 | } 60 | 61 | @Override 62 | public Object getItem(int i) { 63 | return mInnerAdapter.getItem(i); 64 | } 65 | 66 | @Override 67 | public long getItemId(int i) { 68 | return mInnerAdapter.getItemId(i); 69 | } 70 | 71 | @Override 72 | public boolean hasStableIds() { 73 | return mInnerAdapter.hasStableIds(); 74 | } 75 | 76 | @Override 77 | public View getView(int i, View view, ViewGroup viewGroup) { 78 | View convertView = mInnerAdapter.getView(i,view,viewGroup); 79 | mViewToItemIdMap.put(convertView, getItemId(i)); 80 | mHeaderIdToViewMap.add((int) getHeaderId(i), convertView); 81 | if(mCollapseHeaderIds.contains(getHeaderId(i))){ 82 | convertView.setVisibility(View.GONE); 83 | }else { 84 | convertView.setVisibility(View.VISIBLE); 85 | } 86 | return convertView; 87 | } 88 | 89 | @Override 90 | public int getItemViewType(int i) { 91 | return mInnerAdapter.getItemViewType(i); 92 | } 93 | 94 | @Override 95 | public int getViewTypeCount() { 96 | return mInnerAdapter.getViewTypeCount(); 97 | } 98 | 99 | @Override 100 | public boolean isEmpty() { 101 | return mInnerAdapter.isEmpty(); 102 | } 103 | 104 | public List getItemViewsByHeaderId(long headerId){ 105 | return mHeaderIdToViewMap.get((int) headerId); 106 | } 107 | 108 | public boolean isHeaderCollapsed(long headerId){ 109 | return mCollapseHeaderIds.contains(headerId); 110 | } 111 | 112 | public void expand(long headerId) { 113 | if(isHeaderCollapsed(headerId)){ 114 | mCollapseHeaderIds.remove((Object) headerId); 115 | } 116 | } 117 | 118 | public void collapse(long headerId) { 119 | if(!isHeaderCollapsed(headerId)){ 120 | mCollapseHeaderIds.add(headerId); 121 | } 122 | } 123 | 124 | public View findViewByItemId(long itemId){ 125 | return mViewToItemIdMap.getKey(itemId); 126 | } 127 | 128 | public long findItemIdByView(View view){ 129 | return mViewToItemIdMap.get(view); 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/ExpandableStickyListHeadersListView.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.content.Context; 4 | import android.util.AttributeSet; 5 | import android.view.View; 6 | 7 | import java.util.List; 8 | 9 | /** 10 | * add expand/collapse functions like ExpandableListView 11 | * @author lsjwzh 12 | */ 13 | public class ExpandableStickyListHeadersListView extends StickyListHeadersListView { 14 | public interface IAnimationExecutor{ 15 | public void executeAnim(View target,int animType); 16 | } 17 | 18 | public final static int ANIMATION_COLLAPSE = 1; 19 | public final static int ANIMATION_EXPAND = 0; 20 | 21 | ExpandableStickyListHeadersAdapter mExpandableStickyListHeadersAdapter; 22 | 23 | 24 | 25 | IAnimationExecutor mDefaultAnimExecutor = new IAnimationExecutor() { 26 | @Override 27 | public void executeAnim(View target, int animType) { 28 | if(animType==ANIMATION_EXPAND){ 29 | target.setVisibility(VISIBLE); 30 | }else if(animType==ANIMATION_COLLAPSE){ 31 | target.setVisibility(GONE); 32 | } 33 | } 34 | }; 35 | 36 | 37 | public ExpandableStickyListHeadersListView(Context context) { 38 | super(context); 39 | } 40 | 41 | public ExpandableStickyListHeadersListView(Context context, AttributeSet attrs) { 42 | super(context, attrs); 43 | } 44 | 45 | public ExpandableStickyListHeadersListView(Context context, AttributeSet attrs, int defStyle) { 46 | super(context, attrs, defStyle); 47 | } 48 | 49 | @Override 50 | public ExpandableStickyListHeadersAdapter getAdapter() { 51 | return mExpandableStickyListHeadersAdapter; 52 | } 53 | 54 | @Override 55 | public void setAdapter(StickyListHeadersAdapter adapter) { 56 | mExpandableStickyListHeadersAdapter = new ExpandableStickyListHeadersAdapter(adapter); 57 | super.setAdapter(mExpandableStickyListHeadersAdapter); 58 | } 59 | 60 | public View findViewByItemId(long itemId){ 61 | return mExpandableStickyListHeadersAdapter.findViewByItemId(itemId); 62 | } 63 | 64 | public long findItemIdByView(View view){ 65 | return mExpandableStickyListHeadersAdapter.findItemIdByView(view); 66 | } 67 | 68 | public void expand(long headerId) { 69 | if(!mExpandableStickyListHeadersAdapter.isHeaderCollapsed(headerId)){ 70 | return; 71 | } 72 | mExpandableStickyListHeadersAdapter.expand(headerId); 73 | //find and expand views in group 74 | List itemViews = mExpandableStickyListHeadersAdapter.getItemViewsByHeaderId(headerId); 75 | if(itemViews==null){ 76 | return; 77 | } 78 | for (View view : itemViews) { 79 | animateView(view, ANIMATION_EXPAND); 80 | } 81 | } 82 | 83 | public void collapse(long headerId) { 84 | if(mExpandableStickyListHeadersAdapter.isHeaderCollapsed(headerId)){ 85 | return; 86 | } 87 | mExpandableStickyListHeadersAdapter.collapse(headerId); 88 | //find and hide views with the same header 89 | List itemViews = mExpandableStickyListHeadersAdapter.getItemViewsByHeaderId(headerId); 90 | if(itemViews==null){ 91 | return; 92 | } 93 | for (View view : itemViews) { 94 | animateView(view, ANIMATION_COLLAPSE); 95 | } 96 | } 97 | 98 | public boolean isHeaderCollapsed(long headerId){ 99 | return mExpandableStickyListHeadersAdapter.isHeaderCollapsed(headerId); 100 | } 101 | 102 | public void setAnimExecutor(IAnimationExecutor animExecutor) { 103 | this.mDefaultAnimExecutor = animExecutor; 104 | } 105 | 106 | /** 107 | * Performs either COLLAPSE or EXPAND animation on the target view 108 | * 109 | * @param target the view to animate 110 | * @param type the animation type, either ExpandCollapseAnimation.COLLAPSE 111 | * or ExpandCollapseAnimation.EXPAND 112 | */ 113 | private void animateView(final View target, final int type) { 114 | if(ANIMATION_EXPAND==type&&target.getVisibility()==VISIBLE){ 115 | return; 116 | } 117 | if(ANIMATION_COLLAPSE==type&&target.getVisibility()!=VISIBLE){ 118 | return; 119 | } 120 | if(mDefaultAnimExecutor !=null){ 121 | mDefaultAnimExecutor.executeAnim(target,type); 122 | } 123 | 124 | } 125 | 126 | } 127 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/SectionIndexerAdapterWrapper.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.content.Context; 4 | import android.widget.SectionIndexer; 5 | 6 | class SectionIndexerAdapterWrapper extends 7 | AdapterWrapper implements SectionIndexer { 8 | 9 | SectionIndexer mSectionIndexerDelegate; 10 | 11 | SectionIndexerAdapterWrapper(Context context, 12 | StickyListHeadersAdapter delegate) { 13 | super(context, delegate); 14 | mSectionIndexerDelegate = (SectionIndexer) delegate; 15 | } 16 | 17 | @Override 18 | public int getPositionForSection(int section) { 19 | return mSectionIndexerDelegate.getPositionForSection(section); 20 | } 21 | 22 | @Override 23 | public int getSectionForPosition(int position) { 24 | return mSectionIndexerDelegate.getSectionForPosition(position); 25 | } 26 | 27 | @Override 28 | public Object[] getSections() { 29 | return mSectionIndexerDelegate.getSections(); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/StickyListHeadersAdapter.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.view.View; 4 | import android.view.ViewGroup; 5 | import android.widget.ListAdapter; 6 | 7 | public interface StickyListHeadersAdapter extends ListAdapter { 8 | /** 9 | * Get a View that displays the header data at the specified position in the 10 | * set. You can either create a View manually or inflate it from an XML layout 11 | * file. 12 | * 13 | * @param position 14 | * The position of the item within the adapter's data set of the item whose 15 | * header view we want. 16 | * @param convertView 17 | * The old view to reuse, if possible. Note: You should check that this view is 18 | * non-null and of an appropriate type before using. If it is not possible to 19 | * convert this view to display the correct data, this method can create a new 20 | * view. 21 | * @param parent 22 | * The parent that this view will eventually be attached to. 23 | * @return 24 | * A View corresponding to the data at the specified position. 25 | */ 26 | View getHeaderView(int position, View convertView, ViewGroup parent); 27 | 28 | /** 29 | * Get the header id associated with the specified position in the list. 30 | * 31 | * @param position 32 | * The position of the item within the adapter's data set whose header id we 33 | * want. 34 | * @return 35 | * The id of the header at the specified position. 36 | */ 37 | long getHeaderId(int position); 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/WrapperView.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Build; 7 | import android.view.View; 8 | import android.view.ViewGroup; 9 | import android.view.ViewParent; 10 | 11 | /** 12 | * 13 | * the view that wrapps a divider header and a normal list item. The listview sees this as 1 item 14 | * 15 | * @author Emil Sjölander 16 | */ 17 | public class WrapperView extends ViewGroup { 18 | 19 | View mItem; 20 | Drawable mDivider; 21 | int mDividerHeight; 22 | View mHeader; 23 | int mItemTop; 24 | 25 | WrapperView(Context c) { 26 | super(c); 27 | } 28 | 29 | public boolean hasHeader() { 30 | return mHeader != null; 31 | } 32 | 33 | public View getItem() { 34 | return mItem; 35 | } 36 | 37 | public View getHeader() { 38 | return mHeader; 39 | } 40 | 41 | void update(View item, View header, Drawable divider, int dividerHeight) { 42 | 43 | //every wrapperview must have a list item 44 | if (item == null) { 45 | throw new NullPointerException("List view item must not be null."); 46 | } 47 | 48 | //only remove the current item if it is not the same as the new item. this can happen if wrapping a recycled view 49 | if (this.mItem != item) { 50 | removeView(this.mItem); 51 | this.mItem = item; 52 | final ViewParent parent = item.getParent(); 53 | if(parent != null && parent != this) { 54 | if(parent instanceof ViewGroup) { 55 | ((ViewGroup) parent).removeView(item); 56 | } 57 | } 58 | addView(item); 59 | } 60 | 61 | //same logik as above but for the header 62 | if (this.mHeader != header) { 63 | if (this.mHeader != null) { 64 | removeView(this.mHeader); 65 | } 66 | this.mHeader = header; 67 | if (header != null) { 68 | addView(header); 69 | } 70 | } 71 | 72 | if (this.mDivider != divider) { 73 | this.mDivider = divider; 74 | this.mDividerHeight = dividerHeight; 75 | invalidate(); 76 | } 77 | } 78 | 79 | @Override 80 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 81 | int measuredWidth = MeasureSpec.getSize(widthMeasureSpec); 82 | int childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(measuredWidth, 83 | MeasureSpec.EXACTLY); 84 | int measuredHeight = 0; 85 | 86 | //measure header or divider. when there is a header visible it acts as the divider 87 | if (mHeader != null) { 88 | ViewGroup.LayoutParams params = mHeader.getLayoutParams(); 89 | if (params != null && params.height > 0) { 90 | mHeader.measure(childWidthMeasureSpec, 91 | MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY)); 92 | } else { 93 | mHeader.measure(childWidthMeasureSpec, 94 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 95 | } 96 | measuredHeight += mHeader.getMeasuredHeight(); 97 | } else if (mDivider != null&&mItem.getVisibility()!=View.GONE) { 98 | measuredHeight += mDividerHeight; 99 | } 100 | 101 | //measure item 102 | ViewGroup.LayoutParams params = mItem.getLayoutParams(); 103 | //enable hiding listview item,ex. toggle off items in group 104 | if(mItem.getVisibility()==View.GONE){ 105 | mItem.measure(childWidthMeasureSpec, 106 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.EXACTLY)); 107 | }else if (params != null && params.height >= 0) { 108 | mItem.measure(childWidthMeasureSpec, 109 | MeasureSpec.makeMeasureSpec(params.height, MeasureSpec.EXACTLY)); 110 | measuredHeight += mItem.getMeasuredHeight(); 111 | } else { 112 | mItem.measure(childWidthMeasureSpec, 113 | MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)); 114 | measuredHeight += mItem.getMeasuredHeight(); 115 | } 116 | 117 | 118 | setMeasuredDimension(measuredWidth, measuredHeight); 119 | } 120 | 121 | @Override 122 | protected void onLayout(boolean changed, int l, int t, int r, int b) { 123 | 124 | l = 0; 125 | t = 0; 126 | r = getWidth(); 127 | b = getHeight(); 128 | 129 | if (mHeader != null) { 130 | int headerHeight = mHeader.getMeasuredHeight(); 131 | mHeader.layout(l, t, r, headerHeight); 132 | mItemTop = headerHeight; 133 | mItem.layout(l, headerHeight, r, b); 134 | } else if (mDivider != null) { 135 | mDivider.setBounds(l, t, r, mDividerHeight); 136 | mItemTop = mDividerHeight; 137 | mItem.layout(l, mDividerHeight, r, b); 138 | } else { 139 | mItemTop = t; 140 | mItem.layout(l, t, r, b); 141 | } 142 | } 143 | 144 | @Override 145 | protected void dispatchDraw(Canvas canvas) { 146 | super.dispatchDraw(canvas); 147 | if (mHeader == null && mDivider != null&&mItem.getVisibility()!=View.GONE) { 148 | // Drawable.setBounds() does not seem to work pre-honeycomb. So have 149 | // to do this instead 150 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) { 151 | canvas.clipRect(0, 0, getWidth(), mDividerHeight); 152 | } 153 | mDivider.draw(canvas); 154 | } 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /app/src/main/java/se/emilsjolander/stickylistheaders/WrapperViewList.java: -------------------------------------------------------------------------------- 1 | package se.emilsjolander.stickylistheaders; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import android.content.Context; 8 | import android.graphics.Canvas; 9 | import android.graphics.Rect; 10 | import android.os.Build; 11 | import android.view.View; 12 | import android.widget.AbsListView; 13 | import android.widget.ListView; 14 | 15 | class WrapperViewList extends ListView { 16 | 17 | interface LifeCycleListener { 18 | void onDispatchDrawOccurred(Canvas canvas); 19 | } 20 | 21 | private LifeCycleListener mLifeCycleListener; 22 | private List mFooterViews; 23 | private int mTopClippingLength; 24 | private Rect mSelectorRect = new Rect();// for if reflection fails 25 | private Field mSelectorPositionField; 26 | private boolean mClippingToPadding = true; 27 | private boolean mBlockLayoutChildren = false; 28 | 29 | public WrapperViewList(Context context) { 30 | super(context); 31 | 32 | // Use reflection to be able to change the size/position of the list 33 | // selector so it does not come under/over the header 34 | try { 35 | Field selectorRectField = AbsListView.class.getDeclaredField("mSelectorRect"); 36 | selectorRectField.setAccessible(true); 37 | mSelectorRect = (Rect) selectorRectField.get(this); 38 | 39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) { 40 | mSelectorPositionField = AbsListView.class.getDeclaredField("mSelectorPosition"); 41 | mSelectorPositionField.setAccessible(true); 42 | } 43 | } catch (NoSuchFieldException e) { 44 | e.printStackTrace(); 45 | } catch (IllegalArgumentException e) { 46 | e.printStackTrace(); 47 | } catch (IllegalAccessException e) { 48 | e.printStackTrace(); 49 | } 50 | } 51 | 52 | @Override 53 | public boolean performItemClick(View view, int position, long id) { 54 | if (view instanceof WrapperView) { 55 | view = ((WrapperView) view).mItem; 56 | } 57 | return super.performItemClick(view, position, id); 58 | } 59 | 60 | private void positionSelectorRect() { 61 | if (!mSelectorRect.isEmpty()) { 62 | int selectorPosition = getSelectorPosition(); 63 | if (selectorPosition >= 0) { 64 | int firstVisibleItem = getFixedFirstVisibleItem(); 65 | View v = getChildAt(selectorPosition - firstVisibleItem); 66 | if (v instanceof WrapperView) { 67 | WrapperView wrapper = ((WrapperView) v); 68 | mSelectorRect.top = wrapper.getTop() + wrapper.mItemTop; 69 | } 70 | } 71 | } 72 | } 73 | 74 | private int getSelectorPosition() { 75 | if (mSelectorPositionField == null) { // not all supported andorid 76 | // version have this variable 77 | for (int i = 0; i < getChildCount(); i++) { 78 | if (getChildAt(i).getBottom() == mSelectorRect.bottom) { 79 | return i + getFixedFirstVisibleItem(); 80 | } 81 | } 82 | } else { 83 | try { 84 | return mSelectorPositionField.getInt(this); 85 | } catch (IllegalArgumentException e) { 86 | e.printStackTrace(); 87 | } catch (IllegalAccessException e) { 88 | e.printStackTrace(); 89 | } 90 | } 91 | return -1; 92 | } 93 | 94 | @Override 95 | protected void dispatchDraw(Canvas canvas) { 96 | positionSelectorRect(); 97 | if (mTopClippingLength != 0) { 98 | canvas.save(); 99 | Rect clipping = canvas.getClipBounds(); 100 | clipping.top = mTopClippingLength; 101 | canvas.clipRect(clipping); 102 | super.dispatchDraw(canvas); 103 | canvas.restore(); 104 | } else { 105 | super.dispatchDraw(canvas); 106 | } 107 | mLifeCycleListener.onDispatchDrawOccurred(canvas); 108 | } 109 | 110 | void setLifeCycleListener(LifeCycleListener lifeCycleListener) { 111 | mLifeCycleListener = lifeCycleListener; 112 | } 113 | 114 | @Override 115 | public void addFooterView(View v) { 116 | super.addFooterView(v); 117 | addInternalFooterView(v); 118 | } 119 | 120 | @Override 121 | public void addFooterView(View v, Object data, boolean isSelectable) { 122 | super.addFooterView(v, data, isSelectable); 123 | addInternalFooterView(v); 124 | } 125 | 126 | private void addInternalFooterView(View v) { 127 | if (mFooterViews == null) { 128 | mFooterViews = new ArrayList(); 129 | } 130 | mFooterViews.add(v); 131 | } 132 | 133 | @Override 134 | public boolean removeFooterView(View v) { 135 | if (super.removeFooterView(v)) { 136 | mFooterViews.remove(v); 137 | return true; 138 | } 139 | return false; 140 | } 141 | 142 | boolean containsFooterView(View v) { 143 | if (mFooterViews == null) { 144 | return false; 145 | } 146 | return mFooterViews.contains(v); 147 | } 148 | 149 | void setTopClippingLength(int topClipping) { 150 | mTopClippingLength = topClipping; 151 | } 152 | 153 | int getFixedFirstVisibleItem() { 154 | int firstVisibleItem = getFirstVisiblePosition(); 155 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { 156 | return firstVisibleItem; 157 | } 158 | 159 | // first getFirstVisiblePosition() reports items 160 | // outside the view sometimes on old versions of android 161 | for (int i = 0; i < getChildCount(); i++) { 162 | if (getChildAt(i).getBottom() >= 0) { 163 | firstVisibleItem += i; 164 | break; 165 | } 166 | } 167 | 168 | // work around to fix bug with firstVisibleItem being to high 169 | // because list view does not take clipToPadding=false into account 170 | // on old versions of android 171 | if (!mClippingToPadding && getPaddingTop() > 0 && firstVisibleItem > 0) { 172 | if (getChildAt(0).getTop() > 0) { 173 | firstVisibleItem -= 1; 174 | } 175 | } 176 | 177 | return firstVisibleItem; 178 | } 179 | 180 | @Override 181 | public void setClipToPadding(boolean clipToPadding) { 182 | mClippingToPadding = clipToPadding; 183 | super.setClipToPadding(clipToPadding); 184 | } 185 | 186 | public void setBlockLayoutChildren(boolean block) { 187 | mBlockLayoutChildren = block; 188 | } 189 | 190 | @Override 191 | protected void layoutChildren() { 192 | if (!mBlockLayoutChildren) { 193 | super.layoutChildren(); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /app/src/main/jniLibs/armeabi/libmsc.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/src/main/jniLibs/armeabi/libmsc.so -------------------------------------------------------------------------------- /app/src/main/res/drawable/ptr_rotate_arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tony-Shx/Swface/9f1ca75a0fd88ddc1e7a2746adaea3dd7f1a1a28/app/src/main/res/drawable/ptr_rotate_arrow.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_dialog_input_name.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 12 | 16 |