Browse Source

添加项目sybotan-android-devices, sybotan-android-views, sybotan-android-works。

庞利祥 5 năm trước cách đây
mục cha
commit
dc4f37222c
43 tập tin đã thay đổi với 3789 bổ sung3 xóa
  1. BIN
      .gradle/5.2.1/fileChanges/last-build.bin
  2. BIN
      .gradle/5.2.1/fileHashes/fileHashes.lock
  3. 0 0
      .gradle/5.2.1/gc.properties
  4. BIN
      .gradle/buildOutputCleanup/buildOutputCleanup.lock
  5. 2 0
      .gradle/buildOutputCleanup/cache.properties
  6. 0 0
      .gradle/vcs-1/gc.properties
  7. 3 3
      settings.gradle
  8. 162 0
      sybotan-android-devices/build.gradle
  9. BIN
      sybotan-android-devices/libs/LPAPI-2017-11-06-R.jar
  10. 21 0
      sybotan-android-devices/proguard-rules.pro
  11. 24 0
      sybotan-android-devices/src/main/AndroidManifest.xml
  12. 669 0
      sybotan-android-devices/src/main/java/com/sybotan/android/devices/printer/SLPrinter.kt
  13. 25 0
      sybotan-android-devices/src/main/res/values/strings.xml
  14. 176 0
      sybotan-android-views/build.gradle
  15. 21 0
      sybotan-android-views/proguard-rules.pro
  16. 37 0
      sybotan-android-views/src/main/AndroidManifest.xml
  17. BIN
      sybotan-android-views/src/main/assets/test.jpg
  18. 67 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SAutoFitTextureView.kt
  19. 586 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SCameraView.kt
  20. 243 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SClockView.kt
  21. 208 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SQrCodeView.kt
  22. 31 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SRoundTextView.kt
  23. 107 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SVerticalSeekBar.kt
  24. 71 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/SViewPager.kt
  25. 57 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/adapters/SPagerAdapter.kt
  26. 173 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebChromeClient.kt
  27. 102 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebView.kt
  28. 178 0
      sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebViewClient.kt
  29. BIN
      sybotan-android-views/src/main/res/raw/qrcod_beep.ogg
  30. 30 0
      sybotan-android-views/src/main/res/values/attrs.xml
  31. 26 0
      sybotan-android-views/src/main/res/values/strings.xml
  32. 176 0
      sybotan-android-works/build.gradle
  33. 21 0
      sybotan-android-works/proguard-rules.pro
  34. 42 0
      sybotan-android-works/src/main/AndroidManifest.xml
  35. 66 0
      sybotan-android-works/src/main/java/com/sybotan/android/works/activities/SAboutActivity.kt
  36. 54 0
      sybotan-android-works/src/main/java/com/sybotan/android/works/activities/SQrCodeActivity.kt
  37. 154 0
      sybotan-android-works/src/main/java/com/sybotan/android/works/layouts/SBottomNavigationLayout.kt
  38. 83 0
      sybotan-android-works/src/main/res/layout/activity_about.xml
  39. 36 0
      sybotan-android-works/src/main/res/layout/activity_qr_code.xml
  40. 46 0
      sybotan-android-works/src/main/res/layout/layout_bottom_navigation.xml
  41. 29 0
      sybotan-android-works/src/main/res/values/attrs.xml
  42. 29 0
      sybotan-android-works/src/main/res/values/strings.xml
  43. 34 0
      sybotan-android-works/src/main/res/values/styles.xml

BIN
.gradle/5.2.1/fileChanges/last-build.bin


BIN
.gradle/5.2.1/fileHashes/fileHashes.lock


+ 0 - 0
.gradle/5.2.1/gc.properties


BIN
.gradle/buildOutputCleanup/buildOutputCleanup.lock


+ 2 - 0
.gradle/buildOutputCleanup/cache.properties

@@ -0,0 +1,2 @@
+#Tue Sep 17 14:35:54 CST 2019
+gradle.version=5.2.1

+ 0 - 0
.gradle/vcs-1/gc.properties


+ 3 - 3
settings.gradle

@@ -24,8 +24,8 @@ rootProject.name = 'sybotan-android'
 
 include ':sybotan-android-base'
 include ':sybotan-android-database'
-include ':sybotan-android-devices'
+// include ':sybotan-android-devices'
 include ':sybotan-android-graphy'
-include ':sybotan-android-views'
-include ':sybotan-android-works'
+// include ':sybotan-android-views'
+// include ':sybotan-android-works'
 include ':demo'

+ 162 - 0
sybotan-android-devices/build.gradle

@@ -0,0 +1,162 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+group rootProject.group
+version rootProject.version
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+apply plugin: 'org.jetbrains.dokka-android'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion ANDROID_SDK_VERSION.toInteger()
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+
+    defaultConfig {
+        minSdkVersion ANDROID_MIN_SDK_VERSION.toInteger()
+        targetSdkVersion ANDROID_SDK_VERSION.toInteger()
+        versionCode SYBOTAN_CODE.toInteger()
+        versionName SYBOTAN_ANDROID_VERSION
+
+        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
+
+    }
+
+    buildTypes {
+        release {
+            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+        }
+    }
+
+    // 解决lint语法检查错误(kotlin语法无法通过lint)
+    lintOptions {
+        abortOnError false
+    }
+
+//    kotlinOptions {
+//        jvmTarget = JVM_TARGET
+//    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // kotlin 依赖
+    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: KOTLIN_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // android支持
+    implementation group: 'com.android.support', name: 'appcompat-v7', version: ANDROID_SUPPORT_VERSION
+    androidTestImplementation(group: 'com.android.support', name: 'appcompat-v7', version: ANDROID_SUPPORT_VERSION) {
+        force = true
+    }
+    implementation group: 'com.android.support', name: 'design', version: ANDROID_SUPPORT_VERSION
+    // implementation group: 'com.android.support', name: 'support-v4', version: ANDROID_SUPPORT_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // anko依赖
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-common
+    implementation group: 'org.jetbrains.anko', name: 'anko-commons', version: ANKO_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // 测试依赖
+    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
+    testImplementation group: 'org.junit.jupiter', name: 'junit-jupiter-api', version: JUNIT_VERSION
+    androidTestImplementation('com.android.support.test.espresso:espresso-core:3.0.2', {
+        exclude group: 'com.android.support', module: 'support-annotations'
+    })
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 文档生成
+// 源码
+task sourcesJar(type: Jar) {
+    group = 'jar'
+    from "${android.sourceSets.main.java.srcDirs}"
+    classifier = 'sources'
+}
+
+// 文档
+task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
+    jdkVersion = JDK_VERSION.toInteger()
+    noStdlibLink = true
+    outputFormat = 'javadoc'
+    outputDirectory = "$buildDir/javadoc"
+//    sourceDirs = files('src/main/java')
+}
+
+// 上成上传的文档jar包
+task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
+    group = 'jar'
+    classifier = 'javadoc'
+    from "${dokka.outputDirectory}"
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// maven 仓库管理
+
+// 设置上传的包,默认上传aar与pom
+artifacts {
+    // 上传源码包
+    // archives sourcesJar
+    // 上传文档包
+    // archives javadocJar
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.groupId = project.group
+            pom.version = project.version
+            pom.artifactId = project.name
+            pom.project {
+                name project.name
+                packaging 'jar'
+                url SYBOTAN_URL
+                description '斯伯坦机器人世界 android 设备支持库。'
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    }
+                }
+                developers {
+                    developer {
+                        id DEVELOPER_ID
+                        name DEVELOPER_NAME
+                    }
+                }
+            }
+            snapshotRepository(url: MAVEN_REPO_SNAPSHOT_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            repository(url: MAVEN_REPO_RELEASE_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+        }
+    }
+}

BIN
sybotan-android-devices/libs/LPAPI-2017-11-06-R.jar


+ 21 - 0
sybotan-android-devices/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 24 - 0
sybotan-android-devices/src/main/AndroidManifest.xml

@@ -0,0 +1,24 @@
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<manifest package="com.sybotan.android.devices"/>

+ 669 - 0
sybotan-android-devices/src/main/java/com/sybotan/android/devices/printer/SLPrinter.kt

