Forráskód Böngészése

feat二维码扫描,网络库封装

lihao1 3 éve
szülő
commit
a92e1d9882
45 módosított fájl, 2100 hozzáadás és 97 törlés
  1. 27 13
      cadengine/src/main/java/cn/sagacloud/android/cadengine/FloorScene.kt
  2. 24 2
      demo/build.gradle
  3. BIN
      demo/libs/armeabi-v7a/libZBarDecoder.so
  4. BIN
      demo/libs/armeabi/libZBarDecoder.so
  5. BIN
      demo/libs/mips/libZBarDecoder.so
  6. BIN
      demo/libs/x86/libZBarDecoder.so
  7. BIN
      demo/libs/zbardecoder.jar
  8. 2 0
      demo/src/main/AndroidManifest.xml
  9. 29 0
      demo/src/main/java/com/sybotan/android/demo/activities/BaseActivity.java
  10. 103 25
      demo/src/main/java/com/sybotan/android/demo/activities/GraphyActivity.kt
  11. 38 0
      demo/src/main/java/com/sybotan/android/demo/bean/BaseModel.java
  12. 79 0
      demo/src/main/java/com/sybotan/android/demo/bean/UniversalCellClass.java
  13. 15 0
      demo/src/main/java/com/sybotan/android/demo/bean/request/ReqSpaceJob.java
  14. 23 0
      demo/src/main/java/com/sybotan/android/demo/bean/result/SpaceJobModel.java
  15. 96 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/API.java
  16. 37 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/BaseActionEvent.java
  17. 77 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/BaseObserver.java
  18. 107 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/BaseRemoteData.java
  19. 17 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/BaseViewModelInterface.java
  20. 17 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/Constant.java
  21. 21 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/IViewModelAction.java
  22. 31 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/NullOnEmptyConverterFactory.java
  23. 53 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/RetrofitFactory.java
  24. 61 0
      demo/src/main/java/com/sybotan/android/demo/retrofit/RetrofitLogInterceptor.java
  25. 1 1
      demo/src/main/java/com/sybotan/android/demo/tools/Opt.kt
  26. 154 0
      demo/src/main/java/com/sybotan/android/demo/viewmodel/BaseViewModel.java
  27. 48 0
      demo/src/main/java/com/sybotan/android/demo/viewmodel/GraphyVM.java
  28. 79 0
      demo/src/main/java/com/zbar/lib/BeepManager.java
  29. 377 0
      demo/src/main/java/com/zbar/lib/CaptureActivity.java
  30. 54 0
      demo/src/main/java/com/zbar/lib/Config.java
  31. 102 0
      demo/src/main/java/com/zbar/lib/decode/DecodeHandler.java
  32. 67 0
      demo/src/main/java/com/zbar/lib/decode/DecodeThread.java
  33. 113 0
      demo/src/main/java/com/zbar/lib/decode/MainHandler.java
  34. BIN
      demo/src/main/res/drawable/btn_back_normal.png
  35. BIN
      demo/src/main/res/drawable/btn_back_press.png
  36. BIN
      demo/src/main/res/drawable/qr_code_bg.9.png
  37. BIN
      demo/src/main/res/drawable/scan_line.png
  38. 7 0
      demo/src/main/res/drawable/selector_capture_back.xml
  39. BIN
      demo/src/main/res/drawable/shadow.png
  40. 96 0
      demo/src/main/res/layout/activity_capture.xml
  41. 112 56
      demo/src/main/res/layout/activity_graphy.xml
  42. BIN
      demo/src/main/res/mipmap-xhdpi/structure.png
  43. BIN
      demo/src/main/res/raw/qrcode.mp3
  44. 26 0
      demo/src/main/res/values/ids.xml
  45. 7 0
      demo/src/main/res/values/strings.xml

+ 27 - 13
cadengine/src/main/java/cn/sagacloud/android/cadengine/FloorScene.kt