@@ -0,0 +1,669 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.devices.printer
+
+import android.util.Log
+import android.webkit.JavascriptInterface
+import com.dothantech.lpapi.LPAPI
+
+/**
+ * 标签打印机接口
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+class SLPrinter {
+    /**
+     * 类对象
+     */
+    companion object {
+        /** 日志TAG */
+        private val TAG = SLPrinter::class.java.name
+    } // companion object
+
+    /** 打印机实例 */
+    private val printer = LPAPI.Factory.createInstance()
+
+    /**
+     * 以字符串形式返回配对列表中所有指定型号的打印机
+     * 打印机名称列表需要用户自己根据英文逗号(“,”)进行字符串分割操作
+     *
+     * @param   names        打印机型号,如果未指定则返回所有已配对打印机名称
+     * @return  打印机列表字符串,不同打印机名称之间以“,”分割
+     */
+    @JavascriptInterface
+    fun getAllPrinters(names: String): String {
+        return printer.getAllPrinters(names)
+    } // Function getAllPrinters()
+
+    /**
+     * 异步方式打开指定名称或类型的打印机
+     * 连接打印机
+     * 在开始打印任务前,须先调用该方法,或者同系列的方法
+     *
+     * @param   printerName       参数可为下列类型的数据:
+     *                              1、打印机屏幕上显示的名称:用于打开指定名称的打印机;
+     *                              2、打印机型号(名称前缀):打开配对列表中的第一台对应型号的打印机;
+     *                              3、null或者“”,打开配对列表中的第一台打印机;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun openPrinter(printerName: String): Boolean {
+        return printer.openPrinter(printerName)
+    } // Function openPrinter()
+
+    /**
+     * 同步方式打开指定名称或型号的打印机
+     * 确保打印机已经打开,并进行了配对操作
+     *
+     * @param   printerName         参数可为下列类型的数据:
+     *                                1、打印机屏幕上显示的名称:用于打开指定名称的打印机;
+     *                                2、打印机型号(名称前缀):打开配对列表中的第一台对应型号的打印机;
+     *                                3、null或者“”,打开配对列表中的第一台打印机;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun openPrinterSync(printerName: String): Boolean {
+        return printer.openPrinterSync(printerName)
+    } // Function openPrinterSync()
+
+    /**
+     * 获取已连接打印机名称
+     *
+     * @return  如果已连接打印机,则返回对应打印机名称,否则返回null。
+     */
+    @JavascriptInterface
+    fun getPrinterName(): String {
+        return printer.printerName
+    } // Function getPrinterName()
+
+    /**
+     * 判断是否已经连接打印机
+     *
+     * @return  True:表示打印机以链接成功,可以进行打印;false:表示未连接打印机,在打印之前需要链接打印机;
+     */
+    @JavascriptInterface
+    fun isPrinterOpened(): Boolean {
+        return printer.isPrinterOpened
+    } // Function isPrinterOpened()
+
+    /**
+     * 取消打印任务
+     * 取消已经提交(执行过commitJob函数),但尚未开始的打印任务
+     */
+    @JavascriptInterface
+    fun cancel() {
+        printer.cancel()
+        return
+    } // Function cancel()
+
+    /**
+     * 关闭当前已连接打印机
+     * 如果需要多个用户公用同一台打印机,在打印完成之后,可以通过该函数来断开打印机,否则其他用户无法连接到该打印机;
+     * 在打印未完成之前尽量避免调用该函数,否则有可能会导致打印任务终止
+     */
+    @JavascriptInterface
+    fun closePrinter() {
+        printer.closePrinter()
+        return
+    } // Function closePrinter()
+
+    /**
+     * 异步方式打开上次连接过的打印机
+     * 返回成功仅仅表示操作被提交,并不代表着连接成功了,具体的连接结果会通过回调函数给出通知
+     *
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun reopenPrinter(): Boolean {
+        return printer.reopenPrinter()
+    } // Function reopenPrinter()
+
+    /**
+     * 同步方式打开上次连接过的打印机
+     *
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun reopenPrinterSync(): Boolean {
+        return printer.reopenPrinterSync()
+    } // Function reopenPrinterSync()
+
+    /**
+     * 开始绘图任务
+     * 确保打印机已经打开,并进行了配对操作
+     * 开始一个绘图任务时需调用此方法
+     * 开始绘图任务时,之前的任务/数据将会被全部丢弃
+     *
+     * @param   width           标签宽度(基于绘制视图,不考虑标签旋转。单位毫米);
+     * @param   height          标签高度(基于绘制视图,不考虑标签旋转。单位毫米);
+     * @param   orientation     绘图任务的标签页面顺时针旋转角度,0/90/180/270,默认值0;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun startJob(width: Double, height: Double, orientation: Int): Boolean {
+        return printer.startJob(width, height, orientation)
+    } // Function startJob()
+
+    /**
+     * 取消绘图任务
+     * 当前的任务/数据将会被全部丢弃,但是参数设置将会被保留
+     */
+    @JavascriptInterface
+    fun abortJob() {
+        printer.abortJob()
+        return
+    } // Function abortJob()
+
+    /**
+     * 结束绘图任务
+     */
+    @JavascriptInterface
+    fun endJob() {
+        printer.endJob()
+        return
+    } // Function endJob()
+
+    /**
+     * 提交数据打印
+     *
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun commitJob(): Boolean {
+        return printer.commitJob()
+    } // Function commitJob()
+
+    /**
+     * 开始绘制一个页面
+     *
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun startPage(): Boolean {
+        return printer.startPage()
+    } // Function startPage()
+
+    /**
+     * 结束一个页面的绘制
+     */
+    @JavascriptInterface
+    fun endPage() {
+        printer.endPage()
+        return
+    } // Function endPage()
+
+    /**
+     * 设置后续绘制内容的顺时针旋转角度
+     *
+     * @param   orientation      后续绘制内容的顺时针旋转角度(0/90/180/270),默认值为0;
+     */
+    @JavascriptInterface
+    fun setItemOrientation(orientation: Int) {
+        printer.itemOrientation = orientation
+        return
+    } // Function setItemOrientation()
+
+    /**
+     * 得到当前绘制内容的顺时针旋转角度
+     *
+     * @return  当前绘制内容的顺时针旋转角度(0/90/180/270)
+     */
+    @JavascriptInterface
+    fun getItemOrientation(): Int {
+        return printer.itemOrientation
+    } // Function getItemOrientation()
+
+    /**
+     * 设置后续绘制动作的水平对齐方式
+     * 水平方向是基于后续绘制的内容的水平绘制方向,跟随绘制内容的顺时针旋转角度旋转
+     *
+     * @param   alignment      后续绘制动作的水平对齐方式(ItemAlignment),默认值为ItemAlignment.LEFT;
+     */
+    @JavascriptInterface
+    fun setItemHorizontalAlignment(alignment: Int) {
+        printer.itemHorizontalAlignment = alignment
+        return
+    } // Function setItemHorizontalAlignment()
+
+    /**
+     * 得到当前绘制动作的水平对齐方式
+     * 水平方向是基于后续绘制的内容的水平绘制方向,跟随绘制内容的顺时针旋转角度旋转
+     *
+     * @return  当前绘制动作的水平对齐方式(ItemAlignment)
+     */
+    @JavascriptInterface
+    fun getItemHorizontalAlignment(): Int {
+        return printer.itemHorizontalAlignment
+    } // Function getItemHorizontalAlignment()
+
+    /**
+     * 设置后续绘制动作的垂直对齐方式
+     * 垂直方向是基于后续绘制的内容的垂直绘制方向,跟随绘制内容的顺时针旋转角度旋转
+     *
+     * @param   alignment      后续绘制动作的垂直对齐方式(ItemAlignment),默认值为ItemAlignment.TOP;
+     */
+    @JavascriptInterface
+    fun setItemVerticalAlignment(alignment: Int) {
+        printer.itemVerticalAlignment = alignment
+        return
+    } // Function setItemVerticalAlignment()
+
+    /**
+     * 得到当前绘制动作的垂直对齐方式
+     * 垂直方向是基于后续绘制的内容的垂直绘制方向,跟随绘制内容的顺时针旋转角度旋转
+     *
+     * @return  当前绘制动作的垂直对齐方式(ItemAlignment)
+     */
+    @JavascriptInterface
+    fun getItemVerticalAlignment(): Int {
+        return printer.itemVerticalAlignment
+    } // Function getItemVerticalAlignment()
+
+    /**
+     * 设置线条画笔对齐方式
+     *
+     * @param   penAlignmen      线条画笔对齐方式(PenAlignment),数值为以下两者之一;
+     *                              1、PenAlignment.CENTER:绘制的线以指定的位置为中央;
+     *                              2、PenAlignment.INSET:绘制的线在指定的位置内侧;
+     */
+    @JavascriptInterface
+    fun setItemPenAlignment(penAlignmen: Int) {
+        printer.itemPenAlignment = penAlignmen
+        return
+    } // Function setItemPenAlignment()
+
+    /**
+     * 得到线条画笔对齐方式
+     *
+     * @return      线条画笔对齐方式(PenAlignment),数值为以下两者之一;
+     *              1、PenAlignment.CENTER:绘制的线以指定的位置为中央;
+     *              2、PenAlignment.INSET:绘制的线在指定的位置内侧;
+     */
+    @JavascriptInterface
+    fun getItemPenAlignment(): Int {
+        return printer.itemPenAlignment
+    } // Function getItemPenAlignment()
+
+    /**
+     * 绘制文本字符串
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage开始
+     * 绘制一个页面,然后进行文本字符串的绘制。
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     * ontHeight是以毫米为单位
+     * 字号和毫米的转换公式为:
+     * 字号 = 毫米 * 72 / 25.4;
+     * 例如: 9 号字为 3.175 毫米,12 号字为 4.233 毫米
+     *
+     * @param            text            需要绘制的文本字符串;
+     * @param            x               绘制的文本框左上角水平位置(单位毫米);
+     * @param            y               绘制的文本框左上角垂直位置(单位毫米);
+     * @param            width           绘制的文本框水平宽度(单位毫米)。如果 width 为 0,则会根据绘制文本的显示宽度;
+     *                                    根据当前对齐方式进行以 x 为基准点的左中右对齐。默认值为0;
+     * @param            height          绘制的文本框垂直高度(单位毫米)。如果height为 0,则会根据绘制文本的显示高度;
+     *                                    根据当前对齐方式进行以 y 为基准点的上中下对齐。默认值为0;
+     * @param            fontHeight      文字大小(单位毫米);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawText(text: String, x: Double, y: Double, width: Double, height: Double, fontHeight: Double): Boolean {
+        return printer.drawText(text, x, y, width, height, fontHeight)
+    } // Function drawText()
+
+    /**
+     * 绘制一维条码
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage开始
+     * 绘制一个页面,然后进行一维条码的绘制
+     * 绘制的左上角水平垂直位置、宽度、高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param         text          需要绘制的一维条码的内容;
+     * @param         type          一维条码的编码类型(BarcodeType),默认值为60;
+     * @param         x             绘制的一维条码的左上角水平位置(单位毫米);
+     * @param         y             绘制的一维条码的左上角垂直位置(单位毫米);
+     * @param         width         一维条码的整体显示宽度;
+     * @param         height        一维条码的显示高度(包括供人识读文本);
+     * @param         textHeight    供人识读文本的高度(单位毫米),建议为3毫米;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drw1Dbarcode(text: String, type: Int, x: Double, y: Double, width: Double, height: Double, textHeight: Double): Boolean {
+        return printer.draw1DBarcode(text, type, x, y, width, height, textHeight)
+    } // Function drw1Dbarcode()
+
+    /**
+     * 绘制 QrCode 二维码
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行QrCode二维码的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param         text          需要绘制的QrCode二维码的内容;
+     * @param         x             绘制的QrCode二维码的左上角水平位置(单位毫米);
+     * @param         y             绘制的QrCode二维码的左上角垂直位置(单位毫米);
+     * @param         width         绘制的QrCode二维码的水平宽度(单位毫米);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun draw2DQRCode(text: String, x: Double, y: Double, width: Double): Boolean {
+        return printer.draw2DQRCode(text, x, y, width)
+    } // Function draw2DQRCode()
+
+    /**
+     * 绘制 Pdf417 二维码
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行Pdf417二维码的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param          text            需要绘制的Pdf417二维码的内容;
+     * @param          x               绘制的Pdf417二维码的左上角水平位置(单位毫米);
+     * @param          y               绘制的Pdf417二维码的左上角垂直位置(单位毫米);
+     * @param          width           绘制的Pdf417二维码的水平宽度(单位毫米);
+     * @param          height          绘制的Pdf417二维码的垂直高度(单位毫米);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun draw2DPdf417(text: String, x: Double, y: Double, width: Double, height: Double): Boolean {
+        return printer.draw2DPdf417(text, x, y, width, height)
+    } // Function draw2DPdf417()
+
+    /**
+     * 以指定的线宽,绘制矩形框
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行矩形框的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param           x              绘制的矩形框的左上角水平位置(单位mm);
+     * @param           y              绘制的矩形框的左上角垂直位置(单位mm);
+     * @param           width          绘制的矩形框的水平宽度(单位mm);
+     * @param           height         绘制的矩形框的垂直高度(单位mm);
+     * @param           lineWidth      矩形框的线宽(单位mm)。矩形框的线宽是向矩形框内部延伸的;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawRectangle(x: Double, y: Double, width: Double, height: Double, lineWidth: Double): Boolean {
+        return printer.drawRectangle(x, y, width, height, lineWidth)
+    } // Function drawRectangle()
+
+    /**
+     * 绘制填充矩形框
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行矩形框的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param         x               绘制的填充矩形框的左上角水平位置(单位mm);
+     * @param         y               绘制的填充矩形框的左上角垂直位置(单位mm);
+     * @param         width           绘制的填充矩形框的水平宽度(单位mm);
+     * @param         height          绘制的填充矩形框的垂直高度(单位mm);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun fillRectangle(x: Double, y: Double, width: Double, height: Double): Boolean {
+        return printer.fillRectangle(x, y, width, height)
+    } // Function fillRectangle()
+
+    /**
+     * 以指定的线宽,绘制圆角矩形框
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行圆角矩形框的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param           x                  绘制的圆角矩形框的左上角水平位置(单位mm);
+     * @param           y                  绘制的圆角矩形框的左上角垂直位置(单位mm);
+     * @param           width              绘制的圆角矩形框的水平宽度(单位mm);
+     * @param           height             绘制的圆角矩形框的垂直高度(单位mm);
+     * @param           cornerWidth        圆角宽度(单位mm);
+     * @param           cornerHeight       圆角高度(单位mm),默认值为同cornerWidth;
+     * @param           lineWidth          圆角矩形框的线宽(单位mm)。圆角矩形框的线宽是向圆角矩形框内部延伸的;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawRoundRectangle(x: Double, y: Double, width: Double, height: Double, cornerWidth: Double, cornerHeight: Double, lineWidth: Double): Boolean {
+        return printer.drawRoundRectangle(x, y, width, height, cornerWidth, cornerHeight, lineWidth)
+    } // Function drawRoundRectangle()
+
+    /**
+     * 绘制填充圆角矩形框
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行圆角矩形框的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param            x                  绘制的填充圆角矩形框的左上角水平位置(单位mm);
+     * @param            y                  绘制的填充圆角矩形框的左上角垂直位置(单位mm);
+     * @param            width              绘制的填充圆角矩形框的水平宽度(单位mm);
+     * @param            height             绘制的填充圆角矩形框的垂直高度(单位mm);
+     * @param            cornerWidth        圆角宽度(单位mm);
+     * @param            cornerHeight       圆角高度(单位mm),默认值为同cornerWidth;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun fillRoundRectangle(x: Double, y: Double, width: Double, height: Double, cornerWidth: Double, cornerHeight: Double): Boolean {
+        return printer.fillRoundRectangle(x, y, width, height, cornerWidth, cornerHeight)
+    } // Function fillRoundRectangle()
+
+    /**
+     * 以指定的线宽,绘制椭圆
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘绘制一个页面,然后进行椭圆/圆的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     * width 和 height  相等时就是打印圆
+     *
+     * @param            x                  绘制的椭圆的左上角水平位置(单位mm);
+     * @param            y                  绘制的椭圆的左上角垂直位置(单位mm);
+     * @param            width              绘制的椭圆的水平宽度(单位mm);
+     * @param            height             绘制的椭圆的垂直高度(单位mm),默认值为同width,也即绘制圆;
+     * @param            lineWidth          椭圆的线宽(单位mm)。椭圆的线宽是向椭圆内部延伸的;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawEllipse(x: Double, y: Double, width: Double, height: Double, lineWidth: Double): Boolean {
+        return printer.drawEllipse(x, y, width, height, lineWidth)
+    } // Function drawEllipse()
+
+    /**
+     * 绘制填充椭圆
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘绘制一个页面,然后进行椭圆/圆的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     * width 和 height  相等时就是打印圆
+     *
+     * @param            x                  绘制的填充椭圆的左上角水平位置(单位mm);
+     * @param            y                  绘制的填充椭圆的左上角垂直位置(单位mm);
+     * @param            width              绘制的填充椭圆的水平宽度(单位mm);
+     * @param            height             绘制的填充椭圆的垂直高度(单位mm),默认值为同width,也即绘制圆;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun fillEllipse(x: Double, y: Double, width: Double, height: Double): Boolean {
+        return printer.fillEllipse(x, y, width, height)
+    } // Function fillEllipse()
+
+    /**
+     * 以指定的位置为中心,绘制圆
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘绘制一个页面,然后进行圆的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向。
+     *
+     * @param            x                  绘制的圆的x轴中心位置(单位mm);
+     * @param            y                  绘制的圆的y轴中心位置(单位mm);
+     * @param            radius             绘制圆的半径(单位mm);
+     * @param            lineWidth          圆的线宽(单位mm)。圆的线宽是向圆内部延伸;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawCircle(x: Double, y: Double, radius: Double, lineWidth: Double): Boolean {
+        return printer.drawCircle(x, y, radius, lineWidth)
+    } // Function drawCircle()
+
+    /**
+     * 绘制填充圆形
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘绘制一个页面,然后进行圆的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向。
+     *
+     * @param           x                  绘制的填充圆形X轴坐标中心点位置(单位mm);
+     * @param           y                  绘制的填充圆形y轴坐标中心点位置(单位mm);
+     * @param           radius             绘制填充圆形的半径(单位mm);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun fillCircle(x: Double, y: Double, radius: Double): Boolean {
+        return printer.fillCircle(x, y, radius)
+    } // Function fillCircle()
+
+    /**
+     * 绘制线(直线/斜线)
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行直线/斜线的绘制
+     * 绘制的水平垂直位置是基于当前绘制页面的水平垂直方向
+     *
+     * @param           x1                  线的起点的水平位置(单位mm);
+     * @param           y1                  线的起点的垂直位置(单位mm);
+     * @param           x2                  线的终点的水平位置(单位mm);
+     * @param           y2                  线的终点的垂直位置(单位mm);
+     * @param           lineWidth           线宽(单位mm)。线宽是向线的下方延伸的;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawLine(x1: Double, y1: Double, x2: Double, y2: Double, lineWidth: Double): Boolean {
+        return printer.drawLine(x1, y1, x2, y2, lineWidth)
+    } // Function drawLine()
+
+    /**
+     * 绘制点划线
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行点划线的绘制
+     * 绘制的水平垂直位置是基于当前绘制页面的水平垂直方向
+     *
+     * @param             x1                  线的起点的水平位置(单位mm);
+     * @param             y1                  线的起点的垂直位置(单位mm);
+     * @param             x2                  线的终点的水平位置(单位mm);
+     * @param             y2                  线的终点的垂直位置(单位mm);
+     * @param             lineWidth           线宽(单位mm)。线宽是向线的下方延伸的;
+     * @param             dashLen             点划线线段长度的数组;
+     * @param             dashCount           点划线线段长度数组的元素个数;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawDashLine(x1: Double, y1: Double, x2: Double, y2: Double, lineWidth: Double, dashLen: DoubleArray, dashCount: Int): Boolean {
+        return printer.drawDashLine(x1, y1, x2, y2, lineWidth, dashLen, dashCount)
+    } // Function drawDashLine()
+
+    /**
+     * 绘制点划线
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行点划线的绘制
+     * 绘制的水平垂直位置是基于当前绘制页面的水平垂直方向
+     *
+     * @param             x1                  线的起点的水平位置(单位mm);
+     * @param             y1                  线的起点的垂直位置(单位mm);
+     * @param             x2                  线的终点的水平位置(单位mm);
+     * @param             y2                  线的终点的垂直位置(单位mm);
+     * @param             lineWidth           线宽(单位mm)。线宽是向线的下方延伸的;
+     * @param             dashLen1            点划线第一段的长度(单位mm);
+     * @param             dashLen2            点划线第二段的长度(单位mm);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawDashLine2(x1: Double, y1: Double, x2: Double, y2: Double, lineWidth: Double, dashLen1: Double, dashLen2: Double): Boolean {
+        return printer.drawDashLine2(x1, y1, x2, y2, lineWidth, dashLen1, dashLen2)
+    } // Function drawDashLine2()
+
+    /**
+     * 绘制点划线
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行点划线的绘制
+     * 绘制的水平垂直位置是基于当前绘制页面的水平垂直方向
+     *
+     * @param              x1                 线的起点的水平位置(单位mm);
+     * @param              y1                 线的起点的垂直位置(单位mm);
+     * @param              x2                 线的终点的水平位置(单位mm);
+     * @param              y2                 线的终点的垂直位置(单位mm);
+     * @param              lineWidth          线宽(单位mm)。线宽是向线的下方延伸的;
+     * @param              dashLen1           点划线第一段的长度(单位mm);
+     * @param              dashLen2           点划线第二段的长度(单位mm);
+     * @param              dashLen3           点划线第三段的长度(单位mm);
+     * @param              dashLen4           点划线第四段的长度(单位mm);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawDashLine4(x1: Double, y1: Double, x2: Double, y2: Double, lineWidth: Double, dashLen1: Double,
+                     dashLen2: Double, dashLen3: Double, dashLen4: Double): Boolean {
+        return printer.drawDashLine4(x1, y1, x2, y2, lineWidth, dashLen1, dashLen2, dashLen3, dashLen4)
+    } // Function drawDashLine4()
+
+    /**
+     * 绘制指定文件的图片
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行图片的绘制。
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param           imageFile          图片路径;
+     * @param           x                  打印对象在水平方向上的位置(单位mm);
+     * @param           y                  打印对象在垂直方向上的位置(单位mm);
+     * @param           width              打印对象的宽度(单位mm)。如果 width 为 0,则采用加载的位图的宽度。默认值为0;
+     * @param           height             打印对象的高度(单位mm)。如果 height 为0,则采用加载的位图的高度。默认值为0;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawImage(imageFile: String, x: Double, y: Double, width: Double, height: Double): Boolean {
+        Log.d(TAG, "1.9.1 drawImage ($imageFile, $x, $y, $width, $height)")
+        return printer.drawImage(imageFile, x, y, width, height)
+    } // Function drawImage()
+
+    /**
+     * 绘制给定图片实际大小的图片
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行图片的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param             imageFile          图片路径;
+     * @param             x                  打印对象在水平方向上的位置(单位mm);
+     * @param             y                  打印对象在垂直方向上的位置(单位mm);
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawImageWithActualSize(imageFile: String, x: Double, y: Double): Boolean {
+        return printer.drawImageWithActualSize(imageFile, x, y)
+    } // Function drawImageWithActualSize()
+
+    /**
+     * 绘制灰度阀值的图片
+     * 如果之前没有调用 startPage 而直接调用本方法,则本方法会自动调用 startPage 开始
+     * 绘制一个页面,然后进行图片的绘制
+     * 绘制的左上角水平垂直位置、水平宽度、垂直高度是基于当前绘制页面的水平垂直方向
+     *
+     * @param              imageFile             图片路径;
+     * @param              x                     打印对象在水平方向上的位置(单位mm);
+     * @param              y                     打印对象在垂直方向上的位置(单位mm);;
+     * @param              width:                打印对象的宽度(单位mm)。如果 width 为 0,则采用加载的位图的宽度。默认值为0;
+     * @param              height:               打印对象的高度(单位mm)。如果 height 为 0,则采用加载的位图的高度。默认值为0;
+     * @param              threshold:            绘制位图的灰度阀值。256 表示绘制灰度图片,257 表示绘制原色图片,'
+     *                                            0~255表示绘制黑白图片,原图颜色>=灰度阀值的点会被认为是白色,
+     *                                            而原图颜色<灰度阀值的点会被认为是黑色;
+     * @return  成功与否
+     */
+    @JavascriptInterface
+    fun drawImageWithThreshold(imageFile: String, x: Double, y: Double, width: Double, height: Double, threshold: Int): Boolean {
+        return printer.drawImageWithThreshold(imageFile, x, y, width, height, threshold)
+    } // Function drawImageWithThreshold()
+
+} // Class SLPrinter