@@ -23,8 +23,8 @@ import org.greenrobot.eventbus.ThreadMode
 open class FloorScene : SGraphyScene() {
     //点击事件回调到界面
     lateinit var topUp: OnSingleTopUp
-    var item: PointItem? = null
-    val point = Point()
+//    var item: PointItem? = null
+//    val point = Point()
     var canEdit = true
 
     /** 默认缩放比例,用于指定最小缩放比例*/
@@ -35,7 +35,7 @@ open class FloorScene : SGraphyScene() {
      * dot 绘制问题点
      * pipe 绘制管道模式
      */
-    var type: String? = "dot"
+    var type: String? = "normal"
     var pipeLineList: ArrayList<PointF> = ArrayList()
 
     /** item 数据  */
@@ -234,7 +234,7 @@ open class FloorScene : SGraphyScene() {
             addItem(item)
         }
 
-        //添加问题点
+        //添加管道
         for (item in pipeItemList) {
             item.isVisible = isShowSpace
             item.zOrder = 100000f
@@ -385,20 +385,34 @@ open class FloorScene : SGraphyScene() {
                 outSide = true
                 space.paint.color = Opt.spaceChoseColor
                 space.outPaint.color = Color.rgb(0xbd, 0xe8, 0xfe)
-                point.mX = e.x
-                point.mY = e.y
-                if (item != null) {
-                    removeItem(item!!)
-                }
-                item = PointItem(point, null, defaultPointScale)
-                item!!.isVisible = true
-                item!!.zOrder = 100000f
-                addItem(item!!)
+//                val point =Point()
+//                point.mX = e.x
+//                point.mY = e.y
+//                if (item != null) {
+//                    removeItem(item!!)
+//                }
+//                var item = PointItem(point, null, defaultPointScale)
+//                item!!.isVisible = true
+//                item!!.zOrder = 100000f
+//                addItem(item!!)
 //                } else {
 //                    space!!.paint.color = Opt.spaceColor
 //                    space.outPaint.color = Color.rgb(0xff, 0xff, 0xff)
 //                }
             }
+            val point =Point()
+            point.mX = e.x
+            point.mY = e.y
+            var item = PointItem(point, null, defaultPointScale)
+            item!!.isVisible = true
+            item!!.zOrder = 100000f
+            pointItemList.add(item!!)
+            //添加问题点
+            for (item in pointItemList) {
+                item.isVisible = isShowSpace
+                item.zOrder = 100000f
+                addItem(item)
+            }
             if (!outSide) {
                 return true
             }

+ 24 - 2
demo/build.gradle

@@ -37,6 +37,13 @@ android {
 
         testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
         vectorDrawables.useSupportLibrary = true
+        multiDexEnabled true
+        ndk {
+            //选择要添加的对应cpu类型的.so库。
+            abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64'
+            // 还可以添加 abiFilters 'armeabi', 'armeabi-v7a', 'x86', 'x86_64', 'mips', 'mips64', 'arm64-v8a'
+
+        }
     }
 
     buildTypes {
@@ -59,11 +66,18 @@ android {
         targetCompatibility JavaVersion.VERSION_1_8
         sourceCompatibility JavaVersion.VERSION_1_8
     }
+    sourceSets {
+        main {
+            //jni库的调用会到资源文件夹下libs里面找so文件
+            jniLibs.srcDirs = ['libs']
+        }
+    }
 }
 
 dependencies {
     implementation fileTree(dir: 'libs', include: ['*.jar'])
     implementation 'com.android.support.constraint:constraint-layout:1.1.2'
+    implementation files('libs/zbardecoder.jar')
     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'
@@ -133,8 +147,6 @@ dependencies {
     ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
     // Sybotan依赖
     implementation group: 'cn.sagacloud', name: 'saga-kotlin-base', version: SAGA_KOTLIN_VERSION
-    implementation 'com.squareup.retrofit2:retrofit:2.3.0'
-    implementation 'com.squareup.retrofit2:converter-gson:2.3.0'
 //    implementation project(':sybotan-android-base')
 //    implementation project(':sybotan-android-database')
 //    implementation project(':sybotan-android-devices')
@@ -146,4 +158,14 @@ dependencies {
     implementation group: 'cn.sagacloud', name: 'saga-kotlin-database', version: SAGA_KOTLIN_VERSION
     implementation group: 'cn.sagacloud', name: 'sybotan-android-base', version: SYBOTAN_ANDROID_VERSION
     implementation group: 'cn.sagacloud', name: 'sybotan-android-graphy', version: SYBOTAN_ANDROID_VERSION
+    implementation 'androidx.multidex:multidex:2.0.0'
+    implementation 'io.reactivex.rxjava2:rxjava:2.1.1'
+    implementation 'io.reactivex.rxjava2:rxandroid:2.0.1'
+    implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'
+    implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.1'
+    implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'
+
+    implementation 'com.squareup.retrofit2:retrofit:2.9.0'
+    implementation 'com.squareup.retrofit2:converter-gson:2.9.0'
+    implementation 'com.squareup.retrofit2:adapter-rxjava2:2.9.0'
 }

BIN
demo/libs/armeabi-v7a/libZBarDecoder.so


BIN
demo/libs/armeabi/libZBarDecoder.so


BIN
demo/libs/mips/libZBarDecoder.so


BIN
demo/libs/x86/libZBarDecoder.so


BIN
demo/libs/zbardecoder.jar


+ 2 - 0
demo/src/main/AndroidManifest.xml

@@ -14,6 +14,7 @@
 
     <uses-feature android:name="android.hardware.camera" />
     <uses-feature android:name="android.hardware.camera.autofocus" />
+    <uses-permission android:name="android.permission.CAMERA" />
 
     <application
         android:name=".DemoApp"
@@ -46,6 +47,7 @@
         <activity android:name=".activities.PocActivity">
 
         </activity>
+        <activity android:name="com.zbar.lib.CaptureActivity"/>
     </application>
 
 </manifest>

+ 29 - 0
demo/src/main/java/com/sybotan/android/demo/activities/BaseActivity.java

@@ -0,0 +1,29 @@
+package com.sybotan.android.demo.activities;
+
+import android.annotation.TargetApi;
+import android.content.pm.ActivityInfo;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.Toast;
+
+import com.trello.rxlifecycle2.components.support.RxAppCompatActivity;
+
+import java.util.List;
+
+/**
+ * Created by lihao.
+ * Date: 2021/6/8
+ */
+public class BaseActivity extends RxAppCompatActivity {
+
+    @Override
+    protected void onCreate(@Nullable Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+    }
+
+}

+ 103 - 25
demo/src/main/java/com/sybotan/android/demo/activities/GraphyActivity.kt

@@ -23,6 +23,7 @@
 
 package com.sybotan.android.demo.activities
 
+import android.app.Activity
 import android.graphics.BitmapFactory
 import android.graphics.PointF
 import android.support.v7.app.AppCompatActivity
@@ -46,18 +47,35 @@ import org.greenrobot.eventbus.EventBus
 import org.greenrobot.eventbus.Subscribe
 import org.jetbrains.anko.doAsync
 import org.jetbrains.anko.uiThread
+import android.support.v4.app.ActivityCompat.startActivityForResult
 
-class GraphyActivity : AppCompatActivity() {
+import android.content.Intent
+import android.view.View
+import cn.sagacloud.android.cadengine.items.PointItem
+import com.sybotan.android.demo.bean.result.SpaceJobModel
+import com.sybotan.android.demo.viewmodel.GraphyVM
+import com.zbar.lib.CaptureActivity
+
+
+class GraphyActivity : BaseActivity() {
     companion object {
         private val TAG = GraphyActivity::class.java.name
+        private val REQUEST_CODE_SCAN = 0x0000 // 扫描二维码
     }
 
+    //实际距离
+    val distance = 1800f
+
+    //缩放后的同比例距离
+    var realDistance = 0f
+
     val scene = FloorScene()
 
     //    val path = "base/cbde057b91e711eb9901557a780001bf.jsonz"
 //    val path = "base/ebd53bb00d3511eb9a1db95f725712e8.jsonz"
-    val path = "base/a4e671ec8b8111eb9a520dc30d098a92.jsonz"
+//    val path = "base/a4e671ec8b8111eb9a520dc30d098a92.jsonz"
 //    val path = "base/0764ad79555d11ebab557bd2e22cfb56.jsonz"
+    val path = "base/05d5b9e6c8c411eb8437353008b31107.jsonz"
 
     @Subscribe
     fun test(msg: SGraphyViewMoveEvent) {
@@ -112,33 +130,93 @@ class GraphyActivity : AppCompatActivity() {
             }
         }
 
+        addDot.setOnClickListener {
+            scene.type = "dot"
+            addDot.alpha = 0.3f
+        }
+
+        qrcode.setOnClickListener {
+            val intent = Intent(this@GraphyActivity, CaptureActivity::class.java)
+            startActivityForResult(intent, REQUEST_CODE_SCAN)
+        }
+
+        scene.setOnSingleTopUp(object : FloorScene.OnSingleTopUp {
+            override fun onSingUp(x: Float, y: Float) {
+                if ("dot".equals(scene.type)) {
+                    scene.type = "normal"
+                    addDot.alpha = 1f
+                    return
+                }
+
+                if ("normal".equals(scene.type)) {
+                    realDistance = distance * scene.defaultPointScale / graphyView.scale
+                    for (point in scene.pointItemList) {
+                        val sqrt = Math.sqrt(
+                            Math.pow(
+                                Math.abs(x - point.data.mX).toDouble(),
+                                2.0
+                            ) + Math.pow(Math.abs(y - point.data.mY).toDouble(), 2.0)
+                        ).toInt()
+                        if (sqrt < realDistance) {
+                            Toast.makeText(this@GraphyActivity, "x=" + x, Toast.LENGTH_LONG).show()
+                            showDotDetail(point)
+                            break
+                        }
+                    }
+                    return
+                }
+            }
+        })
+        initViewModel()
+
 
         return
     } // Function onCreate()
 
-    /**
-     * 加载墙数据
-     */
-    private fun loadWalls() {
-//        val revit = SJsonUtil.fromJson<Revit>(applicationContext.assets.open("WallDatas.json"), PropertyNamingStrategy.UPPER_CAMEL_CASE)
-//        Log.d(TAG, "name=${revit.name}")
-//        Log.d(TAG, "${revit.floorDatas!![revit.floorDatas!!.size - 1].lines!!}")
-//
-//        Log.d(TAG, "line.size=${revit.floorDatas!![revit.floorDatas!!.size - 1].lines!!.size}")
-//        //for ( line in revit.floorDatas!![revit.floorDatas!!.size - 1].lines!!) {
-//        for ( line in revit.floorDatas!![0].lines!!) {
-//            val pointList = ArrayList<PointF>()
-//            Log.d(TAG, "point.size=${line.size}")
-//            for (t in 0 until  line.size) {
-//                val p1 = line[t].split(",")
-//                val point = PointF(p1[0].toFloat() * 20  + 12000, -p1[1].toFloat() * 20  -20600)
-//                pointList.add(point)
-//            }
-//            scene.addItem(GraphyWallItem(pointList))
-//        }
-//        println()
+    private fun initViewModel() {
+        var graphyVM = GraphyVM({ name, params ->
+            when (name) {
+                GraphyVM.SPACE_JOB -> {
+                    val list = params as (List<SpaceJobModel>)
+                }
+            }
+        }, this)
+        graphyVM.getSpaceJob()
+    }
+
+    private fun showDotDetail(point: PointItem) {
+        spaceStructure.visibility = View.GONE
+        quesDetail.visibility = View.VISIBLE
+        quesTv.setText(
+            "这是描述问题的文本,左侧是拍照图片。" +
+                    "\nx轴:${point.data.mX}" +
+                    "\ny轴:${point.data.mY}" +
+                    "\n点击下方“问题已解决”按钮后可关闭该问题点。"
+        )
+        quesSubmit.setOnClickListener {
+            spaceStructure.visibility = View.VISIBLE
+            quesDetail.visibility = View.GONE
+            scene.removeItem(point)
+            scene.pointItemList.remove(point)
+        }
+    }
+
+    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
+        super.onActivityResult(requestCode, resultCode, data)
+        when (requestCode) {
+            REQUEST_CODE_SCAN ->                 // 扫描二维码回传
+                if (resultCode == RESULT_OK) {
+                    if (data != null) {
+                        //获取扫描结果
+                        val bundle = data.extras
+                        val result = bundle.getString(CaptureActivity.EXTRA_STRING)
+                        Log.e("tag", "扫描结果:$result")
+                    }
+                }
+            else -> {
+            }
+        }
+    }
 
-        return
-    } // Function loadWalls()
 
 } // Class GraphyActivity

+ 38 - 0
demo/src/main/java/com/sybotan/android/demo/bean/BaseModel.java

@@ -0,0 +1,38 @@
+package com.sybotan.android.demo.bean;
+
+import android.text.TextUtils;
+
+import java.io.Serializable;
+
+/**
+ * Created by lihao.
+ * Date: 2021/6/9
+ */
+public class BaseModel<T> implements Serializable {
+    public String result;
+    public String message;
+    public String code;
+    private T rtn;
+
+    public boolean isSuccess() {
+//        if (!TextUtils.isEmpty(result)) {
+//            return "success".equals(result);
+//        } else if (!TextUtils.isEmpty(code)) {
+//            return "1".equals(code);
+//        }
+        return true;
+    }
+
+    public T getData() {
+        if (rtn != null) {
+            return rtn;
+        }
+        return null;
+    }
+
+    public static class Nothing {
+
+    }
+
+
+}

+ 79 - 0
demo/src/main/java/com/sybotan/android/demo/bean/UniversalCellClass.java

@@ -0,0 +1,79 @@
+package com.sybotan.android.demo.bean;
+
+import java.io.Serializable;
+
+/**
+ * Created by Administrator on 2016/1/5.
+ */
+public class UniversalCellClass implements Serializable {
+    public static final String TYPE1 = "cell1";
+    public static final String TYPE2 = "cell2";
+    public static final String TYPE3 = "cell3";
+    public static final String TYPE4 = "cell4";
+    public static final String TYPE5 = "cell5";
+    public static final String TYPE6= "cell6";
+    public static final String TYPE7 = "cell7";
+    public static final String TYPE8 = "cell8";
+    public static final String TYPE9 = "cell9";
+    public static final String TYPE10 = "cell10";
+
+    public String cell_Type;//样式
+
+    public Object cell_Value="";//值
+
+    public String title;
+
+    public int sortPosition;//排序
+
+    public long time;//时间排序
+
+
+
+
+    public Boolean is_Select=false;//是否可以点击
+
+
+    public Object parameter;//用于保存的临时参数,用于向下一级传递
+
+    public UniversalCellClass set_Cell_Type(String type)
+    {
+        cell_Type=type;
+        return this;
+    }
+
+    public UniversalCellClass set_Cell_Value(Object value)
+    {
+        cell_Value=value;
+        return this;
+    }
+
+    public UniversalCellClass set_Is_Select(Boolean select)
+    {
+        is_Select=select;
+        return this;
+    }
+
+    public UniversalCellClass set_Parameter(Object canShu)
+    {
+        parameter=canShu;
+        return this;
+    }
+
+    public int getSortPosition() {
+        return sortPosition;
+    }
+
+    public UniversalCellClass setSortPosition(int sortPosition) {
+        this.sortPosition = sortPosition;
+        return this;
+    }
+
+    public long getTime() {
+        return time;
+    }
+
+    public UniversalCellClass setTime(long time) {
+        this.time = time;
+        return this;
+    }
+}

+ 15 - 0
demo/src/main/java/com/sybotan/android/demo/bean/request/ReqSpaceJob.java

@@ -0,0 +1,15 @@
+package com.sybotan.android.demo.bean.request;
+
+import java.util.ArrayList;
+
+/**
+ * Created by lihao.
+ * Date: 2021/6/9
+ */
+public class ReqSpaceJob {
+    public String buildingId;
+    public String floorId;
+    public String zoneType;
+    public String target = "rtn";
+    public ArrayList<String> jobStatus = new ArrayList<>();
+}

+ 23 - 0
demo/src/main/java/com/sybotan/android/demo/bean/result/SpaceJobModel.java

@@ -0,0 +1,23 @@
+package com.sybotan.android.demo.bean.result;
+
+import com.sybotan.android.demo.bean.BaseModel;
+
+/**
+ * Created by lihao.
+ * Date: 2021/6/9
+ */
+public class SpaceJobModel extends BaseModel {
+    public String bimLocation;
+    public String buildingId;
+    public String classCode;
+    public String createTime;
+    public String floorId;
+    public String id;
+    public String lastUpdate;
+    public String localName;
+    public String name;
+    public String projectId;
+    public String state;
+//    public String statistics;
+    public String job;
+}

+ 96 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/API.java

@@ -0,0 +1,96 @@
+package com.sybotan.android.demo.retrofit;
+
+
+
+import com.sybotan.android.demo.bean.BaseModel;
+import com.sybotan.android.demo.bean.result.SpaceJobModel;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.reactivex.Observable;
+import okhttp3.RequestBody;
+import retrofit2.http.Body;
+import retrofit2.http.GET;
+import retrofit2.http.POST;
+import retrofit2.http.PUT;
+import retrofit2.http.Path;
+import retrofit2.http.Query;
+
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public interface API {
+    //    //查询反馈后设备动作
+//    @POST("server/ipad/custom/getEquipmentAction")
+//    Observable<EquipmentActionModel> getEquipmentAction(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //下班后重新开启调节
+//    @POST("server/ipad/custom/recover")
+//    Observable<RecoverModel> recover(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //下班节能
+//    @POST("server/ipad/custom/leave")
+//    Observable<LeaveModel> leave(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //提交反馈
+//    @POST("server/ipad/custom/feedback")
+//    Observable<FeedbackModel> feedback(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //客服弹窗
+//    @GET("http://54.222.237.147:8090/meeting/all")
+//    Observable<BaseModel<ArrayList<MeetingModel>>> meeting(@Query("token") String token);
+//
+//    //客服弹窗
+//    @PUT("http://54.222.237.147:8090/meetingBooking/cancel/{id}")
+//    Observable<BaseModel<String>> cancel(@Path("id") String id, @Query("token") String token);
+
+    //
+//    //空间列表
+//    @POST("server/ipad/space/getIpadSpaceList")
+//    Observable<GetIpadSpaceListModel> getIpadSpaceList(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //地图展示--楼层空间列表
+//    @GET("server/ipad/space/getIpadFloorSpaceList")
+//    Observable<GetIpadFloorSpaceList> getIpadFloorSpaceList(@Query("pubname") String pubName, @Query("openid") String macAddress);
+//
+//    //查询单独空间中的信息
+//    @POST("server/ipad/space/getIpadSpace")
+//    Observable<BaseModel<ArrayList<QueryModel>>> query(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //查询单独空间中的信息
+//    @POST("http://tenantslink.sagacloud.cn/server/light/onoff/set")
+//    Observable<BaseModel<ArrayList<SetModel>>> set(@Query("projectId") String projectId, @Query("app") String app, @Query("userId") String userId, @Query("userName") String userName, @Query("userPhone") String userPhone, @Body RequestBody requestBody);
+//
+//    //下班后重新开启调节
+//    @POST("server/ipad/custom/recover")
+//    Observable<BaseModel<RecoverModel>> recover(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+//
+//    //下班节能
+//    @POST("server/ipad/custom/leave")
+//    Observable<BaseModel<LeaveModel>> leave(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+
+    //
+//    //投诉反馈
+//    @POST("http://172.16.36.245:50009/feedbackSuggest/save")
+//    @POST("server/feedbackSuggest/save")
+//    Observable<BaseModel> save(@Query("pubname") String pubName, @Query("openid") String macAddress, @Body RequestBody requestBody);
+
+//    /**
+//     * 自动更新 app
+//     */
+//    @POST("https://edition.persagy.com/api/app/saas-version-app/Spring/MVC/entrance/unifier/VersionLastService")
+//    Observable<BaseModel<List<AutoUpDateBean.Content>>> autoUpdateApp(@Body RequestBody body);
+//
+//    /**
+//     * 天气
+//     */
+//    @GET("https://duoduoenv.sagacloud.cn/duoduo-service/fm-service/weather/getCurrent")
+//    Observable<BaseModel<WeatherModel>> getCurrent(@Query("projectId") String projectId);
+//
+    /**
+     * 查询空间和任务
+     */
+    @POST("clmt/job/space_job")
+    Observable<BaseModel<List<SpaceJobModel>>> getSpaceJob(@Body RequestBody body);
+}

+ 37 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/BaseActionEvent.java

@@ -0,0 +1,37 @@
+package com.sybotan.android.demo.retrofit;
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public class BaseActionEvent {
+    private int action;
+    //接口请求时
+    public static final int SHOW_LOADING_DIALOG = 1;
+    //接口请求结束
+    public static final int DISMISS_LOADING_DIALOG = 2;
+    //显示吐司
+    public static final int SHOW_TOAST = 3;
+    //接口请求失败
+    public static final int SHOW_ERROR_LOAD_DIALOG = 4;
+
+    private String message;
+
+    public BaseActionEvent(int action) {
+        this.action = action;
+    }
+
+    public int getAction() {
+        return action;
+    }
+
+    public void setAction(int action) {
+        this.action = action;
+    }
+
+    public String getMessage() {
+        return message;
+    }
+
+    public void setMessage(String message) {
+        this.message = message;
+    }
+}

+ 77 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/BaseObserver.java

@@ -0,0 +1,77 @@
+package com.sybotan.android.demo.retrofit;
+
+import com.sybotan.android.demo.activities.BaseActivity;
+import com.sybotan.android.demo.bean.BaseModel;
+import com.sybotan.android.demo.viewmodel.BaseViewModel;
+
+import io.reactivex.Observer;
+import io.reactivex.disposables.Disposable;
+
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public abstract class BaseObserver<T> implements Observer<BaseModel<T>> {
+    private BaseViewModel baseViewModel;
+
+    private BaseActivity mActivity;
+
+    public BaseObserver(BaseActivity activity, BaseViewModel baseViewModel) {
+        mActivity = activity;
+        this.baseViewModel = baseViewModel;
+    }
+
+    @Override
+    public void onSubscribe(Disposable d) {
+
+    }
+
+    @Override
+    public void onNext(BaseModel<T> value) {
+        if (value.isSuccess()) {
+            T t = value.getData();
+            onSuccess(t);
+        } else {
+            onFailure(value.message);
+        }
+    }
+
+    @Override
+    public void onError(Throwable e) {
+        baseViewModel.showErrorLoading("");
+        /*if (NetUtil.getNetWorkState(MyApplication.getAppContext()) < 0) {
+            onFailure("请检查网络设置");
+        } else if (e instanceof HttpException) {
+            onFailure("请求失败");
+        } else if (e instanceof JsonParseException) {
+            onFailure("数据错误");
+        } else if (e instanceof ConnectException) {
+            onFailure("连接失败");
+        } else if (e instanceof SocketTimeoutException) {
+            onFailure("网络连接超时");
+        } else {
+            onFailure("未知错误");
+        }*/
+        e.printStackTrace();
+    }
+
+    @Override
+    public void onComplete() {
+
+    }
+
+    /**
+     * 只是接口调用成功,此项目的接口返回内容无法判断接口返回是成功/失败
+     *
+     * @param t
+     */
+    protected abstract void onSuccess(T t);
+
+    /**
+     * 接口调用失败
+     *
+     * @param msg
+     */
+    protected void onFailure(String msg) {
+
+    }
+}

+ 107 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/BaseRemoteData.java

@@ -0,0 +1,107 @@
+package com.sybotan.android.demo.retrofit;
+
+import com.google.gson.Gson;
+import com.sybotan.android.demo.viewmodel.BaseViewModel;
+
+import org.json.JSONObject;
+
+import java.util.HashMap;
+
+import io.reactivex.Observable;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.disposables.CompositeDisposable;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public abstract class BaseRemoteData {
+    private CompositeDisposable compositeDisposable;
+    private BaseViewModel baseViewModel;
+    protected final String pubName = "ipad";
+    protected String macAddress;
+    private Gson mGson;
+
+    public BaseRemoteData(BaseViewModel baseViewModel) {
+        this.baseViewModel = baseViewModel;
+        this.compositeDisposable = new CompositeDisposable();
+        //华为
+//        macAddress = "34:46:ec:aa:55:41";
+        //联想
+//        macAddress = "84:B8:B8:10:A7:83";
+//        macAddress = "BE:66:AB:F8:77:C4";
+        mGson = new Gson();
+    }
+
+    private void showLoading(String message) {
+        if (baseViewModel != null) {
+            baseViewModel.showLoading(message);
+        }
+    }
+
+    private void dismissLoading() {
+        if (baseViewModel != null) {
+            baseViewModel.dismissLoading();
+        }
+    }
+
+    /**
+     * 取消订阅
+     */
+    public void dispose() {
+        if (!compositeDisposable.isDisposed()) {
+            compositeDisposable.dispose();
+        }
+    }
+
+    public BaseViewModel getBaseViewModel() {
+        return baseViewModel;
+    }
+
+    /**
+     * 发送网络请求
+     * @param observable
+     * @param observer
+     * @param isShow
+     * @param message
+     * @param <T>
+     */
+    protected <T> void toSubcribe(Observable<T> observable, Observer<T> observer, boolean isShow, String message) {
+        observable.subscribeOn(Schedulers.io())
+                .unsubscribeOn(Schedulers.io())
+                .observeOn(AndroidSchedulers.mainThread())
+                .doOnSubscribe(disposable -> {//允许我们在观察者订阅时立即执行动作
+                    if (disposable != null) {
+                        compositeDisposable.add(disposable);
+                    }
+                    if (isShow) {
+                        showLoading(message);
+                    }
+                }).subscribe(observer);
+    }
+
+    /**
+     * 请求body包装
+     *
+     * @param requestBean 请求body
+     * @param <T>         body类泛型
+     * @return RequestBody包装
+     */
+    public <T> RequestBody getRequestBody(T requestBean) {
+        String params, contentType = "";
+        if (requestBean instanceof JSONObject) {
+            JSONObject jsonObject = (JSONObject) requestBean;
+            params = jsonObject.toString();
+        } else if (requestBean instanceof HashMap) {
+            HashMap<Object, Object> hashMap = (HashMap<Object, Object>) requestBean;
+            params = mGson.toJson(hashMap);
+        } else {
+            params = mGson.toJson(requestBean);
+        }
+        contentType = "application/json";
+        return RequestBody.create(MediaType.parse(contentType), params);
+    }
+}

+ 17 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/BaseViewModelInterface.java

@@ -0,0 +1,17 @@
+package com.sybotan.android.demo.retrofit;
+
+/**
+ * Created by yanfapersagy on 2017/6/29.
+ */
+
+public interface BaseViewModelInterface {
+
+
+    /**
+     * wangmaobo
+     * 在ViewModel初始化的过程中需要传入改接口的实现用于ViewModel向View层发布命令
+     * @param name 命令
+     * @param params 附带的参数,一般情况下为Null
+     */
+    public void SendDircetive(String name, Object params);
+}

+ 17 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/Constant.java

@@ -0,0 +1,17 @@
+package com.sybotan.android.demo.retrofit;
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public class Constant {
+    public final static int CANCEL_TIMER = 29;
+    // 图片缓存最大容量,50M,根据自己的需求进行修改
+    public static final int GLIDE_CATCH_SIZE = 50 * 1024 * 1024;
+    // 图片缓存子目录
+    public static final String GLIDE_CARCH_DIR = "/sagacare";
+    //服务器地址
+    public static final String ServerIP = "http://192.168.0.48:8082/";
+    //图片服务器地址
+    public static final String ServerImgIP = "http://api.sagacloud.cn/dp-auxiliary/image-service/common/image_get?systemId=dataPlatform&key=";
+    //地图服务器地址
+    public static final String ServerMapIP = "http://adm.sagacloud.cn/image-service/common/file_get?systemId=revit&key=";
+}

+ 21 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/IViewModelAction.java

@@ -0,0 +1,21 @@
+package com.sybotan.android.demo.retrofit;
+
+
+import android.arch.lifecycle.MutableLiveData;
+
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public interface IViewModelAction {
+
+    void showLoading(String message);
+
+    void showErrorLoading(String message);
+
+    void dismissLoading();
+
+    void showToast(String message);
+
+    MutableLiveData<BaseActionEvent> getActionLiveData();
+
+}

+ 31 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/NullOnEmptyConverterFactory.java

@@ -0,0 +1,31 @@
+package com.sybotan.android.demo.retrofit;
+
+import java.io.IOException;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import okhttp3.ResponseBody;
+import retrofit2.Converter;
+import retrofit2.Retrofit;
+
+/**
+ * Created by weimo on 2020/7/14.
+ * 遇到HTTP请求code为200,response body为空的情况。
+ * 这种情况下,RetrofitGson解析阶段会出现报错。
+ */
+public class NullOnEmptyConverterFactory extends Converter.Factory {
+    @Override
+    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
+        final Converter<ResponseBody, ?> delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
+        return new Converter<ResponseBody, Object>() {
+            @Override
+            public Object convert(ResponseBody body) throws IOException {
+                long contentLength = body.contentLength();
+                if (contentLength == 0) {
+                    return null;
+                }
+                return delegate.convert(body);
+            }
+        };
+    }
+}

+ 53 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/RetrofitFactory.java

@@ -0,0 +1,53 @@
+package com.sybotan.android.demo.retrofit;
+
+import com.google.gson.FieldNamingPolicy;
+import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
+
+import java.util.concurrent.TimeUnit;
+
+import okhttp3.OkHttpClient;
+import retrofit2.Retrofit;
+import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
+import retrofit2.converter.gson.GsonConverterFactory;
+
+/**
+ * Created by wangmaobo on 2018/8/23.
+ */
+public class RetrofitFactory {
+
+    private static String BASE_URL = "";
+    private static final long TIMEOUT = 30;
+    private static final long TIMEOUT2 = 10;
+    public static OkHttpClient httpClient = new OkHttpClient.Builder()
+//            .addInterceptor(new RetrofitLogInterceptor())
+            .connectTimeout(TIMEOUT, TimeUnit.SECONDS)
+            .writeTimeout(TIMEOUT, TimeUnit.SECONDS)
+            .readTimeout(TIMEOUT, TimeUnit.SECONDS)
+            .build();
+
+    public static API mApi;
+
+    public static API mApiPlus;
+
+    public static synchronized API getInstance() {
+        if (null == mApi || !BASE_URL.contains(Constant.ServerIP)) {
+            BASE_URL = Constant.ServerIP;
+            mApi = new Retrofit.Builder()
+                    .baseUrl(BASE_URL)
+                    .addConverterFactory(GsonConverterFactory.create(createGSon()))
+                    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
+                    .client(httpClient)
+                    .build()
+                    .create(API.class);
+        }
+        return mApi;
+    }
+
+    private static Gson createGSon() {
+        return new GsonBuilder()
+                .serializeNulls()
+                .setFieldNamingPolicy(FieldNamingPolicy.IDENTITY)
+                .create();
+    }
+}

+ 61 - 0
demo/src/main/java/com/sybotan/android/demo/retrofit/RetrofitLogInterceptor.java

@@ -0,0 +1,61 @@
+package com.sybotan.android.demo.retrofit;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import okhttp3.Interceptor;
+import okhttp3.MediaType;
+import okhttp3.Request;
+import okhttp3.RequestBody;
+import okhttp3.Response;
+import okio.Buffer;
+
+import static java.nio.charset.StandardCharsets.UTF_8;
+
+
+/**
+ * Created by weimo on 2020/7/13.
+ */
+public class RetrofitLogInterceptor implements Interceptor {
+    public static String TAG = "RetrofitLogInterceptor";
+
+    @Override
+    public synchronized Response intercept(Chain chain) throws IOException {
+        Request request = chain.request();
+        request = request.newBuilder()
+                .build();
+        long startTime = System.currentTimeMillis();
+        Response response = chain.proceed(request);
+        long endTime = System.currentTimeMillis();
+        long duration = endTime - startTime;
+        MediaType mediaType = null;
+        String content = "";
+        if (request.body() != null) {
+            mediaType = response.body().contentType();
+            content = response.body().string();
+            Log.e(TAG, "请求地址:| " + request.toString());
+            printParams(request.body());
+        }
+        Log.e(TAG, "请求体返回:| Response:" + content);
+        Log.e(TAG, "----------请求耗时:" + duration + "毫秒----------");
+        return response.newBuilder().body(okhttp3.ResponseBody.create(mediaType, content)).build();
+    }
+
+    private void printParams(RequestBody body) {
+        Buffer buffer = new Buffer();
+        try {
+            body.writeTo(buffer);
+            Charset charset = Charset.forName("UTF-8");
+            MediaType contentType = body.contentType();
+            if (contentType != null) {
+                charset = contentType.charset(UTF_8);
+            }
+            String params = buffer.readString(charset);
+            Log.e(TAG, "请求参数: | " + params);
+        } catch (IOException e) {
+            e.printStackTrace();
+        }
+    }
+}

+ 1 - 1
demo/src/main/java/com/sybotan/android/demo/tools/Opt.kt

@@ -33,7 +33,7 @@ object Opt {
 
 
 //    val SERVICE_PATH_IMG = "http://api.sagacloud.cn/dp-auxiliary/image-service/common/"
-    val SERVICE_PATH_IMG = "http://39.102.40.239/image-service/common/"
+    val SERVICE_PATH_IMG = "http://47.93.22.124/image-service/common/"
 //    val SERVICE_PATH_IMG = "http://api.sagacloud.cn/image-service/common/"
     //http://adm.sagacloud.cn/image-service/common/image_upload?systemId=dataPlatform&secret=9e0891a7a8c8e885&overwrite=true&key=1586081610295.jpg
     /** 上传图片服务器地址 */

+ 154 - 0
demo/src/main/java/com/sybotan/android/demo/viewmodel/BaseViewModel.java

@@ -0,0 +1,154 @@
+package com.sybotan.android.demo.viewmodel;
+
+import android.arch.lifecycle.LifecycleOwner;
+import android.arch.lifecycle.MutableLiveData;
+import android.arch.lifecycle.ViewModel;
+import android.text.TextUtils;
+
+
+import com.google.gson.Gson;
+import com.sybotan.android.demo.activities.BaseActivity;
+import com.sybotan.android.demo.bean.BaseModel;
+import com.sybotan.android.demo.bean.UniversalCellClass;
+import com.sybotan.android.demo.retrofit.BaseActionEvent;
+import com.sybotan.android.demo.retrofit.BaseViewModelInterface;
+import com.sybotan.android.demo.retrofit.IViewModelAction;
+import com.trello.rxlifecycle2.LifecycleTransformer;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+
+import io.reactivex.Observable;
+import io.reactivex.ObservableTransformer;
+import io.reactivex.Observer;
+import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.schedulers.Schedulers;
+import okhttp3.MediaType;
+import okhttp3.RequestBody;
+
+/**
+ * Created by lihao on 2020/7/9.
+ */
+public class BaseViewModel extends ViewModel implements IViewModelAction {
+    public BaseActivity mActivity;
+    private MutableLiveData<BaseActionEvent> baseActionEventLiveData;
+    protected LifecycleOwner lifecycleOwner;
+    public Gson mGSon;
+    public BaseViewModelInterface mEmitter;
+    private BaseViewModel baseViewModel;
+    public ArrayList<UniversalCellClass> data_Array = new ArrayList<>();
+
+    public BaseViewModel() {
+        baseActionEventLiveData = new MutableLiveData<>();
+    }
+
+
+    public BaseViewModel(BaseViewModelInterface object, BaseActivity activity) {
+        this.mEmitter = object;
+        this.mGSon = new Gson();
+        baseActionEventLiveData = new MutableLiveData<>();
+        mActivity = activity;
+    }
+
+    //====================================公共部分 end============================================
+    protected <S> void sendRequest(Observable<BaseModel<S>> observable,
+                                   Observer<? super BaseModel<S>> observer, boolean isNeedProgress) {////绑定生命周期,防止内存泄露
+        if (isNeedProgress) {
+            showLoading("");
+        }
+        observable.compose(compose(mActivity.<BaseModel<S>>bindToLifecycle())).subscribe(observer);
+    }
+
+    //====================================待办部分 end============================================
+    protected <S> void sendRequest(Observable<BaseModel<S>> observable,
+                                   Observer<? super BaseModel<S>> observer, boolean isNeedProgress, boolean bo) {////绑定生命周期,防止内存泄露
+        if (isNeedProgress) {
+            showLoading("");
+        }
+        observable.compose(compose(mActivity.<BaseModel<S>>bindToLifecycle(),bo)).subscribe(observer);
+    }
+
+    private <T> ObservableTransformer<T, T> compose(final LifecycleTransformer<T> lifecycle) {
+        return observable
+                -> observable
+                .subscribeOn(Schedulers.io())//改变调用它之前代码的线程,指定的就是发射事件的线程,通常用于网络,读写文件等io密集型的操作;
+                .doOnSubscribe(disposable -> {
+                    // 可添加网络连接判断等
+////                    if (!isHaveInternet()) {
+////                        mActivity.showErrorWithStatus("请检查网络设置");
+////                    }
+                })
+                .observeOn(AndroidSchedulers.mainThread())//改变调用它之后代码的线程  指定的就是订阅者接收事件的线程
+                .compose(lifecycle);
+    }
+    private <T> ObservableTransformer<T, T> compose(final LifecycleTransformer<T> lifecycle ,boolean type) {
+        return observable -> observable
+                .subscribeOn(Schedulers.io())
+                .doOnSubscribe(disposable -> {
+                })
+                .observeOn(AndroidSchedulers.mainThread())//改变调用它之后代码的线程  指定的就是订阅者接收事件的线程
+                .compose(lifecycle);
+    }
+
+    @Override
+    public void showLoading(String message) {
+        BaseActionEvent baseActionEvent = new BaseActionEvent(BaseActionEvent.SHOW_LOADING_DIALOG);
+        baseActionEvent.setMessage(message);
+        baseActionEventLiveData.setValue(baseActionEvent);
+    }
+
+    @Override
+    public void showErrorLoading(String message) {
+        BaseActionEvent baseActionEvent = new BaseActionEvent(BaseActionEvent.SHOW_ERROR_LOAD_DIALOG);
+        baseActionEvent.setMessage(message);
+        baseActionEventLiveData.setValue(baseActionEvent);
+    }
+
+    @Override
+    public void dismissLoading() {
+        baseActionEventLiveData.setValue(new BaseActionEvent(BaseActionEvent.DISMISS_LOADING_DIALOG));
+    }
+
+    @Override
+    public void showToast(String message) {
+        BaseActionEvent baseActionEvent = new BaseActionEvent(BaseActionEvent.SHOW_TOAST);
+        baseActionEvent.setMessage(message);
+        baseActionEventLiveData.setValue(baseActionEvent);
+    }
+
+    @Override
+    public MutableLiveData<BaseActionEvent> getActionLiveData() {
+        return baseActionEventLiveData;
+    }
+
+    public void setLifecycleOwner(LifecycleOwner lifecycleOwner) {
+        this.lifecycleOwner = lifecycleOwner;
+    }
+
+    /**
+     * @param requestBean
+     * @param <T>
+     * @return
+     */
+    protected <T> RequestBody getRequestBody(T requestBean) {
+        String params, contentType = "";
+        if (requestBean instanceof JSONObject) {
+            JSONObject jsonObject = (JSONObject) requestBean;
+            params = jsonObject.toString();
+        } else if (requestBean instanceof HashMap) {
+            HashMap<Object, Object> hashMap = (HashMap<Object, Object>) requestBean;
+            params = mGSon.toJson(hashMap);
+        } else {
+            params = mGSon.toJson(requestBean);
+        }
+
+        contentType = "application/json";
+
+        RequestBody body = RequestBody.create(MediaType.parse(contentType), params);
+        return body;
+    }
+
+}

+ 48 - 0
demo/src/main/java/com/sybotan/android/demo/viewmodel/GraphyVM.java

@@ -0,0 +1,48 @@
+package com.sybotan.android.demo.viewmodel;
+
+import android.util.Log;
+
+import com.sybotan.android.demo.activities.BaseActivity;
+import com.sybotan.android.demo.bean.BaseModel;
+import com.sybotan.android.demo.bean.request.ReqSpaceJob;
+import com.sybotan.android.demo.bean.result.SpaceJobModel;
+import com.sybotan.android.demo.retrofit.BaseObserver;
+import com.sybotan.android.demo.retrofit.BaseViewModelInterface;
+import com.sybotan.android.demo.retrofit.RetrofitFactory;
+
+import java.util.List;
+
+import io.reactivex.Observable;
+
+/**
+ * Created by lihao.
+ * Date: 2021/6/9
+ */
+public class GraphyVM extends BaseViewModel {
+    public static final String SPACE_JOB = "SPACE_JOB";
+
+    public GraphyVM(BaseViewModelInterface object, BaseActivity activity) {
+        super(object, activity);
+    }
+
+    public void getSpaceJob() {
+        ReqSpaceJob bean = new ReqSpaceJob();
+        bean.buildingId="Bd00022200113a69205aebc34aba87b31a2a03a572ab";
+        bean.floorId="Fl00022200115d43d88abaab46cda43f1596b5e3930b";
+        bean.jobStatus.add("01");
+        bean.jobStatus.add("02");
+        Observable<BaseModel<List<SpaceJobModel>>> observable = RetrofitFactory.getInstance().getSpaceJob(getRequestBody(bean));
+        sendRequest(observable, new BaseObserver<List<SpaceJobModel>>(mActivity,this) {
+            @Override
+            protected void onSuccess(List<SpaceJobModel> spaceJobModels) {
+                mEmitter.SendDircetive(SPACE_JOB,spaceJobModels);
+            }
+        },true);
+//        sendRequest(observable, new BaseObserver<List<SpaceJobModel>>(mActivity, this) {
+//            @Override
+//            protected void onSuccess(List<SpaceJobModel> spaceJobModels) {
+//                mEmitter.SendDircetive(SPACE_JOB, spaceJobModels);
+//            }
+//        }, true);
+    }
+}

+ 79 - 0
demo/src/main/java/com/zbar/lib/BeepManager.java

@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zbar.lib;
+
+import android.content.Context;
+import android.media.MediaPlayer;
+
+import com.sybotan.android.demo.R;
+
+
+/**
+ * Desc: 扫描滴滴声
+ * Update by znq on 2016/11/8.
+ */
+public class BeepManager implements MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener {
+    private MediaPlayer mMediaPlayer;
+    private boolean isComplete = false;
+
+    public BeepManager(Context context) {
+        mMediaPlayer = MediaPlayer.create(context, R.raw.qrcode);
+        mMediaPlayer.setLooping(false);
+        mMediaPlayer.setOnCompletionListener(this);
+        mMediaPlayer.setOnErrorListener(this);
+    }
+
+    public void startRing() {
+        if (null != mMediaPlayer) {
+            mMediaPlayer.start();
+        }
+    }
+
+
+    public void releaseRing() {
+        if (null != mMediaPlayer) {
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    public boolean isComplete() {
+        return isComplete;
+    }
+     public int getTimeDuration(){
+         return mMediaPlayer.getDuration();
+     }
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        if (null != mMediaPlayer) {
+            mMediaPlayer.stop();
+            mMediaPlayer.release();
+        }
+        isComplete = true;
+    }
+
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        if (null != mMediaPlayer) {
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+        isComplete = true;
+        return false;
+    }
+}

+ 377 - 0
demo/src/main/java/com/zbar/lib/CaptureActivity.java

@@ -0,0 +1,377 @@
+package com.zbar.lib;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.PackageManager;
+import android.graphics.Rect;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.PermissionChecker;
+import android.util.Log;
+import android.view.SurfaceHolder;
+import android.view.SurfaceView;
+import android.view.View;
+import android.view.animation.Animation;
+import android.view.animation.TranslateAnimation;
+import android.widget.ImageView;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+
+import com.sybotan.android.demo.R;
+import com.zbar.lib.camera.CameraManager;
+import com.zbar.lib.decode.MainHandler;
+
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+
+/**
+ * Desc: 1:启动一个SurfaceView作为取景预览
+ * 2:开启camera,在后台独立线程中完成扫描任务
+ * 3:对解码返回的结果进行处理.
+ * 4:释放资源
+ */
+public class CaptureActivity extends Activity implements SurfaceHolder.Callback {
+    private static final int MY_PERMISSIONS_REQUEST_CAMERA = 26;
+    public static final String EXTRA_STRING = "extra_string";
+    public static final int TYPE_BOOK_COVER = 0x101;
+    public static final int TYPE_BOOK_CHAPTER = 0x102;
+    private static final String TAG = "CaptureActivity";
+    private MainHandler mainHandler;
+    private SurfaceHolder mHolder;
+
+    private CameraManager mCameraManager;
+    private BeepManager beepManager;
+
+    private SurfaceView scanPreview;
+    private RelativeLayout scanContainer;
+    private RelativeLayout scanCropView;
+    private ImageView scanLine;
+    private Rect mCropRect = null;
+
+    private boolean isHasSurface = false;
+    private boolean isOpenCamera = false;
+
+    public Handler getHandler() {
+        return mainHandler;
+    }
+
+
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.activity_capture);
+        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        initView();
+        checkPermissionCamera();
+    }
+
+    private void initView() {
+        scanPreview = (SurfaceView) findViewById(R.id.capture_preview);
+        scanContainer = (RelativeLayout) findViewById(R.id.capture_container);
+        scanCropView = (RelativeLayout) findViewById(R.id.capture_crop_view);
+        scanLine = (ImageView) findViewById(R.id.capture_scan_line);
+        findViewById(R.id.capture_imageview_back).setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                finish();
+            }
+        });
+        isHasSurface = false;
+        beepManager = new BeepManager(this);
+
+        TranslateAnimation animation = new TranslateAnimation(Animation.RELATIVE_TO_PARENT, 0.0f, Animation
+                .RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT, 0.0f, Animation.RELATIVE_TO_PARENT,
+                0.9f);
+        animation.setDuration(3000);
+        animation.setRepeatCount(-1);
+        animation.setRepeatMode(Animation.RESTART);
+        scanLine.startAnimation(animation);
+        scanPreview.setOnClickListener(v -> {
+            mCameraManager.autoFocus();
+        });
+
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (isOpenCamera) {
+            mHolder = scanPreview.getHolder();
+            mCameraManager = new CameraManager(getApplication());
+            if (isHasSurface) {
+                initCamera(mHolder);
+            } else {
+                mHolder.addCallback(this);
+                mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
+            }
+        }
+    }
+
+    @Override
+    public void onPause() {
+        releaseCamera();
+        super.onPause();
+        if (scanLine != null) {
+            scanLine.clearAnimation();
+            scanLine.setVisibility(View.GONE);
+        }
+    }
+
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        //remove SurfaceCallback
+        if (!isHasSurface) {
+            scanPreview.getHolder().removeCallback(this);
+        }
+    }
+
+    //region 初始化和回收相关资源
+    private void initCamera(SurfaceHolder surfaceHolder) {
+        mainHandler = null;
+        try {
+            mCameraManager.openDriver(surfaceHolder);
+            if (mainHandler == null) {
+                mainHandler = new MainHandler(this, mCameraManager);
+            }
+        } catch (IOException ioe) {
+            Log.e(TAG, "相机被占用", ioe);
+        } catch (RuntimeException e) {
+            e.printStackTrace();
+            Log.e(TAG, "Unexpected error initializing camera");
+            displayFrameworkBugMessageAndExit();
+        }
+
+    }
+
+    private void releaseCamera() {
+        if (null != mainHandler) {
+            //关闭聚焦,停止预览,清空预览回调,quit子线程looper
+            mainHandler.quitSynchronously();
+            mainHandler = null;
+        }
+        //关闭声音
+        if (null != beepManager) {
+            Log.e(TAG, "releaseCamera: beepManager release");
+            beepManager.releaseRing();
+            beepManager = null;
+        }
+        //关闭相机
+        if (mCameraManager != null) {
+            mCameraManager.closeDriver();
+            mCameraManager = null;
+        }
+    }
+    //endregion
+
+    //region 检查权限
+    private void checkPermissionCamera() {
+        int checkPermission = 0;
+        if (Build.VERSION.SDK_INT >= 23) {
+            // checkPermission =ContextCompat.checkSelfPermission(this,Manifest.permission.CAMERA);
+            checkPermission = PermissionChecker.checkSelfPermission(this, Manifest.permission.CAMERA);
+            if (checkPermission != PackageManager.PERMISSION_GRANTED) {
+                ActivityCompat.requestPermissions(this,
+                        new String[]{Manifest.permission.CAMERA},
+                        MY_PERMISSIONS_REQUEST_CAMERA);
+            } else {
+                isOpenCamera = true;
+            }
+
+        } else {
+            checkPermission = checkPermission(26);
+            if (checkPermission == AppOpsManager.MODE_ALLOWED) {
+                isOpenCamera = true;
+            } else if (checkPermission == AppOpsManager.MODE_IGNORED) {
+                isOpenCamera = false;
+                displayFrameworkBugMessageAndExit();
+            }
+        }
+    }
+
+    /**
+     * 反射调用系统权限,判断权限是否打开
+     *
+     * @param permissionCode 相应的权限所对应的code
+     * @see {@link AppOpsManager }
+     */
+    private int checkPermission(int permissionCode) {
+        int checkPermission = 0;
+        if (Build.VERSION.SDK_INT >= 19) {
+            AppOpsManager _manager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+            try {
+                Class<?>[] types = new Class[]{int.class, int.class, String.class};
+                Object[] args = new Object[]{permissionCode, Binder.getCallingUid(), getPackageName()};
+                Method method = _manager.getClass().getDeclaredMethod("noteOp", types);
+                method.setAccessible(true);
+                Object _o = method.invoke(_manager, args);
+                if ((_o instanceof Integer)) {
+                    checkPermission = (Integer) _o;
+                }
+            } catch (Exception e) {
+                e.printStackTrace();
+            }
+        } else {
+            checkPermission = 0;
+        }
+        return checkPermission;
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
+        switch (requestCode) {
+            case MY_PERMISSIONS_REQUEST_CAMERA: {
+                if (grantResults.length > 0
+                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
+                    finish();
+
+                } else {
+                    finish();
+                }
+                return;
+            }
+        }
+        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
+    }
+
+    private void displayFrameworkBugMessageAndExit() {
+        String per = String.format(getString(R.string.permission), getString(R.string.camera), getString(R.string.camera));
+        AlertDialog.Builder builder = new AlertDialog.Builder(this);
+        builder.setTitle(getString(R.string.qr_name));
+        builder.setMessage(per);
+        builder.setPositiveButton(getString(R.string.i_know), new DialogInterface.OnClickListener() {
+
+            @Override
+            public void onClick(DialogInterface dialog, int which) {
+                CaptureActivity.this.finish();
+            }
+
+        });
+        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
+
+            @Override
+            public void onCancel(DialogInterface dialog) {
+                CaptureActivity.this.finish();
+            }
+        });
+        builder.show();
+    }
+    //endregion
+
+    //region 扫描结果
+    public void checkResult(final String result) {
+        if (beepManager != null) {
+            beepManager.startRing();
+        }
+        mainHandler.postDelayed(new Runnable() {
+            @Override
+            public void run() {
+                activityResult(result.trim());
+            }
+        }, beepManager.getTimeDuration());
+    }
+
+    private void activityResult(String result) {
+        if (!isFinishing()) {
+            Intent intent = new Intent();
+            Bundle bundle = new Bundle();
+            bundle.putInt("width", mCropRect.width());
+            bundle.putInt("height", mCropRect.height());
+            bundle.putString(EXTRA_STRING, result);
+            intent.putExtras(bundle);
+            setResult(RESULT_OK, intent);
+            CaptureActivity.this.finish();
+        }
+    }
+
+    //endregion
+
+    //region  初始化截取的矩形区域
+    public Rect initCrop() {
+        int cameraWidth = 0;
+        int cameraHeight = 0;
+        if (null != mCameraManager) {
+            cameraWidth = mCameraManager.getCameraResolution().y;
+            cameraHeight = mCameraManager.getCameraResolution().x;
+        }
+
+        /** 获取布局中扫描框的位置信息 */
+        int[] location = new int[2];
+        scanCropView.getLocationInWindow(location);
+
+        int cropLeft = location[0];
+        int cropTop = location[1] - getStatusBarHeight();
+
+        int cropWidth = scanCropView.getWidth();
+        int cropHeight = scanCropView.getHeight();
+
+        /** 获取布局容器的宽高 */
+        int containerWidth = scanContainer.getWidth();
+        int containerHeight = scanContainer.getHeight();
+
+        /** 计算最终截取的矩形的左上角顶点x坐标 */
+        int x = cropLeft * cameraWidth / containerWidth;
+        /** 计算最终截取的矩形的左上角顶点y坐标 */
+        int y = cropTop * cameraHeight / containerHeight;
+
+        /** 计算最终截取的矩形的宽度 */
+        int width = cropWidth * cameraWidth / containerWidth;
+        /** 计算最终截取的矩形的高度 */
+        int height = cropHeight * cameraHeight / containerHeight;
+
+        /** 生成最终的截取的矩形 */
+        mCropRect = new Rect(x, y, width + x, height + y);
+        return new Rect(x, y, width + x, height + y);
+    }
+
+    private int getStatusBarHeight() {
+        try {
+            Class<?> c = Class.forName("com.android.internal.R$dimen");
+            Object obj = c.newInstance();
+            Field field = c.getField("status_bar_height");
+            int x = Integer.parseInt(field.get(obj).toString());
+            return getResources().getDimensionPixelSize(x);
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return 0;
+    }
+    //endregion
+
+    //region SurfaceHolder Callback 回调方法
+    @Override
+    public void surfaceCreated(SurfaceHolder holder) {
+        if (holder == null) {
+            Log.e(TAG, "*** 没有添加SurfaceHolder的Callback");
+        }
+        if (isOpenCamera) {
+            if (!isHasSurface) {
+                isHasSurface = true;
+                initCamera(holder);
+            }
+        }
+
+    }
+
+    @Override
+    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
+        Log.e(TAG, "surfaceChanged: ");
+
+    }
+
+    @Override
+    public void surfaceDestroyed(SurfaceHolder holder) {
+        isHasSurface = false;
+
+    }
+    //endregion
+}

+ 54 - 0
demo/src/main/java/com/zbar/lib/Config.java

@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zbar.lib;
+
+/**
+ * The main settings activity.
+ * 
+ * @author dswitkin@google.com (Daniel Switkin)
+ * @author Sean Owen
+ */
+public final class Config {
+	public static final String KEY_DECODE_1D = "preferences_decode_1D";
+	public static final String KEY_DECODE_1D_PRODUCT = "preferences_decode_1D_product";
+	public static final String KEY_DECODE_1D_INDUSTRIAL = "preferences_decode_1D_industrial";
+	public static final String KEY_DECODE_QR = "preferences_decode_QR";
+	public static final String KEY_DECODE_DATA_MATRIX = "preferences_decode_Data_Matrix";
+	public static final String KEY_DECODE_AZTEC = "preferences_decode_Aztec";
+	public static final String KEY_DECODE_PDF417 = "preferences_decode_PDF417";
+
+	public static final String KEY_CUSTOM_PRODUCT_SEARCH = "preferences_custom_product_search";
+
+	public static final String KEY_PLAY_BEEP = "preferences_play_beep";
+	public static final String KEY_VIBRATE = "preferences_vibrate";
+	public static final String KEY_COPY_TO_CLIPBOARD = "preferences_copy_to_clipboard";
+	public static final String KEY_FRONT_LIGHT_MODE = "preferences_front_light_mode";
+	public static final String KEY_BULK_MODE = "preferences_bulk_mode";
+	public static final String KEY_REMEMBER_DUPLICATES = "preferences_remember_duplicates";
+	public static final String KEY_SUPPLEMENTAL = "preferences_supplemental";
+	public static final String KEY_AUTO_FOCUS = "preferences_auto_focus";
+	public static final String KEY_INVERT_SCAN = "preferences_invert_scan";
+	public static final String KEY_SEARCH_COUNTRY = "preferences_search_country";
+	public static final String KEY_DISABLE_AUTO_ORIENTATION = "preferences_orientation";
+
+	public static final String KEY_DISABLE_CONTINUOUS_FOCUS = "preferences_disable_continuous_focus";
+	public static final String KEY_DISABLE_EXPOSURE = "preferences_disable_exposure";
+	public static final String KEY_DISABLE_METERING = "preferences_disable_metering";
+	public static final String KEY_DISABLE_BARCODE_SCENE_MODE = "preferences_disable_barcode_scene_mode";
+	public static final String KEY_AUTO_OPEN_WEB = "preferences_auto_open_web";
+
+}