+ 25 - 0
sybotan-android-devices/src/main/res/values/strings.xml

@@ -0,0 +1,25 @@
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+</resources>

+ 176 - 0
sybotan-android-views/build.gradle

@@ -0,0 +1,176 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+group rootProject.group
+version rootProject.version
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+apply plugin: 'org.jetbrains.dokka-android'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion ANDROID_SDK_VERSION.toInteger()
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+
+
+    defaultConfig {
+        minSdkVersion ANDROID_MIN_SDK_VERSION.toInteger()
+        targetSdkVersion ANDROID_SDK_VERSION.toInteger()
+        versionCode SYBOTAN_CODE.toInteger()
+        versionName SYBOTAN_ANDROID_VERSION
+
+        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
+
+    }
+
+    buildTypes {
+        release {
+            proguardFile 'proguard-rules.pro'
+        }
+    }
+
+    // 解决lint语法检查错误(kotlin语法无法通过lint)
+    lintOptions {
+        abortOnError false
+    }
+
+    kotlinOptions {
+        jvmTarget = JVM_TARGET
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // kotlin 依赖
+    // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
+    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: KOTLIN_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // android支持
+    implementation group: 'com.android.support', name: 'appcompat-v7', version: ANDROID_SUPPORT_VERSION
+    implementation group: 'com.android.support', name: 'design', version: ANDROID_SUPPORT_VERSION
+//    implementation group: 'com.android.support', name: 'support-v4', version: ANDROID_SUPPORT_VERSION
+    implementation group: 'com.android.support.constraint', name: 'constraint-layout', version: CONSTRAINT_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // anko依赖
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-common
+    implementation group: 'org.jetbrains.anko', name: 'anko-commons', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-sdk25
+    implementation group: 'org.jetbrains.anko', name: 'anko-sdk25', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-design
+    implementation group: 'org.jetbrains.anko', name: 'anko-design', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-appcompat-v7
+    implementation group: 'org.jetbrains.anko', name: 'anko-appcompat-v7', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-support-v4
+    implementation group: 'org.jetbrains.anko', name: 'anko-support-v4', version: ANKO_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // 二维码与条形码
+    // https://mvnrepository.com/artifact/com.google.zxing/core
+    implementation group: 'com.google.zxing', name: 'core', version: ZXING_VERSION
+    // https://mvnrepository.com/artifact/com.google.zxing/android-core
+    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+    implementation group: 'com.google.zxing', name: 'android-core', version: ZXING_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Sybotan依赖
+    implementation group: 'com.sybotan', name: 'sybotan-kotlin-base', version: SYBOTAN_KOTLIN_VERSION
+    implementation project(':sybotan-android-base')
+
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 文档生成
+// 源码
+task sourcesJar(type: Jar) {
+    group = 'jar'
+    from "${android.sourceSets.main.java.srcDirs}"
+    classifier = 'sources'
+}
+
+// 文档
+task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
+    jdkVersion = JDK_VERSION.toInteger()
+    noStdlibLink = true
+    outputFormat = 'javadoc'
+    outputDirectory = "$buildDir/javadoc"
+//    sourceDirs = files('src/main/java')
+}
+
+// 上成上传的文档jar包
+task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
+    group = 'jar'
+    classifier = 'javadoc'
+    from "${dokka.outputDirectory}"
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// maven 仓库管理
+
+// 设置上传的包,默认上传aar与pom
+artifacts {
+    // 上传源码包
+    // archives sourcesJar
+    // 上传文档包
+    // archives javadocJar
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.groupId = project.group
+            pom.version = project.version
+            pom.artifactId = project.name
+            pom.project {
+                name project.name
+                packaging 'jar'
+                url SYBOTAN_URL
+                description '斯伯坦机器人世界 android view组件库。'
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    }
+                }
+                developers {
+                    developer {
+                        id DEVELOPER_ID
+                        name DEVELOPER_NAME
+                    }
+                }
+            }
+            snapshotRepository(url: MAVEN_REPO_SNAPSHOT_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            repository(url: MAVEN_REPO_RELEASE_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+        }
+    }
+}

+ 21 - 0
sybotan-android-views/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 37 - 0
sybotan-android-views/src/main/AndroidManifest.xml

@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.sybotan.views">
+
+  <!-- 摄像头 -->
+  <uses-permission android:name="android.permission.CAMERA" />
+  <uses-feature android:name="android.hardware.camera" />
+  <uses-feature android:name="android.hardware.camera.autofocus" />
+
+  <application>
+    <activity android:name="com.sybotan.android.qrcode.CaptureActivity">
+    </activity>
+  </application>
+
+</manifest>

BIN
sybotan-android-views/src/main/assets/test.jpg


+ 67 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SAutoFitTextureView.kt

@@ -0,0 +1,67 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.content.Context
+import android.util.AttributeSet
+import android.util.Size
+import android.view.TextureView
+import android.view.View
+
+/**
+ * 自动填充视图
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SAutoFitTextureView(context: Context, attrs: AttributeSet? = null) : TextureView(context, attrs) {
+    companion object {
+        private val TAG = SAutoFitTextureView::class.java.simpleName
+    } // companion object
+
+    /** 横纵比 */
+    var aspectRatio: Size = Size(1920,1080)
+        set(value) {
+            field = value
+            requestLayout()
+        }
+
+    /**
+     *
+     */
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        val width = View.MeasureSpec.getSize(widthMeasureSpec)
+        val height = View.MeasureSpec.getSize(heightMeasureSpec)
+        if (0 == aspectRatio.width || 0 == aspectRatio.height) {
+            setMeasuredDimension(width, height)
+        } else {
+            if (width < height * aspectRatio.width / aspectRatio.height) {
+                setMeasuredDimension(width, width * aspectRatio.height / aspectRatio.width)
+            } else {
+                setMeasuredDimension(height * aspectRatio.width / aspectRatio.height, height)
+            }
+        }
+        return
+    } // Function onMeasure()
+} // Class SAutoFitTextureView

+ 586 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SCameraView.kt

@@ -0,0 +1,586 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.content.res.Configuration
+import android.graphics.*
+import android.hardware.camera2.*
+import android.media.ImageReader
+import android.os.Handler
+import android.os.HandlerThread
+import android.util.AttributeSet
+import android.util.Log
+import android.util.Size
+import android.util.SparseIntArray
+import android.view.OrientationEventListener
+import android.view.Surface
+import android.view.TextureView
+import com.sybotan.android.base.comparators.SSizesByAreaComparator
+import com.sybotan.views.R
+import org.jetbrains.anko.dip
+import java.util.*
+import java.util.concurrent.Semaphore
+import java.util.concurrent.TimeUnit
+
+/**
+ * 摄像机View
+ *
+ * @author
+ */
+open class SCameraView(context: Context, attrs: AttributeSet? = null) : SAutoFitTextureView(context, attrs) {
+    companion object {
+        // 日志
+        private val TAG = SCameraView::class.java.name
+        private val ORIENTATIONS =  SparseIntArray()
+        /** 最大预览宽度 */
+        private const val MAX_PREVIEW_WIDTH = 1920
+        /** 最大预览高度 */
+        private const val MAX_PREVIEW_HEIGHT = 1080
+    } // companion object
+
+
+
+    /** 摄像头管理器 */
+    val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
+    /** 摄像头ID列表 */
+    val cameraIdList: Array<out String> = manager.cameraIdList
+    var cameraId = "0"
+    /** 当前打开的摄像头 */
+    var cameraDevice: CameraDevice? = null
+    var previewSize = Size(1920, 1080)
+    /** 摄像头传感器方向 */
+    private var cameraSensorOrientation: Int = 0
+    private var previewRequestBuilder: CaptureRequest.Builder? = null
+    private var cameraCaptureSession: CameraCaptureSession? = null
+
+    // 摄像机后台线程
+    private var cameraThread: HandlerThread? = null
+    private var cameraHandler: Handler? = null
+    private val cameraOpenCloseLock = Semaphore(1)
+    private val stateCallback = CameraStateCallback()
+    private val captureCallback = CaptureCallback()
+
+    // 抓图相关
+    private var imageReader: ImageReader? = null
+    private var imageAvailableListener = ImageAvailableListener()
+
+    /** 旋转方向,默认不旋转 */
+    var rotation: Int = Surface.ROTATION_0
+        set(value) {
+            field = value
+            configureTransform(width, height)
+        }
+
+    /** 旋转监听器 */
+    val orientationEventListener = object : OrientationEventListener(context) {
+        /**
+         * 方向改变时调用
+         *
+         * @param   orientation     方向单位度
+         */
+        override fun onOrientationChanged(orientation: Int) {
+            // 防止负值出现(有时会采集到旋转-1,画面会闪动)
+            val rot = (orientation + 360) % 360
+            Log.v(TAG, "orientation = $rot")
+            rotation = when{
+                (rot <= 45 || rot > 315) -> Surface.ROTATION_0
+                (rot in 46..135) -> Surface.ROTATION_270
+                (rot in 136..225) -> Surface.ROTATION_180
+                else -> Surface.ROTATION_90
+            }
+            return
+        } // Function onOrientationChanged()
+    }
+
+    /** 初始化 */
+    init {
+        // 保证照片竖直显示的映射关系
+        ORIENTATIONS.append(Surface.ROTATION_0, 90)
+        ORIENTATIONS.append(Surface.ROTATION_90, 0)
+        ORIENTATIONS.append(Surface.ROTATION_180, 270)
+        ORIENTATIONS.append(Surface.ROTATION_270, 180)
+
+        // 启动摄像头线程
+        startCameraThread()
+    } // init
+
+    @SuppressLint("MissingPermission")
+    fun openCamera(width:Int = 1920, height: Int = 1080, cameraId: String ="0"): Boolean {
+        if (!isAvailable) {
+            surfaceTextureListener = TextureListener()
+            return true
+        }
+
+        setCameraOutputs(cameraId, width, height)
+        configureTransform(width, height)
+
+        try {
+            if (!cameraOpenCloseLock.tryAcquire(2500, TimeUnit.MILLISECONDS)) {
+                throw RuntimeException("Time out waiting to lock cameraDevice opening.")
+            }
+            Log.d(TAG, "openCamera(cameraId=$cameraId)")
+            manager.openCamera(cameraId, stateCallback, cameraHandler)
+        } catch (e: CameraAccessException) {
+            e.printStackTrace()
+            return false
+        } catch (e: InterruptedException) {
+            e.printStackTrace()
+            return false
+        }
+
+        return true
+    } // Function openCamera()
+
+    /**
+     * 关闭摄像头
+     */
+    fun closeCamera() {
+        try {
+            cameraOpenCloseLock.acquire()
+            if (null != cameraCaptureSession) {
+                cameraCaptureSession!!.close()
+                cameraCaptureSession = null
+            }
+            if (null != cameraDevice) {
+                cameraDevice!!.close()
+                cameraDevice = null
+            }
+            if (null != imageReader) {
+                imageReader!!.close()
+                imageReader = null
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        } finally {
+            cameraOpenCloseLock.release()
+        }
+        return
+    } // Function closeCamera()
+
+    /**
+     * 判断摄像头是否打开
+     *
+     * @return  如果打开返回true,否则返回false。
+     */
+    @Synchronized
+    fun isOpen(): Boolean {
+        return cameraDevice != null
+    } // Function isOpen()
+
+    /**
+     * 将焦点锁定为静态图像捕获的第一步。(对焦)
+     */
+    private fun lockFocus() {
+        try {
+            // 摄像头对焦
+            previewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)
+            // 修改状态
+            //mState = STATE_WAITING_LOCK;
+            cameraCaptureSession!!.capture(previewRequestBuilder!!.build(), captureCallback, cameraHandler)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        return
+    } // Function lockFocus()
+
+    /**
+     * 解锁焦点
+     */
+    private fun unlockFocus() {
+        try {
+            // 重置自动对焦
+            previewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_CANCEL)
+            //setAutoFlash(previewRequestBuilder)
+            cameraCaptureSession!!.capture(previewRequestBuilder!!.build(), captureCallback, cameraHandler)
+            // 将相机恢复正常的预览状态。
+            //mState = STATE_PREVIEW;
+            // 打开连续取景模式
+            // cameraCaptureSession!!.setRepeatingRequest(previewRequest, captureCallback, cameraHandler)
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+
+        return
+    } // Function unlockFocus()
+
+//    /**
+//     * 初始化摄像头
+//     */
+//    private fun initCamera(holder: SurfaceHolder) {
+//        try {
+//            Log.e(TAG, "cameraList ===================")
+//            openCamera("0")
+//        } catch (e: Exception) {
+//
+//        }
+//        return
+//    } // Function initCamera()
+
+    /**
+     * 启动摄像头线程
+     */
+    private fun startCameraThread() {
+        if (cameraThread != null) {
+            return
+        }
+
+        cameraThread = HandlerThread("CameraBackground")
+        cameraThread!!.start()
+        cameraHandler = Handler(cameraThread!!.looper)
+        return
+    } // Function startCameraThread()
+
+    /**
+     * 停止摄像机线程
+     */
+    private fun stopCameraThread() {
+        cameraThread!!.quitSafely()
+        try {
+            cameraThread!!.join()
+            cameraThread = null
+            cameraHandler = null
+        } catch (e: InterruptedException) {
+            e.printStackTrace()
+        }
+        return
+    } // Function stopBackgroundThread()
+
+    /**
+     * 设置摄像头输出
+     *
+     * @param   cameraId    摄像头ID
+     * @param   width       宽度
+     * @param   height      高度
+     */
+    private fun setCameraOutputs(cameraId: String, width: Int, height: Int) {
+        // 获得某个摄像头的特征,支持的参数
+        val characteristics = manager.getCameraCharacteristics(cameraId)
+        // 支持的STREAM CONFIGURATION
+        val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
+        val largest = Collections.max(Arrays.asList<Size>(*map.getOutputSizes(ImageFormat.YUV_420_888)), SSizesByAreaComparator())
+
+        imageReader = ImageReader.newInstance(largest.width, largest.height, ImageFormat.YUV_420_888, 2)
+        //设置图片的质量
+        imageReader!!.setOnImageAvailableListener(imageAvailableListener, cameraHandler)
+
+
+        var swappedDimensions = false
+        cameraSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0
+        when (rotation) {
+            Surface.ROTATION_0, Surface.ROTATION_180 -> {
+                if (cameraSensorOrientation == 90 || cameraSensorOrientation == 270) {
+                    swappedDimensions = true
+                }
+            }
+            Surface.ROTATION_90, Surface.ROTATION_270 -> {
+                if (cameraSensorOrientation == 0 || cameraSensorOrientation == 180) {
+                    swappedDimensions = true
+                }
+            }
+            else -> {
+                Log.e(TAG, "Display rotation is invalid: " + rotation)
+            }
+        }
+
+        val displaySize = Point()
+        //activity.getWindowManager().getDefaultDisplay().getSize(displaySize)
+        var rotatedPreviewWidth = width
+        var rotatedPreviewHeight = height
+        var maxPreviewWidth = displaySize.x
+        var maxPreviewHeight = displaySize.y
+
+        if (swappedDimensions) {
+            rotatedPreviewWidth = height
+            rotatedPreviewHeight = width
+            maxPreviewWidth = displaySize.y
+            maxPreviewHeight = displaySize.x
+        }
+
+        if (maxPreviewWidth > MAX_PREVIEW_WIDTH) {
+            maxPreviewWidth = MAX_PREVIEW_WIDTH
+        }
+
+        if (maxPreviewHeight > MAX_PREVIEW_HEIGHT) {
+            maxPreviewHeight = MAX_PREVIEW_HEIGHT
+        }
+
+        previewSize = chooseOptimalSize(map.getOutputSizes(SurfaceTexture::class.java),
+                rotatedPreviewWidth, rotatedPreviewHeight, maxPreviewWidth,
+                maxPreviewHeight, largest)
+
+        // 根据配置是横屏还是竖屏设置比例
+        val orientation = resources.configuration.orientation
+        aspectRatio = if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
+            Size(width, height)
+        } else {
+            Size(height, width)
+        }
+
+        return
+    } // Function setCameraOutputs()
+
+    private fun chooseOptimalSize(choices: Array<Size>, textureViewWidth: Int,
+                                  textureViewHeight: Int, maxWidth: Int, maxHeight: Int, aspectRatio: Size): Size {
+
+        val bigEnough = ArrayList<Size>()
+        val notBigEnough = ArrayList<Size>()
+        val w = aspectRatio.width
+        val h = aspectRatio.height
+        for (option in choices) {
+            if (option.width <= maxWidth && option.height <= maxHeight &&
+                    option.height == option.width * h / w) {
+                if (option.width >= textureViewWidth && option.height >= textureViewHeight) {
+                    bigEnough.add(option)
+                } else {
+                    notBigEnough.add(option)
+                }
+            }
+        }
+
+        // Pick the smallest of those big enough. If there is no one big enough, pick the
+        // largest of those not big enough.
+        return when {
+            bigEnough.size > 0 -> Collections.min(bigEnough, SSizesByAreaComparator())
+            notBigEnough.size > 0 -> Collections.max(notBigEnough, SSizesByAreaComparator())
+            else -> {
+                choices[0]
+            }
+        }
+    } // Function chooseOptimalSize()
+
+    /**
+     * 配置摄像头变换,使画面的显示与摄像头相同
+     *
+     * @param   viewWidth       视图宽度
+     * @param   viewHeight      视图高度
+     */
+    private fun configureTransform(viewWidth: Int, viewHeight: Int) {
+        val matrix = Matrix()
+        val viewRect = RectF(0f, 0f, viewWidth.toFloat(), viewHeight.toFloat())
+        val bufferRect = RectF(0f, 0f, previewSize.height.toFloat(), previewSize.width.toFloat())
+        val centerX = viewRect.centerX()
+        val centerY = viewRect.centerY()
+
+        if (Surface.ROTATION_90 == rotation || Surface.ROTATION_270 == rotation) {
+            bufferRect.offset(centerX - bufferRect.centerX(), centerY - bufferRect.centerY())
+            matrix.setRectToRect(viewRect, bufferRect, Matrix.ScaleToFit.FILL)
+            val scale = Math.max( viewHeight.toFloat() / previewSize.height, viewWidth.toFloat() / previewSize.width)
+            matrix.postScale(scale, scale, centerX, centerY)
+            matrix.postRotate((90 * (rotation - 2)).toFloat(), centerX, centerY)
+        } else if (Surface.ROTATION_180 == rotation) {
+            matrix.postRotate(180f, centerX, centerY)
+        }
+
+        setTransform(matrix)
+        return
+    } // Function configureTransform()
+
+    /**
+     * 创建摄像头预览会话
+     */
+    private fun createCameraPreviewSession(){
+        Log.d(TAG, "createCameraPreviewSession")
+        try {
+            val surface = Surface(surfaceTexture)
+            previewRequestBuilder = cameraDevice!!.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
+            previewRequestBuilder!!.addTarget(surface)
+            cameraDevice!!.createCaptureSession(arrayListOf(surface, imageReader!!.surface), CaptureSessionStateCallback(), null)
+        } catch (e: CameraAccessException) {
+            e.printStackTrace()
+        }
+        return
+    } // Function createCameraPreviewSession()
+
+    /**
+     * 纹理监听器
+     *
+     * @author 庞利祥(sybotan@126.com)
+     */
+    inner class TextureListener : TextureView.SurfaceTextureListener {
+        /**
+         * 纹理表表创建完成
+         *
+         * @param   surface     纹理表面
+         * @param   width       宽度
+         * @param   height      高度
+         */
+        override fun onSurfaceTextureAvailable(surface: SurfaceTexture?, width: Int, height: Int) {
+            Log.d("SCAMERA","size ===== ${width}, ${height}")
+            openCamera(dip(width), dip(height), cameraId)
+            return
+        } // Function onSurfaceTextureAvailable
+
+        /**
+         * 纹理大小变更回调
+         *
+         * @param   surface     纹理表面
+         * @param   width       宽度
+         * @param   height      高度
+         */
+        override fun onSurfaceTextureSizeChanged(surface: SurfaceTexture?, width: Int, height: Int) {
+            configureTransform(width, height)
+            return
+        } // Function onSurfaceTextureSizeChanged()
+
+        /**
+         * 纹理更新完成
+         *
+         * @param   surface     纹理表面
+         */
+        override fun onSurfaceTextureUpdated(surface: SurfaceTexture?) {
+            // DO NOTHING
+            return
+        } // Function onSurfaceTextureUpdated()
+
+        /**
+         * 纹理注销时调用
+         *
+         * @param   surface     纹理表面
+         */
+        override fun onSurfaceTextureDestroyed(surface: SurfaceTexture?): Boolean {
+            return true
+        } // Function onSurfaceTextureDestroyed
+
+    } // Class TextureListener
+
+    /**
+     * 摄像头状态回调
+     *
+     * @author 庞利祥(sybotan@126.com)
+     */
+    inner class CameraStateCallback: CameraDevice.StateCallback() {
+        /**
+         * 打开摄像头
+         */
+        override fun onOpened(camera: CameraDevice) {
+            cameraOpenCloseLock.release()
+            cameraDevice = camera
+            createCameraPreviewSession()
+            return
+        } // Function onOpened()
+
+        /**
+         * 断开连接
+         */
+        override fun onDisconnected(camera: CameraDevice) {
+            cameraOpenCloseLock.release()
+            if (null != cameraDevice) {
+                cameraDevice!!.close()
+                cameraDevice = null
+            }
+            return
+        } // Function onDisconnected()
+
+        /**
+         * 错误
+         */
+        override fun onError(camera: CameraDevice, error: Int) {
+            onDisconnected(camera)
+            return
+        } // Function onError()
+
+    } // Class CameraStateCallback
+
+    /**
+     * 捕获会话状态回调
+     *
+     * @author
+     */
+    inner class CaptureSessionStateCallback : CameraCaptureSession.StateCallback() {
+        /**
+         * 配置完成时回调
+         *
+         * @param   session     捕捉会话
+         */
+        override fun onConfigured(session: CameraCaptureSession?) {
+            if (null == cameraDevice) {     // 摄像头关闭
+                return
+            }
+            cameraCaptureSession = session
+            try {
+                // 启动自动对焦
+                previewRequestBuilder!!.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
+                // Flash is automatically enabled when necessary.
+                // setAutoFlash(previewRequestBuilder)
+
+                // 启动显示
+                val previewRequest = previewRequestBuilder!!.build()
+                cameraCaptureSession!!.setRepeatingRequest(previewRequest, captureCallback, cameraHandler)
+            } catch (e: CameraAccessException) {
+                e.printStackTrace()
+            }
+
+
+            return
+        } // Function onConfigured()
+
+        /**
+         * 配置失败
+         *
+         * @param   session     捕捉会话
+         */
+        override fun onConfigureFailed(session: CameraCaptureSession?) {
+            Log.e(TAG, "onConfigureFailed")
+            return
+        } // Function onConfigureFailed()
+    } // Class CaptureSessionStateCallback
+
+    inner class CaptureCallback : CameraCaptureSession.CaptureCallback() {
+
+        override fun onCaptureProgressed(session: CameraCaptureSession,
+                                         request: CaptureRequest,
+                                         partialResult: CaptureResult) {
+            //process(partialResult)
+            return
+        } // Function onCaptureProgressed()
+
+        override fun onCaptureCompleted(session: CameraCaptureSession,
+                                        request: CaptureRequest,
+                                        result: TotalCaptureResult) {
+            //process(result)
+            return
+        } // Function onCaptureCompleted()
+    } // Class CaptureSessionCaptureCallback
+
+    /**
+     * 捕捉图像装备好事件
+     *
+     * @author  Andy
+     */
+    inner class ImageAvailableListener : ImageReader.OnImageAvailableListener {
+        override fun onImageAvailable(reader: ImageReader) {
+            val image = reader.acquireNextImage()
+            if (image != null) {
+                val buffer = image.planes[0].buffer
+                val bytes = ByteArray(buffer.remaining())
+                buffer.get(bytes)
+                image.close()
+            }
+            return
+        } // Function onImageAvailable()
+
+    } // Function ImageAvailableListener()
+} // Class SCameraView