+ 102 - 0
demo/src/main/java/com/zbar/lib/decode/DecodeHandler.java

@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2010 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zbar.lib.decode;
+
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+
+import com.dtr.zbar.build.ZBarDecoder;
+import com.sybotan.android.demo.R;
+import com.zbar.lib.CaptureActivity;
+
+
+final class DecodeHandler extends Handler {
+
+    private static final String TAG = DecodeHandler.class.getSimpleName();
+
+    private final CaptureActivity activity;
+    private ZBarDecoder zBarDecoder;
+
+    private boolean running = true;
+
+    DecodeHandler(CaptureActivity activity) {
+        zBarDecoder = new ZBarDecoder();
+        this.activity = activity;
+    }
+
+    @Override
+    public void handleMessage(Message message) {
+        if (!running) {
+            return;
+        }
+        if (message.what == R.id.decode) {
+            decode((byte[]) message.obj, message.arg1, message.arg2);
+
+        } else if (message.what == R.id.quit) {
+            running = false;
+            Looper.myLooper().quit();
+        }
+    }
+
+
+    /**
+     * 解码
+     */
+
+    private void decode(byte[] data, int width, int height) {
+       // long start = System.currentTimeMillis();
+        // 这里需要将获取的data翻转一下,因为相机默认拿的的横屏的数据
+        byte[] rotatedData = new byte[data.length];
+        for (int y = 0; y < height; y++) {
+            for (int x = 0; x < width; x++)
+                rotatedData[x * height + height - y - 1] = data[x + y * width];
+        }
+
+        // 宽高也要调整
+        int tmp = width;
+        width = height;
+        height = tmp;
+        Rect mCropRect = activity.initCrop();
+        String result =null;
+        if (zBarDecoder != null) {
+            try {
+                result = zBarDecoder.decodeCrop(rotatedData, width, height, mCropRect.left, mCropRect.top, mCropRect.width(), mCropRect.height());
+            }catch (Exception ex){
+                ex.printStackTrace();
+                zBarDecoder=null;
+            }
+            Handler handler = activity.getHandler();
+            if (result != null) {
+               // long end = System.currentTimeMillis();
+                if (handler != null) {
+                    Message message = Message.obtain(handler,
+                            R.id.decode_succeeded, result);
+                    message.sendToTarget();
+                }
+            } else {
+                if (handler != null) {
+                    Message message = Message.obtain(handler, R.id.decode_failed);
+                    message.sendToTarget();
+                }
+            }
+        }
+    }
+
+
+}