+ 243 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SClockView.kt

@@ -0,0 +1,243 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.View
+import java.util.*
+import kotlin.math.*
+
+/**
+ * 时钟控件
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SClockView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
+
+    /** 是否显示刻度 */
+    var isShowScale = true
+    /** 刻度颜色 */
+    var scaleColor = Color.BLACK
+    /** 刻度文本颜色 */
+    var textColor = Color.BLACK
+    /** 时针颜色 */
+    var hourColor = Color.BLACK
+    /** 分针颜色 */
+    var minuteColor = Color.BLACK
+    /** 秒针颜色 */
+    var secondColor = Color.RED
+    /** 是否显示秒针 */
+    var isShowSecond = true
+    /** 是否平滑移动秒针 */
+    var isSmooth = true
+    /** 边缘宽度 */
+    var padding = 100f
+
+    private val pen = Paint()
+
+    /** 表半径 */
+    private val radius: Float
+        get() = min(width, height) / 2f - padding
+    /** 绘制间隔时间 */
+    private val interval: Long
+        get() = if (isShowSecond && isSmooth) {     // 如果显示秒针,且要平滑移动秒针,间隔40毫秒
+            40
+        } else {                                    // 否则,间隔1秒
+            1000
+        }
+
+    /** 分解时间的日历对象 */
+    private val calendar = Calendar.getInstance()
+
+    /**
+     * 默认构造函数
+     */
+    init {
+        //TimeZone.setDefault(TimeZone.getTimeZone("GMT+8"))
+    } // init
+
+    /**
+     * 绘制时钟
+     *
+     * @param   canvas      画布
+     */
+    override fun onDraw(canvas: Canvas) {
+        super.onDraw(canvas)
+
+        // 定位坐标原点为控件的中心
+        canvas.translate(width / 2f, height / 2f)
+
+        if (isShowScale) {          // 如果显示刻度
+            drawScale(canvas)
+            drawScaleText(canvas)
+        }
+
+        // 获得时,分,秒
+        calendar.time = Date()
+        val hour = calendar.get(Calendar.HOUR)
+        val minute = calendar.get(Calendar.MINUTE)
+        var second = calendar.get(Calendar.SECOND).toFloat()
+
+        if (isSmooth) {         // 如果是平滑移动秒针
+            second += calendar.get(Calendar.MILLISECOND) / 1000f
+        }
+
+        drawHour(canvas, hour, minute, second)
+        drawMinute(canvas, minute, second)
+
+        if (isShowSecond) {         // 如果显示秒针
+            drawSecond(canvas, second)
+        }
+
+        postInvalidateDelayed(interval)
+
+        return
+    } // Function onDraw()
+
+    /**
+     * 绘制刻度
+     *
+     * @param   canvas      画布
+     */
+    private fun drawScale(canvas: Canvas) {
+        // 刻度长度
+        val scaleLength = max(radius / 10f, 2f)
+        val scaleLength1 = scaleLength * 1.2f
+        val strokeWidth = max(radius / 100f, 2f)
+        val strokeWidth1 = strokeWidth * 2f
+
+        canvas.save()
+        pen.color = scaleColor
+
+        for (i in 1..12) {          // 12小时刻度
+            pen.strokeWidth = strokeWidth1
+            canvas.drawLine(0f, - radius, 0f, - radius + scaleLength1, pen)
+
+            if (radius >= 50) {     // 如果半度大于40显示分钟刻度
+                canvas.rotate(6f)
+                for (j in 1..4) {       // 分钟刻度
+                    pen.strokeWidth = strokeWidth
+                    canvas.drawLine(0f, -radius, 0f, -radius + scaleLength, pen)
+                    canvas.rotate(6.0f)
+                }
+            } else {
+                canvas.rotate(30f)
+            }
+        }
+
+        canvas.restore()
+        return
+    } // Function drawScale()
+
+    /**
+     * 绘制刻度数字
+     *
+     * @param   canvas      画布
+     */
+    private fun drawScaleText(canvas: Canvas) {
+        val fontSize = radius / 7f
+        if (fontSize < 20) {         // 字体小于20,则不绘制刻度数字
+            return
+        }
+
+        pen.color = textColor
+        pen.textAlign = Paint.Align.CENTER
+        pen.textSize = fontSize
+        val offset = (pen.fontMetrics.bottom + pen.fontMetrics.top) / 2
+        val r = radius - radius / 4.5f
+        for (i in 1 .. 12) {    // 依次画12个时钟刻度
+            val x1 = (r * cos(i * PI / 6f - PI / 2f)).toFloat()
+            val y1 = (r * sin(i * PI / 6f - PI / 2f) - offset).toFloat()
+            canvas.drawText("$i", x1, y1, pen)
+        }
+
+        return
+    } // Function drawScaleText()
+
+    /**
+     * 绘制时针
+     *
+     * @param   canvas      画布
+     * @param   hour        时
+     * @param   minute      分
+     * @param   second      秒
+     */
+    private fun drawHour(canvas: Canvas, hour: Int, minute: Int, second: Float) {
+        canvas.save()
+        pen.strokeCap = Paint.Cap.ROUND
+        pen.strokeWidth = max(radius / 30f, 4f)
+        pen.color = hourColor
+        canvas.rotate(hour * 30f + minute * 30f / 60 + second * 30f / 60 / 60)
+        canvas.drawLine(0f, radius / 10f, 0f, - radius / 2f, pen)
+        canvas.drawCircle(0f, 0f, radius / 30f, pen)
+        canvas.restore()
+
+        return
+    } // Function drawHour()
+
+    /**
+     * 绘制分针
+     *
+     * @param   canvas      画布
+     * @param   minute      分
+     * @param   second      秒
+     */
+    private fun drawMinute(canvas: Canvas, minute: Int, second: Float) {
+        canvas.save()
+        pen.strokeCap = Paint.Cap.ROUND
+        pen.strokeWidth = max(radius / 40f, 4f)
+        pen.color = minuteColor
+        canvas.rotate(minute * 6f + second * 6f / 60f)
+        canvas.drawLine(0f, radius / 10f, 0f, - radius * 2f / 3f, pen)
+        pen.color = Color.YELLOW
+        canvas.drawCircle(0f, 0f, radius / 100f, pen)
+        canvas.restore()
+
+        return
+    } // Function drawMinute()
+
+    /**
+     * 绘制秒针
+     *
+     * @param   canvas      画布
+     * @param   second      秒
+     */
+    private fun drawSecond(canvas: Canvas, second: Float) {
+        canvas.save()
+        pen.strokeCap = Paint.Cap.ROUND
+        pen.strokeWidth = max(radius / 100f, 3f)
+        pen.color = secondColor
+        canvas.rotate(second * 6f)
+        canvas.drawLine(0f, radius / 5f, 0f, - radius + radius / 10f, pen)
+        canvas.drawCircle(0f, 0f, radius / 30f, pen)
+        canvas.drawCircle(0f, - radius + radius / 5f, radius / 60f, pen)
+        pen.color = Color.YELLOW
+        canvas.drawCircle(0f, 0f, radius / 100f, pen)
+        canvas.restore()
+
+        return
+    } // Function drawSecond()
+} // Class SClockView

+ 208 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SQrCodeView.kt

@@ -0,0 +1,208 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.View
+import android.widget.RelativeLayout
+import com.google.zxing.*
+import com.google.zxing.common.HybridBinarizer
+import com.sybotan.android.base.utils.SQrCodeUtil
+import com.sybotan.views.R
+import org.jetbrains.anko.dip
+import com.google.zxing.PlanarYUVLuminanceSource
+
+/**
+ * 扫描二维码View
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SQrCodeView(context: Context, attrs: AttributeSet? = null) : RelativeLayout(context, attrs) {
+    /** 摄像视图 */
+    private val cameraView = SCameraView(context)
+    /** 扫描View */
+    private val finderView = FinderView(context)
+
+    /** 蒙板颜色 */
+    var maskColor: Int = 0xA0000000.toInt()
+    /** 扫描框颜色 */
+    var frameColor: Int = 0xFFFFFF
+
+    /** 初始化 */
+    init {
+        // 处理自定义属性
+        parserAttrs(attrs)
+
+        // 设置UI
+        setupUi()
+    } // init
+
+    fun decode(bitmap: Bitmap): String? {
+        val width = bitmap.width
+        val height = bitmap.height
+        val pixels = ByteArray(width * height)
+
+        val viewSize = Math.min(width, height)
+        val frame = Rect(viewSize / 4, viewSize / 4, viewSize * 3 / 4, viewSize * 3 / 4)
+        val luminanceSource = PlanarYUVLuminanceSource(pixels, width, height, frame.left, frame.top, frame.width(), frame.height(), false)
+        val binaryBitmap = BinaryBitmap(HybridBinarizer(luminanceSource))
+        return SQrCodeUtil.decodeQrCode(binaryBitmap)
+    } // Function decode()
+
+    /**
+     * 设置UI
+     */
+    private fun setupUi() {
+        this.addView(cameraView)
+        this.addView(finderView)
+        cameraView.openCamera()
+        return
+    } // Function setupUi()
+
+    /**
+     * 处理自定义属性
+     *
+     * @param   attrs       自定义属性列表
+     */
+    private fun parserAttrs(attrs: AttributeSet?) {
+        try {
+            val t = context.obtainStyledAttributes(attrs, R.styleable.SQrCodeView)
+            maskColor = t.getColor(R.styleable.SQrCodeView_mask_color, 0xA0000000.toInt())
+            frameColor = t.getColor(R.styleable.SQrCodeView_frame_color, 0xFFFFFF)
+            t.recycle()
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        return
+    } // Function parserAttrs()
+
+    /**
+     * 扫描View
+     *
+     * @author  庞利祥(sybotan@126.com)
+     */
+    inner class FinderView(context: Context, attrs: AttributeSet? = null) : View(context, attrs) {
+        var scannerAlpha = 0
+        var offsetY = 0f
+
+        private var possibleResultPoints = ArrayList<ResultPoint>(5)
+        private var lastPossibleResultPoints: List<ResultPoint>? = null
+
+        /** 构造函数 */
+        init {
+
+        } // init
+
+        /**
+         * 重载控件的onDraw函数
+         *
+         * @param   canvas      画布
+         */
+        @SuppressLint("DrawAllocation")
+        override fun onDraw(canvas: Canvas) {
+            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+            val viewSize = Math.min(width, height).toFloat()
+
+            val frame = RectF(viewSize / 4f, viewSize / 4f, viewSize * 0.75f, viewSize * 0.75f)
+
+            // 绘制蒙板
+            drawMask(canvas, frame)
+
+            // 绘制扫描边框
+            drawFrame(canvas, frame)
+
+            // 绘制扫描线
+            paint.color = Color.GRAY
+            paint.strokeWidth = dip(1).toFloat()
+            offsetY = (offsetY + 10) % ((frame.height() - 4) * 2)
+            val middle = frame.bottom - Math.abs(offsetY - frame.height())
+            canvas.drawRect(frame.left + 2, middle+1, frame.right - 1, middle +  3, paint)
+
+            postInvalidateDelayed(30, frame.left.toInt(), frame.top.toInt(), frame.right.toInt(), frame.bottom.toInt())
+            return
+        } // Function onDraw()
+
+        /**
+         * 绘制蒙板
+         *
+         * @param   canvas      画布
+         * @param   frameRect   边框Rect
+         */
+        private fun drawMask(canvas: Canvas, frameRect: RectF) {
+            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+            // 绘制蒙板
+            paint.color = maskColor
+            paint.style = Paint.Style.FILL
+            canvas.drawRect(0f, 0f, width.toFloat(), frameRect.top, paint)
+            canvas.drawRect(0f, frameRect.top, frameRect.left, frameRect.bottom, paint)
+            canvas.drawRect(frameRect.right, frameRect.top, width.toFloat(), frameRect.bottom, paint)
+            canvas.drawRect(0f, frameRect.bottom, width.toFloat(), height.toFloat(), paint)
+
+            return
+        } // Function drawMask()
+        /**
+         * 绘制扫描边框
+         *
+         * @param   canvas      画布
+         * @param   frameRect   边框Rect
+         */
+        private fun drawFrame(canvas: Canvas, frameRect: RectF) {
+            val paint = Paint(Paint.ANTI_ALIAS_FLAG)
+
+            // 绘制边框
+            paint.color = frameColor
+            paint.style = Paint.Style.STROKE
+            paint.strokeWidth = dip(1).toFloat()
+            canvas.drawRect(frameRect, paint)
+
+            paint.strokeWidth = dip(4).toFloat()
+            val cornerOffset = paint.strokeWidth / 2
+            val cornerList = Path()
+            val cornerSize = frameRect.width() / 12
+            // 左上角
+            cornerList.moveTo(frameRect.left - cornerOffset, frameRect.top - cornerOffset + cornerSize)
+            cornerList.rLineTo(0f, - cornerSize)
+            cornerList.rLineTo(cornerSize, 0f)
+            // 右上角
+            cornerList.moveTo(frameRect.right + cornerOffset - cornerSize, frameRect.top - cornerOffset)
+            cornerList.rLineTo(cornerSize, 0f)
+            cornerList.rLineTo(0f, cornerSize)
+            // 右下角
+            cornerList.moveTo(frameRect.right + cornerOffset, frameRect.bottom + cornerOffset - cornerSize)
+            cornerList.rLineTo(0f, cornerSize)
+            cornerList.rLineTo(-cornerSize, 0f)
+            // 左下角
+            cornerList.moveTo(frameRect.left - cornerOffset + cornerSize, frameRect.bottom + cornerOffset)
+            cornerList.rLineTo(-cornerSize, 0f)
+            cornerList.rLineTo(0f, -cornerSize)
+            canvas.drawPath(cornerList, paint)
+
+            return
+        } // Function drawFrame()
+
+    } // Class FinderView()
+} // Class SQrCodeView

+ 31 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SRoundTextView.kt

@@ -0,0 +1,31 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+/**
+ * 走马灯文本控件
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SRoundTextView

+ 107 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SVerticalSeekBar.kt

@@ -0,0 +1,107 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.util.Log
+import android.view.MotionEvent
+import android.widget.SeekBar
+
+/**
+ * 垂直滑块
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SVerticalSeekBar(context: Context, attrs: AttributeSet? = null) : SeekBar(context, attrs) {
+    /**
+     * 定义控件尺寸规则时的回调。因为为竖直,所以宽与高互换
+     *
+     * @param   widthMeasureSpec        宽度尺寸规格
+     * @param   heightMeasureSpec       高度尺寸规格
+     */
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(heightMeasureSpec, widthMeasureSpec)
+        setMeasuredDimension(measuredHeight, measuredWidth)
+        return
+    } // Function onMeasure()
+
+    /**
+     * 大小变更回调
+     *
+     * @param   w           控件宽度
+     * @param   h           控件高度
+     * @param   oldw        控件的原宽度
+     * @param   oldh        控件的原高度
+     */
+    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+        super.onSizeChanged(h, w, oldh, oldw)
+    } // Function onSizeChanged()
+
+    /**
+     * 绘制过程
+     *
+     * @param   canvas   画布
+     */
+    override fun onDraw(canvas: Canvas) {
+        //将SeekBar转转90度
+        canvas.rotate(-90f)
+        //将旋转后的视图移动回来
+        canvas.translate((-height).toFloat(), 0f)
+        Log.i("getHeight()", height.toString() + "")
+        super.onDraw(canvas)
+        return
+    } // Function onDraw()
+
+    /**
+     * 触控操作回调
+     *
+     * @param   event       保存事件参数
+     * @return  处理了该事件返回true,否则返回false。
+     */
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        if (!isEnabled) {
+            return false
+        }
+
+        when (event.action) {
+            MotionEvent.ACTION_DOWN, MotionEvent.ACTION_MOVE, MotionEvent.ACTION_UP -> {
+                //获取滑动的距离
+                var i = max - (max * event.y / height).toInt()
+                //设置进度
+                progress = i
+                Log.i("Progress", progress.toString() + "")
+                //每次拖动SeekBar都会调用
+                onSizeChanged(width, height, 0, 0)
+                Log.i("getWidth()", width.toString() + "")
+                Log.i("getHeight()", height.toString() + "")
+            }
+
+            MotionEvent.ACTION_CANCEL -> {
+            }
+        }
+        return true
+    } // Function onTouchEvent()
+} // Class SVerticalSeekBar

+ 71 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/SViewPager.kt

@@ -0,0 +1,71 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views
+
+import android.content.Context
+import android.support.v4.view.ViewPager
+import android.util.AttributeSet
+import android.view.MotionEvent
+
+/**
+ * 扩展ViwPager是否支持左右滑动功能
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SViewPager(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) {
+    /** 是否支持左右滑动 */
+    var isSlide = false
+    /** 是否平滑滑动 */
+    var smoothScroll = false
+
+    /**
+     * 触摸回调
+     *
+     * @param   event       事件参数
+     * @return  是否处理了事件
+     */
+    override fun onTouchEvent(event: MotionEvent): Boolean {
+        return isSlide && super.onTouchEvent(event)
+    } // Function onTouchEvent()
+
+    /**
+     * 是否拦截触摸事件
+     *
+     * @param   event       事件参数
+     * @return  中断返回true;否则返回false
+     */
+    override fun onInterceptTouchEvent(event: MotionEvent): Boolean {
+        return isSlide && super.onInterceptTouchEvent(event)
+    } // Function onInterceptTouchEvent()
+
+    /**
+     * 设置当前页
+     *
+     * @param   item    页面索引
+     */
+    override fun setCurrentItem(item: Int) {
+        super.setCurrentItem(item, smoothScroll)
+        return
+    } // Function setCurrentItem()
+} // Class SViewPager

+ 57 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/adapters/SPagerAdapter.kt

@@ -0,0 +1,57 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views.adapters
+
+import android.support.v4.view.PagerAdapter
+import android.view.View
+import android.view.ViewGroup
+
+/**
+ * 页面适配器
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+abstract class SPagerAdapter : PagerAdapter() {
+    /**
+     * 判断view与obj是否为同一对象
+     *
+     * @param   view
+     * @param   obj
+     */
+    override fun isViewFromObject(view: View, obj: Any): Boolean {
+        return view === obj
+    } // Function isViewFromObject()
+
+    /**
+     * 释放对象
+     *
+     * @param   container   容器
+     * @param   position    被释放对象在容器中的索引
+     * @param   obj         被释放对象
+     */
+    override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
+        container.removeView(obj as View)
+        return
+    } // Function destroyItem()
+} // Class SPagerAdapter

+ 173 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebChromeClient.kt

@@ -0,0 +1,173 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views.webview
+
+import android.graphics.Bitmap
+import android.webkit.*
+import android.widget.EditText
+import com.sybotan.views.R
+import org.jetbrains.anko.*
+
+/**
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SWebChromeClient : WebChromeClient() {
+    /**
+     * 类对象
+     */
+    companion object {
+        private val TAG = SWebChromeClient::class.java.name
+    } // companion object
+
+    /**
+     * 获取网页的加载进度
+     *
+     * @param   view        WebView视图
+     * @param   newProgress
+     */
+    override fun onProgressChanged(view: WebView, newProgress: Int) {
+        super.onProgressChanged(view, newProgress)
+        return
+    } // Function onProgressChanged()
+
+    /**
+     * 获取网页的title,可以设置在当前webview上
+     *
+     * @param   view        WebView视图
+     * @param   title       网页标题
+     */
+    override fun onReceivedTitle(view: WebView, title: String?) {
+        super.onReceivedTitle(view, title)
+        return
+    } // Function onReceivedTitle()
+
+    /**
+     * 获取网页的图标
+     *
+     * @param   view        WebView视图
+     * @param   icon        网页图标
+     */
+    override fun onReceivedIcon(view: WebView, icon: Bitmap?) {
+        super.onReceivedIcon(view, icon)
+        return
+    } // Function onReceivedIcon()
+
+    /**
+     * 实现js的alert弹窗
+     *
+     * @param   view        WebView视图
+     * @param   url         页面的url地址
+     * @param   message     消息
+     * @param   result      返回结果
+     * @return  返回true表示处理了该弹窗。
+     */
+    override fun onJsAlert(view: WebView, url: String, message: String, result: JsResult): Boolean {
+        view.context.alert(message) {
+            isCancelable = false
+            titleResource = R.string.app_name
+            iconResource = android.R.drawable.ic_dialog_alert
+            positiveButton(R.string.btn_ok){
+                // 注意:因为没有绑定事件,需要强行confirm,否则页面会变黑显示不了内容。
+                result.confirm()
+            }
+            // 屏蔽返回按键
+            onKeyPressed { _, _, _ ->
+                true
+            }
+        }.show()
+
+        return true
+    } // Function onJsAlert()
+
+    /**
+     * 实现js的confirm弹窗
+     *
+     * @param   view        WebView视图
+     * @param   url         页面的url地址
+     * @param   message     消息
+     * @param   result      返回结果
+     * @return  返回true表示处理了该弹窗。
+     */
+    override fun onJsConfirm(view: WebView, url: String, message: String, result: JsResult): Boolean {
+        view.context.alert(message) {
+            isCancelable = false
+            titleResource = R.string.app_name
+            iconResource = android.R.drawable.ic_dialog_alert
+            positiveButton(R.string.btn_ok){
+                // 注意:因为没有绑定事件,需要强行confirm,否则页面会变黑显示不了内容。
+                result.confirm()
+            }
+            negativeButton(R.string.btn_cancel) {
+                result.cancel()
+            }
+            // 屏蔽返回按键
+            onKeyPressed { _, _, _ ->
+                true
+            }
+            onCancelled {
+                result.cancel()
+            }
+        }.show()
+        return true
+    } // Function onJsConfirm()
+
+    /**
+     * 实现js的prompt弹窗
+     *
+     * @param   view        WebView视图
+     * @param   url         页面的url地址
+     * @param   message     消息
+     * @param   result      返回结果
+     * @return  返回true表示处理了该弹窗。
+     */
+    override fun onJsPrompt(view: WebView, url: String, message: String, defaultValue: String, result: JsPromptResult): Boolean {
+        var editPrompt : EditText? = null
+        view.context.alert(message) {
+            isCancelable = false
+            titleResource = R.string.app_name
+            iconResource = android.R.drawable.ic_dialog_alert
+            positiveButton(R.string.btn_ok) {
+                // 注意:因为没有绑定事件,需要强行confirm,否则页面会变黑显示不了内容。
+                result.confirm(editPrompt?.text.toString())
+            }
+            negativeButton(R.string.btn_cancel) {
+                result.cancel()
+            }
+            // 屏蔽返回按键
+            onKeyPressed { _, _, _ ->
+                true
+            }
+            onCancelled {
+                result.cancel()
+            }
+            customView {
+                verticalLayout {
+                    editPrompt = editText(defaultValue) { }
+                }
+            }
+        }.show()
+        return true
+    } // Function onJsPrompt()
+} // Class SWebChromeClient
+