+ 67 - 0
demo/src/main/java/com/zbar/lib/decode/DecodeThread.java

@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2008 ZXing authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.zbar.lib.decode;
+
+import android.os.Handler;
+import android.os.Looper;
+
+
+import com.zbar.lib.CaptureActivity;
+
+import java.util.concurrent.CountDownLatch;
+
+/**
+ * This thread does all the heavy lifting of decoding the images.
+ * 
+ * @author dswitkin@google.com (Daniel Switkin)
+ */
+ final class DecodeThread extends Thread {
+
+
+	private final CaptureActivity activity;
+
+
+	private Handler handler;
+
+	private final CountDownLatch handlerInitLatch;
+
+	DecodeThread(CaptureActivity activity) {
+		this.activity = activity;
+		handlerInitLatch = new CountDownLatch(1);
+
+
+	}
+
+	Handler getHandler() {
+		try {
+			handlerInitLatch.await();
+		}
+		catch (InterruptedException ie) {
+			ie.printStackTrace();
+		}
+		return handler;
+	}
+
+	@Override
+	public void run() {
+		Looper.prepare();
+		handler = new DecodeHandler(activity);
+		handlerInitLatch.countDown();
+		Looper.loop();
+	}
+
+}

+ 113 - 0
demo/src/main/java/com/zbar/lib/decode/MainHandler.java

@@ -0,0 +1,113 @@
+package com.zbar.lib.decode;
+
+import android.os.Handler;
+import android.os.Message;
+import android.text.TextUtils;
+
+import com.sybotan.android.demo.R;
+import com.zbar.lib.CaptureActivity;
+import com.zbar.lib.camera.CameraManager;
+
+
+/**
+ * desc:主线程的Handler
+ * Author: znq
+ * Date: 2016-11-03 15:55
+ */
+public class MainHandler extends Handler {
+    private static final String TAG = "MainHandler";
+
+    private final CaptureActivity activity;
+
+    /**
+     * 真正负责扫描任务的核心线程
+     */
+    private final DecodeThread decodeThread;
+
+    private State state;
+
+    private final CameraManager cameraManager;
+
+    public MainHandler(CaptureActivity activity, CameraManager cameraManager) {
+        this.activity = activity;
+        // 启动扫描线程
+        decodeThread = new DecodeThread(activity);
+        decodeThread.start();
+        state = State.SUCCESS;
+        this.cameraManager = cameraManager;
+
+        // 开启相机预览界面.并且自动聚焦
+        cameraManager.startPreview();
+
+        restartPreviewAndDecode();
+    }
+
+    /**
+     * 当前扫描的状态
+     */
+    private enum State {
+        /**
+         * 预览
+         */
+        PREVIEW,
+        /**
+         * 扫描成功
+         */
+        SUCCESS,
+        /**
+         * 结束扫描
+         */
+        DONE
+    }
+
+
+    @Override
+    public void handleMessage(Message msg) {
+        if (msg.what == R.id.decode_succeeded) {
+            String result = (String) msg.obj;
+            if (!TextUtils.isEmpty(result)) {
+                activity.checkResult(result);
+            }
+        } else if (msg.what == R.id.restart_preview) {
+            restartPreviewAndDecode();
+        } else if (msg.what == R.id.decode_failed) {
+            // We're decoding as fast as possible, so when one decode fails,
+            // start another.
+            state = State.PREVIEW;
+            cameraManager.requestPreviewFrame(decodeThread.getHandler(),
+                    R.id.decode);
+        }
+    }
+
+    /**
+     * 完成一次扫描后,只需要再调用此方法即可
+     */
+    private void restartPreviewAndDecode() {
+        if (state == State.SUCCESS) {
+            state = State.PREVIEW;
+            // 向decodeThread绑定的handler(DecodeHandler)发送解码消息
+            cameraManager.requestPreviewFrame(decodeThread.getHandler(),
+                    R.id.decode);
+        }
+    }
+
+    public void quitSynchronously() {
+        state = State.DONE;
+        cameraManager.stopPreview();
+        Message quit = Message.obtain(decodeThread.getHandler(), R.id.quit);
+        quit.sendToTarget();
+
+        try {
+            // Wait at most half a second; should be enough time, and onPause()
+            // will timeout quickly
+            decodeThread.join(500L);
+        } catch (InterruptedException e) {
+            // continue
+        }
+
+        // Be absolutely sure we don't send any queued up messages
+        removeMessages(R.id.decode_succeeded);
+        removeMessages(R.id.decode_failed);
+    }
+
+}