+ 102 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebView.kt

@@ -0,0 +1,102 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views.webview
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.webkit.*
+
+/**
+ * Sybotan扩展WebKit控件
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SWebView(context: Context, attrs: AttributeSet? = null)
+    : WebView(context, attrs) {
+
+    /**
+     * 类对象
+     */
+    companion object {
+        private val TAG = SWebView::class.java.name
+    } // companion object
+
+    /**
+     * 默认构造函数
+     */
+    init {
+        settings()
+        webChromeClient = SWebChromeClient()
+        webViewClient = SWebViewClient()
+
+    } // init
+
+    @SuppressLint("SetJavaScriptEnabled")
+    private fun settings() {
+        /*启用webview中文件访问,这是针对系统文件。android 的资源文件仍可访问*/
+        settings.allowFileAccess = true
+        settings.layoutAlgorithm = WebSettings.LayoutAlgorithm.NARROW_COLUMNS
+
+        /*设置WebView是否应支持使用屏幕缩放进行缩放*/
+        settings.setSupportZoom(true)
+
+        /*设置缩放控件是否显示*/
+        settings.builtInZoomControls = true
+
+        /*设置为false:始终为webview的宽度,true:为视图上最宽的宽度*/
+        settings.useWideViewPort = true
+
+        /*设置WebView是否支持多个窗口*/
+        settings.setSupportMultipleWindows(true)
+
+        /*是否按宽度缩小内容以适合屏幕*/
+        settings.loadWithOverviewMode = true
+
+        // 设置与Js交互的权限
+        settings.javaScriptEnabled = true
+        // 设置允许JS弹窗
+        settings.javaScriptCanOpenWindowsAutomatically = true
+
+        /*设置是否应启用应用程序缓存API*/
+        settings.setAppCacheEnabled(true)
+
+        /*设置是否启用数据库存储API*/
+        settings.databaseEnabled = true
+
+        /*设置是否启用DOM存储API*/
+        settings.domStorageEnabled = true
+
+        /*设置是否开启定位*/
+        settings.setGeolocationEnabled(true)
+
+        /*设置是否应显示屏幕缩放控件*/
+        settings.displayZoomControls = false
+
+        /*设置缓存模式*/
+        settings.cacheMode = WebSettings.LOAD_NO_CACHE
+        return
+    } // Function settings()
+
+} // Class SWebView

+ 178 - 0
sybotan-android-views/src/main/java/com/sybotan/android/views/webview/SWebViewClient.kt

@@ -0,0 +1,178 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.views.webview
+
+import android.util.Log
+import android.webkit.*
+import com.sybotan.android.base.utils.SQrCodeUtil
+import android.graphics.Bitmap
+import java.io.ByteArrayInputStream
+import java.io.ByteArrayOutputStream
+import android.net.Uri
+import com.sybotan.android.base.extensions.param
+
+
+/**
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SWebViewClient : WebViewClient() {
+    /**
+     * 类对象
+     */
+    companion object {
+        private val TAG = SWebViewClient::class.java.name
+        private const val serviceName = "services"
+    } // companion object
+
+    /**
+     * 页面开始加载 可以设置加载进度条
+     *
+     * @param   view        webview控件
+     * @param   url         url地址
+     * @param   favicon     页面图标
+     */
+    override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
+        return
+    } // Function onPageStarted()
+
+    override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
+        Log.d(TAG, "shouldInterceptRequest===${request.url}")
+        val url = request.url.toString()
+        Log.e(TAG, "url1=$url")
+//        if (url.startsWith("http://android/")) {
+//            return assetsRequest(view, request)
+//        } else if (url.startsWith("$serviceName://")) {
+//            return servicesRequest(view, request)
+//        }
+
+//        if (url =="http://172.16.3.12:8080/dist/") {
+//            Log.d(TAG, "res_vue==dist/${url.substring(28)}")
+//            val input = view.context.assets.open("dist/index.html")
+//            return WebResourceResponse("text/html", "UTF-8", input)
+//        }
+//
+//        if (url.startsWith("http://172.16.3.12:8080/dist/")) {
+//            Log.d(TAG, "res==dist/${url.substring(29)}")
+//            val input = view.context.assets.open("dist/${url.substring(29)}")
+//
+//            return if (url.endsWith(".css"))
+//                WebResourceResponse("text/css", "UTF-8", input)
+//            else {
+//                Log.d(TAG, "----------------------------------------------")
+//                WebResourceResponse("application/x-javascript", "UTF-8", input)
+//            }
+//        }
+
+        return super.shouldInterceptRequest(view, request)
+    } // Function shouldInterceptRequest()
+
+    /**
+     * url请求二维码
+     *
+     * @param   request     url请求
+     * @return  应答信息
+     */
+    private fun assetsRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
+        val url = request.url.toString()
+
+        Log.e(TAG, "urlhead=${url.substring(15)}")
+        val input = view.context.assets.open("/vue/${url.substring(14)}")
+
+        return when {
+            url.endsWith(".css") -> WebResourceResponse("text/css", "UTF-8", input)
+            url.endsWith(".html") -> WebResourceResponse("text/html", "UTF-8", input)
+            else -> {
+                Log.d(TAG, "----------------------------------------------")
+                WebResourceResponse("application/x-javascript", "UTF-8", input)
+            }
+        }
+    } // Function urlRequest()
+
+    /**
+     * services请求
+     *
+     * @param   request     url请求
+     * @return  应答信息
+     */
+    private fun servicesRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
+        val url = request.url.toString()
+        if (url.startsWith("$serviceName://qrcode")) {
+            return servicesQrCodeRequest(view, request)
+        }
+        return null
+    } // Function urlRequest()
+
+    /**
+     * services二维码请求
+     *e
+     * @param   request     url请求
+     * @return  应答信息
+     */
+    private fun servicesQrCodeRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
+        val url = request.url.toString()
+
+        val context = Uri.decode(request.param("context"))
+        var size = 200
+        try {           // 防止解析出错
+            size = request.param("size").toInt()
+        } catch (e: Exception) {
+            // DO NOTHING
+        }
+
+
+        val qrcode = SQrCodeUtil.createQrCode(context, size)
+        val bmpOutput = ByteArrayOutputStream()
+        qrcode!!.compress(Bitmap.CompressFormat.PNG, 100, bmpOutput)
+        val qrcodeInput = ByteArrayInputStream(bmpOutput.toByteArray())
+        return WebResourceResponse("image/png", "UTF-8", qrcodeInput)
+    } // Function urlRequest()
+//
+//    /*页面加载结束,可以设置进度条状态*/
+//    override fun onPageFinished(view: WebView?, url: String?) {
+//
+//    } // Function onPageFinished()
+//
+//    /*加载主机指定的url中的资源*/
+//    override fun onLoadResource(view: WebView?, url: String?) {
+//
+//    } // Function onLoadResource()
+//
+//    /*加载页面的服务器出现错误时候调用*/
+//    override fun onReceivedError(view: WebView?, request: WebResourceRequest?, error: WebResourceError?) {
+//
+//    } // Function onReceivedError()
+//
+//    /*webview加载https请求时报错*/
+//    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
+//
+//        /*继续挂载请求,等待响应*/
+//        handler!!.proceed()
+//
+//        /*取消挂载请求*/
+//        //handler.cancel()
+//
+//        /*发送消息,做其他处理*/
+//        //handler.handleMessage(null)
+//    } // Function onReceivedSslError()
+} // Class SWebViewClient

BIN
sybotan-android-views/src/main/res/raw/qrcod_beep.ogg


+ 30 - 0
sybotan-android-views/src/main/res/values/attrs.xml

@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+  <declare-styleable name="SQrCodeView">
+    <attr name="mask_color" format="color"/>
+    <attr name="frame_color" format="color"/>
+  </declare-styleable>
+</resources>

+ 26 - 0
sybotan-android-views/src/main/res/values/strings.xml

@@ -0,0 +1,26 @@
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+  <string name="app_name">sybotan-android-views</string>
+</resources>

+ 176 - 0
sybotan-android-works/build.gradle

@@ -0,0 +1,176 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+group rootProject.group
+version rootProject.version
+
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+apply plugin: 'kotlin-android-extensions'
+
+apply plugin: 'org.jetbrains.dokka-android'
+apply plugin: 'maven'
+
+android {
+    compileSdkVersion ANDROID_SDK_VERSION.toInteger()
+    buildToolsVersion ANDROID_BUILD_TOOLS_VERSION
+
+
+    defaultConfig {
+        minSdkVersion ANDROID_MIN_SDK_VERSION.toInteger()
+        targetSdkVersion ANDROID_SDK_VERSION.toInteger()
+        versionCode SYBOTAN_CODE.toInteger()
+        versionName SYBOTAN_ANDROID_VERSION
+
+        testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
+
+    }
+
+    buildTypes {
+        release {
+            proguardFile 'proguard-rules.pro'
+        }
+    }
+
+    // 解决lint语法检查错误(kotlin语法无法通过lint)
+    lintOptions {
+        abortOnError false
+    }
+
+    kotlinOptions {
+        jvmTarget = JVM_TARGET
+    }
+}
+
+dependencies {
+    implementation fileTree(dir: 'libs', include: ['*.jar'])
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // kotlin 依赖
+    // https://mvnrepository.com/artifact/org.jetbrains.kotlin/kotlin-stdlib-jdk8
+    implementation group: 'org.jetbrains.kotlin', name: 'kotlin-stdlib-jdk8', version: KOTLIN_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // android支持
+    implementation group: 'com.android.support', name: 'appcompat-v7', version: ANDROID_SUPPORT_VERSION
+    implementation group: 'com.android.support', name: 'design', version: ANDROID_SUPPORT_VERSION
+    // implementation group: 'com.android.support', name: 'support-v4', version: ANDROID_SUPPORT_VERSION
+    implementation group: 'com.android.support.constraint', name: 'constraint-layout', version: CONSTRAINT_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // anko依赖
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-common
+    implementation group: 'org.jetbrains.anko', name: 'anko-commons', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-sdk25
+    implementation group: 'org.jetbrains.anko', name: 'anko-sdk25', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-design
+    implementation group: 'org.jetbrains.anko', name: 'anko-design', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-appcompat-v7
+    implementation group: 'org.jetbrains.anko', name: 'anko-appcompat-v7', version: ANKO_VERSION
+    // https://mvnrepository.com/artifact/org.jetbrains.anko/anko-support-v4
+    implementation group: 'org.jetbrains.anko', name: 'anko-support-v4', version: ANKO_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // 二维码与条形码
+    // https://mvnrepository.com/artifact/com.google.zxing/core
+    implementation group: 'com.google.zxing', name: 'core', version: ZXING_VERSION
+    // https://mvnrepository.com/artifact/com.google.zxing/android-core
+    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
+    implementation group: 'com.google.zxing', name: 'android-core', version: ZXING_VERSION
+
+    ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+    // Sybotan依赖
+    implementation group: 'com.sybotan', name: 'sybotan-kotlin-base', version: SYBOTAN_KOTLIN_VERSION
+    implementation project(':sybotan-android-base')
+    implementation project(':sybotan-android-views')
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// 文档生成
+// 源码
+task sourcesJar(type: Jar) {
+    group = 'jar'
+    from "${android.sourceSets.main.java.srcDirs}"
+    classifier = 'sources'
+}
+
+// 文档
+task dokkaJavadoc(type: org.jetbrains.dokka.gradle.DokkaTask) {
+    jdkVersion = JDK_VERSION.toInteger()
+    noStdlibLink = true
+    outputFormat = 'javadoc'
+    outputDirectory = "$buildDir/javadoc"
+//    sourceDirs = files('src/main/java')
+}
+
+// 上成上传的文档jar包
+task javadocJar(type: Jar, dependsOn: dokkaJavadoc) {
+    group = 'jar'
+    classifier = 'javadoc'
+    from "${dokka.outputDirectory}"
+}
+
+////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// maven 仓库管理
+
+// 设置上传的包,默认上传aar与pom
+artifacts {
+    // 上传源码包
+    // archives sourcesJar
+    // 上传文档包
+    // archives javadocJar
+}
+
+uploadArchives {
+    repositories {
+        mavenDeployer {
+            pom.groupId = project.group
+            pom.version = project.version
+            pom.artifactId = project.name
+            pom.project {
+                name project.name
+                packaging 'jar'
+                url SYBOTAN_URL
+                description '斯伯坦机器人世界 android works组件库。'
+                licenses {
+                    license {
+                        name 'The Apache Software License, Version 2.0'
+                        url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
+                    }
+                }
+                developers {
+                    developer {
+                        id DEVELOPER_ID
+                        name DEVELOPER_NAME
+                    }
+                }
+            }
+            snapshotRepository(url: MAVEN_REPO_SNAPSHOT_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+            repository(url: MAVEN_REPO_RELEASE_URL) {
+                authentication(userName: NEXUS_USERNAME, password: NEXUS_PASSWORD)
+            }
+        }
+    }
+}

+ 21 - 0
sybotan-android-works/proguard-rules.pro

@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+#   http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+#   public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile

+ 42 - 0
sybotan-android-works/src/main/AndroidManifest.xml

@@ -0,0 +1,42 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+          package="com.sybotan.android.works">
+
+  <application>
+    <activity
+        android:name=".activities.SAboutActivity"
+        android:configChanges="orientation|screenSize"
+        android:screenOrientation="portrait"
+        android:theme="@style/AppTheme.NoActionBar">
+    </activity>
+    <activity
+        android:name=".activities.SQrCodeActivity"
+        android:configChanges="orientation|screenSize"
+        android:screenOrientation="portrait"
+        android:theme="@style/AppTheme.NoActionBar">
+    </activity>
+  </application>
+
+</manifest>

+ 66 - 0
sybotan-android-works/src/main/java/com/sybotan/android/works/activities/SAboutActivity.kt

@@ -0,0 +1,66 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.works.activities
+
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+import com.sybotan.android.works.R
+import com.sybotan.android.works.R.id.appbar
+import kotlinx.android.synthetic.main.activity_about.*
+
+/**
+ * 关于界面
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+class SAboutActivity : AppCompatActivity() {
+
+    /**
+     * 创建activity时回调
+     *
+     * @param   savedInstanceState  实例状态
+     */
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        setContentView(R.layout.activity_about)
+
+        updateAppBar()
+        return
+    } // Function onCreate()
+
+    /**
+     * 更新AppBara
+     */
+    private fun updateAppBar() {
+        appbar.setTitle(R.string.activity_about_title)
+
+        setSupportActionBar(appbar)
+        // 标题栏显示返回,点击返回上一页
+        supportActionBar!!.setDisplayHomeAsUpEnabled(true)
+        // 点击返回
+        appbar.setNavigationOnClickListener{ finish() }
+
+        return
+    } // Function updateAppBar()
+} // Class SAboutActivity