BIN
demo/src/main/res/drawable/btn_back_normal.png


BIN
demo/src/main/res/drawable/btn_back_press.png


BIN
demo/src/main/res/drawable/qr_code_bg.9.png


BIN
demo/src/main/res/drawable/scan_line.png


+ 7 - 0
demo/src/main/res/drawable/selector_capture_back.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <item android:drawable="@drawable/btn_back_press" android:state_pressed="true"></item>
+    <item android:drawable="@drawable/btn_back_normal"></item>
+
+</selector>

BIN
demo/src/main/res/drawable/shadow.png


+ 96 - 0
demo/src/main/res/layout/activity_capture.xml

@@ -0,0 +1,96 @@
+<?xml version="1.0" encoding="utf-8"?>
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <SurfaceView
+        android:id="@+id/capture_preview"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" />
+
+    <RelativeLayout
+        android:id="@+id/capture_container"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent" >
+
+        <ImageView
+            android:id="@+id/capture_mask_top"
+            android:layout_width="match_parent"
+            android:layout_height="200dp"
+            android:layout_alignParentTop="true"
+            android:background="@drawable/shadow" />
+
+        <RelativeLayout
+            android:id="@+id/capture_crop_view"
+            android:layout_width="200dp"
+            android:layout_height="200dp"
+            android:layout_centerHorizontal="true"
+            android:layout_below="@id/capture_mask_top"
+            android:background="@drawable/qr_code_bg" >
+
+            <ImageView
+                android:id="@+id/capture_scan_line"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentTop="true"
+                android:layout_marginBottom="5dp"
+                android:layout_marginTop="5dp"
+                android:visibility="visible"
+                android:src="@drawable/scan_line" />
+        </RelativeLayout>
+
+        <ImageView
+            android:id="@+id/capture_mask_bottom"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_alignParentBottom="true"
+            android:layout_below="@id/capture_crop_view"
+            android:background="@drawable/shadow" />
+
+        <ImageView
+            android:id="@+id/capture_mask_left"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_above="@id/capture_mask_bottom"
+            android:layout_alignParentLeft="true"
+            android:layout_below="@id/capture_mask_top"
+            android:layout_toLeftOf="@id/capture_crop_view"
+            android:background="@drawable/shadow" />
+
+        <ImageView
+            android:id="@+id/capture_mask_right"
+            android:layout_width="wrap_content"
+            android:layout_height="match_parent"
+            android:layout_above="@id/capture_mask_bottom"
+            android:layout_alignParentRight="true"
+            android:layout_below="@id/capture_mask_top"
+            android:layout_toRightOf="@id/capture_crop_view"
+            android:background="@drawable/shadow" />
+    </RelativeLayout>
+
+    <RelativeLayout
+        android:layout_width="fill_parent"
+        android:layout_height="50dp"
+        android:layout_gravity="top"
+        android:background="#99000000">
+
+        <ImageButton
+            android:id="@+id/capture_imageview_back"
+            android:layout_width="42dp"
+            android:layout_height="42dp"
+            android:layout_centerVertical="true"
+            android:background="@drawable/selector_capture_back"/>
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_centerInParent="true"
+            android:textColor="#ffffffff"
+            android:textSize="20sp"
+            android:text="扫一扫"/>
+
+    </RelativeLayout>
+
+
+</RelativeLayout>

+ 112 - 56
demo/src/main/res/layout/activity_graphy.xml

@@ -32,88 +32,144 @@
         android:id="@+id/graphyView"
         android:layout_width="match_parent"
         android:layout_height="0dp"
-        android:layout_weight="1"
-        android:layout_marginBottom="5dp"/>
+        android:layout_marginBottom="5dp"
+        android:layout_weight="1" />
 
-    <LinearLayout
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="5dp"
-        android:layout_marginBottom="20dp"
-        android:orientation="vertical">
-
-        <TextView
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:background="@drawable/bg_red_f76b65"
-            android:paddingLeft="15dp"
-            android:paddingTop="10dp"
-            android:paddingRight="15dp"
-            android:paddingBottom="10dp"
-            android:text="记录空间问题"
-            android:textColor="@color/white"
-            android:textSize="15sp" />
+        android:layout_height="wrap_content">
 
         <LinearLayout
-            android:layout_width="wrap_content"
+            android:id="@+id/spaceStructure"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:layout_marginTop="12dp"
-            android:orientation="horizontal">
+            android:layout_marginTop="5dp"
+            android:layout_marginBottom="20dp"
+            android:orientation="vertical">
 
             <TextView
+                android:id="@+id/addDot"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
-                android:background="@drawable/bg_blue_3bacb2"
-                android:paddingLeft="15dp"
+                android:background="@drawable/bg_red_f76b65"
+                android:paddingLeft="90dp"
                 android:paddingTop="10dp"
-                android:paddingRight="15dp"
+                android:paddingRight="90dp"
                 android:paddingBottom="10dp"
-                android:text="绑定二维码"
+                android:text="记录空间问题"
                 android:textColor="@color/white"
                 android:textSize="15sp" />
 
+            <LinearLayout
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="12dp"
+                android:orientation="horizontal">
+
+                <TextView
+                    android:id="@+id/qrcode"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:background="@drawable/bg_blue_3bacb2"
+                    android:paddingLeft="20dp"
+                    android:paddingTop="10dp"
+                    android:paddingRight="20dp"
+                    android:paddingBottom="10dp"
+                    android:text="绑定二维码"
+                    android:textColor="@color/white"
+                    android:textSize="15sp" />
+
+                <TextView
+                    android:id="@+id/nfc"
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:layout_gravity="center_horizontal"
+                    android:layout_marginLeft="20dp"
+                    android:background="@drawable/bg_blue_3bacb2"
+                    android:paddingLeft="20dp"
+                    android:paddingTop="10dp"
+                    android:paddingRight="20dp"
+                    android:paddingBottom="10dp"
+                    android:text="绑定NFC标签"
+                    android:textColor="@color/white"
+                    android:textSize="15sp" />
+            </LinearLayout>
+
+            <CheckBox
+                android:id="@+id/modelCheckBox"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginTop="22dp"
+                android:text="此空间核查完毕,模型和现场无区别" />
+
             <TextView
-                android:id="@+id/nfc"
+                android:id="@+id/modelSubmit"
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_gravity="center_horizontal"
-                android:layout_marginLeft="40dp"
+                android:layout_marginTop="12dp"
+                android:alpha="0.3"
                 android:background="@drawable/bg_blue_3bacb2"
-                android:paddingLeft="15dp"
+                android:enabled="false"
+                android:paddingLeft="100dp"
                 android:paddingTop="10dp"
-                android:paddingRight="15dp"
+                android:paddingRight="100dp"
                 android:paddingBottom="10dp"
-                android:text="绑定NFC标签"
+                android:text="核查完成"
                 android:textColor="@color/white"
                 android:textSize="15sp" />
         </LinearLayout>
 
-        <CheckBox
-            android:id="@+id/modelCheckBox"
-            android:layout_width="wrap_content"
+        <RelativeLayout
+            android:id="@+id/quesDetail"
+            android:layout_width="match_parent"
             android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:layout_marginTop="22dp"
-            android:text="此空间核查完毕,模型和现场无区别" />
+            android:paddingBottom="20dp"
+            android:visibility="gone">
 
-        <TextView
-            android:id="@+id/modelSubmit"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:layout_gravity="center_horizontal"
-            android:layout_marginTop="12dp"
-            android:alpha="0.3"
-            android:background="@drawable/bg_blue_3bacb2"
-            android:enabled="false"
-            android:paddingLeft="15dp"
-            android:paddingTop="10dp"
-            android:paddingRight="15dp"
-            android:paddingBottom="10dp"
-            android:text="核查完成"
-            android:textColor="@color/white"
-            android:textSize="15sp" />
-    </LinearLayout>
+            <ImageView
+                android:id="@+id/quesIma"
+                android:layout_width="106dp"
+                android:layout_height="106dp"
+                android:layout_marginLeft="20dp"
+                android:layout_marginTop="20dp"
+                android:scaleType="centerCrop"
+                android:src="@mipmap/structure" />
+
+            <TextView
+                android:id="@+id/quesTv"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_marginLeft="20dp"
+                android:layout_marginTop="20dp"
+                android:layout_marginRight="20dp"
+                android:layout_toRightOf="@+id/quesIma"
+                android:text="这是一段描述问题的文本,左侧是问题现场拍照图片。\nx轴、y轴坐标。\n点击下方“问题已解决”按钮后可关闭该问题点。"
+                android:textColor="@color/grey_900" />
+
+            <TextView
+                android:id="@+id/quesSubmit"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_below="@+id/quesTv"
+                android:layout_centerHorizontal="true"
+                android:layout_gravity="center_horizontal"
+                android:layout_marginLeft="50dp"
+                android:layout_marginTop="12dp"
+                android:layout_marginRight="50dp"
+                android:background="@drawable/bg_blue_3bacb2"
+                android:gravity="center"
+                android:paddingLeft="15dp"
+                android:paddingTop="10dp"
+                android:paddingRight="15dp"
+                android:paddingBottom="10dp"
+                android:text="问题已解决"
+                android:textColor="@color/white"
+                android:textSize="15sp" />
+        </RelativeLayout>
+    </RelativeLayout>
 </LinearLayout>

BIN
demo/src/main/res/mipmap-xhdpi/structure.png


BIN
demo/src/main/res/raw/qrcode.mp3


+ 26 - 0
demo/src/main/res/values/ids.xml

@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Copyright (C) 2008 ZXing authors
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<resources>
+
+    <item name="decode" type="id"/>
+    <item name="decode_failed" type="id"/>
+    <item name="decode_succeeded" type="id"/>
+    <item name="quit" type="id"/>
+    <item name="restart_preview" type="id"/>
+    <item name="return_scan_result" type="id"/>
+
+</resources>

+ 7 - 0
demo/src/main/res/values/strings.xml

@@ -23,4 +23,11 @@
 
 <resources>
     <string name="app_name">Demo</string>
+
+    <!--扫码-->
+    <string name="qr_name">二维码</string>
+    <string name="i_know">我知道了</string>
+    <string name="permission">抱歉,你的%1$s权限没有打开,无法正常服务
+        \n请点击”设置”->在应用程序中找到”popon”->权限管理->%2$s->”允许”即可 </string>
+    <string name="camera">摄像头(相机)</string>
 </resources>