+ 54 - 0
sybotan-android-works/src/main/java/com/sybotan/android/works/activities/SQrCodeActivity.kt

@@ -0,0 +1,54 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.works.activities
+
+import android.support.v7.app.AppCompatActivity
+import android.os.Bundle
+import android.view.WindowManager
+import com.sybotan.android.base.extensions.immersive
+import com.sybotan.android.works.R
+
+/**
+ * 扫描二维码Activity
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+class SQrCodeActivity : AppCompatActivity() {
+    /**
+     * 创建Activity时回调
+     *
+     * @param   savedInstanceState  实例状态
+     */
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        // 保证屏幕常亮不会黑屏
+        this.window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
+        setContentView(R.layout.activity_qr_code)
+
+        /** 沉浸式APP设置 */
+        immersive()
+
+        return
+    } // Function onCreate()
+} // Class SQrCodeActivity

+ 154 - 0
sybotan-android-works/src/main/java/com/sybotan/android/works/layouts/SBottomNavigationLayout.kt

@@ -0,0 +1,154 @@
+/*
+ * ********************************************************************************************************************
+ *
+ *               iFHS7.
+ *              ;BBMBMBMc                  rZMBMBR              BMB
+ *              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+ *     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+ *     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+ *      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+ *     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+ *     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+ *     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+ *         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+ *     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+ *      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+ *         i0BM: SWKHKGO  MBDv
+ *           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+ *              ,  XMW  ..
+ *                  r                                                                     All rights reserved.
+ *
+ * ********************************************************************************************************************
+ */
+
+package com.sybotan.android.works.layouts
+
+import android.content.Context
+import android.support.constraint.ConstraintLayout
+import android.support.design.widget.BottomNavigationView
+import android.util.AttributeSet
+import android.view.*
+import android.widget.TextView
+import com.sybotan.android.base.extensions.indexOf
+import com.sybotan.android.views.adapters.SPagerAdapter
+import com.sybotan.android.works.R
+import kotlinx.android.synthetic.main.layout_bottom_navigation.view.*
+import kotlin.math.max
+
+/**
+ * 底部导航布局
+ *
+ * @author  庞利祥(sybotan@126.com)
+ */
+open class SBottomNavigationLayout(context: Context, attrs: AttributeSet? = null) : ConstraintLayout(context, attrs) {
+    /** 视图列表 */
+    private val viewList = arrayListOf<View>()
+    /** 当前菜单项 */
+    private var menuItem: MenuItem? = null
+
+    /** 底部导航项目变更监听器 */
+    private val navigationItemSelectedListener = NavigationItemSelectedListener()
+
+    /** 实例化View回调 */
+    var instantiateViewCallback: ((postion: Int) -> View?) ?  = null
+
+    /** 初始化 */
+    init {
+        // 加载布局文件
+        LayoutInflater.from(context).inflate(R.layout.layout_bottom_navigation, this)
+
+        // 处理自定义属性
+        parserAttrs(attrs)
+
+        // 设置UI
+        setupUi()
+    } // init
+
+    /**
+     * 设置UI
+     */
+    private fun setupUi() {
+        viewPager.adapter = ViewPagerAdapter()
+        viewPager.isSlide = false
+
+        // 关闭导航条Shift模式
+        navMain.labelVisibilityMode = 1
+        navMain.setOnNavigationItemSelectedListener(navigationItemSelectedListener)
+        return
+    } // Function setupUi()
+
+    /**
+     * 处理自定义属性
+     *
+     * @param   attrs       自定义属性列表
+     */
+    private fun parserAttrs(attrs: AttributeSet?) {
+        try {
+            val t = context.obtainStyledAttributes(attrs, R.styleable.SBottomNavigationLayout)
+            val menuId = t.getResourceId(R.styleable.SBottomNavigationLayout_nav_menu, 0)
+            t.recycle()
+            if (menuId != 0) {
+                navMain.inflateMenu(menuId)
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+        return
+    } // Function parserAttrs()
+
+    /**
+     * View页面适配器
+     *
+     * @author  庞利祥(sybotan@126.com)
+     */
+    inner class ViewPagerAdapter : SPagerAdapter() {
+
+        /** 构造函数 */
+        init {
+            //viewList.add(MyPaper(this@MainActivity))
+        } // init
+
+        /**
+         * 获得列表项数量
+         *
+         * @return  列表项数量
+         */
+        override fun getCount(): Int {
+            return navMain.menu.size()
+        } // Function getCount()
+
+        /**
+         * 实例化列表项视图
+         *
+         * @param   container   容器对象
+         * @param   position    数据项索引
+         * @return  列表项视图
+         */
+        override fun instantiateItem(container: ViewGroup, position: Int): Any {
+            return try {
+                val itemView = instantiateViewCallback?.invoke(position)!!
+                container.addView(itemView)
+                itemView
+            } catch (e: Exception) {
+                val view = TextView(context)
+                view.text = "$position"
+                container.addView(view)
+                view
+            }
+        } // Function instantiateItem()
+    } // Class MainPagerAdapter
+
+    /**
+     * 底部导航项目变更监听器
+     *
+     * @author  庞利祥(sybotan@126.com)
+     */
+    inner class NavigationItemSelectedListener : BottomNavigationView.OnNavigationItemSelectedListener {
+        override fun onNavigationItemSelected(item: MenuItem): Boolean {
+            val index = navMain.menu.indexOf(item)
+            viewPager.currentItem = max(index, 0)
+            return true
+        } // Function onNavigationItemSelected()
+    } // Class  NavigationItemSelectedListener
+} // Class SBottomNavigationLayout
+

+ 83 - 0
sybotan-android-works/src/main/res/layout/activity_about.xml

@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:orientation="vertical"
+    android:background="@color/grey_300"
+    tools:context=".activities.SAboutActivity">
+
+  <android.support.design.widget.AppBarLayout
+      android:layout_height="wrap_content"
+      android:layout_width="match_parent"
+      android:theme="@style/AppTheme.AppBarOverlay">
+
+    <android.support.v7.widget.Toolbar
+        android:id="@+id/appbar"
+        android:layout_width="match_parent"
+        android:layout_height="?attr/actionBarSize"
+        android:background="?attr/colorPrimary"
+        app:popupTheme="@style/AppTheme.PopupOverlay"/>
+
+  </android.support.design.widget.AppBarLayout>
+
+  <ScrollView
+      android:layout_width="match_parent"
+      android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content">
+      <ImageView
+          android:id="@+id/uiLogo"
+          android:layout_width="match_parent"
+          android:layout_height="120dp"
+          android:scaleType="centerInside"
+          android:background="@color/white"
+          app:srcCompat="@drawable/sybotan_logo_anim"/>
+      <TextView
+          android:text="斯伯坦机器人 v2.0.0"
+          android:textAlignment="center"
+          android:layout_marginTop="10dp"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"/>
+      <TextView
+          android:text="联系人:庞利祥  庞艳霞"
+          android:textAlignment="center"
+          android:layout_marginTop="10dp"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"/>
+      <TextView
+          android:text="联系电话:18301697319  18301697029"
+          android:textAlignment="center"
+          android:layout_marginTop="10dp"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"/>
+    </LinearLayout>
+  </ScrollView>
+</LinearLayout>

+ 36 - 0
sybotan-android-works/src/main/res/layout/activity_qr_code.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<merge
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    tools:context="com.sybotan.android.works.activities.SQrCodeActivity">
+  <com.sybotan.android.views.SQrCodeView
+      android:id="@+id/previewView"
+      android:layout_width="fill_parent"
+      android:layout_height="fill_parent"
+      app:mask_color="#A0000000"
+      app:frame_color="#e0a000"/>
+</merge>

+ 46 - 0
sybotan-android-works/src/main/res/layout/layout_bottom_navigation.xml

@@ -0,0 +1,46 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<android.support.constraint.ConstraintLayout
+        xmlns:android="http://schemas.android.com/apk/res/android"
+        xmlns:app="http://schemas.android.com/apk/res-auto"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent">
+
+    <com.sybotan.android.views.SViewPager
+            android:id="@+id/viewPager"
+            android:layout_width="match_parent"
+            android:layout_height="0dp"
+            app:layout_constraintTop_toTopOf="parent"
+            app:layout_constraintBottom_toTopOf="@id/navMain"/>
+
+    <android.support.design.widget.BottomNavigationView
+            android:id="@+id/navMain"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="?android:attr/windowBackground"
+            app:layout_constraintBottom_toBottomOf="parent"
+            app:layout_constraintLeft_toLeftOf="parent"
+            app:layout_constraintRight_toRightOf="parent"/>
+</android.support.constraint.ConstraintLayout>

+ 29 - 0
sybotan-android-works/src/main/res/values/attrs.xml

@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+  <declare-styleable name="SBottomNavigationLayout">
+    <attr name="nav_menu" format="integer"/>
+  </declare-styleable>
+</resources>

+ 29 - 0
sybotan-android-works/src/main/res/values/strings.xml

@@ -0,0 +1,29 @@
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+  <string name="app_name">sybotan-android-works</string>
+
+  <!-- activity -->
+  <string name="activity_about_title">关于</string>
+</resources>

+ 34 - 0
sybotan-android-works/src/main/res/values/styles.xml

@@ -0,0 +1,34 @@
+<!--
+  ~ *********************************************************************************************************************
+  ~
+  ~               iFHS7.
+  ~              ;BBMBMBMc                  rZMBMBR              BMB
+  ~              MBEr:;PBM,               7MBMMEOBB:             BBB                       RBW
+  ~     XK:      BO     SB.     :SZ       MBM.       c;;     ir  BBM :FFr       :SSF:    ;xBMB:r   iuGXv.    i:. iF2;
+  ~     DBBM0r.  :D     S7   ;XMBMB       GMBMu.     MBM:   BMB  MBMBBBMBMS   WMBMBMBBK  MBMBMBM  BMBRBMBW  .MBMBMBMBB
+  ~      :JMRMMD  ..    ,  1MMRM1;         ;MBMBBR:   MBM  ;MB:  BMB:   MBM. RMBr   sBMH   BM0         UMB,  BMB.  KMBv
+  ~     ;.   XOW  B1; :uM: 1RE,   i           .2BMBs  rMB. MBO   MBO    JMB; MBB     MBM   BBS    7MBMBOBM:  MBW   :BMc
+  ~     OBRJ.SEE  MRDOWOR, 3DE:7OBM       .     ;BMB   RMR7BM    BMB    MBB. BMB    ,BMR  .BBZ   MMB   rMB,  BMM   rMB7
+  ~     :FBRO0D0  RKXSXPR. JOKOOMPi       BMBSSWBMB;    BMBB:    MBMB0ZMBMS  .BMBOXRBMB    MBMDE RBM2;SMBM;  MBB   xBM2
+  ~         iZGE  O0SHSPO. uGZ7.          sBMBMBDL      :BMO     OZu:BMBK,     rRBMB0;     ,EBMB  xBMBr:ER.  RDU   :OO;
+  ~     ,BZ, 1D0  RPSFHXR. xWZ .SMr                  . .BBB
+  ~      :0BMRDG  RESSSKR. 2WOMBW;                   BMBMR
+  ~         i0BM: SWKHKGO  MBDv
+  ~           .UB  OOGDM. MK,                                          Copyright (c) 2015-2019.  斯伯坦机器人
+  ~              ,  XMW  ..
+  ~                  r                                                                     All rights reserved.
+  ~
+  ~ *********************************************************************************************************************
+  -->
+
+<resources>
+    <!-- Base application theme. -->
+    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
+    </style>
+    <style name="AppTheme.NoActionBar">
+        <item name="windowActionBar">false</item>
+        <item name="windowNoTitle">true</item>
+    </style>
+    <style name="AppTheme.AppBarOverlay" parent="ThemeOverlay.AppCompat.Dark.ActionBar"/>
+    <style name="AppTheme.PopupOverlay" parent="ThemeOverlay.AppCompat.Light"/>
+</resources>